#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/blk-mq.h>
+#include <linux/list_sort.h>
 
 #include <trace/events/block.h>
 
        blk_mq_run_hw_queue(hctx, true);
 }
 
+static int sched_rq_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+       struct request *rqa = container_of(a, struct request, queuelist);
+       struct request *rqb = container_of(b, struct request, queuelist);
+
+       return rqa->mq_hctx > rqb->mq_hctx;
+}
+
+static bool blk_mq_dispatch_hctx_list(struct list_head *rq_list)
+{
+       struct blk_mq_hw_ctx *hctx =
+               list_first_entry(rq_list, struct request, queuelist)->mq_hctx;
+       struct request *rq;
+       LIST_HEAD(hctx_list);
+       unsigned int count = 0;
+       bool ret;
+
+       list_for_each_entry(rq, rq_list, queuelist) {
+               if (rq->mq_hctx != hctx) {
+                       list_cut_before(&hctx_list, rq_list, &rq->queuelist);
+                       goto dispatch;
+               }
+               count++;
+       }
+       list_splice_tail_init(rq_list, &hctx_list);
+
+dispatch:
+       ret = blk_mq_dispatch_rq_list(hctx, &hctx_list, count);
+       return ret;
+}
+
 #define BLK_MQ_BUDGET_DELAY    3               /* ms units */
 
 /*
  * Returns -EAGAIN if hctx->dispatch was found non-empty and run_work has to
  * be run again.  This is necessary to avoid starving flushes.
  */
-static int blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx)
+static int __blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx)
 {
        struct request_queue *q = hctx->queue;
        struct elevator_queue *e = q->elevator;
+       bool multi_hctxs = false, run_queue = false;
+       bool dispatched = false, busy = false;
+       unsigned int max_dispatch;
        LIST_HEAD(rq_list);
-       int ret = 0;
-       struct request *rq;
+       int count = 0;
+
+       if (hctx->dispatch_busy)
+               max_dispatch = 1;
+       else
+               max_dispatch = hctx->queue->nr_requests;
 
        do {
+               struct request *rq;
+
                if (e->type->ops.has_work && !e->type->ops.has_work(hctx))
                        break;
 
                if (!list_empty_careful(&hctx->dispatch)) {
-                       ret = -EAGAIN;
+                       busy = true;
                        break;
                }
 
                         * no guarantee anyone will kick the queue.  Kick it
                         * ourselves.
                         */
-                       blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY);
+                       run_queue = true;
                        break;
                }
 
                 * if this rq won't be queued to driver via .queue_rq()
                 * in blk_mq_dispatch_rq_list().
                 */
-               list_add(&rq->queuelist, &rq_list);
-       } while (blk_mq_dispatch_rq_list(rq->mq_hctx, &rq_list, 1));
+               list_add_tail(&rq->queuelist, &rq_list);
+               if (rq->mq_hctx != hctx)
+                       multi_hctxs = true;
+       } while (++count < max_dispatch);
+
+       if (!count) {
+               if (run_queue)
+                       blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY);
+       } else if (multi_hctxs) {
+               /*
+                * Requests from different hctx may be dequeued from some
+                * schedulers, such as bfq and deadline.
+                *
+                * Sort the requests in the list according to their hctx,
+                * dispatch batching requests from same hctx at a time.
+                */
+               list_sort(NULL, &rq_list, sched_rq_cmp);
+               do {
+                       dispatched |= blk_mq_dispatch_hctx_list(&rq_list);
+               } while (!list_empty(&rq_list));
+       } else {
+               dispatched = blk_mq_dispatch_rq_list(hctx, &rq_list, count);
+       }
+
+       if (busy)
+               return -EAGAIN;
+       return !!dispatched;
+}
+
+static int blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx)
+{
+       int ret;
+
+       do {
+               ret = __blk_mq_do_dispatch_sched(hctx);
+       } while (ret == 1);
 
        return ret;
 }