}
 EXPORT_SYMBOL(blk_mq_run_hw_queue);
 
+/*
+ * Is the request queue handled by an IO scheduler that does not respect
+ * hardware queues when dispatching?
+ */
+static bool blk_mq_has_sqsched(struct request_queue *q)
+{
+       struct elevator_queue *e = q->elevator;
+
+       if (e && e->type->ops.dispatch_request &&
+           !(e->type->elevator_features & ELEVATOR_F_MQ_AWARE))
+               return true;
+       return false;
+}
+
+/*
+ * Return prefered queue to dispatch from (if any) for non-mq aware IO
+ * scheduler.
+ */
+static struct blk_mq_hw_ctx *blk_mq_get_sq_hctx(struct request_queue *q)
+{
+       struct blk_mq_hw_ctx *hctx;
+
+       /*
+        * If the IO scheduler does not respect hardware queues when
+        * dispatching, we just don't bother with multiple HW queues and
+        * dispatch from hctx for the current CPU since running multiple queues
+        * just causes lock contention inside the scheduler and pointless cache
+        * bouncing.
+        */
+       hctx = blk_mq_map_queue_type(q, HCTX_TYPE_DEFAULT,
+                                    raw_smp_processor_id());
+       if (!blk_mq_hctx_stopped(hctx))
+               return hctx;
+       return NULL;
+}
+
 /**
  * blk_mq_run_hw_queues - Run all hardware queues in a request queue.
  * @q: Pointer to the request queue to run.
  */
 void blk_mq_run_hw_queues(struct request_queue *q, bool async)
 {
-       struct blk_mq_hw_ctx *hctx;
+       struct blk_mq_hw_ctx *hctx, *sq_hctx;
        int i;
 
+       sq_hctx = NULL;
+       if (blk_mq_has_sqsched(q))
+               sq_hctx = blk_mq_get_sq_hctx(q);
        queue_for_each_hw_ctx(q, hctx, i) {
                if (blk_mq_hctx_stopped(hctx))
                        continue;
-
-               blk_mq_run_hw_queue(hctx, async);
+               /*
+                * Dispatch from this hctx either if there's no hctx preferred
+                * by IO scheduler or if it has requests that bypass the
+                * scheduler.
+                */
+               if (!sq_hctx || sq_hctx == hctx ||
+                   !list_empty_careful(&hctx->dispatch))
+                       blk_mq_run_hw_queue(hctx, async);
        }
 }
 EXPORT_SYMBOL(blk_mq_run_hw_queues);
  */
 void blk_mq_delay_run_hw_queues(struct request_queue *q, unsigned long msecs)
 {
-       struct blk_mq_hw_ctx *hctx;
+       struct blk_mq_hw_ctx *hctx, *sq_hctx;
        int i;
 
+       sq_hctx = NULL;
+       if (blk_mq_has_sqsched(q))
+               sq_hctx = blk_mq_get_sq_hctx(q);
        queue_for_each_hw_ctx(q, hctx, i) {
                if (blk_mq_hctx_stopped(hctx))
                        continue;
-
-               blk_mq_delay_run_hw_queue(hctx, msecs);
+               /*
+                * Dispatch from this hctx either if there's no hctx preferred
+                * by IO scheduler or if it has requests that bypass the
+                * scheduler.
+                */
+               if (!sq_hctx || sq_hctx == hctx ||
+                   !list_empty_careful(&hctx->dispatch))
+                       blk_mq_delay_run_hw_queue(hctx, msecs);
        }
 }
 EXPORT_SYMBOL(blk_mq_delay_run_hw_queues);