Blob Blame History Raw
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_DONE;
 }