diff options
-rw-r--r-- | config/kernel-elevator-change.m4 | 25 | ||||
-rw-r--r-- | config/kernel.m4 | 1 | ||||
-rw-r--r-- | module/zfs/vdev_disk.c | 41 |
3 files changed, 49 insertions, 18 deletions
diff --git a/config/kernel-elevator-change.m4 b/config/kernel-elevator-change.m4 new file mode 100644 index 000000000..90ab51b2e --- /dev/null +++ b/config/kernel-elevator-change.m4 @@ -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" +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 262dc7fcb..89b0a9806 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -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" diff --git a/module/zfs/vdev_disk.c b/module/zfs/vdev_disk.c index ffb2980d2..c56225693 100644 --- a/module/zfs/vdev_disk.c +++ b/module/zfs/vdev_disk.c @@ -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); } |