From: Stefan Haberland <sth@linux.ibm.com>
Subject: dasd: fix deadlock in dasd_times_out
Patch-mainline: never, fixed differently
References: bsc#1121477, LTC#174111
Description: dasd: fix deadlock in dasd_times_out
Symptom: System does not respond after disk timeout.
Problem: The dasd_times_out function processes requests that have an
internal timeout as well as an external timeout called through
dasd_ioctl_abortio().
For the latter type of requests the CQR may be already in a final
state. The if-else path in dasd_times_out does not handle CQRs in
the final states DASD_CQR_DONE, DASD_CQR_FAILED and
DASD_CQR_TERMINATED and therefore the ccw device lock is never
freed.
Solution: Provide __dasd_cancel_req that is called with the ccw device lock
held to simplify the locking in dasd_times_out and fix the
deadlock issue. Also ensure that interrupts are disabled. The
function may get called with interrupts enabled or already
disabled.
Reproduction: Run IO on disks that do not respond to IO requests in a given
amount of time for example due to hardware failure or reserved
disk state.
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
[ ptesarik: Commit 1bcdb5354aee2c0abcd13d912be35ae39a4144b6 is backported
separately. The interrupt disable issue is fixed in mainline by commit
e443343e509aac82e7281020f25bf8fa0dd46ab7; not backported to SLE15. ]
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
drivers/s390/block/dasd.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -3072,6 +3072,7 @@ enum blk_eh_timer_return dasd_times_out(
struct dasd_ccw_req *cqr = req->completion_data;
struct dasd_block *block = req->q->queuedata;
struct dasd_device *device;
+ unsigned long flags;
int rc = 0;
if (!cqr)
@@ -3084,7 +3085,7 @@ enum blk_eh_timer_return dasd_times_out(
" dasd_times_out cqr %p status %x",
cqr, cqr->status);
- spin_lock(&block->queue_lock);
+ spin_lock_irqsave(&block->queue_lock, flags);
spin_lock(get_ccwdev_lock(device->cdev));
cqr->retries = -1;
cqr->intrc = -ETIMEDOUT;
@@ -3124,7 +3125,7 @@ enum blk_eh_timer_return dasd_times_out(
}
spin_unlock(get_ccwdev_lock(device->cdev));
dasd_schedule_block_bh(block);
- spin_unlock(&block->queue_lock);
+ spin_unlock_irqrestore(&block->queue_lock, flags);
return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
}