diff --git a/block/elevator.c b/block/elevator.c index ac7c8ad580bafe65ba7d4e440214bd9f5bc54064..520d6b224b74cd3c976f8ac30afb481abdda817b 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -651,9 +651,46 @@ 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) { @@ -663,15 +700,15 @@ void elevator_init_mq(struct request_queue *q) if (!elv_support_iosched(q)) return; - if (q->nr_hw_queues != 1) - return; - WARN_ON_ONCE(test_bit(QUEUE_FLAG_REGISTERED, &q->queue_flags)); 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;