diff options
Diffstat (limited to 'drivers/scsi/scsi_sysfs.c')
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 56 | 
1 files changed, 36 insertions, 20 deletions
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 04c2a278076..093d4f6a54d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -35,6 +35,7 @@ static const struct {  	{ SDEV_DEL, "deleted" },  	{ SDEV_QUIESCE, "quiesce" },  	{ SDEV_OFFLINE,	"offline" }, +	{ SDEV_TRANSPORT_OFFLINE, "transport-offline" },  	{ SDEV_BLOCK,	"blocked" },  	{ SDEV_CREATED_BLOCK, "created-blocked" },  }; @@ -966,16 +967,20 @@ void __scsi_remove_device(struct scsi_device *sdev)  		device_del(dev);  	} else  		put_device(&sdev->sdev_dev); + +	/* +	 * Stop accepting new requests and wait until all queuecommand() and +	 * scsi_run_queue() invocations have finished before tearing down the +	 * device. +	 */  	scsi_device_set_state(sdev, SDEV_DEL); +	blk_cleanup_queue(sdev->request_queue); +	cancel_work_sync(&sdev->requeue_work); +  	if (sdev->host->hostt->slave_destroy)  		sdev->host->hostt->slave_destroy(sdev);  	transport_destroy_device(dev); -	/* cause the request function to reject all I/O requests */ -	sdev->request_queue->queuedata = NULL; - -	/* Freeing the queue signals to block that we're done */ -	scsi_free_queue(sdev->request_queue);  	put_device(dev);  } @@ -1000,7 +1005,6 @@ static void __scsi_remove_target(struct scsi_target *starget)  	struct scsi_device *sdev;  	spin_lock_irqsave(shost->host_lock, flags); -	starget->reap_ref++;   restart:  	list_for_each_entry(sdev, &shost->__devices, siblings) {  		if (sdev->channel != starget->channel || @@ -1014,14 +1018,6 @@ static void __scsi_remove_target(struct scsi_target *starget)  		goto restart;  	}  	spin_unlock_irqrestore(shost->host_lock, flags); -	scsi_target_reap(starget); -} - -static int __remove_child (struct device * dev, void * data) -{ -	if (scsi_is_target_device(dev)) -		__scsi_remove_target(to_scsi_target(dev)); -	return 0;  }  /** @@ -1034,14 +1030,34 @@ static int __remove_child (struct device * dev, void * data)   */  void scsi_remove_target(struct device *dev)  { -	if (scsi_is_target_device(dev)) { -		__scsi_remove_target(to_scsi_target(dev)); -		return; +	struct Scsi_Host *shost = dev_to_shost(dev->parent); +	struct scsi_target *starget, *found; +	unsigned long flags; + + restart: +	found = NULL; +	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; +		}  	} +	spin_unlock_irqrestore(shost->host_lock, flags); -	get_device(dev); -	device_for_each_child(dev, NULL, __remove_child); -	put_device(dev); +	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; +	}  }  EXPORT_SYMBOL(scsi_remove_target);  |