diff options
author | James Bottomley <James.Bottomley@steeleye.com> | 2005-12-15 23:01:43 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-12-17 11:48:08 -0500 |
commit | 863a930a40eb7f2d18534c2c166b22582f5c6cfd (patch) | |
tree | d2f74a0351a09e184e124fd6ecf16e02ab768a0b | |
parent | 42e33148df38c60b99d984b76b302c64397ebe4c (diff) |
[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>
-rw-r--r-- | drivers/scsi/scsi_scan.c | 48 |
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, | |||
400 | return found_target; | 400 | return found_target; |
401 | } | 401 | } |
402 | 402 | ||
403 | struct work_queue_wrapper { | ||
404 | struct work_struct work; | ||
405 | struct scsi_target *starget; | ||
406 | }; | ||
407 | |||
408 | static void scsi_target_reap_work(void *data) { | ||
409 | struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; | ||
410 | struct scsi_target *starget = wqw->starget; | ||
411 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
412 | unsigned long flags; | ||
413 | |||
414 | kfree(wqw); | ||
415 | |||
416 | spin_lock_irqsave(shost->host_lock, flags); | ||
417 | |||
418 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | ||
419 | list_del_init(&starget->siblings); | ||
420 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
421 | device_del(&starget->dev); | ||
422 | transport_unregister_device(&starget->dev); | ||
423 | put_device(&starget->dev); | ||
424 | return; | ||
425 | |||
426 | } | ||
427 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
428 | |||
429 | return; | ||
430 | } | ||
431 | |||
403 | /** | 432 | /** |
404 | * scsi_target_reap - check to see if target is in use and destroy if not | 433 | * scsi_target_reap - check to see if target is in use and destroy if not |
405 | * | 434 | * |
@@ -411,19 +440,18 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
411 | */ | 440 | */ |
412 | void scsi_target_reap(struct scsi_target *starget) | 441 | void scsi_target_reap(struct scsi_target *starget) |
413 | { | 442 | { |
414 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 443 | struct work_queue_wrapper *wqw = |
415 | unsigned long flags; | 444 | kzalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC); |
416 | spin_lock_irqsave(shost->host_lock, flags); | ||
417 | 445 | ||
418 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | 446 | if (!wqw) { |
419 | list_del_init(&starget->siblings); | 447 | starget_printk(KERN_ERR, starget, |
420 | spin_unlock_irqrestore(shost->host_lock, flags); | 448 | "Failed to allocate memory in scsi_reap_target()\n"); |
421 | device_del(&starget->dev); | ||
422 | transport_unregister_device(&starget->dev); | ||
423 | put_device(&starget->dev); | ||
424 | return; | 449 | return; |
425 | } | 450 | } |
426 | spin_unlock_irqrestore(shost->host_lock, flags); | 451 | |
452 | INIT_WORK(&wqw->work, scsi_target_reap_work, wqw); | ||
453 | wqw->starget = starget; | ||
454 | schedule_work(&wqw->work); | ||
427 | } | 455 | } |
428 | 456 | ||
429 | /** | 457 | /** |