Commit 67022ef1 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Ben Hutchings

scsi: restart list search after unlock in scsi_remove_target

commit 40998193 upstream.

When dropping a lock while iterating a list we must restart the search
as other threads could have manipulated the list under us.  Without this
we can get stuck in an endless loop.  This bug was introduced by

commit bc3f02a7
Author: Dan Williams <djbw@fb.com>
Date:   Tue Aug 28 22:12:10 2012 -0700

    [SCSI] scsi_remove_target: fix softlockup regression on hot remove

Which was itself trying to fix a reported soft lockup issue

http://thread.gmane.org/gmane.linux.kernel/1348679

However, we believe even with this revert of the original patch, the soft
lockup problem has been fixed by

commit f2495e22
Author: James Bottomley <JBottomley@Parallels.com>
Date:   Tue Jan 21 07:01:41 2014 -0800

    [SCSI] dual scan thread bug fix

Thanks go to Dan Williams <dan.j.williams@intel.com> for tracking all this
prior history down.
Reported-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Tested-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Fixes: bc3f02a7Signed-off-by: default avatarJames Bottomley <JBottomley@Odin.com>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 785381e9
...@@ -1029,31 +1029,23 @@ static void __scsi_remove_target(struct scsi_target *starget) ...@@ -1029,31 +1029,23 @@ static void __scsi_remove_target(struct scsi_target *starget)
void scsi_remove_target(struct device *dev) void scsi_remove_target(struct device *dev)
{ {
struct Scsi_Host *shost = dev_to_shost(dev->parent); struct Scsi_Host *shost = dev_to_shost(dev->parent);
struct scsi_target *starget, *last = NULL; struct scsi_target *starget;
unsigned long flags; unsigned long flags;
/* remove targets being careful to lookup next entry before restart:
* deleting the last
*/
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry(starget, &shost->__targets, siblings) { list_for_each_entry(starget, &shost->__targets, siblings) {
if (starget->state == STARGET_DEL) if (starget->state == STARGET_DEL)
continue; continue;
if (starget->dev.parent == dev || &starget->dev == dev) { if (starget->dev.parent == dev || &starget->dev == dev) {
/* assuming new targets arrive at the end */
kref_get(&starget->reap_ref); kref_get(&starget->reap_ref);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
if (last)
scsi_target_reap(last);
last = starget;
__scsi_remove_target(starget); __scsi_remove_target(starget);
spin_lock_irqsave(shost->host_lock, flags); scsi_target_reap(starget);
goto restart;
} }
} }
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
if (last)
scsi_target_reap(last);
} }
EXPORT_SYMBOL(scsi_remove_target); EXPORT_SYMBOL(scsi_remove_target);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment