Commit e85248e5 authored by Matthew Wilcox's avatar Matthew Wilcox

NVMe: Record the timeout for each command

In addition to recording the completion data for each command, record
the anticipated completion time.  Choose a timeout of 5 seconds for
normal I/Os and 60 seconds for admin I/Os.
Signed-off-by: default avatarMatthew Wilcox <matthew.r.wilcox@intel.com>
parent ec6ce618
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) #define SQ_SIZE(depth) (depth * sizeof(struct nvme_command))
#define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) #define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion))
#define NVME_MINORS 64 #define NVME_MINORS 64
#define IO_TIMEOUT (5 * HZ)
#define ADMIN_TIMEOUT (60 * HZ)
static int nvme_major; static int nvme_major;
module_param(nvme_major, int, 0); module_param(nvme_major, int, 0);
...@@ -119,6 +121,16 @@ static inline void _nvme_check_size(void) ...@@ -119,6 +121,16 @@ static inline void _nvme_check_size(void)
BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64);
} }
struct nvme_cmd_info {
unsigned long ctx;
unsigned long timeout;
};
static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq)
{
return (void *)&nvmeq->cmdid_data[BITS_TO_LONGS(nvmeq->q_depth)];
}
/** /**
* alloc_cmdid - Allocate a Command ID * alloc_cmdid - Allocate a Command ID
* @param nvmeq The queue that will be used for this command * @param nvmeq The queue that will be used for this command
...@@ -131,10 +143,11 @@ static inline void _nvme_check_size(void) ...@@ -131,10 +143,11 @@ static inline void _nvme_check_size(void)
* Passing in a pointer that's not 4-byte aligned will cause a BUG. * Passing in a pointer that's not 4-byte aligned will cause a BUG.
* We can change this if it becomes a problem. * We can change this if it becomes a problem.
*/ */
static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler) static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler,
unsigned timeout)
{ {
int depth = nvmeq->q_depth; int depth = nvmeq->q_depth;
unsigned long data = (unsigned long)ctx | handler; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
int cmdid; int cmdid;
BUG_ON((unsigned long)ctx & 3); BUG_ON((unsigned long)ctx & 3);
...@@ -145,16 +158,17 @@ static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler) ...@@ -145,16 +158,17 @@ static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler)
return -EBUSY; return -EBUSY;
} while (test_and_set_bit(cmdid, nvmeq->cmdid_data)); } while (test_and_set_bit(cmdid, nvmeq->cmdid_data));
nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(depth)] = data; info[cmdid].ctx = (unsigned long)ctx | handler;
info[cmdid].timeout = jiffies + timeout;
return cmdid; return cmdid;
} }
static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
int handler) int handler, unsigned timeout)
{ {
int cmdid; int cmdid;
wait_event_killable(nvmeq->sq_full, wait_event_killable(nvmeq->sq_full,
(cmdid = alloc_cmdid(nvmeq, ctx, handler)) >= 0); (cmdid = alloc_cmdid(nvmeq, ctx, handler, timeout)) >= 0);
return (cmdid < 0) ? -EINTR : cmdid; return (cmdid < 0) ? -EINTR : cmdid;
} }
...@@ -175,12 +189,12 @@ enum { ...@@ -175,12 +189,12 @@ enum {
static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid)
{ {
unsigned long data; unsigned long data;
unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
if (cmdid > nvmeq->q_depth) if (cmdid >= nvmeq->q_depth)
return CMD_CTX_INVALID; return CMD_CTX_INVALID;
data = nvmeq->cmdid_data[offset]; data = info[cmdid].ctx;
nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED; info[cmdid].ctx = CMD_CTX_COMPLETED;
clear_bit(cmdid, nvmeq->cmdid_data); clear_bit(cmdid, nvmeq->cmdid_data);
wake_up(&nvmeq->sq_full); wake_up(&nvmeq->sq_full);
return data; return data;
...@@ -188,8 +202,8 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) ...@@ -188,8 +202,8 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid)
static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid)
{ {
unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
nvmeq->cmdid_data[offset] = CMD_CTX_CANCELLED; info[cmdid].ctx = CMD_CTX_CANCELLED;
} }
static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
...@@ -327,7 +341,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, ...@@ -327,7 +341,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
goto congestion; goto congestion;
info->bio = bio; info->bio = bio;
cmdid = alloc_cmdid(nvmeq, info, bio_completion_id); cmdid = alloc_cmdid(nvmeq, info, bio_completion_id, IO_TIMEOUT);
if (unlikely(cmdid < 0)) if (unlikely(cmdid < 0))
goto free_info; goto free_info;
...@@ -506,7 +520,7 @@ static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) ...@@ -506,7 +520,7 @@ static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid)
* if the result is positive, it's an NVM Express status code * if the result is positive, it's an NVM Express status code
*/ */
static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
struct nvme_command *cmd, u32 *result) struct nvme_command *cmd, u32 *result, unsigned timeout)
{ {
int cmdid; int cmdid;
struct sync_cmd_info cmdinfo; struct sync_cmd_info cmdinfo;
...@@ -514,7 +528,8 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, ...@@ -514,7 +528,8 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
cmdinfo.task = current; cmdinfo.task = current;
cmdinfo.status = -EINTR; cmdinfo.status = -EINTR;
cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id); cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id,
timeout);
if (cmdid < 0) if (cmdid < 0)
return cmdid; return cmdid;
cmd->common.command_id = cmdid; cmd->common.command_id = cmdid;
...@@ -537,7 +552,7 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, ...@@ -537,7 +552,7 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd, static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
u32 *result) u32 *result)
{ {
return nvme_submit_sync_cmd(dev->queues[0], cmd, result); return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT);
} }
static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id) static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
...@@ -630,7 +645,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, ...@@ -630,7 +645,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
int depth, int vector) int depth, int vector)
{ {
struct device *dmadev = &dev->pci_dev->dev; struct device *dmadev = &dev->pci_dev->dev;
unsigned extra = (depth + BITS_TO_LONGS(depth)) * sizeof(long); unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info));
struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL); struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);
if (!nvmeq) if (!nvmeq)
return NULL; return NULL;
...@@ -892,7 +907,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) ...@@ -892,7 +907,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
* additional races since q_lock already protects against other CPUs. * additional races since q_lock already protects against other CPUs.
*/ */
put_nvmeq(nvmeq); put_nvmeq(nvmeq);
status = nvme_submit_sync_cmd(nvmeq, &c, &result); status = nvme_submit_sync_cmd(nvmeq, &c, &result, IO_TIMEOUT);
nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents);
put_user(result, &uio->result); put_user(result, &uio->result);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment