Whamcloud - gitweb
LU-9091 procfs: remove old string parsing routines
[fs/lustre-release.git] / lustre / obdclass / lprocfs_status.c
index 717c272..c97936a 100644 (file)
@@ -207,103 +207,6 @@ lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
 }
 EXPORT_SYMBOL(lprocfs_add_vars);
 
-#ifndef HAVE_REMOVE_PROC_SUBTREE
-/* for b=10866, global variable */
-DECLARE_RWSEM(_lprocfs_lock);
-EXPORT_SYMBOL(_lprocfs_lock);
-
-static void lprocfs_remove_nolock(struct proc_dir_entry **proot)
-{
-       struct proc_dir_entry *root = *proot;
-       struct proc_dir_entry *temp = root;
-       struct proc_dir_entry *rm_entry;
-       struct proc_dir_entry *parent;
-
-       *proot = NULL;
-       if (!root || IS_ERR(root))
-               return;
-
-       parent = root->parent;
-       LASSERT(parent != NULL);
-
-       while (1) {
-               while (temp->subdir)
-                       temp = temp->subdir;
-
-               rm_entry = temp;
-               temp = temp->parent;
-
-               /*
-                * Memory corruption once caused this to fail, and
-                * without this LASSERT we would loop here forever.
-                */
-               LASSERTF(strlen(rm_entry->name) == rm_entry->namelen,
-                        "0x%p  %s/%s len %d\n", rm_entry, temp->name,
-                        rm_entry->name, (int)strlen(rm_entry->name));
-
-               remove_proc_entry(rm_entry->name, temp);
-               if (temp == parent)
-                       break;
-       }
-}
-
-int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
-{
-       struct proc_dir_entry *t = NULL;
-       struct proc_dir_entry **p;
-       int len, busy = 0;
-
-       LASSERT(parent != NULL);
-       len = strlen(name);
-
-       down_write(&_lprocfs_lock);
-       /* lookup target name */
-       for (p = &parent->subdir; *p; p = &(*p)->next) {
-               if ((*p)->namelen != len)
-                       continue;
-               if (memcmp(name, (*p)->name, len))
-                       continue;
-               t = *p;
-               break;
-       }
-
-       if (t) {
-               /* verify it's empty: do not count "num_refs" */
-               for (p = &t->subdir; *p; p = &(*p)->next) {
-                       if ((*p)->namelen != strlen("num_refs")) {
-                               busy = 1;
-                               break;
-                       }
-                       if (memcmp("num_refs", (*p)->name,
-                                  strlen("num_refs"))) {
-                               busy = 1;
-                               break;
-                       }
-               }
-       }
-
-       if (busy == 0)
-               lprocfs_remove_nolock(&t);
-
-       up_write(&_lprocfs_lock);
-       return 0;
-}
-#endif /* !HAVE_REMOVE_PROC_SUBTREE */
-
-#ifndef HAVE_PROC_REMOVE
-void proc_remove(struct proc_dir_entry *de)
-{
-#ifndef HAVE_REMOVE_PROC_SUBTREE
-       down_write(&_lprocfs_lock); /* search vs remove race */
-       lprocfs_remove_nolock(&de);
-       up_write(&_lprocfs_lock);
-#else
-       if (de)
-               remove_proc_subtree(de->name, de->parent);
-#endif
-}
-#endif
-
 void lprocfs_remove(struct proc_dir_entry **rooth)
 {
        proc_remove(*rooth);
@@ -318,31 +221,6 @@ void lprocfs_remove_proc_entry(const char *name, struct proc_dir_entry *parent)
 }
 EXPORT_SYMBOL(lprocfs_remove_proc_entry);
 
-struct dentry *ldebugfs_register(const char *name, struct dentry *parent,
-                                struct lprocfs_vars *list, void *data)
-{
-       struct dentry *entry;
-
-       entry = debugfs_create_dir(name, parent);
-       if (IS_ERR_OR_NULL(entry)) {
-               entry = entry ?: ERR_PTR(-ENOMEM);
-               goto out;
-       }
-
-       if (!IS_ERR_OR_NULL(list)) {
-               int rc;
-
-               rc = ldebugfs_add_vars(entry, list, data);
-               if (rc) {
-                       debugfs_remove(entry);
-                       entry = ERR_PTR(rc);
-               }
-       }
-out:
-       return entry;
-}
-EXPORT_SYMBOL_GPL(ldebugfs_register);
-
 struct proc_dir_entry *
 lprocfs_register(const char *name, struct proc_dir_entry *parent,
                 struct lprocfs_vars *list, void *data)
@@ -1221,21 +1099,9 @@ int lprocfs_obd_setup(struct obd_device *obd, bool uuid_only)
 
        if (!obd->obd_type->typ_procroot)
                debugfs_vars = obd->obd_vars;
-       obd->obd_debugfs_entry = ldebugfs_register(obd->obd_name,
-                                                  obd->obd_type->typ_debugfs_entry,
-                                                  debugfs_vars, obd);
-       if (IS_ERR_OR_NULL(obd->obd_debugfs_entry)) {
-               rc = obd->obd_debugfs_entry ? PTR_ERR(obd->obd_debugfs_entry)
-                                           : -ENOMEM;
-               CERROR("error %d setting up debugfs for %s\n",
-                      rc, obd->obd_name);
-               obd->obd_debugfs_entry = NULL;
-
-               sysfs_remove_files(&obd->obd_kset.kobj, obd->obd_attrs);
-               obd->obd_attrs = NULL;
-               kset_unregister(&obd->obd_kset);
-               return rc;
-       }
+       obd->obd_debugfs_entry = debugfs_create_dir(
+               obd->obd_name, obd->obd_type->typ_debugfs_entry);
+       ldebugfs_add_vars(obd->obd_debugfs_entry, debugfs_vars, obd);
 
        if (obd->obd_proc_entry || !obd->obd_type->typ_procroot)
                GOTO(already_registered, rc);
@@ -1361,8 +1227,7 @@ struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num,
        spin_lock_init(&stats->ls_lock);
 
        /* alloc num of counter headers */
-       LIBCFS_ALLOC(stats->ls_cnt_header,
-                    stats->ls_num * sizeof(struct lprocfs_counter_header));
+       CFS_ALLOC_PTR_ARRAY(stats->ls_cnt_header, stats->ls_num);
        if (!stats->ls_cnt_header)
                goto fail;
 
@@ -1409,8 +1274,7 @@ void lprocfs_free_stats(struct lprocfs_stats **statsh)
                if (stats->ls_percpu[i])
                        LIBCFS_FREE(stats->ls_percpu[i], percpusize);
        if (stats->ls_cnt_header)
-               LIBCFS_FREE(stats->ls_cnt_header, stats->ls_num *
-                                       sizeof(struct lprocfs_counter_header));
+               CFS_FREE_PTR_ARRAY(stats->ls_cnt_header, stats->ls_num);
        LIBCFS_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_entry]));
 }
 EXPORT_SYMBOL(lprocfs_free_stats);
@@ -1547,10 +1411,6 @@ static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
        struct seq_file *seq;
        int rc;
 
-       rc = LPROCFS_ENTRY_CHECK(inode);
-       if (rc < 0)
-               return rc;
-
        rc = seq_open(file, &lprocfs_stats_seq_sops);
        if (rc)
                return rc;
@@ -1790,7 +1650,7 @@ EXPORT_SYMBOL(lprocfs_read_helper);
  *
  *  - ``-EINVAL``: @buffer is not a proper numerical string
  *  - ``-EOVERFLOW``: results does not fit into 64 bits.
- *  - ``-E2BIG ``: @buffer is not large
+ *  - ``-E2BIG ``: @buffer is too large (not a valid number)
  */
 int string_to_size(u64 *size, const char *buffer, size_t count)
 {
@@ -1800,10 +1660,10 @@ int string_to_size(u64 *size, const char *buffer, size_t count)
         * up into block size units so we don't support ZiB or YiB.
         */
        static const char *const units_10[] = {
-               "kB", "MB", "GB", "TB", "PB", "EB"
+               "kB", "MB", "GB", "TB", "PB", "EB",
        };
        static const char *const units_2[] = {
-               "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"
+               "K",  "M",  "G",  "T",  "P",  "E",
        };
        static const char *const *const units_str[] = {
                [STRING_UNITS_2] = units_2,
@@ -1813,30 +1673,34 @@ int string_to_size(u64 *size, const char *buffer, size_t count)
                [STRING_UNITS_10] = 1000,
                [STRING_UNITS_2] = 1024,
        };
-       enum string_size_units unit;
+       enum string_size_units unit = STRING_UNITS_2;
        u64 whole, blk_size = 1;
        char kernbuf[22], *end;
        size_t len = count;
        int rc;
        int i;
 
-       if (count >= sizeof(kernbuf))
+       if (count >= sizeof(kernbuf)) {
+               CERROR("count %zd > buffer %zd\n", count, sizeof(kernbuf));
                return -E2BIG;
+       }
 
        *size = 0;
-       /* 'iB' is used for based 2 numbers. If @buffer contains only a 'B'
-        * or only numbers then we treat it as a direct number which doesn't
-        * matter if its STRING_UNITS_2 or STRING_UNIT_10.
+       /* The "iB" suffix is optionally allowed for indicating base-2 numbers.
+        * If suffix is only "B" and not "iB" then we treat it as base-10.
         */
-       unit = strstr(buffer, "iB") ? STRING_UNITS_2 : STRING_UNITS_10;
+       end = strstr(buffer, "B");
+       if (end && *(end - 1) != 'i')
+               unit = STRING_UNITS_10;
+
        i = unit == STRING_UNITS_2 ? ARRAY_SIZE(units_2) - 1 :
                                     ARRAY_SIZE(units_10) - 1;
        do {
-               end = strstr(buffer, units_str[unit][i]);
+               end = strnstr(buffer, units_str[unit][i], count);
                if (end) {
                        for (; i >= 0; i--)
                                blk_size *= coeff[unit];
-                       len -= strlen(end);
+                       len = end - buffer;
                        break;
                }
        } while (i--);
@@ -1846,19 +1710,21 @@ int string_to_size(u64 *size, const char *buffer, size_t count)
         */
        if (!end) {
                /* 'B' is only acceptable letter at this point */
-               end = strchr(buffer, 'B');
+               end = strnchr(buffer, count, 'B');
                if (end) {
-                       len -= strlen(end);
+                       len = end - buffer;
 
                        if (count - len > 2 ||
-                           (count - len == 2 && strcmp(end, "B\n") != 0))
+                           (count - len == 2 && strcmp(end, "B\n") != 0)) {
+                               CDEBUG(D_INFO, "unknown suffix '%s'\n", buffer);
                                return -EINVAL;
+                       }
                }
                /* kstrtoull will error out if it has non digits */
                goto numbers_only;
        }
 
-       end = strchr(buffer, '.');
+       end = strnchr(buffer, count, '.');
        if (end) {
                /* need to limit 3 decimal places */
                char rem[4] = "000";
@@ -1920,365 +1786,38 @@ EXPORT_SYMBOL(string_to_size);
 int sysfs_memparse(const char *buffer, size_t count, u64 *val,
                   const char *defunit)
 {
-       char param[23];
+       const char *param = buffer;
+       char tmp_buf[23];
        int rc;
 
-       if (count >= sizeof(param))
-               return -E2BIG;
-
        count = strlen(buffer);
-       if (count && buffer[count - 1] == '\n')
+       while (count > 0 && isspace(buffer[count - 1]))
                count--;
 
        if (!count)
-               return -EINVAL;
+               RETURN(-EINVAL);
 
-       if (isalpha(buffer[count - 1])) {
-               if (buffer[count - 1] != 'B') {
-                       scnprintf(param, sizeof(param), "%.*siB",
-                                 (int)count, buffer);
-               } else {
-                       memcpy(param, buffer, sizeof(param));
+       /* If there isn't already a unit on this value, append @defunit.
+        * Units of 'B' don't affect the value, so don't bother adding.
+        */
+       if (!isalpha(buffer[count - 1]) && defunit[0] != 'B') {
+               if (count + 3 >= sizeof(tmp_buf)) {
+                       CERROR("count %zd > size %zd\n", count, sizeof(param));
+                       RETURN(-E2BIG);
                }
-       } else {
-               scnprintf(param, sizeof(param), "%.*s%s", (int)count,
+
+               scnprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s", (int)count,
                          buffer, defunit);
+               param = tmp_buf;
+               count = strlen(param);
        }
 
-       rc = string_to_size(val, param, strlen(param));
+       rc = string_to_size(val, param, count);
+
        return rc < 0 ? rc : 0;
 }
 EXPORT_SYMBOL(sysfs_memparse);
 
-/* Obtains the conversion factor for the unit specified */
-static int get_mult(char unit, __u64 *mult)
-{
-       __u64 units = 1;
-
-       switch (unit) {
-       /* peta, tera, giga, mega, and kilo */
-       case 'p':
-       case 'P':
-               units <<= 10;
-               /* fallthrough */
-       case 't':
-       case 'T':
-               units <<= 10;
-               /* fallthrough */
-       case 'g':
-       case 'G':
-               units <<= 10;
-               /* fallthrough */
-       case 'm':
-       case 'M':
-               units <<= 10;
-               /* fallthrough */
-       case 'k':
-       case 'K':
-               units <<= 10;
-               break;
-       /* some tests expect % to be accepted */
-       case '%':
-               units = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       *mult = units;
-
-       return 0;
-}
-
-/*
- * Ensures the numeric string is valid. The function provides the final
- * multiplier in the case a unit exists at the end of the string. It also
- * locates the start of the whole and fractional parts (if any). This
- * function modifies the string so kstrtoull can be used to parse both
- * the whole and fraction portions. This function also figures out
- * the base of the number.
- */
-static int preprocess_numeric_str(char *buffer, __u64 *mult, __u64 def_mult,
-                                 bool allow_units, char **whole, char **frac,
-                                 unsigned int *base)
-{
-       bool hit_decimal = false;
-       bool hit_unit = false;
-       int rc = 0;
-       char *start;
-       *mult = def_mult;
-       *whole = NULL;
-       *frac = NULL;
-       *base = 10;
-
-       /* a hex string if it starts with "0x" */
-       if (buffer[0] == '0' && tolower(buffer[1]) == 'x') {
-               *base = 16;
-               buffer += 2;
-       }
-
-       start = buffer;
-
-       while (*buffer) {
-               /* allow for a single new line before the null terminator */
-               if (*buffer == '\n') {
-                       *buffer = '\0';
-                       buffer++;
-
-                       if (*buffer)
-                               return -EINVAL;
-
-                       break;
-               }
-
-               /* any chars after our unit indicates a malformed string */
-               if (hit_unit)
-                       return -EINVAL;
-
-               /* ensure we only hit one decimal */
-               if (*buffer == '.') {
-                       if (hit_decimal)
-                               return -EINVAL;
-
-                       /* if past start, there's a whole part */
-                       if (start != buffer)
-                               *whole = start;
-
-                       *buffer = '\0';
-                       start = buffer + 1;
-                       hit_decimal = true;
-               } else if (!isdigit(*buffer) &&
-                          !(*base == 16 && isxdigit(*buffer))) {
-                       if (allow_units) {
-                               /* if we allow units, attempt to get mult */
-                               hit_unit = true;
-                               rc = get_mult(*buffer, mult);
-                               if (rc)
-                                       return rc;
-
-                               /* string stops here, but keep processing */
-                               *buffer = '\0';
-                       } else {
-                               /* bad string */
-                               return -EINVAL;
-                       }
-               }
-
-               buffer++;
-       }
-
-       if (hit_decimal) {
-               /* hit a decimal, make sure there's a fractional part */
-               if (!*start)
-                       return -EINVAL;
-
-               *frac = start;
-       } else {
-               /* didn't hit a decimal, but may have a whole part */
-               if (start != buffer && *start)
-                       *whole = start;
-       }
-
-       /* malformed string if we didn't get anything */
-       if (!*frac && !*whole)
-               return -EINVAL;
-
-       return 0;
-}
-
-/*
- * Parses a numeric string which can contain a whole and fraction portion
- * into a __u64. Accepts a multiplier to apply to the value parsed. Also
- * allows the string to have a unit at the end. The function handles
- * wrapping of the final unsigned value.
- */
-static int str_to_u64_parse(char *buffer, unsigned long count,
-                           __u64 *val, __u64 def_mult, bool allow_units)
-{
-       __u64 whole = 0;
-       __u64 frac = 0;
-       unsigned int frac_d = 1;
-       __u64 wrap_indicator = ULLONG_MAX;
-       int rc = 0;
-       __u64 mult;
-       char *strwhole;
-       char *strfrac;
-       unsigned int base = 10;
-
-       rc = preprocess_numeric_str(buffer, &mult, def_mult, allow_units,
-                                   &strwhole, &strfrac, &base);
-
-       if (rc)
-               return rc;
-
-       if (mult == 0) {
-               *val = 0;
-               return 0;
-       }
-
-       /* the multiplier limits how large the value can be */
-       wrap_indicator = div64_u64(wrap_indicator, mult);
-
-       if (strwhole) {
-               rc = kstrtoull(strwhole, base, &whole);
-               if (rc)
-                       return rc;
-
-               if (whole > wrap_indicator)
-                       return -ERANGE;
-
-               whole *= mult;
-       }
-
-       if (strfrac) {
-               if (strlen(strfrac) > 10)
-                       strfrac[10] = '\0';
-
-               rc = kstrtoull(strfrac, base, &frac);
-               if (rc)
-                       return rc;
-
-               /* determine power of fractional portion */
-               while (*strfrac) {
-                       frac_d *= base;
-                       strfrac++;
-               }
-
-               /* fractional portion is too large to perform calculation */
-               if (frac > wrap_indicator)
-                       return -ERANGE;
-
-               frac *= mult;
-               do_div(frac, frac_d);
-       }
-
-       /* check that the sum of whole and fraction fits in u64 */
-       if (whole > (ULLONG_MAX - frac))
-               return -ERANGE;
-
-       *val = whole + frac;
-
-       return 0;
-}
-
-/*
- * This function parses numeric/hex strings into __s64. It accepts a multiplier
- * which will apply to the value parsed. It also can allow the string to
- * have a unit as the last character. The function handles overflow/underflow
- * of the signed integer.
- */
-int lu_str_to_s64(char *buffer, unsigned long count, __s64 *val, char defunit)
-{
-       __u64 mult = 1;
-       __u64 tmp;
-       unsigned int offset = 0;
-       int signed sign = 1;
-       __u64 max = LLONG_MAX;
-       int rc = 0;
-
-       if (defunit != '1') {
-               rc = get_mult(defunit, &mult);
-               if (rc)
-                       return rc;
-       }
-
-       /* keep track of our sign */
-       if (*buffer == '-') {
-               sign = -1;
-               offset++;
-               /* equivalent to max = -LLONG_MIN, avoids overflow */
-               max++;
-       }
-
-       rc = str_to_u64_parse(buffer + offset, count - offset,
-                             &tmp, mult, true);
-       if (rc)
-               return rc;
-
-       /* check for overflow/underflow */
-       if (max < tmp)
-               return -ERANGE;
-
-       *val = (__s64)tmp * sign;
-
-       return 0;
-}
-EXPORT_SYMBOL(lu_str_to_s64);
-
-/* identical to s64 version, but does not handle overflow */
-static int str_to_u64_internal(const char __user *buffer, unsigned long count,
-                              __u64 *val, __u64 def_mult, bool allow_units)
-{
-       char kernbuf[22];
-       unsigned int offset = 0;
-       int rc = 0;
-
-       if (count > (sizeof(kernbuf) - 1))
-               return -EINVAL;
-
-       if (copy_from_user(kernbuf, buffer, count))
-               return -EFAULT;
-
-       kernbuf[count] = '\0';
-
-       rc = str_to_u64_parse(kernbuf + offset, count - offset,
-                             val, def_mult, allow_units);
-       if (rc)
-               return rc;
-
-       return 0;
-}
-/**
- * Convert a user string into a signed 64 bit number. This function produces
- * an error when the value parsed from the string times multiplier underflows or
- * overflows. This function only accepts strings that contains digits, an
- * optional decimal, and a char representing a unit at the end. If a unit is
- * specified in the string, the multiplier provided by the caller is ignored.
- * This function can also accept hexadecimal strings which are prefixed with
- * "0x".
- *
- * \param[in] buffer   string consisting of numbers, a decimal, and a unit
- * \param[in] count    buffer length
- * \param[in] val      if successful, the value represented by the string
- * \param[in] defunit  default unit if string doesn't contain one
- *
- * \retval             0 on success
- * \retval             negative number on error
- */
-int lprocfs_str_with_units_to_s64(const char __user *buffer,
-                                 unsigned long count, __s64 *val, char defunit)
-{
-       char kernbuf[22];
-
-       if (count > (sizeof(kernbuf) - 1))
-               return -EINVAL;
-
-       if (copy_from_user(kernbuf, buffer, count))
-               return -EFAULT;
-
-       kernbuf[count] = '\0';
-
-       return lu_str_to_s64(kernbuf, count, val, defunit);
-}
-EXPORT_SYMBOL(lprocfs_str_with_units_to_s64);
-
-/* identical to s64 version above, but does not handle overflow */
-int lprocfs_str_with_units_to_u64(const char __user *buffer,
-                                 unsigned long count, __u64 *val, char defunit)
-{
-       __u64 mult = 1;
-       int rc;
-
-       if (defunit != '1') {
-               rc = get_mult(defunit, &mult);
-               if (rc)
-                       return rc;
-       }
-
-       return str_to_u64_internal(buffer, count, val, mult, true);
-}
-EXPORT_SYMBOL(lprocfs_str_with_units_to_u64);
-
 char *lprocfs_strnstr(const char *s1, const char *s2, size_t len)
 {
        size_t l2;
@@ -2363,14 +1902,14 @@ int lprocfs_seq_create(struct proc_dir_entry *parent,
 }
 EXPORT_SYMBOL(lprocfs_seq_create);
 
-int lprocfs_obd_seq_create(struct obd_device *dev,
+int lprocfs_obd_seq_create(struct obd_device *obd,
                           const char *name,
                           mode_t mode,
                           const struct file_operations *seq_fops,
                           void *data)
 {
-        return (lprocfs_seq_create(dev->obd_proc_entry, name,
-                                   mode, seq_fops, data));
+       return lprocfs_seq_create(obd->obd_proc_entry, name,
+                                 mode, seq_fops, data);
 }
 EXPORT_SYMBOL(lprocfs_obd_seq_create);
 
@@ -2441,8 +1980,8 @@ EXPORT_SYMBOL_GPL(lustre_sysfs_ops);
 
 int lprocfs_obd_max_pages_per_rpc_seq_show(struct seq_file *m, void *data)
 {
-       struct obd_device *dev = data;
-       struct client_obd *cli = &dev->u.cli;
+       struct obd_device *obd = data;
+       struct client_obd *cli = &obd->u.cli;
 
        spin_lock(&cli->cl_loi_list_lock);
        seq_printf(m, "%d\n", cli->cl_max_pages_per_rpc);
@@ -2456,24 +1995,31 @@ ssize_t lprocfs_obd_max_pages_per_rpc_seq_write(struct file *file,
                                                size_t count, loff_t *off)
 {
        struct seq_file *m = file->private_data;
-       struct obd_device *dev = m->private;
-       struct client_obd *cli = &dev->u.cli;
+       struct obd_device *obd = m->private;
+       struct client_obd *cli = &obd->u.cli;
        struct obd_import *imp;
        struct obd_connect_data *ocd;
        int chunk_mask, rc;
-       s64 val;
+       char kernbuf[22];
+       u64 val;
+
+       if (count > sizeof(kernbuf) - 1)
+               return -EINVAL;
+
+       if (copy_from_user(kernbuf, buffer, count))
+               return -EFAULT;
 
-       rc = lprocfs_str_with_units_to_s64(buffer, count, &val, '1');
+       kernbuf[count] = '\0';
+
+       rc = sysfs_memparse(kernbuf, count, &val, "B");
        if (rc)
                return rc;
-       if (val < 0)
-               return -ERANGE;
 
        /* if the max_pages is specified in bytes, convert to pages */
        if (val >= ONE_MB_BRW_SIZE)
                val >>= PAGE_SHIFT;
 
-       with_imp_locked(dev, imp, rc) {
+       with_imp_locked(obd, imp, rc) {
                ocd = &imp->imp_connect_data;
                chunk_mask = ~((1 << (cli->cl_chunkbits - PAGE_SHIFT)) - 1);
                /* max_pages_per_rpc must be chunk aligned */
@@ -2496,9 +2042,9 @@ EXPORT_SYMBOL(lprocfs_obd_max_pages_per_rpc_seq_write);
 ssize_t short_io_bytes_show(struct kobject *kobj, struct attribute *attr,
                            char *buf)
 {
-       struct obd_device *dev = container_of(kobj, struct obd_device,
+       struct obd_device *obd = container_of(kobj, struct obd_device,
                                              obd_kset.kobj);
-       struct client_obd *cli = &dev->u.cli;
+       struct client_obd *cli = &obd->u.cli;
        int rc;
 
        spin_lock(&cli->cl_loi_list_lock);
@@ -2514,24 +2060,19 @@ EXPORT_SYMBOL(short_io_bytes_show);
 ssize_t short_io_bytes_store(struct kobject *kobj, struct attribute *attr,
                             const char *buffer, size_t count)
 {
-       struct obd_device *dev = container_of(kobj, struct obd_device,
+       struct obd_device *obd = container_of(kobj, struct obd_device,
                                              obd_kset.kobj);
-       struct client_obd *cli = &dev->u.cli;
-       char kernbuf[32];
-       s64 val;
+       struct client_obd *cli = &obd->u.cli;
+       u64 val;
        int rc;
 
-       if (count >= sizeof(kernbuf))
-               return -EINVAL;
-
-       memcpy(kernbuf, buffer, count);
-       kernbuf[count] = '\0';
-       rc = lu_str_to_s64(kernbuf, count, &val, '1');
-       if (rc)
-               GOTO(out, rc);
-
-       if (val == -1)
+       if (strcmp(buffer, "-1") == 0) {
                val = OBD_DEF_SHORT_IO_BYTES;
+       } else {
+               rc = sysfs_memparse(buffer, count, &val, "B");
+               if (rc)
+                       GOTO(out, rc);
+       }
 
        if (val && (val < MIN_SHORT_IO_BYTES || val > LNET_MTU))
                GOTO(out, rc = -ERANGE);