*/
#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;
vdev_disk_error(zio_t *zio)
{
#ifdef ZFS_DEBUG
- printk("ZFS: zio error=%d type=%d offset=%llu size=%llu "
+ printk(KERN_WARNING "ZFS: zio error=%d type=%d offset=%llu size=%llu "
"flags=%x\n", zio->io_error, zio->io_type,
(u_longlong_t)zio->io_offset, (u_longlong_t)zio->io_size,
zio->io_flags);
* 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
* "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);
" 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);
+ printk(KERN_NOTICE "ZFS: Unable to set \"%s\" scheduler"
+ " for %s (%s): %d\n", elevator, v->vdev_path, device,
+ error);
}
/*
if (dr->dr_error == 0) {
#ifdef HAVE_1ARG_BIO_END_IO_T
- dr->dr_error = -(bio->bi_error);
+ dr->dr_error = BIO_END_IO_ERROR(bio);
#else
if (error)
dr->dr_error = -(error);
return (abd_scatter_bio_map_off(bio, abd, size, off));
}
-#ifndef bio_set_op_attrs
-#define bio_set_op_attrs(bio, rw, flags) \
- do { (bio)->bi_rw |= (rw)|(flags); } while (0)
-#endif
-
static inline void
vdev_submit_bio_impl(struct bio *bio)
{
#endif
}
+#ifndef HAVE_BIO_SET_DEV
+static inline void
+bio_set_dev(struct bio *bio, struct block_device *bdev)
+{
+ bio->bi_bdev = bdev;
+}
+#endif /* !HAVE_BIO_SET_DEV */
+
static inline void
vdev_submit_bio(struct bio *bio)
{
retry:
dr = vdev_disk_dio_alloc(bio_count);
if (dr == NULL)
- return (ENOMEM);
+ return (SET_ERROR(ENOMEM));
if (zio && !(zio->io_flags & (ZIO_FLAG_IO_RETRY | ZIO_FLAG_TRYHARD)))
bio_set_flags_failfast(bdev, &flags);
/* bio_alloc() with __GFP_WAIT never returns NULL */
dr->dr_bio[i] = bio_alloc(GFP_NOIO,
MIN(abd_nr_pages_off(zio->io_abd, bio_size, abd_offset),
- BIO_MAX_PAGES));
+ BIO_MAX_PAGES));
if (unlikely(dr->dr_bio[i] == NULL)) {
vdev_disk_dio_free(dr);
- return (ENOMEM);
+ return (SET_ERROR(ENOMEM));
}
/* Matching put called by vdev_disk_physio_completion */
vdev_disk_dio_get(dr);
- dr->dr_bio[i]->bi_bdev = bdev;
+ bio_set_dev(dr->dr_bio[i], bdev);
BIO_BI_SECTOR(dr->dr_bio[i]) = bio_offset >> 9;
dr->dr_bio[i]->bi_end_io = vdev_disk_physio_completion;
dr->dr_bio[i]->bi_private = dr;
/* Remaining size is returned to become the new size */
bio_size = bio_map_abd_off(dr->dr_bio[i], zio->io_abd,
- bio_size, abd_offset);
+ bio_size, abd_offset);
/* Advance in buffer and construct another bio if needed */
abd_offset += BIO_BI_SIZE(dr->dr_bio[i]);
return (error);
}
-BIO_END_IO_PROTO(vdev_disk_io_flush_completion, bio, rc)
+BIO_END_IO_PROTO(vdev_disk_io_flush_completion, bio, error)
{
zio_t *zio = bio->bi_private;
#ifdef HAVE_1ARG_BIO_END_IO_T
- int rc = bio->bi_error;
+ zio->io_error = BIO_END_IO_ERROR(bio);
+#else
+ zio->io_error = -error;
#endif
- zio->io_error = -rc;
- if (rc && (rc == -EOPNOTSUPP))
+ if (zio->io_error && (zio->io_error == EOPNOTSUPP))
zio->io_vd->vdev_nowritecache = B_TRUE;
bio_put(bio);
q = bdev_get_queue(bdev);
if (!q)
- return (ENXIO);
+ return (SET_ERROR(ENXIO));
bio = bio_alloc(GFP_NOIO, 0);
/* bio_alloc() with __GFP_WAIT never returns NULL */
if (unlikely(bio == NULL))
- return (ENOMEM);
+ return (SET_ERROR(ENOMEM));
bio->bi_end_io = vdev_disk_io_flush_completion;
bio->bi_private = zio;
- bio->bi_bdev = bdev;
- bio_set_op_attrs(bio, 0, VDEV_WRITE_FLUSH_FUA);
+ bio_set_dev(bio, bdev);
+ bio_set_flush(bio);
vdev_submit_bio(bio);
invalidate_bdev(bdev);
return;
zio->io_error = error;
- if (error == ENOTSUP)
- v->vdev_nowritecache = B_TRUE;
break;
/* 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,
vdev_disk_io_start,
vdev_disk_io_done,
NULL,
+ NULL,
vdev_disk_hold,
vdev_disk_rele,
+ NULL,
VDEV_TYPE_DISK, /* name of this vdev type */
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");