size_t count, loff_t *off);
int string_to_size(u64 *size, const char *buffer, size_t count);
+int sysfs_memparse_total(const char *buffer, size_t count, u64 *val,
+ u64 total, const char *defunit);
int sysfs_memparse(const char *buffer, size_t count, u64 *val,
const char *defunit);
char *lprocfs_find_named_value(const char *buffer, const char *name,
u64 pages_number;
int rc;
char kernbuf[128], *ptr;
- bool percent = false;
ENTRY;
if (count >= sizeof(kernbuf))
if (copy_from_user(kernbuf, buffer, count))
RETURN(-EFAULT);
- if (count > 0 && kernbuf[count - 1] == '%') {
- percent = true;
- /* strip off the % */
- kernbuf[count - 1] = '\0';
- } else {
- kernbuf[count] = '\0';
- }
-
+ kernbuf[count] = '\0';
ptr = lprocfs_find_named_value(kernbuf, "max_cached_mb:", &count);
- if (percent)
- rc = sysfs_memparse(ptr, count, &value, "B");
- else
- rc = sysfs_memparse(ptr, count, &value, "MiB");
- if (rc)
- RETURN(rc);
-
- if (percent)
- pages_number = cfs_totalram_pages() * value / 100;
- else
- pages_number = value >> PAGE_SHIFT;
-
- if (pages_number < 0 || pages_number > cfs_totalram_pages()) {
+ rc = sysfs_memparse_total(ptr, count, &value,
+ cfs_totalram_pages() << PAGE_SHIFT, "MiB");
+ if (rc == -ERANGE) {
CERROR("%s: can't set max cache more than %lu MB\n",
sbi->ll_fsname,
PAGES_TO_MiB(cfs_totalram_pages()));
- RETURN(-ERANGE);
+ RETURN(rc);
}
+ if (rc)
+ RETURN(rc);
+
+ pages_number = value >> PAGE_SHIFT;
/* Allow enough cache so clients can make well-formed RPCs */
pages_number = max_t(long, pages_number, PTLRPC_MAX_BRW_PAGES);
struct lod_device *lod = dt2lod_dev(dt);
u64 val;
int rc;
- char *pct;
-
- pct = strnchr(buffer, count, '%');
- if (pct) {
- rc = string_to_size(&val, buffer, pct - buffer);
- if (rc < 0)
- return rc;
- val = mult_frac(lod->lod_lsfs_total_mb,
- min_t(unsigned int, val, 100), 100);
- } else {
- rc = sysfs_memparse(buffer, count, &val, "MiB");
- if (rc < 0)
- return rc;
- val >>= 20;
- }
+ rc = sysfs_memparse_total(buffer, count, &val,
+ lod->lod_lsfs_total_mb << 20, "MiB");
+ if (rc < 0)
+ return rc;
+
+ val >>= 20;
spin_lock(&lod->lod_lsfs_lock);
lod->lod_dom_threshold_free_mb = val;
lod_dom_stripesize_recalc(lod);
.fops = &obd_psdev_fops,
};
-#define test_string_to_size_err(value, expect, def_unit, __rc) \
+#define test_string_to_size_err_total(value, expect, total, def_unit, __rc) \
({ \
u64 __size; \
int __ret; \
\
- __ret = sysfs_memparse(value, sizeof(value) - 1, &__size, def_unit); \
+ if (total == 0) \
+ __ret = sysfs_memparse(value, sizeof(value) - 1, &__size, \
+ def_unit); \
+ else \
+ __ret = sysfs_memparse_total(value, sizeof(value) - 1, \
+ &__size, total, def_unit); \
if (__ret != __rc) { \
CERROR("string_helper: parsing '%s' expect rc %d != got %d\n", \
value, __rc, __ret); \
} \
__ret; \
})
-#define test_string_to_size_one(value, expect, def_unit) \
- test_string_to_size_err(value, expect, def_unit, 0)
+#define test_string_to_size_total(value, expect, total, def_unit) \
+ test_string_to_size_err_total(value, expect, total, def_unit, 0)
+
+#define test_string_to_size_err(value, expect, def_unit, __rc) \
+ test_string_to_size_err_total(value, expect, 0, def_unit, __rc)
+
+#define test_string_to_size_one(value, expect, def_unit) \
+ test_string_to_size_err_total(value, expect, 0, def_unit, 0)
+
static int __init obd_init_checks(void)
{
if (ret)
RETURN(ret);
+ /* percent_memparse unit handling */
+ ret = 0;
+ ret = ret ?: test_string_to_size_total("0B", 0, 1, "B");
+ ret = ret ?: test_string_to_size_total("512B", 512, 512, "B");
+ ret = ret ?: test_string_to_size_total("1.067kB", 1067, 1 << 20, "B");
+ ret = ret ?: test_string_to_size_total("1.042KiB", 1067, 1 << 20, "B");
+ ret = ret ?: test_string_to_size_total("8", 8388608, 8 << 20, "M");
+ ret = ret ?: test_string_to_size_total("65536", 65536, 1 << 20, "B");
+ ret = ret ?: test_string_to_size_total("128", 131072, 128 << 10, "K");
+ ret = ret ?: test_string_to_size_total("1M", 1048576, 1 << 20, "B");
+ ret = ret ?: test_string_to_size_total("0.5T", 549755813888ULL,
+ 1ULL << 40, "K");
+ ret = ret ?: test_string_to_size_total("256.5G", 275414777856ULL,
+ 1ULL << 40, "G");
+
+ ret = ret ?: test_string_to_size_total("50%", 50, 100, "G");
+ ret = ret ?: test_string_to_size_total("31%", 31, 100, "G");
+ ret = ret ?: test_string_to_size_total("32.2 ", 322, 1000, "%");
+ ret = ret ?: test_string_to_size_total("32.21 ", 3221, 10000, "%");
+ ret = ret ?: test_string_to_size_total("50.5%", 505, 1000, "G");
+ ret = ret ?: test_string_to_size_total("0.5%", 5, 1000, "G");
+ ret = ret ?: test_string_to_size_total(".5%", 5, 1000, "G");
+ ret = ret ?: test_string_to_size_total("0%", 0, 1000, "G");
+ ret = ret ?: test_string_to_size_total("100%", 1000, 1000, "G");
+ ret = ret ?: test_string_to_size_total("50.012345678%", 50012,
+ 100000, "G");
+ ret = ret ?: test_string_to_size_total("50.012345678%", 500123,
+ 1000000, "G");
+ ret = ret ?: test_string_to_size_total("50.012345678%", 5001234,
+ 10000000, "G");
+
+ if (ret)
+ RETURN(ret);
+
+ if (test_string_to_size_err_total("200%", 2000, 1000, "B", -ERANGE)) {
+ CERROR("string_helpers: percent values > 100 should be rejected\n");
+ ret = -EINVAL;
+ }
+ if (test_string_to_size_err_total("2K", 2048, 1024, "K", -ERANGE)) {
+ CERROR("string_helpers: size values > total should be rejected\n");
+ ret = -EINVAL;
+ }
+
/* string helper values */
ret = ret ?: test_string_to_size_one("16", 16777216, "MiB");
ret = ret ?: test_string_to_size_one("8.39MB", 8390000, "MiB");
ret = ret ?: test_string_to_size_one("16PiB", 18014398509481984ULL,
"PiB");
ret = ret ?: test_string_to_size_one("0.5EiB", 1ULL << 59, "EiB");
+ ret = ret ?: test_string_to_size_total("50%", 1ULL << 62, 1ULL << 63,
+ "%");
+ ret = ret ?: test_string_to_size_total("50%", (~0ULL) >> 1, ~0ULL, "%");
if (ret)
RETURN(ret);
}
static int __string_to_size(u64 *size, const char *buffer, size_t count,
- const char *defunit)
+ u64 total, const char *defunit)
{
u64 whole, frac, blk_size;
- u32 frac_div;
+ u32 frac_div, rem;
const char *ptr;
size_t len, unit_len;
int rc;
unit_len = count - len;
}
+ if (*ptr == '%') {
+ if (!total)
+ return -EINVAL;
+ if (whole > 100 || (whole == 100 && frac))
+ return -ERANGE;
+
+ /* *size = (total * whole + total * frac / frac_dev) / 100 */
+ rc = scale64_rem(total, 100, &whole, &rem);
+ if (rc)
+ return rc;
+ rc = scale64_rem(total, frac_div, &frac, NULL);
+ if (rc)
+ return rc;
+ frac += rem;
+ do_div(frac, 100);
+
+ *size = whole + frac;
+ if (ptr != defunit)
+ len++;
+
+ return len;
+ }
+
rc = string_to_blksize(&blk_size, ptr, unit_len);
if (rc < 0)
return rc;
return rc;
*size = whole + frac;
+ if (total && *size > total)
+ return -ERANGE;
return len;
}
*/
int string_to_size(u64 *size, const char *buffer, size_t count)
{
- return __string_to_size(size, buffer, count, NULL);
+ return __string_to_size(size, buffer, count, 0, NULL);
}
EXPORT_SYMBOL(string_to_size);
if (!count)
RETURN(-EINVAL);
- rc = __string_to_size(val, param, count, defunit);
+ rc = __string_to_size(val, param, count, 0, defunit);
return rc < 0 ? rc : 0;
}
EXPORT_SYMBOL(sysfs_memparse);
/**
+ * sysfs_memparse_total - extend the sys_memparse() function to parse
+ * percent value
+ *
+ * @buffer: kernel pointer to input string
+ * @count: number of bytes in the input @buffer
+ * @val: (output) binary value returned to caller
+ * @total: total size value to compute a percentage
+ * @defunit: default unit suffix to use if none is provided
+ *
+ * Parses a string into a number. The number stored at @buffer is
+ * potentially suffixed with K, M, G, T, P, E, %. Besides these other
+ * valid suffix units are shown in the __string_to_size() function.
+ * If the string lacks a suffix then the defunit is used. The defunit
+ * should be given as a binary unit (e.g. MiB) as that is the standard
+ * for tunables in Lustre. If no unit suffix is given (e.g. only "G"
+ * instead of "GB"), then it is assumed to be in binary units ("GiB").
+ *
+ * The function will return -ERANGE if the parsed size exceeds the
+ * @total size (> 100%).
+ *
+ * Returns: 0 on success or -errno on failure.
+ */
+int sysfs_memparse_total(const char *buffer, size_t count, u64 *val,
+ u64 total, const char *defunit)
+{
+ const char *param = buffer;
+ int rc;
+
+ count = strnlen(buffer, count);
+ if (!count)
+ RETURN(-EINVAL);
+
+ rc = __string_to_size(val, param, count, total, defunit);
+
+ return rc < 0 ? rc : 0;
+}
+EXPORT_SYMBOL(sysfs_memparse_total);
+
+/**
* Find the string \a name in the input \a buffer, and return a pointer to the
* value immediately following \a name, reducing \a count appropriately.
* If \a name is not found the original \a buffer is returned.