#define SQ_SIZE(depth) (depth * sizeof(struct nvme_command))
#define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion))
#define NVME_MINORS 64
+#define IO_TIMEOUT (5 * HZ)
+#define ADMIN_TIMEOUT (60 * HZ)
static int nvme_major;
module_param(nvme_major, int, 0);
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
* @param nvmeq The queue that will be used for this command
* Passing in a pointer that's not 4-byte aligned will cause a BUG.
* 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;
- unsigned long data = (unsigned long)ctx | handler;
+ struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
int cmdid;
BUG_ON((unsigned long)ctx & 3);
return -EBUSY;
} 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;
}
static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
- int handler)
+ int handler, unsigned timeout)
{
int cmdid;
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;
}
static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid)
{
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;
- data = nvmeq->cmdid_data[offset];
- nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED;
+ data = info[cmdid].ctx;
+ info[cmdid].ctx = CMD_CTX_COMPLETED;
clear_bit(cmdid, nvmeq->cmdid_data);
wake_up(&nvmeq->sq_full);
return data;
static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid)
{
- unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth);
- nvmeq->cmdid_data[offset] = CMD_CTX_CANCELLED;
+ struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+ info[cmdid].ctx = CMD_CTX_CANCELLED;
}
static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
goto congestion;
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))
goto free_info;
* if the result is positive, it's an NVM Express status code
*/
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;
struct sync_cmd_info cmdinfo;
cmdinfo.task = current;
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)
return cmdid;
cmd->common.command_id = cmdid;
static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
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)
int depth, int vector)
{
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);
if (!nvmeq)
return NULL;
* additional races since q_lock already protects against other CPUs.
*/
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);
put_user(result, &uio->result);