[PATCH v2] nvme: fix dcache invalidation range in identify command
Neil Armstrong
neil.armstrong at linaro.org
Tue May 26 08:59:18 CEST 2026
On 5/24/26 12:06, 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.
>
> Suggested-by: Neil Armstrong <neil.armstrong at linaro.org>
> Signed-off-by: Prashant Kamble <prashant.kamble223 at gmail.com>
>
> ---
> v2:
> - Align invalidate range to cacheline boundaries
> - Add local helper function to calculate the invalidate range
> ---
> drivers/nvme/nvme.c | 26 ++++++++++++++++++++++----
> 1 file 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;
> }
Reviewed-by: Neil Armstrong <neil.armstrong at linaro.org>
Thanks,
Neil
More information about the U-Boot
mailing list