mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
SCSI: scsi_remove_target: fix softlockup regression on hot remove
commitbc3f02a795upstream. John reports: BUG: soft lockup - CPU#2 stuck for 23s! [kworker/u:8:2202] [..] Call Trace: [<ffffffff8141782a>] scsi_remove_target+0xda/0x1f0 [<ffffffff81421de5>] sas_rphy_remove+0x55/0x60 [<ffffffff81421e01>] sas_rphy_delete+0x11/0x20 [<ffffffff81421e35>] sas_port_delete+0x25/0x160 [<ffffffff814549a3>] mptsas_del_end_device+0x183/0x270 ...introduced by commit3b661a9"[SCSI] fix hot unplug vs async scan race". Don't restart lookup of more stargets in the multi-target case, just arrange to traverse the list once, on the assumption that new targets are always added at the end. There is no guarantee that the target will change state in scsi_target_reap() so we can end up spinning if we restart. Acked-by: Jack Wang <jack_wang@usish.com> LKML-Reference: <CAEhu1-6wq1YsNiscGMwP4ud0Q+MrViRzv=kcWCQSBNc8c68N5Q@mail.gmail.com> Reported-by: John Drescher <drescherjm@gmail.com> Tested-by: John Drescher <drescherjm@gmail.com> Signed-off-by: Dan Williams <djbw@fb.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
fc3ef182a1
commit
8fda07927a
|
|
@ -988,33 +988,31 @@ static void __scsi_remove_target(struct scsi_target *starget)
|
|||
void scsi_remove_target(struct device *dev)
|
||||
{
|
||||
struct Scsi_Host *shost = dev_to_shost(dev->parent);
|
||||
struct scsi_target *starget, *found;
|
||||
struct scsi_target *starget, *last = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
restart:
|
||||
found = NULL;
|
||||
/* remove targets being careful to lookup next entry before
|
||||
* deleting the last
|
||||
*/
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
list_for_each_entry(starget, &shost->__targets, siblings) {
|
||||
if (starget->state == STARGET_DEL)
|
||||
continue;
|
||||
if (starget->dev.parent == dev || &starget->dev == dev) {
|
||||
found = starget;
|
||||
found->reap_ref++;
|
||||
break;
|
||||
/* assuming new targets arrive at the end */
|
||||
starget->reap_ref++;
|
||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||
if (last)
|
||||
scsi_target_reap(last);
|
||||
last = starget;
|
||||
__scsi_remove_target(starget);
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||
|
||||
if (found) {
|
||||
__scsi_remove_target(found);
|
||||
scsi_target_reap(found);
|
||||
/* in the case where @dev has multiple starget children,
|
||||
* continue removing.
|
||||
*
|
||||
* FIXME: does such a case exist?
|
||||
*/
|
||||
goto restart;
|
||||
}
|
||||
if (last)
|
||||
scsi_target_reap(last);
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_remove_target);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user