summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_scan.c
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2006-02-23 14:27:18 -0600
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-02-27 23:37:45 -0600
commitffedb4522571ac170f941678d138a31bc0884ab4 (patch)
tree996572da6cecf4295c730b13c959d5d19836a8c5 /drivers/scsi/scsi_scan.c
parent1fa44ecad2b86475e038aed81b0bf333fa484f8b (diff)
[SCSI] fix scsi process problems and clean up the target reap issues
In order to use the new execute_in_process_context() API, you have to provide it with the work storage, which I do in SCSI in scsi_device and scsi_target, but which also means that we can no longer queue up the target reaps, so instead I moved the target to a state model which allows target_alloc to detect if we've received a dying target and wait for it to be gone. Hopefully, this should also solve the target namespace race. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/scsi_scan.c')
-rw-r--r--drivers/scsi/scsi_scan.c49
1 files changed, 33 insertions, 16 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 94b86d5b146..84f01fd0c8f 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -349,6 +349,8 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
starget->channel = channel;
INIT_LIST_HEAD(&starget->siblings);
INIT_LIST_HEAD(&starget->devices);
+ starget->state = STARGET_RUNNING;
+ retry:
spin_lock_irqsave(shost->host_lock, flags);
found_target = __scsi_find_target(parent, channel, id);
@@ -381,8 +383,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
found_target->reap_ref++;
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(parent);
- kfree(starget);
- return found_target;
+ if (found_target->state != STARGET_DEL) {
+ kfree(starget);
+ return found_target;
+ }
+ /* Unfortunately, we found a dying target; need to
+ * wait until it's dead before we can get a new one */
+ put_device(&found_target->dev);
+ flush_scheduled_work();
+ goto retry;
}
static void scsi_target_reap_usercontext(void *data)
@@ -391,21 +400,13 @@ static void scsi_target_reap_usercontext(void *data)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
+ transport_remove_device(&starget->dev);
+ device_del(&starget->dev);
+ transport_destroy_device(&starget->dev);
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);
- transport_remove_device(&starget->dev);
- device_del(&starget->dev);
- transport_destroy_device(&starget->dev);
- put_device(&starget->dev);
- return;
-
- }
+ list_del_init(&starget->siblings);
spin_unlock_irqrestore(shost->host_lock, flags);
-
- return;
+ put_device(&starget->dev);
}
/**
@@ -419,7 +420,23 @@ static void scsi_target_reap_usercontext(void *data)
*/
void scsi_target_reap(struct scsi_target *starget)
{
- scsi_execute_in_process_context(scsi_target_reap_usercontext, starget);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+ BUG_ON(starget->state == STARGET_DEL);
+ starget->state = STARGET_DEL;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ execute_in_process_context(scsi_target_reap_usercontext,
+ starget, &starget->ew);
+ return;
+
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ return;
}
/**