summary refs log tree commit diff
path: root/block
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-04-11 14:13:10 +0200
committerJens Axboe <jaxboe@fusionio.com>2011-04-11 14:13:10 +0200
commit109b81296c63228578d4760794d8dd46e02eddfb (patch)
tree2f723e1b69ab08e12528f677f04edbfc7473b7c5 /block
parent4263a2f1dad8c8e7ce2352a0cbc882c2b0c044a9 (diff)
downloadlinux-109b81296c63228578d4760794d8dd46e02eddfb.tar.gz
block: splice plug list to local context
If the request_fn ends up blocking, we could be re-entering
the plug flush. Since the list is protected by explicitly
not allowing schedule events, this isn't a terribly good idea.

Additionally, it can cause us to recurse. As request_fn called by
__blk_run_queue is allowed to 'schedule()' (after dropping the queue
lock of course), it is possible to get a recursive call:

 schedule -> blk_flush_plug -> __blk_finish_plug -> flush_plug_list
      -> __blk_run_queue -> request_fn -> schedule

We must make sure that the second schedule does not call into
blk_flush_plug again.  So instead of leaving the list of requests on
blk_plug->list, move them to a separate list leaving blk_plug->list
empty.

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'block')
-rw-r--r--block/blk-core.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/block/blk-core.c b/block/blk-core.c
index 90f22cc30799..eeaca0998df5 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -2673,19 +2673,24 @@ static void flush_plug_list(struct blk_plug *plug)
 	struct request_queue *q;
 	unsigned long flags;
 	struct request *rq;
+	LIST_HEAD(list);
 
 	BUG_ON(plug->magic != PLUG_MAGIC);
 
 	if (list_empty(&plug->list))
 		return;
 
-	if (plug->should_sort)
-		list_sort(NULL, &plug->list, plug_rq_cmp);
+	list_splice_init(&plug->list, &list);
+
+	if (plug->should_sort) {
+		list_sort(NULL, &list, plug_rq_cmp);
+		plug->should_sort = 0;
+	}
 
 	q = NULL;
 	local_irq_save(flags);
-	while (!list_empty(&plug->list)) {
-		rq = list_entry_rq(plug->list.next);
+	while (!list_empty(&list)) {
+		rq = list_entry_rq(list.next);
 		list_del_init(&rq->queuelist);
 		BUG_ON(!(rq->cmd_flags & REQ_ON_PLUG));
 		BUG_ON(!rq->q);
@@ -2713,7 +2718,6 @@ static void flush_plug_list(struct blk_plug *plug)
 		spin_unlock(q->queue_lock);
 	}
 
-	BUG_ON(!list_empty(&plug->list));
 	local_irq_restore(flags);
 }