[PATCH 1/3] scsi: fix potential dead lock for host runtime pm

Lin Ming
2011-11-02T06:21:38Z

In later patch hooks will be added to do ata port runtime pm through scsi layer.
libata schedules scsi EH to handle suspend, then dead lock happens
because scsi EH in turn waits for the ongoing suspend, as below.

<scsi host runtime suspend>
scsi_autopm_put_host
pm_runtime_put_sync
<scsi_host runtime pm status updated to RPM_SUSPENDING>
......
<call libata hook to do suspend>
<wake up scsi EH to handle suspend>
<wait for scsi EH ...>

<scsi EH wake up>
scsi_error_handler
<resume scsi host>
scsi_autopm_get_host
pm_runtime_get_sync
.....
<sleep to wait for the ongoing scsi host suspend>

This patch fixes the dead lock by checking if there is ongoing runtime PM request.
If there is ongoing runtime PM request, scsi_autopm_get_host_noresume is called to
increase the usage count, but don't resume the host.

Signed-off-by: Lin Ming <ming.m.lin@intel.com>
---
drivers/scsi/scsi_error.c | 4 +++-
drivers/scsi/scsi_pm.c | 11 +++++++++++
drivers/scsi/scsi_priv.h | 2 ++
3 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index a4b9cdb..d35d8f7 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1804,7 +1804,9 @@ int scsi_error_handler(void *data)
* what we need to do to get it up and online again (if we can).
* If we fail, we end up taking the thing offline.
*/
- if (scsi_autopm_get_host(shost) != 0) {
+ if (scsi_autopm_host_busy(shost))
+ scsi_autopm_get_host_noresume(shost);
+ else if (scsi_autopm_get_host(shost) != 0) {
SCSI_LOG_ERROR_RECOVERY(1,
printk(KERN_ERR "Error handler scsi_eh_%d "
"unable to autoresume\n",
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index d82a023a..1be6c5a 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -185,6 +185,17 @@ void scsi_autopm_put_host(struct Scsi_Host *shost)
pm_runtime_put_sync(&shost->shost_gendev);
}

+bool scsi_autopm_host_busy(struct Scsi_Host *shost)
+{
+ return (shost->shost_gendev.power.runtime_status == RPM_RESUMING
+ || shost->shost_gendev.power.runtime_status == RPM_SUSPENDING);
+}
+
+void scsi_autopm_get_host_noresume(struct Scsi_Host *shost)
+{
+ pm_runtime_get_noresume(&shost->shost_gendev);
+}
+
#else

#define scsi_runtime_suspend NULL
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 2a58895..1750651 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -156,6 +156,8 @@ extern void scsi_autopm_get_target(struct scsi_target *);
extern void scsi_autopm_put_target(struct scsi_target *);
extern int scsi_autopm_get_host(struct Scsi_Host *);
extern void scsi_autopm_put_host(struct Scsi_Host *);
+extern bool scsi_autopm_host_busy(struct Scsi_Host *shost);
+extern void scsi_autopm_get_host_noresume(struct Scsi_Host *);
#else
static inline void scsi_autopm_get_target(struct scsi_target *t) {}
static inline void scsi_autopm_put_target(struct scsi_target *t) {}
--
1.7.2.5