scsi: restart list search after unlock in scsi_remove_target
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 commitbc3f02a795
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 commitf2495e228f
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: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Christoph Hellwig <hch@lst.de> Tested-by: Johannes Thumshirn <jthumshirn@suse.de> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Fixes:bc3f02a795
Cc: stable@vger.kernel.org Signed-off-by: James Bottomley <JBottomley@Odin.com>
This commit is contained in:
parent
a82544c7ba
commit
4099819356
@ -1158,31 +1158,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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user