Modify vdev_elevator_switch() to use elevator_change()
authorBrian Behlendorf <behlendorf1@llnl.gov>
Sun, 26 Aug 2012 20:38:06 +0000 (03:38 +0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 3 Oct 2012 20:31:44 +0000 (13:31 -0700)
As of Linux 2.6.36 an elevator_change() interface was added.
This commit updates vdev_elevator_switch() to use this interface
when available, otherwise it falls back to the usermodehelper
method.

Original-patch-by: foobarz <sysop@xeon.(none)>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #906

config/kernel-elevator-change.m4 [new file with mode: 0644]
config/kernel.m4
module/zfs/vdev_disk.c

diff --git a/config/kernel-elevator-change.m4 b/config/kernel-elevator-change.m4
new file mode 100644 (file)
index 0000000..90ab51b
--- /dev/null
@@ -0,0 +1,25 @@
+dnl #
+dnl # 2.6.36 API change
+dnl # Verify the elevator_change() symbol is available.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_ELEVATOR_CHANGE], [
+       AC_MSG_CHECKING([whether elevator_change() is available])
+       tmp_flags="$EXTRA_KCFLAGS"
+       EXTRA_KCFLAGS="-Wno-unused-but-set-variable"
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/blkdev.h>
+               #include <linux/elevator.h>
+       ],[
+               int ret;
+               struct request_queue *q = NULL;
+               char *elevator = NULL;
+               ret = elevator_change(q, elevator);
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_ELEVATOR_CHANGE, 1,
+                         [elevator_change() is available])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+       EXTRA_KCFLAGS="$tmp_flags"
+])
index 262dc7f..89b0a98 100644 (file)
@@ -67,6 +67,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
        ZFS_AC_KERNEL_BDI
        ZFS_AC_KERNEL_BDI_SETUP_AND_REGISTER
        ZFS_AC_KERNEL_SET_NLINK
+       ZFS_AC_KERNEL_ELEVATOR_CHANGE
 
        AS_IF([test "$LINUX_OBJ" != "$LINUX"], [
                KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$LINUX_OBJ"
index ffb2980..c562256 100644 (file)
@@ -111,19 +111,7 @@ vdev_disk_error(zio_t *zio)
  * elevator to do the maximum front/back merging allowed by the
  * physical device.  This yields the largest possible requests for
  * the device with the lowest total overhead.
- *
- * Unfortunately we cannot directly call the elevator_switch() function
- * because it is not exported from the block layer.  This means we have
- * to use the sysfs interface and a user space upcall.  Pools will be
- * 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)
 {
@@ -131,8 +119,6 @@ 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 *argv[] = { "/bin/sh", "-c", NULL, NULL };
-       char *envp[] = { NULL };
        int error;
 
        /* Skip devices which are not whole disks (partitions) */
@@ -147,14 +133,33 @@ vdev_elevator_switch(vdev_t *v, char *elevator)
        if (!strncmp(elevator, "none", 4) && (strlen(elevator) == 4))
                return (0);
 
-       argv[2] = kmem_asprintf(SET_SCHEDULER_CMD, device, elevator);
-       error = call_usermodehelper(argv[0], argv, envp, 1);
+#ifdef HAVE_ELEVATOR_CHANGE
+       error = elevator_change(q, elevator);
+#else
+       /* For pre-2.6.36 kernels elevator_change() is not available.
+        * Therefore we fall back to using a usermodehelper to echo the
+        * elevator into sysfs;  This requires /bin/echo and sysfs to be
+        * mounted which may not be true early in the boot process.
+        */
+# define SET_SCHEDULER_CMD \
+       "exec 0</dev/null " \
+       "     1>/sys/block/%s/queue/scheduler " \
+       "     2>/dev/null; " \
+       "echo %s"
+
+       {
+               char *argv[] = { "/bin/sh", "-c", NULL, NULL };
+               char *envp[] = { NULL };
+
+               argv[2] = kmem_asprintf(SET_SCHEDULER_CMD, device, elevator);
+               error = call_usermodehelper(argv[0], argv, envp, 1);
+               strfree(argv[2]);
+       }
+#endif /* HAVE_ELEVATOR_CHANGE */
        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);
 }