Fix spurious -EFAULT when setting I/O scheduler
authorBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 22 Apr 2011 20:50:17 +0000 (13:50 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 22 Apr 2011 21:55:35 +0000 (14:55 -0700)
Occasionally we would see an -EFAULT returned when setting the
I/O scheduler on a vdev.  This was caused an improperly formatted
user mode helper command.

This commit restructures the command to something simpler, allocates
space for it dynamically to save stack, and removes the retry logic
which is no longer needed.

Closes #169

module/zfs/vdev_disk.c

index ec627a9..5cb3fe2 100644 (file)
@@ -118,6 +118,12 @@ vdev_disk_error(zio_t *zio)
  * automatically imported on module load so we must do this at device
  * open time from the kernel.
  */
+#define SET_SCHEDULER_CMD \
+       "exec 0</dev/null " \
+       "     1>/sys/block/%s/queue/scheduler " \
+       "     2>/dev/null; " \
+       "echo %s"
+
 static int
 vdev_elevator_switch(vdev_t *v, char *elevator)
 {
@@ -125,11 +131,9 @@ vdev_elevator_switch(vdev_t *v, char *elevator)
        struct block_device *bdev = vd->vd_bdev;
        struct request_queue *q = bdev_get_queue(bdev);
        char *device = bdev->bd_disk->disk_name;
-       char sh_path[] = "/bin/sh";
-       char sh_cmd[128];
-       char *argv[] = { sh_path, "-c", sh_cmd };
+       char *argv[] = { "/bin/sh", "-c", NULL, NULL };
        char *envp[] = { NULL };
-       int count = 0, error;
+       int error;
 
        /* Skip devices which are not whole disks (partitions) */
        if (!v->vdev_wholedisk)
@@ -143,23 +147,14 @@ vdev_elevator_switch(vdev_t *v, char *elevator)
        if (!strncmp(elevator, "none", 4) && (strlen(elevator) == 4))
                return (0);
 
-       /*
-        * Set the desired scheduler with a three attempt retry for
-        * -EFAULT which has been observed to occur spuriously.
-        */
-       sprintf(sh_cmd, "%s \"%s\" >/sys/block/%s/queue/scheduler",
-           "/bin/echo", elevator, device);
-
-       while (++count <= 3) {
-               error = call_usermodehelper(sh_path, argv, envp, 1);
-               if ((error == 0) || (error != -EFAULT))
-                      break;
-       }
-
+       argv[2] = kmem_asprintf(SET_SCHEDULER_CMD, device, elevator);
+       error = call_usermodehelper(argv[0], argv, envp, 1);
        if (error)
                printk("ZFS: Unable to set \"%s\" scheduler for %s (%s): %d\n",
                       elevator, v->vdev_path, device, error);
 
+       strfree(argv[2]);
+
        return (error);
 }