From 9c0fc1d37f531fd33abc9b3a009b818ff1bddc22 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Fri, 28 Nov 2025 13:23:03 -0800 Subject: [PATCH 1/2] drivers/nvdimm: Use local kmaps Replace the now deprecated kmap_atomic() with kmap_local_page(). Optimizing nvdimm/pmem for highmem makes no sense as this is always 64bit, and the mapped regions for both btt and pmem do not require disabling preemption and pagefaults. Specifically, kmap does not care about the caller's atomic context (such as reads holding the btt arena spinlock) or NVDIMM_IO_ATOMIC semantics to avoid error handling when accessing the btt arena in general. Same for the memcpy cases. kmap local temporary mappings will hold valid across any context switches. Signed-off-by: Davidlohr Bueso Reviewed-by: Dave Jiang > --- Link: https://patch.msgid.link/20251128212303.2170933-1-dave@stgolabs.net Signed-off-by: Ira Weiny --- drivers/nvdimm/btt.c | 12 ++++++------ drivers/nvdimm/pmem.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index a933db961ed7..237edfa1c624 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1104,10 +1104,10 @@ static int btt_data_read(struct arena_info *arena, struct page *page, { int ret; u64 nsoff = to_namespace_offset(arena, lba); - void *mem = kmap_atomic(page); + void *mem = kmap_local_page(page); ret = arena_read_bytes(arena, nsoff, mem + off, len, NVDIMM_IO_ATOMIC); - kunmap_atomic(mem); + kunmap_local(mem); return ret; } @@ -1117,20 +1117,20 @@ static int btt_data_write(struct arena_info *arena, u32 lba, { int ret; u64 nsoff = to_namespace_offset(arena, lba); - void *mem = kmap_atomic(page); + void *mem = kmap_local_page(page); ret = arena_write_bytes(arena, nsoff, mem + off, len, NVDIMM_IO_ATOMIC); - kunmap_atomic(mem); + kunmap_local(mem); return ret; } static void zero_fill_data(struct page *page, unsigned int off, u32 len) { - void *mem = kmap_atomic(page); + void *mem = kmap_local_page(page); memset(mem + off, 0, len); - kunmap_atomic(mem); + kunmap_local(mem); } #ifdef CONFIG_BLK_DEV_INTEGRITY diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 05785ff21a8b..92c67fbbc1c8 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -128,10 +128,10 @@ static void write_pmem(void *pmem_addr, struct page *page, void *mem; while (len) { - mem = kmap_atomic(page); + mem = kmap_local_page(page); chunk = min_t(unsigned int, len, PAGE_SIZE - off); memcpy_flushcache(pmem_addr, mem + off, chunk); - kunmap_atomic(mem); + kunmap_local(mem); len -= chunk; off = 0; page++; @@ -147,10 +147,10 @@ static blk_status_t read_pmem(struct page *page, unsigned int off, void *mem; while (len) { - mem = kmap_atomic(page); + mem = kmap_local_page(page); chunk = min_t(unsigned int, len, PAGE_SIZE - off); rem = copy_mc_to_kernel(mem + off, pmem_addr, chunk); - kunmap_atomic(mem); + kunmap_local(mem); if (rem) return BLK_STS_IOERR; len -= chunk; From a9ba6733c7f1096c4506bf4e34a546e07242df74 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Tue, 3 Feb 2026 10:13:51 +0800 Subject: [PATCH 2/2] nvdimm: virtio_pmem: serialize flush requests Under heavy concurrent flush traffic, virtio-pmem can overflow its request virtqueue (req_vq): virtqueue_add_sgs() starts returning -ENOSPC and the driver logs "no free slots in the virtqueue". Shortly after that the device enters VIRTIO_CONFIG_S_NEEDS_RESET and flush requests fail with "virtio pmem device needs a reset". Serialize virtio_pmem_flush() with a per-device mutex so only one flush request is in-flight at a time. This prevents req_vq descriptor overflow under high concurrency. Reproducer (guest with virtio-pmem): - mkfs.ext4 -F /dev/pmem0 - mount -t ext4 -o dax,noatime /dev/pmem0 /mnt/bench - fio: ioengine=io_uring rw=randwrite bs=4k iodepth=64 numjobs=64 direct=1 fsync=1 runtime=30s time_based=1 - dmesg: "no free slots in the virtqueue" "virtio pmem device needs a reset" Fixes: 6e84200c0a29 ("virtio-pmem: Add virtio pmem driver") Signed-off-by: Li Chen Acked-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260203021353.121091-1-me@linux.beauty Signed-off-by: Ira Weiny --- drivers/nvdimm/nd_virtio.c | 3 ++- drivers/nvdimm/virtio_pmem.c | 1 + drivers/nvdimm/virtio_pmem.h | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c index c3f07be4aa22..af82385be7c6 100644 --- a/drivers/nvdimm/nd_virtio.c +++ b/drivers/nvdimm/nd_virtio.c @@ -44,6 +44,8 @@ static int virtio_pmem_flush(struct nd_region *nd_region) unsigned long flags; int err, err1; + guard(mutex)(&vpmem->flush_lock); + /* * Don't bother to submit the request to the device if the device is * not activated. @@ -53,7 +55,6 @@ static int virtio_pmem_flush(struct nd_region *nd_region) return -EIO; } - might_sleep(); req_data = kmalloc(sizeof(*req_data), GFP_KERNEL); if (!req_data) return -ENOMEM; diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index 2396d19ce549..77b196661905 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -64,6 +64,7 @@ static int virtio_pmem_probe(struct virtio_device *vdev) goto out_err; } + mutex_init(&vpmem->flush_lock); vpmem->vdev = vdev; vdev->priv = vpmem; err = init_vq(vpmem); diff --git a/drivers/nvdimm/virtio_pmem.h b/drivers/nvdimm/virtio_pmem.h index 0dddefe594c4..f72cf17f9518 100644 --- a/drivers/nvdimm/virtio_pmem.h +++ b/drivers/nvdimm/virtio_pmem.h @@ -13,6 +13,7 @@ #include #include #include +#include #include struct virtio_pmem_request { @@ -35,6 +36,9 @@ struct virtio_pmem { /* Virtio pmem request queue */ struct virtqueue *req_vq; + /* Serialize flush requests to the device. */ + struct mutex flush_lock; + /* nvdimm bus registers virtio pmem device */ struct nvdimm_bus *nvdimm_bus; struct nvdimm_bus_descriptor nd_desc;