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>
This commit is contained in:
parent
4263a2f1da
commit
109b81296c
1 changed files with 9 additions and 5 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue