Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / block / blk-mq.c
index 576e7112f8071179b0c8f44bef799dfa5bc75c33..6a63da101bc4db2126ae768ade7f6719426b2fa3 100644 (file)
@@ -672,7 +672,20 @@ static void blk_mq_timeout_work(struct work_struct *work)
        };
        int i;
 
-       if (blk_queue_enter(q, true))
+       /* A deadlock might occur if a request is stuck requiring a
+        * timeout at the same time a queue freeze is waiting
+        * completion, since the timeout code would not be able to
+        * acquire the queue reference here.
+        *
+        * That's why we don't use blk_queue_enter here; instead, we use
+        * percpu_ref_tryget directly, because we need to be able to
+        * obtain a reference even in the short window between the queue
+        * starting to freeze, by dropping the first reference in
+        * blk_mq_freeze_queue_start, and the moment the last request is
+        * consumed, marked by the instant q_usage_counter reaches
+        * zero.
+        */
+       if (!percpu_ref_tryget(&q->q_usage_counter))
                return;
 
        blk_mq_queue_tag_busy_iter(q, blk_mq_check_expired, &data);