{
struct scsi_device *sdev;
struct device *parent;
+ struct scsi_target *starget;
unsigned long flags;
- int delete;
parent = dev->parent;
sdev = to_scsi_device(dev);
+ starget = to_scsi_target(parent);
spin_lock_irqsave(sdev->host->host_lock, flags);
- /* If we're the last LUN on the target, destroy the target */
- delete = list_empty(&sdev->same_target_siblings);
+ starget->reap_ref++;
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
list_del(&sdev->starved_entry);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
- if (delete) {
- struct scsi_target *starget = to_scsi_target(parent);
- if (!starget->create) {
- transport_remove_device(&starget->dev);
- device_del(parent);
- }
- transport_destroy_device(&starget->dev);
-
- put_device(parent);
- }
- if (sdev->request_queue)
+ if (sdev->request_queue) {
+ sdev->request_queue->queuedata = NULL;
scsi_free_queue(sdev->request_queue);
+ /* temporary expedient, try to catch use of queue lock
+ * after free of sdev */
+ sdev->request_queue = NULL;
+ }
+
+ scsi_target_reap(scsi_target(sdev));
kfree(sdev->inquiry);
kfree(sdev);
static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL);
+static ssize_t
+show_iostat_counterbits(struct device *dev, char *buf)
+{
+ return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);
+}
+
+static DEVICE_ATTR(iocounterbits, S_IRUGO, show_iostat_counterbits, NULL);
+
+#define show_sdev_iostat(field) \
+static ssize_t \
+show_iostat_##field(struct device *dev, char *buf) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ unsigned long long count = atomic_read(&sdev->field); \
+ return snprintf(buf, 20, "0x%llx\n", count); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_iostat_##field, NULL)
+
+show_sdev_iostat(iorequest_cnt);
+show_sdev_iostat(iodone_cnt);
+show_sdev_iostat(ioerr_cnt);
+
/* Default template for device attributes. May NOT be modified */
static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
&dev_attr_delete,
&dev_attr_state,
&dev_attr_timeout,
+ &dev_attr_iocounterbits,
+ &dev_attr_iorequest_cnt,
+ &dev_attr_iodone_cnt,
+ &dev_attr_ioerr_cnt,
NULL
};
return device_create_file(dev, attr);
}
-static void scsi_target_dev_release(struct device *dev)
-{
- struct scsi_target *starget = to_scsi_target(dev);
- struct device *parent = dev->parent;
- kfree(starget);
- put_device(parent);
-}
-
/**
* scsi_sysfs_add_sdev - add scsi device to sysfs
* @sdev: scsi_device to add
**/
int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{
- struct scsi_target *starget = sdev->sdev_target;
- struct Scsi_Host *shost = sdev->host;
- int error, i, create;
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- create = starget->create;
- starget->create = 0;
- spin_unlock_irqrestore(shost->host_lock, flags);
-
- if (create) {
- error = device_add(&starget->dev);
- if (error) {
- printk(KERN_ERR "Target device_add failed\n");
- return error;
- }
- transport_add_device(&starget->dev);
- }
+ int error, i;
if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
return error;
out:
return error;
- class_device_del(&sdev->sdev_classdev);
clean_device:
scsi_device_set_state(sdev, SDEV_CANCEL);
sdev->host->hostt->slave_destroy(sdev);
transport_unregister_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
-
out:
up(&shost->scan_mutex);
}
EXPORT_SYMBOL(scsi_remove_device);
+void __scsi_remove_target(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+ struct scsi_device *sdev, *tmp;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->reap_ref++;
+ list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
+ if (sdev->channel != starget->channel ||
+ sdev->id != starget->id)
+ continue;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_remove_device(sdev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_target_reap(starget);
+}
+
+/**
+ * scsi_remove_target - try to remove a target and all its devices
+ * @dev: generic starget or parent of generic stargets to be removed
+ *
+ * Note: This is slightly racy. It is possible that if the user
+ * requests the addition of another device then the target won't be
+ * removed.
+ */
+void scsi_remove_target(struct device *dev)
+{
+ struct device *rdev, *idev, *next;
+
+ if (scsi_is_target_device(dev)) {
+ __scsi_remove_target(to_scsi_target(dev));
+ return;
+ }
+
+ rdev = get_device(dev);
+ list_for_each_entry_safe(idev, next, &dev->children, node) {
+ if (scsi_is_target_device(idev))
+ __scsi_remove_target(to_scsi_target(idev));
+ }
+ put_device(rdev);
+}
+EXPORT_SYMBOL(scsi_remove_target);
+
int scsi_register_driver(struct device_driver *drv)
{
drv->bus = &scsi_bus_type;
void scsi_sysfs_device_initialize(struct scsi_device *sdev)
{
+ unsigned long flags;
+ struct Scsi_Host *shost = sdev->host;
+ struct scsi_target *starget = sdev->sdev_target;
+
device_initialize(&sdev->sdev_gendev);
sdev->sdev_gendev.bus = &scsi_bus_type;
sdev->sdev_gendev.release = scsi_device_dev_release;
snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
-
+ sdev->scsi_level = SCSI_2;
transport_setup_device(&sdev->sdev_gendev);
-}
-
-int scsi_is_sdev_device(const struct device *dev)
-{
- return dev->release == scsi_device_dev_release;
-}
-EXPORT_SYMBOL(scsi_is_sdev_device);
-
-int scsi_sysfs_target_initialize(struct scsi_device *sdev)
-{
- struct scsi_target *starget = NULL;
- struct Scsi_Host *shost = sdev->host;
- struct scsi_device *device;
- struct device *dev = NULL;
- unsigned long flags;
- int create = 0;
-
spin_lock_irqsave(shost->host_lock, flags);
- /*
- * Search for an existing target for this sdev.
- */
- list_for_each_entry(device, &shost->__devices, siblings) {
- if (device->id == sdev->id &&
- device->channel == sdev->channel) {
- list_add_tail(&sdev->same_target_siblings,
- &device->same_target_siblings);
- sdev->scsi_level = device->scsi_level;
- starget = device->sdev_target;
- break;
- }
- }
-
- if (!starget) {
- const int size = sizeof(*starget) +
- shost->transportt->target_size;
- starget = kmalloc(size, GFP_ATOMIC);
- if (!starget) {
- printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
- spin_unlock_irqrestore(shost->host_lock,
- flags);
- return -ENOMEM;
- }
- memset(starget, 0, size);
- dev = &starget->dev;
- device_initialize(dev);
- dev->parent = get_device(&shost->shost_gendev);
- dev->release = scsi_target_dev_release;
- sprintf(dev->bus_id, "target%d:%d:%d",
- shost->host_no, sdev->channel, sdev->id);
- starget->id = sdev->id;
- starget->channel = sdev->channel;
- create = starget->create = 1;
- /*
- * If there wasn't another lun already configured at
- * this target, then default this device to SCSI_2
- * until we know better
- */
- sdev->scsi_level = SCSI_2;
- }
- get_device(&starget->dev);
- sdev->sdev_gendev.parent = &starget->dev;
- sdev->sdev_target = starget;
+ list_add_tail(&sdev->same_target_siblings, &starget->devices);
list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags);
- if (create)
- transport_setup_device(&starget->dev);
- return 0;
}
-int scsi_is_target_device(const struct device *dev)
+int scsi_is_sdev_device(const struct device *dev)
{
- return dev->release == scsi_target_dev_release;
+ return dev->release == scsi_device_dev_release;
}
-EXPORT_SYMBOL(scsi_is_target_device);
+EXPORT_SYMBOL(scsi_is_sdev_device);
/* A blank transport template that is used in drivers that don't
* yet implement Transport Attributes */
-struct scsi_transport_template blank_transport_template = { NULL, };
+struct scsi_transport_template blank_transport_template = { { { {NULL, }, }, }, };