Whamcloud - gitweb
LU-18163 obdclass: add sysfs_memparse_total() string helper 34/56334/3
authorEtienne AUJAMES <etienne.aujames@cea.fr>
Wed, 11 Sep 2024 18:31:37 +0000 (20:31 +0200)
committerOleg Drokin <green@whamcloud.com>
Mon, 16 Dec 2024 08:12:13 +0000 (08:12 +0000)
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 <eaujames@ddn.com>
Change-Id: Ic9390f2dbb64f0c2ca8756e78ba4100346e08f3f
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56334
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Neil Brown <neilb@suse.de>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lprocfs_status.h
lustre/llite/lproc_llite.c
lustre/lod/lproc_lod.c
lustre/obdclass/class_obd.c
lustre/obdclass/lprocfs_status.c

index a7a9e9f..356f558 100644 (file)
@@ -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,
index 88695b3..82da954 100644 (file)
@@ -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);
 
index d295e34..0a8fb0f 100644 (file)
@@ -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);
index 5e11c8d..8e2f543 100644 (file)
@@ -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);
 
index 5a22fc1..6b90f7f 100644 (file)
@@ -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.