block: test-iosched: fix spinlock recursion

blk_run_queue() takes the queue spinlock and disabled irqs.
Consider the following callstack:

blk_run_queue
 ->__blk_run_queue
  -> scsi_request_fn
   -> blk_peek_request
    -> __elv_next_request
     -> elevator_dispatch_fn
      -> test_dispatch_requests
       -> test_dispatch_from

test_dispatch_from() will release the test-iosched spinlock
using spin_unlock_irq which will enable interrupts, however,
caller is assuming interrupts are disabled.
An interrupt can occur now and scsi soft-irq may be scheduled
with the following call stack:

scsi_softirq_done
 -> scsi_finish_command
  -> scsi_device_unbusy

scsi_device_unbusy() tries to lock the queue spinlock which was
previously locked when blk_run_queue was called, resulting in a
spinlock recursion.

Change test_dispatch_from() to use the spinlock irq save/restore variants
to prevent enabling the irq in case they were previously disabled.

Change-Id: Icaea4f9ba54771edb0302c6005047fcc5478ce8d
Signed-off-by: Gilad Broner <gbroner@codeaurora.org>
This commit is contained in:
Gilad Broner 2014-12-28 16:50:10 +02:00
parent 8806bc6d8d
commit 60cbc8e7fd
1 changed files with 5 additions and 4 deletions

View File

@ -988,24 +988,25 @@ static int test_dispatch_from(struct request_queue *q,
struct test_request *test_rq;
struct request *rq;
int ret = 0;
unsigned long flags;
if (!ptd)
goto err;
spin_lock_irq(&ptd->lock);
spin_lock_irqsave(&ptd->lock, flags);
if (!list_empty(queue)) {
test_rq = list_entry(queue->next, struct test_request,
queuelist);
rq = test_rq->rq;
if (!rq) {
pr_err("%s: null request,return", __func__);
spin_unlock_irq(&ptd->lock);
spin_unlock_irqrestore(&ptd->lock, flags);
goto err;
}
list_move_tail(&test_rq->queuelist, &ptd->dispatched_queue);
ptd->dispatched_count++;
(*count)--;
spin_unlock_irq(&ptd->lock);
spin_unlock_irqrestore(&ptd->lock, flags);
print_req(rq);
elv_dispatch_sort(q, rq);
@ -1013,7 +1014,7 @@ static int test_dispatch_from(struct request_queue *q,
ret = 1;
goto err;
}
spin_unlock_irq(&ptd->lock);
spin_unlock_irqrestore(&ptd->lock, flags);
err:
return ret;