aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2006-02-23 15:27:18 -0500
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-02-28 00:37:45 -0500
commitffedb4522571ac170f941678d138a31bc0884ab4 (patch)
tree996572da6cecf4295c730b13c959d5d19836a8c5
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>
-rw-r--r--drivers/scsi/scsi_lib.c58
-rw-r--r--drivers/scsi/scsi_scan.c49
-rw-r--r--drivers/scsi/scsi_sysfs.c4
-rw-r--r--include/scsi/scsi.h2
-rw-r--r--include/scsi/scsi_device.h10
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}
2259EXPORT_SYMBOL_GPL(scsi_target_unblock); 2259EXPORT_SYMBOL_GPL(scsi_target_unblock);
2260
2261
2262struct work_queue_work {
2263 struct work_struct work;
2264 void (*fn)(void *);
2265 void *data;
2266};
2267
2268static 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 */
2293int 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}
2317EXPORT_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
388static void scsi_target_reap_usercontext(void *data) 397static 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 */
420void scsi_target_reap(struct scsi_target *starget) 421void 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
257static void scsi_device_dev_release(struct device *dev) 257static 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
262static struct class sdev_class = { 264static 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
436int 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
9struct request_queue; 10struct 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
159enum 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!!!! */