scsi: ufs: implement scsi host timeout handler
authorYaniv Gardi <ygardi@codeaurora.org>
Thu, 10 Mar 2016 15:37:07 +0000 (17:37 +0200)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 15 Mar 2016 01:04:45 +0000 (21:04 -0400)
A race condition exists between request requeueing and scsi layer
error handling:
When UFS driver queuecommand returns a busy status for a request,
it will be requeued and its tag will be freed and set to -1.
At the same time it is possible that the request will timeout and
scsi layer will start error handling for it. The scsi layer reuses
the request and its tag to send error related commands to the device,
however its tag is no longer valid.
As this request was never really sent to the device, there is no
point to start error handling with the device.
Implement the scsi error handling timeout callback and bypass SCSI
error handling for request that were not actually sent to the device.
For such requests simply reset the block layer timer. Otherwise, let
SCSI layer perform the usual error handling.

Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Gilad Broner <gbroner@codeaurora.org>
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/ufs/ufshcd.c

index de7280c..3400ceb 100644 (file)
@@ -4568,6 +4568,41 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
        ufshcd_probe_hba(hba);
 }
 
+static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
+{
+       unsigned long flags;
+       struct Scsi_Host *host;
+       struct ufs_hba *hba;
+       int index;
+       bool found = false;
+
+       if (!scmd || !scmd->device || !scmd->device->host)
+               return BLK_EH_NOT_HANDLED;
+
+       host = scmd->device->host;
+       hba = shost_priv(host);
+       if (!hba)
+               return BLK_EH_NOT_HANDLED;
+
+       spin_lock_irqsave(host->host_lock, flags);
+
+       for_each_set_bit(index, &hba->outstanding_reqs, hba->nutrs) {
+               if (hba->lrb[index].cmd == scmd) {
+                       found = true;
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(host->host_lock, flags);
+
+       /*
+        * Bypass SCSI error handling and reset the block layer timer if this
+        * SCSI command was not actually dispatched to UFS driver, otherwise
+        * let SCSI layer handle the error as usual.
+        */
+       return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
+}
+
 static struct scsi_host_template ufshcd_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = UFSHCD,
@@ -4580,6 +4615,7 @@ static struct scsi_host_template ufshcd_driver_template = {
        .eh_abort_handler       = ufshcd_abort,
        .eh_device_reset_handler = ufshcd_eh_device_reset_handler,
        .eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
+       .eh_timed_out           = ufshcd_eh_timed_out,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .cmd_per_lun            = UFSHCD_CMD_PER_LUN,