| From cfa58837ea67bb466941daddf9af56e796fadd1e Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 3 Feb 2026 10:13:51 +0800 |
| Subject: nvdimm: virtio_pmem: serialize flush requests |
| |
| From: Li Chen <me@linux.beauty> |
| |
| [ Upstream commit a9ba6733c7f1096c4506bf4e34a546e07242df74 ] |
| |
| 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 <me@linux.beauty> |
| Acked-by: Pankaj Gupta <pankaj.gupta.linux@gmail.com> |
| Acked-by: Michael S. Tsirkin <mst@redhat.com> |
| Link: https://patch.msgid.link/20260203021353.121091-1-me@linux.beauty |
| Signed-off-by: Ira Weiny <ira.weiny@intel.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| 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 f55d60922b87d..e8ac7425c97cd 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 c9b97aeabf854..89d3d4fc3ce89 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 0dddefe594c46..f72cf17f9518f 100644 |
| --- a/drivers/nvdimm/virtio_pmem.h |
| +++ b/drivers/nvdimm/virtio_pmem.h |
| @@ -13,6 +13,7 @@ |
| #include <linux/module.h> |
| #include <uapi/linux/virtio_pmem.h> |
| #include <linux/libnvdimm.h> |
| +#include <linux/mutex.h> |
| #include <linux/spinlock.h> |
| |
| 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; |
| -- |
| 2.51.0 |
| |