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