From: Shahar Shitrit <shshitrit@nvidia.com>
To: netdev@vger.kernel.org, mst@redhat.com, jasowang@redhat.com,
pabeni@redhat.com
Cc: virtualization@lists.linux.dev, parav@nvidia.com,
shshitrit@nvidia.com, yohadt@nvidia.com,
xuanzhuo@linux.alibaba.com, eperezma@redhat.com, jgg@ziepe.ca,
kevin.tian@intel.com, kuba@kernel.org, andrew+netdev@lunn.ch,
edumazet@google.com, danielj@nvidia.com, gal@nvidia.com,
tariqt@nvidia.com
Subject: [PATCH net-next v22 02/14] virtio_pci: Fix sleeping under spinlock in admin command path
Date: Sun, 16 Aug 2026 15:20:59 +0300 [thread overview]
Message-ID: <20260816122111.2495240-3-shshitrit@nvidia.com> (raw)
In-Reply-To: <20260816122111.2495240-1-shshitrit@nvidia.com>
virtqueue_exec_admin_cmd() calls virtqueue_add_sgs() with GFP_KERNEL
while holding a spinlock. GFP_KERNEL allocations can sleep, which is
illegal in atomic context.
The issue becomes more significant with the next commit, which exposes
vp_modern_admin_cmd_exec() through virtio_config_ops. This makes the
admin command path available to device drivers and potentially reachable
from userspace, increasing the likelihood of hitting the invalid sleep
under memory pressure.
Fix this by replacing the spinlock with a mutex, which allows GFP_KERNEL
to sleep as needed. Since the virtqueue done callback runs in interrupt
context and cannot take a mutex, defer its work to a work item that runs
in process context.
Signed-off-by: Shahar Shitrit <shshitrit@nvidia.com>
---
drivers/virtio/virtio_pci_common.c | 5 ++++
drivers/virtio/virtio_pci_common.h | 5 +++-
drivers/virtio/virtio_pci_modern.c | 37 +++++++++++++++++++-----------
3 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index 164f480b18a6..ec0c92c782d6 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -256,6 +256,11 @@ static void vp_del_vq(struct virtqueue *vq, struct virtio_pci_vq_info *info)
spin_unlock_irqrestore(&vp_dev->lock, flags);
}
+ if (vp_is_avq(vq->vdev, vq->index)) {
+ cancel_work_sync(&vp_dev->admin_vq.work);
+ vp_dev->admin_vq.info = NULL;
+ }
+
vp_dev->del_vq(info);
kfree(info);
}
diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h
index fc26e035e7a6..8b0178295342 100644
--- a/drivers/virtio/virtio_pci_common.h
+++ b/drivers/virtio/virtio_pci_common.h
@@ -30,6 +30,7 @@
#include <linux/highmem.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
struct virtio_pci_vq_info {
/* the actual virtqueue */
@@ -46,7 +47,9 @@ struct virtio_pci_admin_vq {
/* Virtqueue info associated with this admin queue. */
struct virtio_pci_vq_info *info;
/* Protects virtqueue access. */
- spinlock_t lock;
+ struct mutex lock;
+ /* Admin command completion work. */
+ struct work_struct work;
u64 supported_cmds;
u8 max_dev_parts_objects;
struct ida dev_parts_ida;
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index ef4a64eb9982..dab63b044a31 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -47,13 +47,20 @@ static int vp_avq_index(struct virtio_device *vdev, u16 *index, u16 *num)
void vp_modern_avq_done(struct virtqueue *vq)
{
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
- struct virtio_pci_admin_vq *admin_vq = &vp_dev->admin_vq;
+
+ schedule_work(&vp_dev->admin_vq.work);
+}
+
+static void vp_modern_avq_work(struct work_struct *work)
+{
unsigned int status_size = sizeof(struct virtio_admin_cmd_status);
+ struct virtio_pci_admin_vq *admin_vq =
+ container_of(work, struct virtio_pci_admin_vq, work);
+ struct virtqueue *vq = admin_vq->info->vq;
struct virtio_admin_cmd *cmd;
- unsigned long flags;
unsigned int len;
- spin_lock_irqsave(&admin_vq->lock, flags);
+ mutex_lock(&admin_vq->lock);
do {
virtqueue_disable_cb(vq);
while ((cmd = virtqueue_get_buf(vq, &len))) {
@@ -71,7 +78,7 @@ void vp_modern_avq_done(struct virtqueue *vq)
complete(&cmd->completion);
}
} while (!virtqueue_enable_cb(vq));
- spin_unlock_irqrestore(&admin_vq->lock, flags);
+ mutex_unlock(&admin_vq->lock);
}
static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq,
@@ -82,7 +89,6 @@ static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq,
struct virtio_admin_cmd *cmd)
{
struct virtqueue *vq;
- unsigned long flags;
int ret;
vq = admin_vq->info->vq;
@@ -100,11 +106,11 @@ static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq,
if (virtqueue_is_broken(vq))
return -EIO;
- spin_lock_irqsave(&admin_vq->lock, flags);
+ mutex_lock(&admin_vq->lock);
ret = virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, GFP_KERNEL);
if (ret < 0) {
if (ret == -ENOSPC) {
- spin_unlock_irqrestore(&admin_vq->lock, flags);
+ mutex_unlock(&admin_vq->lock);
cpu_relax();
goto again;
}
@@ -112,14 +118,14 @@ static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq,
}
if (!virtqueue_kick(vq))
goto unlock_err;
- spin_unlock_irqrestore(&admin_vq->lock, flags);
+ mutex_unlock(&admin_vq->lock);
wait_for_completion(&cmd->completion);
return cmd->ret;
unlock_err:
- spin_unlock_irqrestore(&admin_vq->lock, flags);
+ mutex_unlock(&admin_vq->lock);
return -EIO;
}
@@ -350,6 +356,8 @@ static void vp_modern_avq_cleanup(struct virtio_device *vdev)
if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
return;
+ cancel_work_sync(&vp_dev->admin_vq.work);
+
vq = vp_dev->admin_vq.info->vq;
if (!vq)
return;
@@ -554,10 +562,12 @@ static void vp_reset(struct virtio_device *vdev)
while (vp_modern_get_status(mdev))
msleep(1);
- vp_modern_avq_cleanup(vdev);
-
- /* Flush pending VQ/configuration callbacks. */
+ /* Flush pending VQ/configuration callbacks before cleanup, so that
+ * vp_modern_avq_done() can no longer schedule admin_vq.work.
+ */
vp_synchronize_vectors(vdev);
+
+ vp_modern_avq_cleanup(vdev);
}
static int vp_active_vq(struct virtqueue *vq, u16 msix_vec)
@@ -1284,8 +1294,9 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
vp_dev->avq_index = vp_avq_index;
vp_dev->isr = mdev->isr;
vp_dev->vdev.id = mdev->id;
+ mutex_init(&vp_dev->admin_vq.lock);
+ INIT_WORK(&vp_dev->admin_vq.work, vp_modern_avq_work);
- spin_lock_init(&vp_dev->admin_vq.lock);
return 0;
}
--
2.49.0
next prev parent reply other threads:[~2026-08-16 12:21 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-16 12:20 [PATCH net-next v22 00/14] virtio_net: Add ethtool flow rules support Shahar Shitrit
2026-08-16 12:20 ` [PATCH net-next v22 01/14] virtio_pci: Remove supported_caps cache and build assert Shahar Shitrit
2026-08-16 12:20 ` Shahar Shitrit [this message]
2026-08-16 12:21 ` [PATCH net-next v22 03/14] virtio: Add config_op for admin commands Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 04/14] virtio: Expose generic device capability operations Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 05/14] virtio: Expose object create and destroy API Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 06/14] virtio_net: Query and set flow filter caps Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 07/14] virtio_net: Create a FF group for ethtool steering Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 08/14] ethtool: Introduce ethtool_flow_type_mask() Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 09/14] virtio_net: Implement layer 2 ethtool flow rules Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 10/14] virtio_net: Use existing classifier if possible Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 11/14] virtio_net: Implement IPv4 ethtool flow rules Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 12/14] virtio_net: Add support for IPv6 ethtool steering Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 13/14] virtio_net: Add support for TCP and UDP ethtool rules Shahar Shitrit
2026-08-16 12:21 ` [PATCH net-next v22 14/14] virtio_net: Add get ethtool flow rules ops Shahar Shitrit
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260816122111.2495240-3-shshitrit@nvidia.com \
--to=shshitrit@nvidia.com \
--cc=andrew+netdev@lunn.ch \
--cc=danielj@nvidia.com \
--cc=edumazet@google.com \
--cc=eperezma@redhat.com \
--cc=gal@nvidia.com \
--cc=jasowang@redhat.com \
--cc=jgg@ziepe.ca \
--cc=kevin.tian@intel.com \
--cc=kuba@kernel.org \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=parav@nvidia.com \
--cc=tariqt@nvidia.com \
--cc=virtualization@lists.linux.dev \
--cc=xuanzhuo@linux.alibaba.com \
--cc=yohadt@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox