On 11/23/12 11:37, Bart Van Assche wrote:
> On 10/26/12 14:00, Bart Van Assche wrote:
>> Fix a few race conditions that can be triggered by removing a device:
>> [ ... ]
> 
> I'd like to add the patch below to this series. [ ... ]

Below is another patch (hopefully the last) that I'd like to add to
this series. Note: the reason that I only ran into this issue now is
because I only started testing very recently with an ib_srp version
with the host state check removed from srp_queuecommand().

[PATCH] Make scsi_remove_host() wait for device removal

SCSI LLDs may start cleaning up host resources needed by their
queuecommand() callback as soon as scsi_remove_host() finished.
Hence scsi_remove_host() must wait until blk_cleanup_queue() for
all devices associated with the host has finished.

This patch fixes the following kernel oops:

BUG: spinlock bad magic on CPU#0, multipathd/20128
 lock: 0xffff8801b32e8bc8, .magic: ffff8801, .owner: <none>/-1, .owner_cpu: 
-1556759232
Pid: 20128, comm: multipathd Not tainted 3.7.0-rc7-debug+ #1
Call Trace:
 [<ffffffff8141206f>] spin_dump+0x8c/0x91
 [<ffffffff81412095>] spin_bug+0x21/0x26
 [<ffffffff81218a6f>] do_raw_spin_lock+0x13f/0x150
 [<ffffffff81417b38>] _raw_spin_lock_irqsave+0x78/0xa0
 [<ffffffffa0293c6c>] srp_queuecommand+0x3c/0xc80 [ib_srp]
 [<ffffffffa0002f18>] scsi_dispatch_cmd+0x148/0x310 [scsi_mod]
 [<ffffffffa000a080>] scsi_request_fn+0x320/0x520 [scsi_mod]
 [<ffffffff811ec397>] __blk_run_queue+0x37/0x50
 [<ffffffff811ec3ee>] queue_unplugged+0x3e/0xd0
 [<ffffffff811eef33>] blk_flush_plug_list+0x1c3/0x240
 [<ffffffff811eefc8>] blk_finish_plug+0x18/0x50
 [<ffffffff8119661e>] do_io_submit+0x29e/0xa00
 [<ffffffff81196d90>] sys_io_submit+0x10/0x20
 [<ffffffff81420d82>] system_call_fastpath+0x16/0x1b
---
 drivers/scsi/hosts.c      |   16 ++++++++++++++++
 drivers/scsi/scsi_priv.h  |    2 +-
 drivers/scsi/scsi_scan.c  |    5 ++++-
 drivers/scsi/scsi_sysfs.c |   15 ++++++++++++---
 include/scsi/scsi_host.h  |    1 +
 5 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 593085a..7b6b0b2 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -150,6 +150,19 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum 
scsi_host_state state)
 }
 EXPORT_SYMBOL(scsi_host_set_state);
 
+static bool scsi_device_list_empty(struct Scsi_Host *shost)
+{
+       bool res;
+
+       WARN_ON_ONCE(irqs_disabled());
+
+       spin_lock_irq(shost->host_lock);
+       res = list_empty(&shost->__devices);
+       spin_unlock_irq(shost->host_lock);
+
+       return res;
+}
+
 /**
  * scsi_remove_host - remove a scsi host
  * @shost:     a pointer to a scsi host to remove
@@ -178,6 +191,8 @@ void scsi_remove_host(struct Scsi_Host *shost)
                BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
        spin_unlock_irqrestore(shost->host_lock, flags);
 
+       wait_event(shost->device_list_empty, scsi_device_list_empty(shost));
+
        transport_unregister_device(&shost->shost_gendev);
        device_unregister(&shost->shost_dev);
        device_del(&shost->shost_gendev);
@@ -351,6 +366,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template 
*sht, int privsize)
        shost->shost_state = SHOST_CREATED;
        INIT_LIST_HEAD(&shost->__devices);
        INIT_LIST_HEAD(&shost->__targets);
+       init_waitqueue_head(&shost->device_list_empty);
        INIT_LIST_HEAD(&shost->eh_cmd_q);
        INIT_LIST_HEAD(&shost->starved_list);
        init_waitqueue_head(&shost->host_wait);
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 8f9a0ca..86250fb 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -130,7 +130,7 @@ extern int scsi_sysfs_add_sdev(struct scsi_device *);
 extern int scsi_sysfs_add_host(struct Scsi_Host *);
 extern int scsi_sysfs_register(void);
 extern void scsi_sysfs_unregister(void);
-extern void scsi_sysfs_device_initialize(struct scsi_device *);
+extern int scsi_sysfs_device_initialize(struct scsi_device *);
 extern int scsi_sysfs_target_initialize(struct scsi_device *);
 extern struct scsi_transport_template blank_transport_template;
 extern void __scsi_remove_device(struct scsi_device *);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 3e58b22..ddea318 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -289,7 +289,10 @@ static struct scsi_device *scsi_alloc_sdev(struct 
scsi_target *starget,
        sdev->request_queue->queuedata = sdev;
        scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
 
-       scsi_sysfs_device_initialize(sdev);
+       if (scsi_sysfs_device_initialize(sdev)) {
+               display_failure_msg = 0;
+               goto out_device_destroy;
+       }
 
        if (shost->hostt->slave_alloc) {
                ret = shost->hostt->slave_alloc(sdev);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 2661a957..760fc85 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -348,6 +348,8 @@ static void scsi_device_dev_release_usercontext(struct 
work_struct *work)
        starget->reap_ref++;
        list_del(&sdev->siblings);
        list_del(&sdev->same_target_siblings);
+       if (list_empty(&sdev->host->__devices))
+               wake_up(&sdev->host->device_list_empty);
        spin_unlock_irqrestore(sdev->host->host_lock, flags);
 
        cancel_work_sync(&sdev->event_work);
@@ -1109,11 +1111,12 @@ static struct device_type scsi_dev_type = {
        .groups =       scsi_sdev_attr_groups,
 };
 
-void scsi_sysfs_device_initialize(struct scsi_device *sdev)
+int scsi_sysfs_device_initialize(struct scsi_device *sdev)
 {
        unsigned long flags;
        struct Scsi_Host *shost = sdev->host;
        struct scsi_target  *starget = sdev->sdev_target;
+       int ret = -ENODEV;
 
        device_initialize(&sdev->sdev_gendev);
        sdev->sdev_gendev.bus = &scsi_bus_type;
@@ -1128,10 +1131,16 @@ void scsi_sysfs_device_initialize(struct scsi_device 
*sdev)
                     sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
        sdev->scsi_level = starget->scsi_level;
        transport_setup_device(&sdev->sdev_gendev);
+
        spin_lock_irqsave(shost->host_lock, flags);
-       list_add_tail(&sdev->same_target_siblings, &starget->devices);
-       list_add_tail(&sdev->siblings, &shost->__devices);
+       if (scsi_host_scan_allowed(shost)) {
+               list_add_tail(&sdev->same_target_siblings, &starget->devices);
+               list_add_tail(&sdev->siblings, &shost->__devices);
+               ret = 0;
+       }
        spin_unlock_irqrestore(shost->host_lock, flags);
+
+       return ret;
 }
 
 int scsi_is_sdev_device(const struct device *dev)
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 4908480..4ad2d9f 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -561,6 +561,7 @@ struct Scsi_Host {
         */
        struct list_head        __devices;
        struct list_head        __targets;
+       wait_queue_head_t       device_list_empty;
        
        struct scsi_host_cmd_pool *cmd_pool;
        spinlock_t              free_list_lock;
-- 
1.7.10.4



--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to