diff options
author | James Bottomley <James.Bottomley@steeleye.com> | 2006-02-23 15:27:18 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-02-28 00:37:45 -0500 |
commit | ffedb4522571ac170f941678d138a31bc0884ab4 (patch) | |
tree | 996572da6cecf4295c730b13c959d5d19836a8c5 | |
parent | 1fa44ecad2b86475e038aed81b0bf333fa484f8b (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>
-rw-r--r-- | drivers/scsi/scsi_lib.c | 58 | ||||
-rw-r--r-- | drivers/scsi/scsi_scan.c | 49 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 4 | ||||
-rw-r--r-- | include/scsi/scsi.h | 2 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 10 |
5 files changed, 46 insertions, 77 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index eab303d148d8..3042520c413c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -2257,61 +2257,3 @@ scsi_target_unblock(struct device *dev) | |||
2257 | device_for_each_child(dev, NULL, target_unblock); | 2257 | device_for_each_child(dev, NULL, target_unblock); |
2258 | } | 2258 | } |
2259 | EXPORT_SYMBOL_GPL(scsi_target_unblock); | 2259 | EXPORT_SYMBOL_GPL(scsi_target_unblock); |
2260 | |||
2261 | |||
2262 | struct work_queue_work { | ||
2263 | struct work_struct work; | ||
2264 | void (*fn)(void *); | ||
2265 | void *data; | ||
2266 | }; | ||
2267 | |||
2268 | static void execute_in_process_context_work(void *data) | ||
2269 | { | ||
2270 | void (*fn)(void *data); | ||
2271 | struct work_queue_work *wqw = data; | ||
2272 | |||
2273 | fn = wqw->fn; | ||
2274 | data = wqw->data; | ||
2275 | |||
2276 | kfree(wqw); | ||
2277 | |||
2278 | fn(data); | ||
2279 | } | ||
2280 | |||
2281 | /** | ||
2282 | * scsi_execute_in_process_context - reliably execute the routine with user context | ||
2283 | * @fn: the function to execute | ||
2284 | * @data: data to pass to the function | ||
2285 | * | ||
2286 | * Executes the function immediately if process context is available, | ||
2287 | * otherwise schedules the function for delayed execution. | ||
2288 | * | ||
2289 | * Returns: 0 - function was executed | ||
2290 | * 1 - function was scheduled for execution | ||
2291 | * <0 - error | ||
2292 | */ | ||
2293 | int scsi_execute_in_process_context(void (*fn)(void *data), void *data) | ||
2294 | { | ||
2295 | struct work_queue_work *wqw; | ||
2296 | |||
2297 | if (!in_interrupt()) { | ||
2298 | fn(data); | ||
2299 | return 0; | ||
2300 | } | ||
2301 | |||
2302 | wqw = kmalloc(sizeof(struct work_queue_work), GFP_ATOMIC); | ||
2303 | |||
2304 | if (unlikely(!wqw)) { | ||
2305 | printk(KERN_ERR "Failed to allocate memory\n"); | ||
2306 | WARN_ON(1); | ||
2307 | return -ENOMEM; | ||
2308 | } | ||
2309 | |||
2310 | INIT_WORK(&wqw->work, execute_in_process_context_work, wqw); | ||
2311 | wqw->fn = fn; | ||
2312 | wqw->data = data; | ||
2313 | schedule_work(&wqw->work); | ||
2314 | |||
2315 | return 1; | ||
2316 | } | ||
2317 | EXPORT_SYMBOL_GPL(scsi_execute_in_process_context); | ||
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 94b86d5b1469..84f01fd0c8fd 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, | |||
349 | starget->channel = channel; | 349 | starget->channel = channel; |
350 | INIT_LIST_HEAD(&starget->siblings); | 350 | INIT_LIST_HEAD(&starget->siblings); |
351 | INIT_LIST_HEAD(&starget->devices); | 351 | INIT_LIST_HEAD(&starget->devices); |
352 | starget->state = STARGET_RUNNING; | ||
353 | retry: | ||
352 | spin_lock_irqsave(shost->host_lock, flags); | 354 | spin_lock_irqsave(shost->host_lock, flags); |
353 | 355 | ||
354 | found_target = __scsi_find_target(parent, channel, id); | 356 | found_target = __scsi_find_target(parent, channel, id); |
@@ -381,8 +383,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
381 | found_target->reap_ref++; | 383 | found_target->reap_ref++; |
382 | spin_unlock_irqrestore(shost->host_lock, flags); | 384 | spin_unlock_irqrestore(shost->host_lock, flags); |
383 | put_device(parent); | 385 | put_device(parent); |
384 | kfree(starget); | 386 | if (found_target->state != STARGET_DEL) { |
385 | return found_target; | 387 | kfree(starget); |
388 | return found_target; | ||
389 | } | ||
390 | /* Unfortunately, we found a dying target; need to | ||
391 | * wait until it's dead before we can get a new one */ | ||
392 | put_device(&found_target->dev); | ||
393 | flush_scheduled_work(); | ||
394 | goto retry; | ||
386 | } | 395 | } |
387 | 396 | ||
388 | static void scsi_target_reap_usercontext(void *data) | 397 | static void scsi_target_reap_usercontext(void *data) |
@@ -391,21 +400,13 @@ static void scsi_target_reap_usercontext(void *data) | |||
391 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 400 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
392 | unsigned long flags; | 401 | unsigned long flags; |
393 | 402 | ||
403 | transport_remove_device(&starget->dev); | ||
404 | device_del(&starget->dev); | ||
405 | transport_destroy_device(&starget->dev); | ||
394 | spin_lock_irqsave(shost->host_lock, flags); | 406 | spin_lock_irqsave(shost->host_lock, flags); |
395 | 407 | list_del_init(&starget->siblings); | |
396 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | ||
397 | list_del_init(&starget->siblings); | ||
398 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
399 | transport_remove_device(&starget->dev); | ||
400 | device_del(&starget->dev); | ||
401 | transport_destroy_device(&starget->dev); | ||
402 | put_device(&starget->dev); | ||
403 | return; | ||
404 | |||
405 | } | ||
406 | spin_unlock_irqrestore(shost->host_lock, flags); | 408 | spin_unlock_irqrestore(shost->host_lock, flags); |
407 | 409 | put_device(&starget->dev); | |
408 | return; | ||
409 | } | 410 | } |
410 | 411 | ||
411 | /** | 412 | /** |
@@ -419,7 +420,23 @@ static void scsi_target_reap_usercontext(void *data) | |||
419 | */ | 420 | */ |
420 | void scsi_target_reap(struct scsi_target *starget) | 421 | void scsi_target_reap(struct scsi_target *starget) |
421 | { | 422 | { |
422 | scsi_execute_in_process_context(scsi_target_reap_usercontext, starget); | 423 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
424 | unsigned long flags; | ||
425 | |||
426 | spin_lock_irqsave(shost->host_lock, flags); | ||
427 | |||
428 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | ||
429 | BUG_ON(starget->state == STARGET_DEL); | ||
430 | starget->state = STARGET_DEL; | ||
431 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
432 | execute_in_process_context(scsi_target_reap_usercontext, | ||
433 | starget, &starget->ew); | ||
434 | return; | ||
435 | |||
436 | } | ||
437 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
438 | |||
439 | return; | ||
423 | } | 440 | } |
424 | 441 | ||
425 | /** | 442 | /** |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 902a5def8e62..89055494dfee 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -256,7 +256,9 @@ static void scsi_device_dev_release_usercontext(void *data) | |||
256 | 256 | ||
257 | static void scsi_device_dev_release(struct device *dev) | 257 | static void scsi_device_dev_release(struct device *dev) |
258 | { | 258 | { |
259 | scsi_execute_in_process_context(scsi_device_dev_release_usercontext, dev); | 259 | struct scsi_device *sdp = to_scsi_device(dev); |
260 | execute_in_process_context(scsi_device_dev_release_usercontext, dev, | ||
261 | &sdp->ew); | ||
260 | } | 262 | } |
261 | 263 | ||
262 | static struct class sdev_class = { | 264 | static struct class sdev_class = { |
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 9c331258bc27..c60b8ff2f5e4 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h | |||
@@ -433,6 +433,4 @@ struct scsi_lun { | |||
433 | /* Used to obtain the PCI location of a device */ | 433 | /* Used to obtain the PCI location of a device */ |
434 | #define SCSI_IOCTL_GET_PCI 0x5387 | 434 | #define SCSI_IOCTL_GET_PCI 0x5387 |
435 | 435 | ||
436 | int scsi_execute_in_process_context(void (*fn)(void *data), void *data); | ||
437 | |||
438 | #endif /* _SCSI_SCSI_H */ | 436 | #endif /* _SCSI_SCSI_H */ |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 8d77da932d2c..1ec17ee12815 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/device.h> | 4 | #include <linux/device.h> |
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/spinlock.h> | 6 | #include <linux/spinlock.h> |
7 | #include <linux/workqueue.h> | ||
7 | #include <asm/atomic.h> | 8 | #include <asm/atomic.h> |
8 | 9 | ||
9 | struct request_queue; | 10 | struct request_queue; |
@@ -137,6 +138,8 @@ struct scsi_device { | |||
137 | struct device sdev_gendev; | 138 | struct device sdev_gendev; |
138 | struct class_device sdev_classdev; | 139 | struct class_device sdev_classdev; |
139 | 140 | ||
141 | struct execute_work ew; /* used to get process context on put */ | ||
142 | |||
140 | enum scsi_device_state sdev_state; | 143 | enum scsi_device_state sdev_state; |
141 | unsigned long sdev_data[0]; | 144 | unsigned long sdev_data[0]; |
142 | } __attribute__((aligned(sizeof(unsigned long)))); | 145 | } __attribute__((aligned(sizeof(unsigned long)))); |
@@ -153,6 +156,11 @@ struct scsi_device { | |||
153 | #define scmd_printk(prefix, scmd, fmt, a...) \ | 156 | #define scmd_printk(prefix, scmd, fmt, a...) \ |
154 | dev_printk(prefix, &(scmd)->device->sdev_gendev, fmt, ##a) | 157 | dev_printk(prefix, &(scmd)->device->sdev_gendev, fmt, ##a) |
155 | 158 | ||
159 | enum scsi_target_state { | ||
160 | STARGET_RUNNING = 1, | ||
161 | STARGET_DEL, | ||
162 | }; | ||
163 | |||
156 | /* | 164 | /* |
157 | * scsi_target: representation of a scsi target, for now, this is only | 165 | * scsi_target: representation of a scsi target, for now, this is only |
158 | * used for single_lun devices. If no one has active IO to the target, | 166 | * used for single_lun devices. If no one has active IO to the target, |
@@ -172,6 +180,8 @@ struct scsi_target { | |||
172 | /* means no lun present */ | 180 | /* means no lun present */ |
173 | 181 | ||
174 | char scsi_level; | 182 | char scsi_level; |
183 | struct execute_work ew; | ||
184 | enum scsi_target_state state; | ||
175 | void *hostdata; /* available to low-level driver */ | 185 | void *hostdata; /* available to low-level driver */ |
176 | unsigned long starget_data[0]; /* for the transport */ | 186 | unsigned long starget_data[0]; /* for the transport */ |
177 | /* starget_data must be the last element!!!! */ | 187 | /* starget_data must be the last element!!!! */ |