summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrashant Kamble <[email protected]>2026-05-24 15:36:21 +0530
committerNeil Armstrong <[email protected]>2026-05-28 12:56:02 +0200
commit29c40bb2a13ed1c294497044e2299cc0a511ce32 (patch)
tree606c1d7b214ac076770a33ceadd94396a10c91e1
parent987907ae4bcc5d6055bdf7d318a3edf53e14d5fa (diff)
nvme: fix dcache invalidation range in identify command
When the identify buffer crosses a page boundary, PRP2 is used and dma_addr is advanced to the second page: dma_addr += (page_size - offset); The subsequent invalidate_dcache_range() calls then use the modified dma_addr instead of the original buffer start address. As a result, the beginning of the identify buffer is not invalidated and the invalidation range extends past the end of the buffer. Fix this by preserving the original DMA buffer address for cache invalidation. Suggested-by: Neil Armstrong <[email protected]> Signed-off-by: Prashant Kamble <[email protected]> Reviewed-by: Neil Armstrong <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Neil Armstrong <[email protected]>
-rw-r--r--drivers/nvme/nvme.c26
1 files changed, 22 insertions, 4 deletions
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index 0631b190b97..da7dad9f52b 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -27,6 +27,23 @@
#define IO_TIMEOUT 30
#define MAX_PRP_POOL 512
+/**
+ * nvme_invalidate_cache_aligned() - invalidate cache with proper alignment
+ *
+ * Aligns cache invalidation to cacheline boundaries to ensure correct
+ * behavior even when the DMA buffer is not aligned to page boundaries.
+ *
+ * @addr: The start address of the buffer
+ * @length: The length of the buffer in bytes
+ */
+static inline void nvme_invalidate_cache_aligned(uintptr_t addr, int length)
+{
+ uintptr_t start_addr = addr & ~(ARCH_DMA_MINALIGN - 1);
+ uintptr_t end_addr = ALIGN(addr + length, ARCH_DMA_MINALIGN);
+
+ invalidate_dcache_range(start_addr, end_addr);
+}
+
static int nvme_wait_csts(struct nvme_dev *dev, u32 mask, u32 val)
{
int timeout;
@@ -456,6 +473,7 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
u32 page_size = dev->page_size;
int offset = dma_addr & (page_size - 1);
int length = sizeof(struct nvme_id_ctrl);
+ dma_addr_t orig_dma_addr = dma_addr;
int ret;
memset(&c, 0, sizeof(c));
@@ -473,13 +491,13 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
c.identify.cns = cpu_to_le32(cns);
- invalidate_dcache_range(dma_addr,
- dma_addr + sizeof(struct nvme_id_ctrl));
+ nvme_invalidate_cache_aligned((uintptr_t)orig_dma_addr,
+ sizeof(struct nvme_id_ctrl));
ret = nvme_submit_admin_cmd(dev, &c, NULL);
if (!ret)
- invalidate_dcache_range(dma_addr,
- dma_addr + sizeof(struct nvme_id_ctrl));
+ nvme_invalidate_cache_aligned((uintptr_t)orig_dma_addr,
+ sizeof(struct nvme_id_ctrl));
return ret;
}