[PATCH] nvme: fix dcache invalidation range in identify command
Neil Armstrong
neil.armstrong at linaro.org
Tue May 19 09:16:47 CEST 2026
On 5/19/26 05:56, Prashant Kamble wrote:
> 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.
>
> Signed-off-by: Prashant Kamble <prashant.kamble223 at gmail.com>
> ---
> drivers/nvme/nvme.c | 9 +++++----
> 1 file changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
> index 4f9473367d3..298010de536 100644
> --- a/drivers/nvme/nvme.c
> +++ b/drivers/nvme/nvme.c
> @@ -456,6 +456,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 +474,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));
> + invalidate_dcache_range(orig_dma_addr,
> + 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));
> + invalidate_dcache_range(orig_dma_addr,
> + orig_dma_addr + sizeof(struct nvme_id_ctrl));
The fix is good, but I think this is not enough, the page_size used in this function is
the NVMe page_size, not the host CPU cacheline size, so the dma_addr could not be aligned
on a proper page boundary and the invalidate op could be a no-op.
The proper way to do that is like I added into dwc3:
static inline void dwc3_invalidate_cache(uintptr_t addr, int length)
{
uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1);
uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE);
invalidate_dcache_range((unsigned long)start_addr, (unsigned long)end_addr);
}
Neil
>
> return ret;
> }
More information about the U-Boot
mailing list