@@ -6190,6 +6190,9 @@
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
scanned for Logical Units (default 1).
+ The delay can have up to 3 decimal places, giving a
+ resolution of one millisecond.
+ Example: delay_use=2.567
usb-storage.quirks=
[UMS] A list of quirks entries to supplement or
@@ -67,9 +67,119 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
-static unsigned int delay_use = 1;
-module_param(delay_use, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+static unsigned int delay_use = 1 * MSEC_PER_SEC;
+
+/**
+ * str_to_fixed_point_uint - parse an unsigned fixed-point decimal integer
+ * @str: String to parse.
+ * @ndecimals: Number of decimal places in the fixed-point value.
+ * @val: Where to store the parsed value.
+ *
+ * Parse an unsigned fixed-point decimal value in @str, containing at
+ * most ndecimal digits to the right of the decimal point.
+ * Stores the parsed value in @val, scaled by 10^(@ndecimal).
+ *
+ * As with kstrtouint(), the string must be NUL-terminated and may
+ * include a single newline before the terminating NUL. The first
+ * character may be a plus sign but not a minus sign. The decimal
+ * point and fractional digits are optional.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int str_to_fixed_point_uint(const char *str, int ndecimals,
+ unsigned int *val)
+{
+ int n, n1, n2;
+ const char *p;
+ char *q;
+ char buf[16];
+
+ n = strlen(str);
+ if (n > 0 && str[n - 1] == '\n')
+ --n;
+
+ p = strnchr(str, n, '.');
+ if (p) {
+ n1 = p++ - str;
+ n2 = n - (n1 + 1);
+ if (n2 > ndecimals)
+ return -EINVAL;
+ } else {
+ n1 = n;
+ n2 = 0;
+ }
+ if (n1 + n2 == 0 || n1 + ndecimals > sizeof(buf) - 1)
+ return -EINVAL;
+
+ memcpy(buf, str, n1);
+ if (p)
+ memcpy(buf + n1, p, n2);
+ for (q = buf + n1 + n2; n2 < ndecimals; ++n2)
+ *q++ = '0';
+ *q = 0;
+
+ return kstrtouint(buf, 10, val);
+}
+
+/**
+ * fixed_point_uint_to_str - format a fixed-point decimal value into a string
+ * @val: The integer value to format, scaled by 10^(@ndecimals).
+ * @ndecimals: Number of decimal places in the fixed-point value.
+ * @str: Where to store the formatted string.
+ * @size: The size of buffer for @str.
+ *
+ * Format a fixed-point decimal value in @val scaled by 10^(@ndecimals)
+ * into a string in @str where to store the formatted string.
+ * The string trailing fractional part '0' is trimmed.
+ *
+ * Returns the number of characters written into @str.
+ */
+static int fixed_point_uint_to_str(unsigned int val, int ndecimals,
+ char *str, int size)
+{
+ unsigned int delay_ms = val;
+ unsigned int rem = do_div(delay_ms, int_pow(10, ndecimals));
+ int len;
+ char buf[16];
+
+ len = scnprintf(buf, sizeof(buf), "%d", delay_ms);
+ if (rem) {
+ char format[8];
+
+ snprintf(format, sizeof(format) - 1, ".%%0%dd", ndecimals);
+ len += scnprintf(buf + len, sizeof(buf) - len, format, rem);
+ while (buf[--len] == '0')
+ buf[len] = '\0';
+ }
+ return scnprintf(str, size, "%s\n", buf);
+}
+
+static int delay_use_set(const char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms;
+ int ret;
+
+ ret = str_to_fixed_point_uint(skip_spaces(s), 3, &delay_ms);
+ if (ret < 0)
+ return ret;
+
+ *((unsigned int *)kp->arg) = delay_ms;
+ return 0;
+}
+
+static int delay_use_get(char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms = *((unsigned int *)kp->arg);
+
+ return fixed_point_uint_to_str(delay_ms, 3, s, PAGE_SIZE);
+}
+
+static const struct kernel_param_ops delay_use_ops = {
+ .set = delay_use_set,
+ .get = delay_use_get,
+};
+module_param_cb(delay_use, &delay_use_ops, &delay_use, 0644);
+MODULE_PARM_DESC(delay_use, "time to delay before using a new device");
static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
@@ -1066,7 +1176,7 @@ int usb_stor_probe2(struct us_data *us)
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
- delay_use * HZ);
+ msecs_to_jiffies(delay_use));
return 0;
/* We come here if there are any problems */
Current storage scan delay is reduced by the following old commit. a4a47bc03fe5 ("Lower USB storage settling delay to something more reasonable") It means that delay is at least 'one second', or zero with delay_use=0. 'one second' is still long delay especially for embedded system but when delay_use is set to 0 (no delay), still error observed on some USB drives. So delay_use should not be set to 0 but 'one second' is quite long. Especially for embedded system, it's important for end user how quickly access to USB drive when it's connected. That's why we have a chance to minimize such a constant long delay. This patch optimizes scan delay more precisely to minimize delay time but not to have any problems on USB drives by extending module parameter 'delay_use' in milliseconds internally. The parameter 'delay_use' is changed to be parsed as 3 decimal point value if it has digit values with '.'. It makes the range of value to 1 / 1000 in internal 32-bit value but it's still enough to set the delay time. By default, delay time is 'one second' for backward compatibility. For example, it seems to be good by changing delay_use=0.1, that is 100 millisecond delay without issues for most USB pen drives. Signed-off-by: Norihiko Hama <Norihiko.Hama@alpsalpine.com> --- V4 -> V5: Simplify parser/formatter code and fix documentaion V3 -> V4: Separate parser functions from module parameter set/get V2 -> V3: Change to use kstrtouint only for parsing decimal point V1 -> V2: Extend existing module parameter 'delay_use' to support decimal points .../admin-guide/kernel-parameters.txt | 3 + drivers/usb/storage/usb.c | 118 +++++++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-)