Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / scsi / scsi_sysfs.c
index aa70d92..9dad2fd 100644 (file)
@@ -443,18 +443,15 @@ static void scsi_device_cls_release(struct device *class_dev)
 
 static void scsi_device_dev_release_usercontext(struct work_struct *work)
 {
-       struct scsi_device *sdev;
+       struct scsi_device *sdev = container_of(work, struct scsi_device,
+                                               ew.work);
+       struct scsi_target *starget = sdev->sdev_target;
        struct device *parent;
        struct list_head *this, *tmp;
        struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL;
        struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL;
        struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL;
        unsigned long flags;
-       struct module *mod;
-
-       sdev = container_of(work, struct scsi_device, ew.work);
-
-       mod = sdev->host->hostt->module;
 
        scsi_dh_release_device(sdev);
 
@@ -516,19 +513,16 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
        kfree(sdev->inquiry);
        kfree(sdev);
 
+       if (starget && atomic_dec_return(&starget->sdev_count) == 0)
+               wake_up(&starget->sdev_wq);
+
        if (parent)
                put_device(parent);
-       module_put(mod);
 }
 
 static void scsi_device_dev_release(struct device *dev)
 {
        struct scsi_device *sdp = to_scsi_device(dev);
-
-       /* Set module pointer as NULL in case of module unloading */
-       if (!try_module_get(sdp->host->hostt->module))
-               sdp->host->hostt->module = NULL;
-
        execute_in_process_context(scsi_device_dev_release_usercontext,
                                   &sdp->ew);
 }
@@ -1535,6 +1529,14 @@ static void __scsi_remove_target(struct scsi_target *starget)
                goto restart;
        }
        spin_unlock_irqrestore(shost->host_lock, flags);
+
+       /*
+        * After scsi_remove_target() returns its caller can remove resources
+        * associated with @starget, e.g. an rport or session. Wait until all
+        * devices associated with @starget have been removed to prevent that
+        * a SCSI error handling callback function triggers a use-after-free.
+        */
+       wait_event(starget->sdev_wq, atomic_read(&starget->sdev_count) == 0);
 }
 
 /**
@@ -1645,6 +1647,9 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
        list_add_tail(&sdev->same_target_siblings, &starget->devices);
        list_add_tail(&sdev->siblings, &shost->__devices);
        spin_unlock_irqrestore(shost->host_lock, flags);
+
+       atomic_inc(&starget->sdev_count);
+
        /*
         * device can now only be removed via __scsi_remove_device() so hold
         * the target.  Target will be held in CREATED state until something