vserver 2.0 rc7
[linux-2.6.git] / drivers / scsi / scsi_sysfs.c
index 11a46f8..e75ee46 100644 (file)
@@ -154,32 +154,29 @@ void scsi_device_dev_release(struct device *dev)
 {
        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);
@@ -408,6 +405,28 @@ show_queue_type_field(struct device *dev, char *buf)
 
 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[] = {
@@ -423,6 +442,10 @@ 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
 };
 
@@ -534,14 +557,6 @@ static int attr_add(struct device *dev, struct device_attribute *attr)
        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
@@ -551,24 +566,7 @@ static void scsi_target_dev_release(struct device *dev)
  **/
 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;
@@ -617,7 +615,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
  out:
        return error;
 
-       class_device_del(&sdev->sdev_classdev);
  clean_device:
        scsi_device_set_state(sdev, SDEV_CANCEL);
 
@@ -647,12 +644,57 @@ void scsi_remove_device(struct scsi_device *sdev)
                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;
@@ -741,6 +783,10 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost)
 
 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;
@@ -754,83 +800,20 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
        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, }, }, }, };