summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/zfs/vdev_disk.c71
1 files changed, 53 insertions, 18 deletions
diff --git a/module/zfs/vdev_disk.c b/module/zfs/vdev_disk.c
index f671d079d..c6bb24a33 100644
--- a/module/zfs/vdev_disk.c
+++ b/module/zfs/vdev_disk.c
@@ -27,13 +27,14 @@
*/
#include <sys/zfs_context.h>
-#include <sys/spa.h>
+#include <sys/spa_impl.h>
#include <sys/vdev_disk.h>
#include <sys/vdev_impl.h>
#include <sys/abd.h>
#include <sys/fs/zfs.h>
#include <sys/zio.h>
#include <sys/sunldi.h>
+#include <linux/mod_compat.h>
char *zfs_vdev_scheduler = VDEV_SCHEDULER;
static void *zfs_vdev_holder = VDEV_HOLDER;
@@ -113,15 +114,23 @@ vdev_disk_error(zio_t *zio)
* physical device. This yields the largest possible requests for
* the device with the lowest total overhead.
*/
-static int
+static void
vdev_elevator_switch(vdev_t *v, char *elevator)
{
vdev_disk_t *vd = v->vdev_tsd;
- struct block_device *bdev = vd->vd_bdev;
- struct request_queue *q = bdev_get_queue(bdev);
- char *device = bdev->bd_disk->disk_name;
+ struct request_queue *q;
+ char *device;
int error;
+ for (int c = 0; c < v->vdev_children; c++)
+ vdev_elevator_switch(v->vdev_child[c], elevator);
+
+ if (!v->vdev_ops->vdev_op_leaf || vd->vd_bdev == NULL)
+ return;
+
+ q = bdev_get_queue(vd->vd_bdev);
+ device = vd->vd_bdev->bd_disk->disk_name;
+
/*
* Skip devices which are not whole disks (partitions).
* Device-mapper devices are excepted since they may be whole
@@ -131,15 +140,15 @@ vdev_elevator_switch(vdev_t *v, char *elevator)
* "Skip devices without schedulers" check below will fail.
*/
if (!v->vdev_wholedisk && strncmp(device, "dm-", 3) != 0)
- return (0);
+ return;
/* Skip devices without schedulers (loop, ram, dm, etc) */
if (!q->elevator || !blk_queue_stackable(q))
- return (0);
+ return;
/* Leave existing scheduler when set to "none" */
if ((strncmp(elevator, "none", 4) == 0) && (strlen(elevator) == 4))
- return (0);
+ return;
#ifdef HAVE_ELEVATOR_CHANGE
error = elevator_change(q, elevator);
@@ -156,20 +165,16 @@ vdev_elevator_switch(vdev_t *v, char *elevator)
" 2>/dev/null; " \
"echo %s"
- {
- char *argv[] = { "/bin/sh", "-c", NULL, NULL };
- char *envp[] = { NULL };
+ 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, UMH_WAIT_PROC);
- strfree(argv[2]);
- }
+ argv[2] = kmem_asprintf(SET_SCHEDULER_CMD, device, elevator);
+ error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+ 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);
-
- return (error);
}
/*
@@ -790,6 +795,35 @@ vdev_disk_rele(vdev_t *vd)
/* XXX: Implement me as a vnode rele for the device */
}
+static int
+param_set_vdev_scheduler(const char *val, zfs_kernel_param_t *kp)
+{
+ spa_t *spa = NULL;
+ char *p;
+
+ if (val == NULL)
+ return (SET_ERROR(-EINVAL));
+
+ if ((p = strchr(val, '\n')) != NULL)
+ *p = '\0';
+
+ mutex_enter(&spa_namespace_lock);
+ while ((spa = spa_next(spa)) != NULL) {
+ if (spa_state(spa) != POOL_STATE_ACTIVE ||
+ !spa_writeable(spa) || spa_suspended(spa))
+ continue;
+
+ spa_open_ref(spa, FTAG);
+ mutex_exit(&spa_namespace_lock);
+ vdev_elevator_switch(spa->spa_root_vdev, (char *)val);
+ mutex_enter(&spa_namespace_lock);
+ spa_close(spa, FTAG);
+ }
+ mutex_exit(&spa_namespace_lock);
+
+ return (param_set_charp(val, kp));
+}
+
vdev_ops_t vdev_disk_ops = {
vdev_disk_open,
vdev_disk_close,
@@ -804,5 +838,6 @@ vdev_ops_t vdev_disk_ops = {
B_TRUE /* leaf vdev */
};
-module_param(zfs_vdev_scheduler, charp, 0644);
+module_param_call(zfs_vdev_scheduler, param_set_vdev_scheduler,
+ param_get_charp, &zfs_vdev_scheduler, 0644);
MODULE_PARM_DESC(zfs_vdev_scheduler, "I/O scheduler");