block: fix the problem of io_ticks becoming smaller
[linux-2.6-microblaze.git] / block / blk-mq-sched.c
index 996a4b2..c838d81 100644 (file)
@@ -168,9 +168,19 @@ static int __blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx)
                 * in blk_mq_dispatch_rq_list().
                 */
                list_add_tail(&rq->queuelist, &rq_list);
+               count++;
                if (rq->mq_hctx != hctx)
                        multi_hctxs = true;
-       } while (++count < max_dispatch);
+
+               /*
+                * If we cannot get tag for the request, stop dequeueing
+                * requests from the IO scheduler. We are unlikely to be able
+                * to submit them anyway and it creates false impression for
+                * scheduling heuristics that the device can take more IO.
+                */
+               if (!blk_mq_get_driver_tag(rq))
+                       break;
+       } while (count < max_dispatch);
 
        if (!count) {
                if (run_queue)
@@ -284,8 +294,7 @@ static int blk_mq_do_dispatch_ctx(struct blk_mq_hw_ctx *hctx)
 static int __blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx)
 {
        struct request_queue *q = hctx->queue;
-       struct elevator_queue *e = q->elevator;
-       const bool has_sched_dispatch = e && e->type->ops.dispatch_request;
+       const bool has_sched = q->elevator;
        int ret = 0;
        LIST_HEAD(rq_list);
 
@@ -316,12 +325,12 @@ static int __blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx)
        if (!list_empty(&rq_list)) {
                blk_mq_sched_mark_restart_hctx(hctx);
                if (blk_mq_dispatch_rq_list(hctx, &rq_list, 0)) {
-                       if (has_sched_dispatch)
+                       if (has_sched)
                                ret = blk_mq_do_dispatch_sched(hctx);
                        else
                                ret = blk_mq_do_dispatch_ctx(hctx);
                }
-       } else if (has_sched_dispatch) {
+       } else if (has_sched) {
                ret = blk_mq_do_dispatch_sched(hctx);
        } else if (hctx->dispatch_busy) {
                /* dequeue request one by one from sw queue if queue is busy */
@@ -390,9 +399,10 @@ bool __blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio,
        return ret;
 }
 
-bool blk_mq_sched_try_insert_merge(struct request_queue *q, struct request *rq)
+bool blk_mq_sched_try_insert_merge(struct request_queue *q, struct request *rq,
+                                  struct list_head *free)
 {
-       return rq_mergeable(rq) && elv_attempt_insert_merge(q, rq);
+       return rq_mergeable(rq) && elv_attempt_insert_merge(q, rq, free);
 }
 EXPORT_SYMBOL_GPL(blk_mq_sched_try_insert_merge);
 
@@ -453,7 +463,7 @@ void blk_mq_sched_insert_request(struct request *rq, bool at_head,
                goto run;
        }
 
-       if (e && e->type->ops.insert_requests) {
+       if (e) {
                LIST_HEAD(list);
 
                list_add(&rq->queuelist, &list);
@@ -484,9 +494,9 @@ void blk_mq_sched_insert_requests(struct blk_mq_hw_ctx *hctx,
        percpu_ref_get(&q->q_usage_counter);
 
        e = hctx->queue->elevator;
-       if (e && e->type->ops.insert_requests)
+       if (e) {
                e->type->ops.insert_requests(hctx, list, false);
-       else {
+       else {
                /*
                 * try to issue requests directly if the hw queue isn't
                 * busy in case of 'none' scheduler, and this way may save
@@ -509,11 +519,9 @@ static void blk_mq_sched_free_tags(struct blk_mq_tag_set *set,
                                   struct blk_mq_hw_ctx *hctx,
                                   unsigned int hctx_idx)
 {
-       unsigned int flags = set->flags & ~BLK_MQ_F_TAG_HCTX_SHARED;
-
        if (hctx->sched_tags) {
                blk_mq_free_rqs(set, hctx->sched_tags, hctx_idx);
-               blk_mq_free_rq_map(hctx->sched_tags, flags);
+               blk_mq_free_rq_map(hctx->sched_tags, set->flags);
                hctx->sched_tags = NULL;
        }
 }
@@ -523,12 +531,10 @@ static int blk_mq_sched_alloc_tags(struct request_queue *q,
                                   unsigned int hctx_idx)
 {
        struct blk_mq_tag_set *set = q->tag_set;
-       /* Clear HCTX_SHARED so tags are init'ed */
-       unsigned int flags = set->flags & ~BLK_MQ_F_TAG_HCTX_SHARED;
        int ret;
 
        hctx->sched_tags = blk_mq_alloc_rq_map(set, hctx_idx, q->nr_requests,
-                                              set->reserved_tags, flags);
+                                              set->reserved_tags, set->flags);
        if (!hctx->sched_tags)
                return -ENOMEM;
 
@@ -546,16 +552,50 @@ static void blk_mq_sched_tags_teardown(struct request_queue *q)
        int i;
 
        queue_for_each_hw_ctx(q, hctx, i) {
-               /* Clear HCTX_SHARED so tags are freed */
-               unsigned int flags = hctx->flags & ~BLK_MQ_F_TAG_HCTX_SHARED;
-
                if (hctx->sched_tags) {
-                       blk_mq_free_rq_map(hctx->sched_tags, flags);
+                       blk_mq_free_rq_map(hctx->sched_tags, hctx->flags);
                        hctx->sched_tags = NULL;
                }
        }
 }
 
+static int blk_mq_init_sched_shared_sbitmap(struct request_queue *queue)
+{
+       struct blk_mq_tag_set *set = queue->tag_set;
+       int alloc_policy = BLK_MQ_FLAG_TO_ALLOC_POLICY(set->flags);
+       struct blk_mq_hw_ctx *hctx;
+       int ret, i;
+
+       /*
+        * Set initial depth at max so that we don't need to reallocate for
+        * updating nr_requests.
+        */
+       ret = blk_mq_init_bitmaps(&queue->sched_bitmap_tags,
+                                 &queue->sched_breserved_tags,
+                                 MAX_SCHED_RQ, set->reserved_tags,
+                                 set->numa_node, alloc_policy);
+       if (ret)
+               return ret;
+
+       queue_for_each_hw_ctx(queue, hctx, i) {
+               hctx->sched_tags->bitmap_tags =
+                                       &queue->sched_bitmap_tags;
+               hctx->sched_tags->breserved_tags =
+                                       &queue->sched_breserved_tags;
+       }
+
+       sbitmap_queue_resize(&queue->sched_bitmap_tags,
+                            queue->nr_requests - set->reserved_tags);
+
+       return 0;
+}
+
+static void blk_mq_exit_sched_shared_sbitmap(struct request_queue *queue)
+{
+       sbitmap_queue_free(&queue->sched_bitmap_tags);
+       sbitmap_queue_free(&queue->sched_breserved_tags);
+}
+
 int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
 {
        struct blk_mq_hw_ctx *hctx;
@@ -580,12 +620,18 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
        queue_for_each_hw_ctx(q, hctx, i) {
                ret = blk_mq_sched_alloc_tags(q, hctx, i);
                if (ret)
-                       goto err;
+                       goto err_free_tags;
+       }
+
+       if (blk_mq_is_sbitmap_shared(q->tag_set->flags)) {
+               ret = blk_mq_init_sched_shared_sbitmap(q);
+               if (ret)
+                       goto err_free_tags;
        }
 
        ret = e->ops.init_sched(q, e);
        if (ret)
-               goto err;
+               goto err_free_sbitmap;
 
        blk_mq_debugfs_register_sched(q);
 
@@ -605,7 +651,10 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
 
        return 0;
 
-err:
+err_free_sbitmap:
+       if (blk_mq_is_sbitmap_shared(q->tag_set->flags))
+               blk_mq_exit_sched_shared_sbitmap(q);
+err_free_tags:
        blk_mq_sched_free_requests(q);
        blk_mq_sched_tags_teardown(q);
        q->elevator = NULL;
@@ -631,6 +680,7 @@ void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e)
 {
        struct blk_mq_hw_ctx *hctx;
        unsigned int i;
+       unsigned int flags = 0;
 
        queue_for_each_hw_ctx(q, hctx, i) {
                blk_mq_debugfs_unregister_sched_hctx(hctx);
@@ -638,10 +688,13 @@ void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e)
                        e->type->ops.exit_hctx(hctx, i);
                        hctx->sched_data = NULL;
                }
+               flags = hctx->flags;
        }
        blk_mq_debugfs_unregister_sched(q);
        if (e->type->ops.exit_sched)
                e->type->ops.exit_sched(e);
        blk_mq_sched_tags_teardown(q);
+       if (blk_mq_is_sbitmap_shared(flags))
+               blk_mq_exit_sched_shared_sbitmap(q);
        q->elevator = NULL;
 }