diff options
Diffstat (limited to 'drivers/scsi/scsi_lib.c')
-rw-r--r-- | drivers/scsi/scsi_lib.c | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 3574ba935af8..701a328f7beb 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> |
@@ -436,6 +437,7 @@ free_bios: | |||
436 | * scsi_execute_async - insert request | 437 | * scsi_execute_async - insert request |
437 | * @sdev: scsi device | 438 | * @sdev: scsi device |
438 | * @cmd: scsi command | 439 | * @cmd: scsi command |
440 | * @cmd_len: length of scsi cdb | ||
439 | * @data_direction: data direction | 441 | * @data_direction: data direction |
440 | * @buffer: data buffer (this can be a kernel buffer or scatterlist) | 442 | * @buffer: data buffer (this can be a kernel buffer or scatterlist) |
441 | * @bufflen: len of buffer | 443 | * @bufflen: len of buffer |
@@ -445,7 +447,7 @@ free_bios: | |||
445 | * @flags: or into request flags | 447 | * @flags: or into request flags |
446 | **/ | 448 | **/ |
447 | int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, | 449 | int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, |
448 | int data_direction, void *buffer, unsigned bufflen, | 450 | int cmd_len, int data_direction, void *buffer, unsigned bufflen, |
449 | int use_sg, int timeout, int retries, void *privdata, | 451 | int use_sg, int timeout, int retries, void *privdata, |
450 | void (*done)(void *, char *, int, int), gfp_t gfp) | 452 | void (*done)(void *, char *, int, int), gfp_t gfp) |
451 | { | 453 | { |
@@ -472,7 +474,7 @@ int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, | |||
472 | if (err) | 474 | if (err) |
473 | goto free_req; | 475 | goto free_req; |
474 | 476 | ||
475 | req->cmd_len = COMMAND_SIZE(cmd[0]); | 477 | req->cmd_len = cmd_len; |
476 | memcpy(req->cmd, cmd, req->cmd_len); | 478 | memcpy(req->cmd, cmd, req->cmd_len); |
477 | req->sense = sioc->sense; | 479 | req->sense = sioc->sense; |
478 | req->sense_len = 0; | 480 | req->sense_len = 0; |
@@ -1496,7 +1498,7 @@ static void scsi_kill_request(struct request *req, request_queue_t *q) | |||
1496 | static void scsi_softirq_done(struct request *rq) | 1498 | static void scsi_softirq_done(struct request *rq) |
1497 | { | 1499 | { |
1498 | struct scsi_cmnd *cmd = rq->completion_data; | 1500 | struct scsi_cmnd *cmd = rq->completion_data; |
1499 | unsigned long wait_for = cmd->allowed * cmd->timeout_per_command; | 1501 | unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command; |
1500 | int disposition; | 1502 | int disposition; |
1501 | 1503 | ||
1502 | INIT_LIST_HEAD(&cmd->eh_entry); | 1504 | INIT_LIST_HEAD(&cmd->eh_entry); |
@@ -2247,3 +2249,61 @@ scsi_target_unblock(struct device *dev) | |||
2247 | device_for_each_child(dev, NULL, target_unblock); | 2249 | device_for_each_child(dev, NULL, target_unblock); |
2248 | } | 2250 | } |
2249 | 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); | ||