Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / block / blk-mq.c
index ec79115..323c9cb 100644 (file)
@@ -93,7 +93,7 @@ static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx,
 
 struct mq_inflight {
        struct hd_struct *part;
-       unsigned int *inflight;
+       unsigned int inflight[2];
 };
 
 static bool blk_mq_check_inflight(struct blk_mq_hw_ctx *hctx,
@@ -102,45 +102,29 @@ static bool blk_mq_check_inflight(struct blk_mq_hw_ctx *hctx,
 {
        struct mq_inflight *mi = priv;
 
-       /*
-        * index[0] counts the specific partition that was asked for.
-        */
        if (rq->part == mi->part)
-               mi->inflight[0]++;
+               mi->inflight[rq_data_dir(rq)]++;
 
        return true;
 }
 
 unsigned int blk_mq_in_flight(struct request_queue *q, struct hd_struct *part)
 {
-       unsigned inflight[2];
-       struct mq_inflight mi = { .part = part, .inflight = inflight, };
+       struct mq_inflight mi = { .part = part };
 
-       inflight[0] = inflight[1] = 0;
        blk_mq_queue_tag_busy_iter(q, blk_mq_check_inflight, &mi);
 
-       return inflight[0];
-}
-
-static bool blk_mq_check_inflight_rw(struct blk_mq_hw_ctx *hctx,
-                                    struct request *rq, void *priv,
-                                    bool reserved)
-{
-       struct mq_inflight *mi = priv;
-
-       if (rq->part == mi->part)
-               mi->inflight[rq_data_dir(rq)]++;
-
-       return true;
+       return mi.inflight[0] + mi.inflight[1];
 }
 
 void blk_mq_in_flight_rw(struct request_queue *q, struct hd_struct *part,
                         unsigned int inflight[2])
 {
-       struct mq_inflight mi = { .part = part, .inflight = inflight, };
+       struct mq_inflight mi = { .part = part };
 
-       inflight[0] = inflight[1] = 0;
-       blk_mq_queue_tag_busy_iter(q, blk_mq_check_inflight_rw, &mi);
+       blk_mq_queue_tag_busy_iter(q, blk_mq_check_inflight, &mi);
+       inflight[0] = mi.inflight[0];
+       inflight[1] = mi.inflight[1];
 }
 
 void blk_freeze_queue_start(struct request_queue *q)
@@ -276,12 +260,6 @@ void blk_mq_wake_waiters(struct request_queue *q)
                        blk_mq_tag_wakeup_all(hctx->tags, true);
 }
 
-bool blk_mq_can_queue(struct blk_mq_hw_ctx *hctx)
-{
-       return blk_mq_has_free_tags(hctx->tags);
-}
-EXPORT_SYMBOL(blk_mq_can_queue);
-
 /*
  * Only need start/end time stamping if we have iostat or
  * blk stats enabled, or using an IO scheduler.
@@ -663,18 +641,6 @@ bool blk_mq_complete_request(struct request *rq)
 }
 EXPORT_SYMBOL(blk_mq_complete_request);
 
-int blk_mq_request_started(struct request *rq)
-{
-       return blk_mq_rq_state(rq) != MQ_RQ_IDLE;
-}
-EXPORT_SYMBOL_GPL(blk_mq_request_started);
-
-int blk_mq_request_completed(struct request *rq)
-{
-       return blk_mq_rq_state(rq) == MQ_RQ_COMPLETE;
-}
-EXPORT_SYMBOL_GPL(blk_mq_request_completed);
-
 void blk_mq_start_request(struct request *rq)
 {
        struct request_queue *q = rq->q;
@@ -1064,7 +1030,7 @@ bool blk_mq_get_driver_tag(struct request *rq)
        bool shared;
 
        if (rq->tag != -1)
-               goto done;
+               return true;
 
        if (blk_mq_tag_is_reserved(data.hctx->sched_tags, rq->internal_tag))
                data.flags |= BLK_MQ_REQ_RESERVED;
@@ -1079,7 +1045,6 @@ bool blk_mq_get_driver_tag(struct request *rq)
                data.hctx->tags->rqs[rq->tag] = rq;
        }
 
-done:
        return rq->tag != -1;
 }
 
@@ -1486,7 +1451,7 @@ void blk_mq_delay_run_hw_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs)
 }
 EXPORT_SYMBOL(blk_mq_delay_run_hw_queue);
 
-bool blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async)
+void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async)
 {
        int srcu_idx;
        bool need_run;
@@ -1504,12 +1469,8 @@ bool blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async)
                blk_mq_hctx_has_pending(hctx);
        hctx_unlock(hctx, srcu_idx);
 
-       if (need_run) {
+       if (need_run)
                __blk_mq_delay_run_hw_queue(hctx, async, 0);
-               return true;
-       }
-
-       return false;
 }
 EXPORT_SYMBOL(blk_mq_run_hw_queue);
 
@@ -2789,6 +2750,23 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
        int i, j, end;
        struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx;
 
+       if (q->nr_hw_queues < set->nr_hw_queues) {
+               struct blk_mq_hw_ctx **new_hctxs;
+
+               new_hctxs = kcalloc_node(set->nr_hw_queues,
+                                      sizeof(*new_hctxs), GFP_KERNEL,
+                                      set->numa_node);
+               if (!new_hctxs)
+                       return;
+               if (hctxs)
+                       memcpy(new_hctxs, hctxs, q->nr_hw_queues *
+                              sizeof(*hctxs));
+               q->queue_hw_ctx = new_hctxs;
+               q->nr_hw_queues = set->nr_hw_queues;
+               kfree(hctxs);
+               hctxs = new_hctxs;
+       }
+
        /* protect against switching io scheduler  */
        mutex_lock(&q->sysfs_lock);
        for (i = 0; i < set->nr_hw_queues; i++) {
@@ -2844,19 +2822,6 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
        mutex_unlock(&q->sysfs_lock);
 }
 
-/*
- * Maximum number of hardware queues we support. For single sets, we'll never
- * have more than the CPUs (software queues). For multiple sets, the tag_set
- * user may have set ->nr_hw_queues larger.
- */
-static unsigned int nr_hw_queues(struct blk_mq_tag_set *set)
-{
-       if (set->nr_maps == 1)
-               return nr_cpu_ids;
-
-       return max(set->nr_hw_queues, nr_cpu_ids);
-}
-
 struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
                                                  struct request_queue *q,
                                                  bool elevator_init)
@@ -2876,12 +2841,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
        /* init q->mq_kobj and sw queues' kobjects */
        blk_mq_sysfs_init(q);
 
-       q->nr_queues = nr_hw_queues(set);
-       q->queue_hw_ctx = kcalloc_node(q->nr_queues, sizeof(*(q->queue_hw_ctx)),
-                                               GFP_KERNEL, set->numa_node);
-       if (!q->queue_hw_ctx)
-               goto err_sys_init;
-
        INIT_LIST_HEAD(&q->unused_hctx_list);
        spin_lock_init(&q->unused_hctx_lock);
 
@@ -2929,7 +2888,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
 err_hctxs:
        kfree(q->queue_hw_ctx);
        q->nr_hw_queues = 0;
-err_sys_init:
        blk_mq_sysfs_deinit(q);
 err_poll:
        blk_stat_free_callback(q->poll_cb);
@@ -3030,6 +2988,29 @@ static int blk_mq_update_queue_map(struct blk_mq_tag_set *set)
        }
 }
 
+static int blk_mq_realloc_tag_set_tags(struct blk_mq_tag_set *set,
+                                 int cur_nr_hw_queues, int new_nr_hw_queues)
+{
+       struct blk_mq_tags **new_tags;
+
+       if (cur_nr_hw_queues >= new_nr_hw_queues)
+               return 0;
+
+       new_tags = kcalloc_node(new_nr_hw_queues, sizeof(struct blk_mq_tags *),
+                               GFP_KERNEL, set->numa_node);
+       if (!new_tags)
+               return -ENOMEM;
+
+       if (set->tags)
+               memcpy(new_tags, set->tags, cur_nr_hw_queues *
+                      sizeof(*set->tags));
+       kfree(set->tags);
+       set->tags = new_tags;
+       set->nr_hw_queues = new_nr_hw_queues;
+
+       return 0;
+}
+
 /*
  * Alloc a tag set to be associated with one or more request queues.
  * May fail with EINVAL for various error conditions. May adjust the
@@ -3083,9 +3064,7 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
        if (set->nr_maps == 1 && set->nr_hw_queues > nr_cpu_ids)
                set->nr_hw_queues = nr_cpu_ids;
 
-       set->tags = kcalloc_node(nr_hw_queues(set), sizeof(struct blk_mq_tags *),
-                                GFP_KERNEL, set->numa_node);
-       if (!set->tags)
+       if (blk_mq_realloc_tag_set_tags(set, 0, set->nr_hw_queues) < 0)
                return -ENOMEM;
 
        ret = -ENOMEM;
@@ -3126,7 +3105,7 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set)
 {
        int i, j;
 
-       for (i = 0; i < nr_hw_queues(set); i++)
+       for (i = 0; i < set->nr_hw_queues; i++)
                blk_mq_free_map_and_requests(set, i);
 
        for (j = 0; j < set->nr_maps; j++) {
@@ -3270,10 +3249,6 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
 
        list_for_each_entry(q, &set->tag_list, tag_set_list)
                blk_mq_freeze_queue(q);
-       /*
-        * Sync with blk_mq_queue_tag_busy_iter.
-        */
-       synchronize_rcu();
        /*
         * Switch IO scheduler to 'none', cleaning up the data associated
         * with the previous scheduler. We will switch back once we are done
@@ -3288,6 +3263,10 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
                blk_mq_sysfs_unregister(q);
        }
 
+       if (blk_mq_realloc_tag_set_tags(set, set->nr_hw_queues, nr_hw_queues) <
+           0)
+               goto reregister;
+
        prev_nr_hw_queues = set->nr_hw_queues;
        set->nr_hw_queues = nr_hw_queues;
        blk_mq_update_queue_map(set);
@@ -3304,6 +3283,7 @@ fallback:
                blk_mq_map_swqueue(q);
        }
 
+reregister:
        list_for_each_entry(q, &set->tag_list, tag_set_list) {
                blk_mq_sysfs_register(q);
                blk_mq_debugfs_register_hctxs(q);