From: Etienne AUJAMES Date: Wed, 11 Sep 2024 18:31:37 +0000 (+0200) Subject: LU-18163 obdclass: add sysfs_memparse_total() string helper X-Git-Tag: 2.16.51~68 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=refs%2Fchanges%2F34%2F56334%2F3;p=fs%2Flustre-release.git LU-18163 obdclass: add sysfs_memparse_total() string helper Add sysfs_memparse_total() to parse '%' unit. The size returned is computed based on a total size argument. If the parsed size exceeds the total value (> 100% of total size), the function returns -ERANGE. The following string format are accepted: "1.0%", "0.1%", ".1%" It adds some unit tests for sysfs_memparse_total() in obd_init_checks(). Signed-off-by: Etienne AUJAMES Change-Id: Ic9390f2dbb64f0c2ca8756e78ba4100346e08f3f Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56334 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Neil Brown Reviewed-by: James Simmons Reviewed-by: Oleg Drokin --- diff --git a/lustre/include/lprocfs_status.h b/lustre/include/lprocfs_status.h index a7a9e9f..356f558 100644 --- a/lustre/include/lprocfs_status.h +++ b/lustre/include/lprocfs_status.h @@ -628,6 +628,8 @@ lprocfs_pinger_recov_seq_write(struct file *file, const char __user *buffer, 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, diff --git a/lustre/llite/lproc_llite.c b/lustre/llite/lproc_llite.c index 88695b3..82da954 100644 --- a/lustre/llite/lproc_llite.c +++ b/lustre/llite/lproc_llite.c @@ -507,7 +507,6 @@ static ssize_t ll_max_cached_mb_seq_write(struct file *file, u64 pages_number; int rc; char kernbuf[128], *ptr; - bool percent = false; ENTRY; if (count >= sizeof(kernbuf)) @@ -516,33 +515,20 @@ static ssize_t ll_max_cached_mb_seq_write(struct file *file, 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); diff --git a/lustre/lod/lproc_lod.c b/lustre/lod/lproc_lod.c index d295e34..0a8fb0f 100644 --- a/lustre/lod/lproc_lod.c +++ b/lustre/lod/lproc_lod.c @@ -162,22 +162,13 @@ static ssize_t dom_threshold_free_mb_store(struct kobject *kobj, 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); diff --git a/lustre/obdclass/class_obd.c b/lustre/obdclass/class_obd.c index 5e11c8d..8e2f543 100644 --- a/lustre/obdclass/class_obd.c +++ b/lustre/obdclass/class_obd.c @@ -548,12 +548,17 @@ static struct miscdevice obd_psdev = { .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); \ @@ -567,8 +572,15 @@ static struct miscdevice obd_psdev = { } \ __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) { @@ -703,6 +715,49 @@ 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"); @@ -721,6 +776,9 @@ static int __init obd_init_checks(void) 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); diff --git a/lustre/obdclass/lprocfs_status.c b/lustre/obdclass/lprocfs_status.c index 5a22fc1..6b90f7f 100644 --- a/lustre/obdclass/lprocfs_status.c +++ b/lustre/obdclass/lprocfs_status.c @@ -2060,10 +2060,10 @@ static int scale64_rem(u64 mult, u32 div, u64 *base, u32 *remp) } 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; @@ -2087,6 +2087,29 @@ static int __string_to_size(u64 *size, const char *buffer, size_t count, 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; @@ -2111,6 +2134,8 @@ static int __string_to_size(u64 *size, const char *buffer, size_t count, return rc; *size = whole + frac; + if (total && *size > total) + return -ERANGE; return len; } @@ -2138,7 +2163,7 @@ static int __string_to_size(u64 *size, const char *buffer, size_t count, */ 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); @@ -2171,13 +2196,52 @@ int sysfs_memparse(const char *buffer, size_t count, u64 *val, 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.