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>
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c
index 06a6aea..4bfed59 100644
--- a/drivers/block/nvme.c
+++ b/drivers/block/nvme.c
@@ -155,7 +155,9 @@
 }
 
 /* If you need more than four handlers, you'll need to change how
- * alloc_cmdid and nvme_process_cq work
+ * alloc_cmdid and nvme_process_cq work.  Also, aborted commands take
+ * the sync_completion path (if they complete), so don't put anything
+ * else in slot zero.
  */
 enum {
 	sync_completion_id = 0,
@@ -172,6 +174,11 @@
 	return data;
 }
 
+static void clear_cmdid_data(struct nvme_queue *nvmeq, int cmdid)
+{
+	nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = 0;
+}
+
 static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
 {
 	int qid, cpu = get_cpu();
@@ -386,6 +393,8 @@
 						struct nvme_completion *cqe)
 {
 	struct sync_cmd_info *cmdinfo = ctx;
+	if (!cmdinfo)
+		return;	/* Command aborted */
 	cmdinfo->result = le32_to_cpup(&cqe->result);
 	cmdinfo->status = le16_to_cpup(&cqe->status) >> 1;
 	wake_up_process(cmdinfo->task);
@@ -446,12 +455,19 @@
 	return nvme_process_cq(data);
 }
 
+static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid)
+{
+	spin_lock_irq(&nvmeq->q_lock);
+	clear_cmdid_data(nvmeq, cmdid);
+	spin_unlock_irq(&nvmeq->q_lock);
+}
+
 /*
  * Returns 0 on success.  If the result is negative, it's a Linux error code;
  * if the result is positive, it's an NVM Express status code
  */
-static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd,
-								u32 *result)
+static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
+					struct nvme_command *cmd, u32 *result)
 {
 	int cmdid;
 	struct sync_cmd_info cmdinfo;
@@ -459,15 +475,20 @@
 	cmdinfo.task = current;
 	cmdinfo.status = -EINTR;
 
-	cmdid = alloc_cmdid_killable(q, &cmdinfo, sync_completion_id);
+	cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id);
 	if (cmdid < 0)
 		return cmdid;
 	cmd->common.command_id = cmdid;
 
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	nvme_submit_cmd(q, cmd);
+	set_current_state(TASK_KILLABLE);
+	nvme_submit_cmd(nvmeq, cmd);
 	schedule();
 
+	if (cmdinfo.status == -EINTR) {
+		nvme_abort_command(nvmeq, cmdid);
+		return -EINTR;
+	}
+
 	if (result)
 		*result = cmdinfo.result;