[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v7 40/48] nvme: handle dma errors
From: |
Klaus Jensen |
Subject: |
[PATCH v7 40/48] nvme: handle dma errors |
Date: |
Wed, 15 Apr 2020 07:51:32 +0200 |
From: Klaus Jensen <address@hidden>
Handling DMA errors gracefully is required for the device to pass the
block/011 test ("disable PCI device while doing I/O") in the blktests
suite.
With this patch the device passes the test by retrying "critical"
transfers (posting of completion entries and processing of submission
queue entries).
If DMA errors occur at any other point in the execution of the command
(say, while mapping the PRPs), the command is aborted with a Data
Transfer Error status code.
Signed-off-by: Klaus Jensen <address@hidden>
Acked-by: Keith Busch <address@hidden>
Reviewed-by: Maxim Levitsky <address@hidden>
---
hw/block/nvme.c | 45 ++++++++++++++++++++++++++++++++-----------
hw/block/trace-events | 2 ++
include/block/nvme.h | 2 +-
3 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index eb15a0bd3cf9..6dcd9c4b4cd0 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -74,14 +74,14 @@ static inline bool nvme_addr_is_cmb(NvmeCtrl *n, hwaddr
addr)
return addr >= low && addr < hi;
}
-static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
+static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
{
if (n->bar.cmbsz && nvme_addr_is_cmb(n, addr)) {
memcpy(buf, nvme_addr_to_cmb(n, addr), size);
- return;
+ return 0;
}
- pci_dma_read(&n->parent_obj, addr, buf, size);
+ return pci_dma_read(&n->parent_obj, addr, buf, size);
}
static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid)
@@ -185,7 +185,7 @@ static uint16_t nvme_map_addr_cmb(NvmeCtrl *n, QEMUIOVector
*iov, hwaddr addr,
}
if (!nvme_addr_is_cmb(n, addr) || !nvme_addr_is_cmb(n, addr + len - 1)) {
- return NVME_DATA_TRAS_ERROR;
+ return NVME_DATA_TRANSFER_ERROR;
}
qemu_iovec_add(iov, nvme_addr_to_cmb(n, addr), len);
@@ -238,6 +238,7 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, QEMUSGList *qsg,
QEMUIOVector *iov,
int num_prps = (len >> n->page_bits) + 1;
uint16_t status;
bool prp_list_in_cmb = false;
+ int ret;
trace_nvme_dev_map_prp(nvme_cid(req), trans_len, len, prp1, prp2,
num_prps);
@@ -277,7 +278,12 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, QEMUSGList *qsg,
QEMUIOVector *iov,
nents = (len + n->page_size - 1) >> n->page_bits;
prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
- nvme_addr_read(n, prp2, (void *)prp_list, prp_trans);
+ ret = nvme_addr_read(n, prp2, (void *)prp_list, prp_trans);
+ if (ret) {
+ trace_nvme_dev_err_addr_read(prp2);
+ status = NVME_DATA_TRANSFER_ERROR;
+ goto unmap;
+ }
while (len != 0) {
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
@@ -296,8 +302,13 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, QEMUSGList *qsg,
QEMUIOVector *iov,
i = 0;
nents = (len + n->page_size - 1) >> n->page_bits;
prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
- nvme_addr_read(n, prp_ent, (void *)prp_list,
- prp_trans);
+ ret = nvme_addr_read(n, prp_ent, (void *)prp_list,
+ prp_trans);
+ if (ret) {
+ trace_nvme_dev_err_addr_read(prp_ent);
+ status = NVME_DATA_TRANSFER_ERROR;
+ goto unmap;
+ }
prp_ent = le64_to_cpu(prp_list[i]);
}
@@ -502,6 +513,7 @@ static void nvme_post_cqes(void *opaque)
NvmeCQueue *cq = opaque;
NvmeCtrl *n = cq->ctrl;
NvmeRequest *req, *next;
+ int ret;
QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) {
NvmeSQueue *sq;
@@ -511,15 +523,21 @@ static void nvme_post_cqes(void *opaque)
break;
}
- QTAILQ_REMOVE(&cq->req_list, req, entry);
sq = req->sq;
req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase);
req->cqe.sq_id = cpu_to_le16(sq->sqid);
req->cqe.sq_head = cpu_to_le16(sq->head);
addr = cq->dma_addr + cq->tail * n->cqe_size;
+ ret = pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe,
+ sizeof(req->cqe));
+ if (ret) {
+ trace_nvme_dev_err_addr_write(addr);
+ timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ 500 * SCALE_MS);
+ break;
+ }
+ QTAILQ_REMOVE(&cq->req_list, req, entry);
nvme_inc_cq_tail(cq);
- pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe,
- sizeof(req->cqe));
nvme_req_clear(req);
QTAILQ_INSERT_TAIL(&sq->req_list, req, entry);
}
@@ -1664,7 +1682,12 @@ static void nvme_process_sq(void *opaque)
while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) {
addr = sq->dma_addr + sq->head * n->sqe_size;
- nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd));
+ if (nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd))) {
+ trace_nvme_dev_err_addr_read(addr);
+ timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ 500 * SCALE_MS);
+ break;
+ }
nvme_inc_sq_head(sq);
req = QTAILQ_FIRST(&sq->req_list);
diff --git a/hw/block/trace-events b/hw/block/trace-events
index 7c277a2999c0..75bde5e676a5 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -87,6 +87,8 @@ nvme_dev_mmio_doorbell_sq(uint16_t sqid, uint16_t new_tail)
"cqid %"PRIu16" new_
# nvme traces for error conditions
nvme_dev_err_mdts(uint16_t cid, size_t mdts, size_t len) "cid %"PRIu16" mdts
%"PRIu64" len %"PRIu64""
nvme_dev_err_aio(uint16_t cid, void *aio, const char *blkname, uint64_t
offset, const char *opc, void *req, uint16_t status) "cid %"PRIu16" aio %p blk
\"%s\" offset %"PRIu64" opc \"%s\" req %p status 0x%"PRIx16""
+nvme_dev_err_addr_read(uint64_t addr) "addr 0x%"PRIx64""
+nvme_dev_err_addr_write(uint64_t addr) "addr 0x%"PRIx64""
nvme_dev_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
nvme_dev_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or
not page aligned: 0x%"PRIx64""
nvme_dev_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned:
0x%"PRIx64""
diff --git a/include/block/nvme.h b/include/block/nvme.h
index c4c669e32fc4..03bee32c27c4 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -457,7 +457,7 @@ enum NvmeStatusCodes {
NVME_INVALID_OPCODE = 0x0001,
NVME_INVALID_FIELD = 0x0002,
NVME_CID_CONFLICT = 0x0003,
- NVME_DATA_TRAS_ERROR = 0x0004,
+ NVME_DATA_TRANSFER_ERROR = 0x0004,
NVME_POWER_LOSS_ABORT = 0x0005,
NVME_INTERNAL_DEV_ERROR = 0x0006,
NVME_CMD_ABORT_REQ = 0x0007,
--
2.26.0
- [PATCH v7 24/48] nvme: add mapping helpers, (continued)
- [PATCH v7 24/48] nvme: add mapping helpers, Klaus Jensen, 2020/04/15
- [PATCH v7 34/48] nvme: refactor NvmeRequest, Klaus Jensen, 2020/04/15
- [PATCH v7 22/48] nvme: bump supported version to v1.3, Klaus Jensen, 2020/04/15
- [PATCH v7 21/48] nvme: provide the mandatory subnqn field, Klaus Jensen, 2020/04/15
- [PATCH v7 25/48] nvme: replace dma_acct with blk_acct equivalent, Klaus Jensen, 2020/04/15
- [PATCH v7 32/48] nvme: add check for mdts, Klaus Jensen, 2020/04/15
- [PATCH v7 27/48] nvme: refactor dma read/write, Klaus Jensen, 2020/04/15
- [PATCH v7 23/48] nvme: memset preallocated requests structures, Klaus Jensen, 2020/04/15
- [PATCH v7 31/48] nvme: refactor request bounds checking, Klaus Jensen, 2020/04/15
- [PATCH v7 33/48] nvme: be consistent about zeros vs zeroes, Klaus Jensen, 2020/04/15
- [PATCH v7 40/48] nvme: handle dma errors,
Klaus Jensen <=
- [PATCH v7 26/48] nvme: remove redundant has_sg member, Klaus Jensen, 2020/04/15
- [PATCH v7 30/48] nvme: verify validity of prp lists in the cmb, Klaus Jensen, 2020/04/15
- [PATCH v7 35/48] nvme: remove NvmeCmd parameter, Klaus Jensen, 2020/04/15
- [PATCH v7 36/48] nvme: allow multiple aios per command, Klaus Jensen, 2020/04/15
- [PATCH v7 37/48] nvme: add nvme_check_rw helper, Klaus Jensen, 2020/04/15
- [PATCH v7 39/48] pci: pass along the return value of dma_memory_rw, Klaus Jensen, 2020/04/15
- [PATCH v7 28/48] nvme: pass request along for tracing, Klaus Jensen, 2020/04/15
- [PATCH v7 44/48] nvme: refactor identify active namespace id list, Klaus Jensen, 2020/04/15
- [PATCH v7 41/48] nvme: harden cmb access, Klaus Jensen, 2020/04/15