summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian King <brking@linux.vnet.ibm.com>2010-06-17 20:56:02 +0200
committerJames Bottomley <James.Bottomley@suse.de>2010-07-27 19:03:46 +0200
commit1117ef8aed95521f46dae3052c7120baae48c2bb (patch)
tree212f2b5fdfd67f619c8ac3930aef2c5fa9825cfa
parent[SCSI] ibmvscsi: Fix softlockup on resume (diff)
downloadlinux-1117ef8aed95521f46dae3052c7120baae48c2bb.tar.xz
linux-1117ef8aed95521f46dae3052c7120baae48c2bb.zip
[SCSI] ibmvscsi: Fix error path deadlock
Fixes a deadlock that can occur if we hit a command timeout during the virtual adapter initialization. The event done functions are written with the assumption that no locks are held, however, when purging requests this is not true. Fix up the purge function to drop the lock so that the done function is not called with the lock held, which can cause a deadlock. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c31
1 files changed, 17 insertions, 14 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index e50fad96329c..83b5a174164c 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -474,23 +474,26 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
*/
static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
{
- struct srp_event_struct *tmp_evt, *pos;
+ struct srp_event_struct *evt;
unsigned long flags;
spin_lock_irqsave(hostdata->host->host_lock, flags);
- list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
- list_del(&tmp_evt->list);
- del_timer(&tmp_evt->timer);
- if (tmp_evt->cmnd) {
- tmp_evt->cmnd->result = (error_code << 16);
- unmap_cmd_data(&tmp_evt->iu.srp.cmd,
- tmp_evt,
- tmp_evt->hostdata->dev);
- if (tmp_evt->cmnd_done)
- tmp_evt->cmnd_done(tmp_evt->cmnd);
- } else if (tmp_evt->done)
- tmp_evt->done(tmp_evt);
- free_event_struct(&tmp_evt->hostdata->pool, tmp_evt);
+ while (!list_empty(&hostdata->sent)) {
+ evt = list_first_entry(&hostdata->sent, struct srp_event_struct, list);
+ list_del(&evt->list);
+ del_timer(&evt->timer);
+
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ if (evt->cmnd) {
+ evt->cmnd->result = (error_code << 16);
+ unmap_cmd_data(&evt->iu.srp.cmd, evt,
+ evt->hostdata->dev);
+ if (evt->cmnd_done)
+ evt->cmnd_done(evt->cmnd);
+ } else if (evt->done)
+ evt->done(evt);
+ free_event_struct(&evt->hostdata->pool, evt);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
}
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
}