summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2005-12-15 20:01:43 -0800
committerJames Bottomley <jejb@mulgrave.(none)>2005-12-17 10:48:08 -0600
commit863a930a40eb7f2d18534c2c166b22582f5c6cfd (patch)
treed2f74a0351a09e184e124fd6ecf16e02ab768a0b /drivers/scsi
parent42e33148df38c60b99d984b76b302c64397ebe4c (diff)
downloadlinux-863a930a40eb7f2d18534c2c166b22582f5c6cfd.tar.bz2
[SCSI] fix scsi_reap_target() device_del from atomic context
scsi_reap_target() was desgined to be called from any context. However it must do a device_del() of the target device, which may only be called from user context. Thus we have to reimplement scsi_reap_target() via a workqueue. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/scsi_scan.c48
1 files changed, 38 insertions, 10 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 94e5167f260d..e36c21e06d31 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -400,6 +400,35 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
return found_target;
}
+struct work_queue_wrapper {
+ struct work_struct work;
+ struct scsi_target *starget;
+};
+
+static void scsi_target_reap_work(void *data) {
+ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data;
+ struct scsi_target *starget = wqw->starget;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+
+ kfree(wqw);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+ list_del_init(&starget->siblings);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ device_del(&starget->dev);
+ transport_unregister_device(&starget->dev);
+ put_device(&starget->dev);
+ return;
+
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ return;
+}
+
/**
* scsi_target_reap - check to see if target is in use and destroy if not
*
@@ -411,19 +440,18 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
*/
void scsi_target_reap(struct scsi_target *starget)
{
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- unsigned long flags;
- spin_lock_irqsave(shost->host_lock, flags);
+ struct work_queue_wrapper *wqw =
+ kzalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC);
- if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
- list_del_init(&starget->siblings);
- spin_unlock_irqrestore(shost->host_lock, flags);
- device_del(&starget->dev);
- transport_unregister_device(&starget->dev);
- put_device(&starget->dev);
+ if (!wqw) {
+ starget_printk(KERN_ERR, starget,
+ "Failed to allocate memory in scsi_reap_target()\n");
return;
}
- spin_unlock_irqrestore(shost->host_lock, flags);
+
+ INIT_WORK(&wqw->work, scsi_target_reap_work, wqw);
+ wqw->starget = starget;
+ schedule_work(&wqw->work);
}
/**