|
@@ -1031,33 +1031,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);
|
|
|
|