aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/nvme.c
diff options
context:
space:
mode:
authorMatthew Wilcox <matthew.r.wilcox@intel.com>2011-02-04 16:03:56 -0500
committerMatthew Wilcox <matthew.r.wilcox@intel.com>2011-11-04 15:52:55 -0400
commit3c0cf138d7789feb3f335f6f1d24ad8fc8b3a23f (patch)
treed3c4fe55735c275d6571b2dc7dcb80baedbb7490 /drivers/block/nvme.c
parentdb5d0c198d673b6a932b449d4db95a2ad50c755e (diff)
NVMe: Allow fatal signals to interrupt I/O
If the user sends a fatal signal, sleeping in the TASK_KILLABLE state permits the task to be aborted. The only wrinkle is making sure that if/when the command completes later that it doesn't upset anything. Handle this by setting the data pointer to 0, and checking the value isn't NULL in the sync completion path. Eventually, bios can be cancelled through this path too. Note that the cmdid isn't freed to prevent reuse. We should also abort the command in the future, but this is a good start. Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme.c')
-rw-r--r--drivers/block/nvme.c33
1 files changed, 27 insertions, 6 deletions
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c
index 06a6aeaa827a..4bfed59f3629 100644
--- a/drivers/block/nvme.c
+++ b/drivers/block/nvme.c
@@ -155,7 +155,9 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
155} 155}
156 156
157/* If you need more than four handlers, you'll need to change how 157/* If you need more than four handlers, you'll need to change how
158 * alloc_cmdid and nvme_process_cq work 158 * alloc_cmdid and nvme_process_cq work. Also, aborted commands take
159 * the sync_completion path (if they complete), so don't put anything
160 * else in slot zero.
159 */ 161 */
160enum { 162enum {
161 sync_completion_id = 0, 163 sync_completion_id = 0,
@@ -172,6 +174,11 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid)
172 return data; 174 return data;
173} 175}
174 176
177static void clear_cmdid_data(struct nvme_queue *nvmeq, int cmdid)
178{
179 nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = 0;
180}
181
175static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) 182static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
176{ 183{
177 int qid, cpu = get_cpu(); 184 int qid, cpu = get_cpu();
@@ -386,6 +393,8 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx,
386 struct nvme_completion *cqe) 393 struct nvme_completion *cqe)
387{ 394{
388 struct sync_cmd_info *cmdinfo = ctx; 395 struct sync_cmd_info *cmdinfo = ctx;
396 if (!cmdinfo)
397 return; /* Command aborted */
389 cmdinfo->result = le32_to_cpup(&cqe->result); 398 cmdinfo->result = le32_to_cpup(&cqe->result);
390 cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; 399 cmdinfo->status = le16_to_cpup(&cqe->status) >> 1;
391 wake_up_process(cmdinfo->task); 400 wake_up_process(cmdinfo->task);
@@ -446,12 +455,19 @@ static irqreturn_t nvme_irq(int irq, void *data)
446 return nvme_process_cq(data); 455 return nvme_process_cq(data);
447} 456}
448 457
458static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid)
459{
460 spin_lock_irq(&nvmeq->q_lock);
461 clear_cmdid_data(nvmeq, cmdid);
462 spin_unlock_irq(&nvmeq->q_lock);
463}
464
449/* 465/*
450 * Returns 0 on success. If the result is negative, it's a Linux error code; 466 * Returns 0 on success. If the result is negative, it's a Linux error code;
451 * if the result is positive, it's an NVM Express status code 467 * if the result is positive, it's an NVM Express status code
452 */ 468 */
453static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd, 469static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
454 u32 *result) 470 struct nvme_command *cmd, u32 *result)
455{ 471{
456 int cmdid; 472 int cmdid;
457 struct sync_cmd_info cmdinfo; 473 struct sync_cmd_info cmdinfo;
@@ -459,15 +475,20 @@ static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd,
459 cmdinfo.task = current; 475 cmdinfo.task = current;
460 cmdinfo.status = -EINTR; 476 cmdinfo.status = -EINTR;
461 477
462 cmdid = alloc_cmdid_killable(q, &cmdinfo, sync_completion_id); 478 cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id);
463 if (cmdid < 0) 479 if (cmdid < 0)
464 return cmdid; 480 return cmdid;
465 cmd->common.command_id = cmdid; 481 cmd->common.command_id = cmdid;
466 482
467 set_current_state(TASK_UNINTERRUPTIBLE); 483 set_current_state(TASK_KILLABLE);
468 nvme_submit_cmd(q, cmd); 484 nvme_submit_cmd(nvmeq, cmd);
469 schedule(); 485 schedule();
470 486
487 if (cmdinfo.status == -EINTR) {
488 nvme_abort_command(nvmeq, cmdid);
489 return -EINTR;
490 }
491
471 if (result) 492 if (result)
472 *result = cmdinfo.result; 493 *result = cmdinfo.result;
473 494