diff options
author | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-02-06 08:49:55 -0500 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-11-04 15:52:55 -0400 |
commit | b36235df01ec4141b4e589571d6789076c346d88 (patch) | |
tree | 0fc4ba6cd2cbb4f563f398a48153dbbdf69483c6 /drivers/block/nvme.c | |
parent | be7b62754e097adc0cb16c25c9ee86ee20de62fb (diff) |
NVMe: Detect commands that are completed twice
Set the context value to CMD_CTX_COMPLETED, and print a message in the
sync_completion handler if we see it.
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme.c')
-rw-r--r-- | drivers/block/nvme.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 802d763d9d06..2dd09e7e142d 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c | |||
@@ -169,12 +169,15 @@ enum { | |||
169 | 169 | ||
170 | #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) | 170 | #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) |
171 | #define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) | 171 | #define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) |
172 | #define CMD_CTX_COMPLETED (0x2010 + CMD_CTX_BASE) | ||
172 | 173 | ||
173 | static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) | 174 | static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) |
174 | { | 175 | { |
175 | unsigned long data; | 176 | unsigned long data; |
177 | unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); | ||
176 | 178 | ||
177 | data = nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)]; | 179 | data = nvmeq->cmdid_data[offset]; |
180 | nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED; | ||
178 | clear_bit(cmdid, nvmeq->cmdid_data); | 181 | clear_bit(cmdid, nvmeq->cmdid_data); |
179 | wake_up(&nvmeq->sq_full); | 182 | wake_up(&nvmeq->sq_full); |
180 | return data; | 183 | return data; |
@@ -182,8 +185,8 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) | |||
182 | 185 | ||
183 | static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) | 186 | static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) |
184 | { | 187 | { |
185 | nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = | 188 | unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); |
186 | CMD_CTX_CANCELLED; | 189 | nvmeq->cmdid_data[offset] = CMD_CTX_CANCELLED; |
187 | } | 190 | } |
188 | 191 | ||
189 | static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) | 192 | static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) |
@@ -402,6 +405,12 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, | |||
402 | struct sync_cmd_info *cmdinfo = ctx; | 405 | struct sync_cmd_info *cmdinfo = ctx; |
403 | if ((unsigned long)cmdinfo == CMD_CTX_CANCELLED) | 406 | if ((unsigned long)cmdinfo == CMD_CTX_CANCELLED) |
404 | return; | 407 | return; |
408 | if (unlikely((unsigned long)cmdinfo == CMD_CTX_COMPLETED)) { | ||
409 | dev_warn(nvmeq->q_dmadev, | ||
410 | "completed id %d twice on queue %d\n", | ||
411 | cqe->command_id, le16_to_cpup(&cqe->sq_id)); | ||
412 | return; | ||
413 | } | ||
405 | cmdinfo->result = le32_to_cpup(&cqe->result); | 414 | cmdinfo->result = le32_to_cpup(&cqe->result); |
406 | cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; | 415 | cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; |
407 | wake_up_process(cmdinfo->task); | 416 | wake_up_process(cmdinfo->task); |