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>
This commit is contained in:
Matthew Wilcox 2011-02-04 16:03:56 -05:00
parent db5d0c198d
commit 3c0cf138d7

View file

@ -155,7 +155,9 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
}
/* 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 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid)
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 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx,
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 @@ static irqreturn_t nvme_irq(int irq, void *data)
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 @@ static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd,
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;