For block devices that do not specify required features, preserve the
current default elevator selection (mq-deadline for single queue
devices, none for multi-queue devices). For devices specifying a
required feature (e.g. zoned block devices ELEVATOR_F_ZONED_BLOCK_DEV
feature), select the first available elevator providing the required
features. In all cases, default to "none" if no elevator is available or
if the initialization of the default elevator fails.

This change allows in particular the selection of the correct
mq-deadline elevator for zoned block devices that are also multi-queue,
such as null_blk devices with zoned mode enabled or any SMR disk
connected to a smartpqi HBA (and exposed as multi-queue devices by the
HBA).

Signed-off-by: Damien Le Moal <damien.lem...@wdc.com>
---
 block/elevator.c | 50 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 5 deletions(-)

diff --git a/block/elevator.c b/block/elevator.c
index de11beb32893..ec75dfee7e96 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -626,14 +626,51 @@ static inline bool elv_support_iosched(struct 
request_queue *q)
 }
 
 /*
- * For blk-mq devices supporting IO scheduling, we default to using 
mq-deadline,
- * if available, for single queue devices. If deadline isn't available OR
- * deadline initialization fails OR we have multiple queues, default to "none".
+ * For single queue devices, default to using mq-deadline. If we have multiple
+ * queues or mq-deadline is not available, default to "none".
+ */
+static struct elevator_type *elevator_get_default(struct request_queue *q)
+{
+       if (q->nr_hw_queues != 1)
+               return NULL;
+
+       return elevator_get(q, "mq-deadline", false);
+}
+
+/*
+ * Get the first elevator providing the features required by the request queue.
+ * Default to "none" if no matching elevator is found.
+ */
+static struct elevator_type *elevator_get_by_features(struct request_queue *q)
+{
+       struct elevator_type *e;
+
+       spin_lock(&elv_list_lock);
+
+       list_for_each_entry(e, &elv_list, list) {
+               if (elv_support_features(e->elevator_features,
+                                        q->required_elevator_features))
+                       break;
+       }
+
+       if (e && !try_module_get(e->elevator_owner))
+               e = NULL;
+
+       spin_unlock(&elv_list_lock);
+
+       return e;
+}
+
+/*
+ * For a device queue that has no required features, use the default elevator
+ * settings. Otherwise, use the first elevator available matching the required
+ * features. If no suitable elevator is find or if the chosen elevator
+ * initialization fails, fall back to the "none" elevator (no elevator).
  */
 void elevator_init_mq(struct request_queue *q)
 {
        struct elevator_type *e;
-       int err = 0;
+       int err;
 
        if (!elv_support_iosched(q))
                return;
@@ -644,7 +681,10 @@ void elevator_init_mq(struct request_queue *q)
        if (unlikely(q->elevator))
                return;
 
-       e = elevator_get(q, "mq-deadline", false);
+       if (!q->required_elevator_features)
+               e = elevator_get_default(q);
+       else
+               e = elevator_get_by_features(q);
        if (!e)
                return;
 
-- 
2.21.0

Reply via email to