[PATCH] add scsi_execute_in_process_context() API

We have several points in the SCSI stack (primarily for our device
functions) where we need to guarantee process context, but (given the
place where the last reference was released) we cannot guarantee this.

This API gets around the issue by executing the function directly if
the caller has process context, but scheduling a workqueue to execute
in process context if the caller doesn't have it. Unfortunately, it
requires memory allocation in interrupt context, but it's better than
what we have previously. The true solution will require a bit of
re-engineering, so isn't appropriate for 2.6.16.

Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>

authored by James Bottomley and committed by faead26d e2230eac

+61
+59
drivers/scsi/scsi_lib.c
··· 16 16 #include <linux/init.h> 17 17 #include <linux/pci.h> 18 18 #include <linux/delay.h> 19 + #include <linux/hardirq.h> 19 20 20 21 #include <scsi/scsi.h> 21 22 #include <scsi/scsi_dbg.h> ··· 2249 2248 device_for_each_child(dev, NULL, target_unblock); 2250 2249 } 2251 2250 EXPORT_SYMBOL_GPL(scsi_target_unblock); 2251 + 2252 + 2253 + struct work_queue_work { 2254 + struct work_struct work; 2255 + void (*fn)(void *); 2256 + void *data; 2257 + }; 2258 + 2259 + static void execute_in_process_context_work(void *data) 2260 + { 2261 + void (*fn)(void *data); 2262 + struct work_queue_work *wqw = data; 2263 + 2264 + fn = wqw->fn; 2265 + data = wqw->data; 2266 + 2267 + kfree(wqw); 2268 + 2269 + fn(data); 2270 + } 2271 + 2272 + /** 2273 + * scsi_execute_in_process_context - reliably execute the routine with user context 2274 + * @fn: the function to execute 2275 + * @data: data to pass to the function 2276 + * 2277 + * Executes the function immediately if process context is available, 2278 + * otherwise schedules the function for delayed execution. 2279 + * 2280 + * Returns: 0 - function was executed 2281 + * 1 - function was scheduled for execution 2282 + * <0 - error 2283 + */ 2284 + int scsi_execute_in_process_context(void (*fn)(void *data), void *data) 2285 + { 2286 + struct work_queue_work *wqw; 2287 + 2288 + if (!in_interrupt()) { 2289 + fn(data); 2290 + return 0; 2291 + } 2292 + 2293 + wqw = kmalloc(sizeof(struct work_queue_work), GFP_ATOMIC); 2294 + 2295 + if (unlikely(!wqw)) { 2296 + printk(KERN_ERR "Failed to allocate memory\n"); 2297 + WARN_ON(1); 2298 + return -ENOMEM; 2299 + } 2300 + 2301 + INIT_WORK(&wqw->work, execute_in_process_context_work, wqw); 2302 + wqw->fn = fn; 2303 + wqw->data = data; 2304 + schedule_work(&wqw->work); 2305 + 2306 + return 1; 2307 + } 2308 + EXPORT_SYMBOL_GPL(scsi_execute_in_process_context);
+2
include/scsi/scsi.h
··· 433 433 /* Used to obtain the PCI location of a device */ 434 434 #define SCSI_IOCTL_GET_PCI 0x5387 435 435 436 + int scsi_execute_in_process_context(void (*fn)(void *data), void *data); 437 + 436 438 #endif /* _SCSI_SCSI_H */