[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