block: Introduce elevator features
authorDamien Le Moal <damien.lemoal@wdc.com>
Thu, 5 Sep 2019 09:51:31 +0000 (18:51 +0900)
committerJens Axboe <axboe@kernel.dk>
Fri, 6 Sep 2019 01:52:33 +0000 (19:52 -0600)
Introduce the definition of elevator features through the
elevator_features flags in the elevator_type structure. Each flag can
represent a feature supported by an elevator. The first feature defined
by this patch is support for zoned block device sequential write
constraint with the flag ELEVATOR_F_ZBD_SEQ_WRITE, which is implemented
by the mq-deadline elevator using zone write locking.

Other possible features are IO priorities, write hints, latency targets
or single-LUN dual-actuator disks (for which the elevator could maintain
one LBA ordered list per actuator).

The required_elevator_features field is also added to the request_queue
structure to allow a device driver to specify elevator feature flags
that an elevator must support for the correct operation of the device
(e.g. device drivers for zoned block devices can have the
ELEVATOR_F_ZBD_SEQ_WRITE flag as a required feature).
The helper function blk_queue_required_elevator_features() is
defined for setting this new field.

With these two new fields in place, the elevator functions
elevator_match() and elevator_find() are modified to allow a user to set
only an elevator with a set of features that satisfies the device
required features. Elevators not matching the device requirements are
not shown in the device sysfs queue/scheduler file to prevent their use.

The "none" elevator can always be selected as before.

Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-settings.c
block/elevator.c
block/mq-deadline.c
include/linux/blkdev.h
include/linux/elevator.h

index a058997..6bd1e3b 100644 (file)
@@ -832,6 +832,22 @@ void blk_queue_write_cache(struct request_queue *q, bool wc, bool fua)
 }
 EXPORT_SYMBOL_GPL(blk_queue_write_cache);
 
+/**
+ * blk_queue_required_elevator_features - Set a queue required elevator features
+ * @q:         the request queue for the target device
+ * @features:  Required elevator features OR'ed together
+ *
+ * Tell the block layer that for the device controlled through @q, only the
+ * only elevators that can be used are those that implement at least the set of
+ * features specified by @features.
+ */
+void blk_queue_required_elevator_features(struct request_queue *q,
+                                         unsigned int features)
+{
+       q->required_elevator_features = features;
+}
+EXPORT_SYMBOL_GPL(blk_queue_required_elevator_features);
+
 static int __init blk_settings_init(void)
 {
        blk_max_low_pfn = max_low_pfn - 1;
index 2944c12..ac7c8ad 100644 (file)
@@ -83,8 +83,26 @@ bool elv_bio_merge_ok(struct request *rq, struct bio *bio)
 }
 EXPORT_SYMBOL(elv_bio_merge_ok);
 
-static bool elevator_match(const struct elevator_type *e, const char *name)
+static inline bool elv_support_features(unsigned int elv_features,
+                                       unsigned int required_features)
 {
+       return (required_features & elv_features) == required_features;
+}
+
+/**
+ * elevator_match - Test an elevator name and features
+ * @e: Scheduler to test
+ * @name: Elevator name to test
+ * @required_features: Features that the elevator must provide
+ *
+ * Return true is the elevator @e name matches @name and if @e provides all the
+ * the feratures spcified by @required_features.
+ */
+static bool elevator_match(const struct elevator_type *e, const char *name,
+                          unsigned int required_features)
+{
+       if (!elv_support_features(e->elevator_features, required_features))
+               return false;
        if (!strcmp(e->elevator_name, name))
                return true;
        if (e->elevator_alias && !strcmp(e->elevator_alias, name))
@@ -93,15 +111,21 @@ static bool elevator_match(const struct elevator_type *e, const char *name)
        return false;
 }
 
-/*
- * Return scheduler with name 'name'
+/**
+ * elevator_find - Find an elevator
+ * @name: Name of the elevator to find
+ * @required_features: Features that the elevator must provide
+ *
+ * Return the first registered scheduler with name @name and supporting the
+ * features @required_features and NULL otherwise.
  */
-static struct elevator_type *elevator_find(const char *name)
+static struct elevator_type *elevator_find(const char *name,
+                                          unsigned int required_features)
 {
        struct elevator_type *e;
 
        list_for_each_entry(e, &elv_list, list) {
-               if (elevator_match(e, name))
+               if (elevator_match(e, name, required_features))
                        return e;
        }
 
@@ -120,12 +144,12 @@ static struct elevator_type *elevator_get(struct request_queue *q,
 
        spin_lock(&elv_list_lock);
 
-       e = elevator_find(name);
+       e = elevator_find(name, q->required_elevator_features);
        if (!e && try_loading) {
                spin_unlock(&elv_list_lock);
                request_module("%s-iosched", name);
                spin_lock(&elv_list_lock);
-               e = elevator_find(name);
+               e = elevator_find(name, q->required_elevator_features);
        }
 
        if (e && !try_module_get(e->elevator_owner))
@@ -525,7 +549,7 @@ int elv_register(struct elevator_type *e)
 
        /* register, don't allow duplicate names */
        spin_lock(&elv_list_lock);
-       if (elevator_find(e->elevator_name)) {
+       if (elevator_find(e->elevator_name, 0)) {
                spin_unlock(&elv_list_lock);
                kmem_cache_destroy(e->icq_cache);
                return -EBUSY;
@@ -709,7 +733,8 @@ static int __elevator_change(struct request_queue *q, const char *name)
        if (!e)
                return -EINVAL;
 
-       if (q->elevator && elevator_match(q->elevator->type, elevator_name)) {
+       if (q->elevator &&
+           elevator_match(q->elevator->type, elevator_name, 0)) {
                elevator_put(e);
                return 0;
        }
@@ -749,11 +774,13 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name)
 
        spin_lock(&elv_list_lock);
        list_for_each_entry(__e, &elv_list, list) {
-               if (elv && elevator_match(elv, __e->elevator_name)) {
+               if (elv && elevator_match(elv, __e->elevator_name, 0)) {
                        len += sprintf(name+len, "[%s] ", elv->elevator_name);
                        continue;
                }
-               if (elv_support_iosched(q))
+               if (elv_support_iosched(q) &&
+                   elevator_match(__e, __e->elevator_name,
+                                  q->required_elevator_features))
                        len += sprintf(name+len, "%s ", __e->elevator_name);
        }
        spin_unlock(&elv_list_lock);
index 35e84bc..b490f47 100644 (file)
@@ -794,6 +794,7 @@ static struct elevator_type mq_deadline = {
        .elevator_attrs = deadline_attrs,
        .elevator_name = "mq-deadline",
        .elevator_alias = "deadline",
+       .elevator_features = ELEVATOR_F_ZBD_SEQ_WRITE,
        .elevator_owner = THIS_MODULE,
 };
 MODULE_ALIAS("mq-deadline-iosched");
index d0ad21e..b196124 100644 (file)
@@ -496,6 +496,8 @@ struct request_queue {
 
        struct queue_limits     limits;
 
+       unsigned int            required_elevator_features;
+
 #ifdef CONFIG_BLK_DEV_ZONED
        /*
         * Zoned block device information for request dispatch control.
@@ -1097,6 +1099,8 @@ extern void blk_queue_dma_alignment(struct request_queue *, int);
 extern void blk_queue_update_dma_alignment(struct request_queue *, int);
 extern void blk_queue_rq_timeout(struct request_queue *, unsigned int);
 extern void blk_queue_write_cache(struct request_queue *q, bool enabled, bool fua);
+extern void blk_queue_required_elevator_features(struct request_queue *q,
+                                                unsigned int features);
 
 /*
  * Number of physical segments as sent to the device.
index 1dd014c..901bda3 100644 (file)
@@ -76,6 +76,7 @@ struct elevator_type
        struct elv_fs_entry *elevator_attrs;
        const char *elevator_name;
        const char *elevator_alias;
+       const unsigned int elevator_features;
        struct module *elevator_owner;
 #ifdef CONFIG_BLK_DEBUG_FS
        const struct blk_mq_debugfs_attr *queue_debugfs_attrs;
@@ -165,5 +166,12 @@ extern struct request *elv_rb_find(struct rb_root *, sector_t);
 #define rq_entry_fifo(ptr)     list_entry((ptr), struct request, queuelist)
 #define rq_fifo_clear(rq)      list_del_init(&(rq)->queuelist)
 
+/*
+ * Elevator features.
+ */
+
+/* Supports zoned block devices sequential write constraint */
+#define ELEVATOR_F_ZBD_SEQ_WRITE       (1U << 0)
+
 #endif /* CONFIG_BLOCK */
 #endif