+ *size = 0;
+ /* 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.
+ */
+ 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 = strnstr(buffer, units_str[unit][i], count);
+ if (end) {
+ for (; i >= 0; i--)
+ blk_size *= coeff[unit];
+ len = end - buffer;
+ break;
+ }
+ } while (i--);
+
+ /* as 'B' is a substring of all units, we need to handle it
+ * separately.
+ */
+ if (!end) {
+ /* 'B' is only acceptable letter at this point */
+ end = strnchr(buffer, count, 'B');
+ if (end) {
+ len = end - buffer;
+
+ if (count - len > 2 ||
+ (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 = strnchr(buffer, count, '.');
+ if (end) {
+ /* need to limit 3 decimal places */
+ char rem[4] = "000";
+ u64 frac = 0;
+ size_t off;
+
+ len = end - buffer;
+ end++;
+
+ /* limit to 3 decimal points */
+ off = min_t(size_t, 3, strspn(end, "0123456789"));
+ /* need to limit frac_d to a u32 */
+ memcpy(rem, end, off);
+ rc = kstrtoull(rem, 10, &frac);
+ if (rc)
+ return rc;
+
+ if (fls64(frac) + fls64(blk_size) - 1 > 64)
+ return -EOVERFLOW;
+
+ frac *= blk_size;
+ do_div(frac, 1000);
+ *size += frac;
+ }
+numbers_only:
+ snprintf(kernbuf, sizeof(kernbuf), "%.*s", (int)len, buffer);
+ rc = kstrtoull(kernbuf, 10, &whole);
+ if (rc)
+ return rc;
+
+ if (whole != 0 && fls64(whole) + fls64(blk_size) - 1 > 64)
+ return -EOVERFLOW;
+
+ *size += whole * blk_size;
+
+ return count;
+}
+EXPORT_SYMBOL(string_to_size);
+
+/**
+ * sysfs_memparse - parse a ASCII string to 64-bit binary value,
+ * with optional units
+ *
+ * @buffer: kernel pointer to input string
+ * @count: number of bytes in the input @buffer
+ * @val: (output) binary value returned to caller
+ * @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. 'G'), then
+ * it is assumed to be in binary units.
+ *
+ * Returns: 0 on success or -errno on failure.
+ */
+int sysfs_memparse(const char *buffer, size_t count, u64 *val,
+ const char *defunit)
+{
+ const char *param = buffer;
+ char tmp_buf[23];
+ int rc;
+
+ count = strlen(buffer);
+ while (count > 0 && isspace(buffer[count - 1]))
+ count--;
+
+ if (!count)
+ RETURN(-EINVAL);
+
+ /* 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);
+ }
+
+ scnprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s", (int)count,
+ buffer, defunit);
+ param = tmp_buf;
+ count = strlen(param);
+ }
+
+ rc = string_to_size(val, param, count);
+
+ return rc < 0 ? rc : 0;