* Re: [PATCH net-next v3] virtio-net: xsk: support tx wake up
From: Menglong Dong @ 2026-06-22 12:27 UTC (permalink / raw)
To: Menglong Dong, Michael S. Tsirkin
Cc: xuanzhuo, eperezma, jasowang, andrew+netdev, davem, edumazet,
kuba, pabeni, netdev, virtualization, linux-kernel
In-Reply-To: <20260621182119-mutt-send-email-mst@kernel.org>
On 2026/6/22 06:31 Michael S. Tsirkin <mst@redhat.com> write:
> On Tue, Jun 16, 2026 at 07:59:12PM +0800, Menglong Dong wrote:
[...]
> >
> > + vring_size = virtqueue_get_vring_size(sq->vq);
> > + need_wakeup = xsk_uses_need_wakeup(pool);
> > +
> > + if (need_wakeup && vring_size == sq->vq->num_free)
> > + xsk_set_tx_need_wakeup(pool);
> > +
>
> why are we doing this here?
> the check after virtnet_xsk_xmit_batch not enough?
> I vaguely think it's some kind of race we are closing?
> Pls add a comment to explain.
Hi, Michael. Thanks for your review.
Yeah, it's for a race condition between user space and kernel
space. I added a comment in V2, which is too confusing, and
I removed it 😢. I'll make it more clear and add it in the V4. The
origin comment is:
* If the sq->vq is empty, and the tx ring is empty, and the user
* submit an entry to the tx ring after virtnet_xsk_xmit_batch() and
* before xsk_set_tx_need_wakeup(), we will lose the chance to wake
* up the tx napi, so we have to set the need_wakeup flag here.
And the logic is like this:
Kernel: tx NAPI is waked up from skb_xmit_done() ->
Kernel: sq->vq and xsk->tx_ring are both empty ->
Kernel: call virtnet_xsk_xmit_batch()
User: submit a entry to the xsk->tx_ring
User: check the wakeup flag
User: wakeup flag is not set, skip send()
Kernel: call xsk_set_tx_need_wakeup(), because sq->vq is empty
If we don't send more data, the data in the xsk->tx_ring will
not be sent forever.
>
> > sent = virtnet_xsk_xmit_batch(sq, pool, budget, &kicks);
> >
> > + if (need_wakeup) {
> > + if (vring_size == sq->vq->num_free)
> > + /* we can't wake up by ourself, and it should be done
> > + * by the user.
> > + */
> > + xsk_set_tx_need_wakeup(pool);
> > + else
> > + /* we can wake up from skb_xmit_done() */
> > + xsk_clear_tx_need_wakeup(pool);
>
> But what if we don't have get tx napi so no wakeup in skb_xmit_done?
Sorry that I'm not sure what "get tx napi" means here ;(
There are entry in sq->vq, so skb_xmit_done() will be called after
the entries in the ring is consumed by the HOST, right?
Then, the corresponding sq->napi will be scheduled, as we ensure
that tx napi is always enabled, which means napi->weight is not
zero, in this commit:
1df5116a41a8 ("virtio_net: xsk: prevent disable tx napi")
Right?
Thanks!
Menglong Dong
>
>
> > + }
> > +
> > if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
> > check_sq_full_and_disable(vi, vi->dev, sq);
> >
> > @@ -1470,9 +1488,6 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> > u64_stats_add(&sq->stats.xdp_tx, sent);
> > u64_stats_update_end(&sq->stats.syncp);
> >
> > - if (xsk_uses_need_wakeup(pool))
> > - xsk_set_tx_need_wakeup(pool);
> > -
> > return sent;
> > }
> >
> > --
> > 2.54.0
>
>
>
^ permalink raw reply
* Re: [PATCH net-next v3] virtio-net: xsk: support tx wake up
From: Menglong Dong @ 2026-06-22 12:28 UTC (permalink / raw)
To: Menglong Dong, Xuan Zhuo
Cc: mst, jasowang, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, virtualization, linux-kernel, eperezma
In-Reply-To: <1782096043.3540094-1-xuanzhuo@linux.alibaba.com>
On 2026/6/22 10:40 Xuan Zhuo <xuanzhuo@linux.alibaba.com> write:
> On Tue, 16 Jun 2026 19:59:12 +0800, Menglong Dong <menglong8.dong@gmail.com> wrote:
> > For now, XDP_RING_NEED_WAKEUP is not supported properly by the virtio-net
> > in the tx path for example: we set xsk_set_tx_need_wakeup() in
> > virtnet_xsk_xmit(), but we didn't call xsk_clear_tx_need_wakeup()
> > anywhere, which means the user will call send() for every packet.
> >
> > We call xsk_set_tx_need_wakeup() after virtnet_xsk_xmit_batch() if sq->vq
> > is empty, as we can't be wakeup by the skb_xmit_done() in this case.
> > Otherwise, we will clear the wakeup flag.
> >
> > Race condition is considered for tx path.
> >
> > Fixes: 89f86675cb03 ("virtio_net: xsk: tx: support xmit xsk buffer")
>
> This is not a bug, so we do not need this.
> And you post this to net-next.
Okay, I'll remove this tag in the V4.
>
>
> > Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
> > ---
> > v3:
[...]
> > +
> > + if (need_wakeup && vring_size == sq->vq->num_free)
> > + xsk_set_tx_need_wakeup(pool);
>
> You need to comment this.
Ack!
>
>
> > +
[...]
> > +
> > if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
> > check_sq_full_and_disable(vi, vi->dev, sq);
>
>
> After fixed above comments, you can add:
>
> Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
OK! Thanks for the review :)
>
> Thanks.
>
>
> >
> > @@ -1470,9 +1488,6 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> > u64_stats_add(&sq->stats.xdp_tx, sent);
> > u64_stats_update_end(&sq->stats.syncp);
> >
> > - if (xsk_uses_need_wakeup(pool))
> > - xsk_set_tx_need_wakeup(pool);
> > -
> > return sent;
> > }
> >
> > --
> > 2.54.0
> >
>
>
^ permalink raw reply
* Re: [patch V2 18/25] timekeeping: Prepare for cross timestamps on arbitrary clock IDs
From: David Woodhouse @ 2026-06-22 12:34 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Miroslav Lichvar, John Stultz, Stephen Boyd, Anna-Maria Behnsen,
Frederic Weisbecker, thomas.weissschuh, Arthur Kiyanovski,
Rodolfo Giometti, Vincent Donnefort, Marc Zyngier, Oliver Upton,
kvmarm, Oliver Upton, Richard Cochran, netdev, Takashi Iwai,
Miri Korenblit, Johannes Berg, Jacob Keller, Tony Nguyen,
Saeed Mahameed, Peter Hilber, Michael S. Tsirkin, virtualization,
linux-wireless, linux-sound, Vadim Fedorenko
In-Reply-To: <87se6eltod.ffs@fw13>
[-- Attachment #1: Type: text/plain, Size: 861 bytes --]
On Mon, 2026-06-22 at 13:07 +0200, Thomas Gleixner wrote:
> On Mon, Jun 22 2026 at 09:55, David Woodhouse wrote:
> > We ended up with ktime_get_snapshot_id() also supporting CLOCK_BOOTTIME
> > and CLOCK_MONOTONIC_RAW, but not get_device_system_crosststamp().
> > Should we make that consistent?
>
> Maybe. The BOOTTIME support is only there for that ARM64 hyper trace muck,
> but has no other relevance.
>
> MONORAW is there for the PTP EXTENDED IOCTL, but with PRECISE the
> snapshot already contains the raw value and you'd have to prevent the
> historical adjustment part for RAW. So I don't see the actual value, but
> I don't have a strong opinion either.
Yeah, I'm not sure I see the need for it; it's just the consistency
thing that slightly bothered me once I had them both in my sights doing
the snapshot_ntp_error() thing in both.
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v3] virtio-net: xsk: support tx wake up
From: Menglong Dong @ 2026-06-22 12:38 UTC (permalink / raw)
To: Jakub Kicinski
Cc: xuanzhuo, Menglong Dong, eperezma, mst, jasowang, andrew+netdev,
davem, edumazet, pabeni, netdev, virtualization, linux-kernel
In-Reply-To: <20260621150610.0ad5d02e@kernel.org>
On 2026/6/22 06:06 Jakub Kicinski <kuba@kernel.org> write:
> On Tue, 16 Jun 2026 19:59:12 +0800 Menglong Dong wrote:
> > For now, XDP_RING_NEED_WAKEUP is not supported properly by the virtio-net
> > in the tx path for example: we set xsk_set_tx_need_wakeup() in
> > virtnet_xsk_xmit(), but we didn't call xsk_clear_tx_need_wakeup()
> > anywhere, which means the user will call send() for every packet.
> >
> > We call xsk_set_tx_need_wakeup() after virtnet_xsk_xmit_batch() if sq->vq
> > is empty, as we can't be wakeup by the skb_xmit_done() in this case.
> > Otherwise, we will clear the wakeup flag.
> >
> > Race condition is considered for tx path.
>
> Seems to follow what mlx5 does so presumably this is fine but IDK if
Yeah, I followed the logic of mlx5. It's amazing that you found it :)
> there's anything virtio-specific that we need to be worried about.
>
> Xuan Zhuo, please TAL?
> --
> mping: VIRTIO NET DRIVER
>
>
^ permalink raw reply
* Re: [PATCH net-next v3] virtio-net: xsk: support tx wake up
From: Michael S. Tsirkin @ 2026-06-22 13:24 UTC (permalink / raw)
To: Menglong Dong
Cc: Menglong Dong, xuanzhuo, eperezma, jasowang, andrew+netdev, davem,
edumazet, kuba, pabeni, netdev, virtualization, linux-kernel
In-Reply-To: <BMgCMK8nRYC94QK7N1SXpQ@linux.dev>
On Mon, Jun 22, 2026 at 08:27:12PM +0800, Menglong Dong wrote:
> On 2026/6/22 06:31 Michael S. Tsirkin <mst@redhat.com> write:
> > On Tue, Jun 16, 2026 at 07:59:12PM +0800, Menglong Dong wrote:
> [...]
> > >
> > > + vring_size = virtqueue_get_vring_size(sq->vq);
> > > + need_wakeup = xsk_uses_need_wakeup(pool);
> > > +
> > > + if (need_wakeup && vring_size == sq->vq->num_free)
> > > + xsk_set_tx_need_wakeup(pool);
> > > +
> >
> > why are we doing this here?
> > the check after virtnet_xsk_xmit_batch not enough?
> > I vaguely think it's some kind of race we are closing?
> > Pls add a comment to explain.
>
> Hi, Michael. Thanks for your review.
>
> Yeah, it's for a race condition between user space and kernel
> space. I added a comment in V2, which is too confusing, and
> I removed it 😢. I'll make it more clear and add it in the V4. The
> origin comment is:
>
> * If the sq->vq is empty, and the tx ring is empty, and the user
> * submit an entry to the tx ring after virtnet_xsk_xmit_batch() and
> * before xsk_set_tx_need_wakeup(), we will lose the chance to wake
> * up the tx napi, so we have to set the need_wakeup flag here.
>
> And the logic is like this:
>
> Kernel: tx NAPI is waked up from skb_xmit_done() ->
> Kernel: sq->vq and xsk->tx_ring are both empty ->
> Kernel: call virtnet_xsk_xmit_batch()
>
> User: submit a entry to the xsk->tx_ring
> User: check the wakeup flag
> User: wakeup flag is not set, skip send()
>
> Kernel: call xsk_set_tx_need_wakeup(), because sq->vq is empty
>
> If we don't send more data, the data in the xsk->tx_ring will
> not be sent forever.
I'm not 100% sure I understand, but when someone fixes cross-CPU races
with no synchronization or CPU memory barriers just with extra checks,
this always gives me pause.
AI helped write this for me, for example:
1. Kernel: xsk_set_tx_need_wakeup stores NEED_WAKEUP (sits in store buffer)
2. Kernel: xsk_tx_peek_release_desc_batch - load, sees empty (reordered before the store is globally visible)
3. Kernel: peek finds nothing, returns 0
4. Userspace: stores entry + producer
5. Userspace: loads flags - doesn't see NEED_WAKEUP yet (still in kernel's store buffer)
6. Userspeace: skips send()
7. Kernel: NEED_WAKEUP store finally becomes visible - too late
Seems legit?
> >
> > > sent = virtnet_xsk_xmit_batch(sq, pool, budget, &kicks);
> > >
> > > + if (need_wakeup) {
> > > + if (vring_size == sq->vq->num_free)
> > > + /* we can't wake up by ourself, and it should be done
> > > + * by the user.
> > > + */
> > > + xsk_set_tx_need_wakeup(pool);
> > > + else
> > > + /* we can wake up from skb_xmit_done() */
> > > + xsk_clear_tx_need_wakeup(pool);
> >
> > But what if we don't have get tx napi so no wakeup in skb_xmit_done?
>
> Sorry that I'm not sure what "get tx napi" means here ;(
>
> There are entry in sq->vq, so skb_xmit_done() will be called after
> the entries in the ring is consumed by the HOST, right?
> Then, the corresponding sq->napi will be scheduled, as we ensure
> that tx napi is always enabled, which means napi->weight is not
> zero, in this commit:
> 1df5116a41a8 ("virtio_net: xsk: prevent disable tx napi")
Oh I forgot we did that. But can xsk bind when tx napi has already
been disabled previously?
> Right?
>
> Thanks!
> Menglong Dong
>
> >
> >
> > > + }
> > > +
> > > if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
> > > check_sq_full_and_disable(vi, vi->dev, sq);
> > >
> > > @@ -1470,9 +1488,6 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> > > u64_stats_add(&sq->stats.xdp_tx, sent);
> > > u64_stats_update_end(&sq->stats.syncp);
> > >
> > > - if (xsk_uses_need_wakeup(pool))
> > > - xsk_set_tx_need_wakeup(pool);
> > > -
> > > return sent;
> > > }
> > >
> > > --
> > > 2.54.0
> >
> >
> >
>
>
>
^ permalink raw reply
* [PATCH 2/2] virtio_balloon: quiesce balloon work before device shutdown
From: Denis V. Lunev @ 2026-06-22 13:37 UTC (permalink / raw)
To: mst, david; +Cc: virtualization, linux-kernel, Denis V. Lunev
In-Reply-To: <20260622133715.3707707-1-den@openvz.org>
Commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
device_shutdown()") added a generic virtio bus .shutdown handler that
breaks and resets every virtio device during device_shutdown(), i.e. on
reboot and kexec.
virtio_balloon provides no .shutdown of its own, so that generic path
runs while the balloon's asynchronous work is still armed. Once the
device has been broken, virtqueue_add_inbuf() in
virtballoon_free_page_report() returns -EIO and trips its
WARN_ON_ONCE(). On a kernel booted with panic_on_warn that turns an
ordinary reboot, for example a kexec based upgrade, into a fatal panic
in the middle of device_shutdown(), so the machine never reaches the
new kernel.
Relaxing that single WARN_ON_ONCE() would only hide the symptom: the
inflate/deflate and OOM paths do not warn, they call
wait_event(vb->acked, ...) and would instead block forever on a broken
queue that can no longer complete. The device has to be quiesced, not
just kept quiet.
Add a .shutdown handler that quiesces the balloon via the shared
virtballoon_quiesce() helper while the device is still alive, and only
then breaks and resets it. The break and reset are repeated here rather
than reused from virtio_dev_shutdown(): drv->shutdown replaces the
generic handler rather than augmenting it, so that drivers such as
virtio-gpu can opt out of the reset. Unlike virtballoon_remove() the
balloon workqueue is not destroyed, as shutdown does not free the
device and cancel_work_sync() together with stop_update already prevent
any further work from being queued.
Fixes: 8bd2fa086a04 ("virtio: break and reset virtio devices on device_shutdown()")
Signed-off-by: Denis V. Lunev <den@openvz.org>
---
drivers/virtio/virtio_balloon.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 5b02d9191ac6..e35ada767b4b 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1137,6 +1137,15 @@ static void virtballoon_remove(struct virtio_device *vdev)
kfree(vb);
}
+static void virtballoon_shutdown(struct virtio_device *vdev)
+{
+ virtballoon_quiesce(vdev->priv);
+
+ virtio_break_device(vdev);
+ virtio_synchronize_cbs(vdev);
+ vdev->config->reset(vdev);
+}
+
#ifdef CONFIG_PM_SLEEP
static int virtballoon_freeze(struct virtio_device *vdev)
{
@@ -1202,6 +1211,7 @@ static struct virtio_driver virtio_balloon_driver = {
.validate = virtballoon_validate,
.probe = virtballoon_probe,
.remove = virtballoon_remove,
+ .shutdown = virtballoon_shutdown,
.config_changed = virtballoon_changed,
#ifdef CONFIG_PM_SLEEP
.freeze = virtballoon_freeze,
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] virtio_balloon: quiesce balloon work on device shutdown
From: Denis V. Lunev @ 2026-06-22 13:37 UTC (permalink / raw)
To: mst, david; +Cc: virtualization, linux-kernel, Denis V. Lunev
Since commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
device_shutdown()") the virtio bus breaks and resets every virtio device
during device_shutdown(), i.e. on reboot and kexec. virtio_balloon has no
.shutdown of its own, so that generic path runs while the balloon's
asynchronous work is still armed: the free page reporting worker, the
inflate/deflate and stats workers, the OOM notifier and the free page
shrinker.
Once the device has been broken, virtqueue_add_inbuf() in
virtballoon_free_page_report() returns -EIO and trips its WARN_ON_ONCE().
On a kernel booted with panic_on_warn that turns an ordinary reboot into a
fatal panic in the middle of device_shutdown(), so the machine never
reaches the new kernel. The inflate/deflate and OOM paths do not warn but
are no better off: they call wait_event(vb->acked, ...) and would block
forever on a queue that can no longer complete.
This was hit in the field as an intermittent failure of a virtualization
cluster upgrade: guest storage nodes were rebooted via kexec into the new
kernel, and the ones whose free page reporting happened to run during
device_shutdown() panicked (the guests run with panic_on_warn) and never
came back, stalling the rolling upgrade. The crash dump showed the WARN at
virtio_balloon.c:216 in a page_reporting kworker, with all the balloon
virtqueues already broken.
Patch 1 factors the teardown out of virtballoon_remove() into a
virtballoon_quiesce() helper (no functional change). Patch 2 adds a
virtio_balloon .shutdown handler that quiesces via that helper while the
device is still alive, then breaks and resets it the way the generic
virtio_dev_shutdown() would.
Relaxing the single WARN_ON_ONCE() instead was considered and rejected: it
would silence the panic but leave the inflate/deflate and OOM paths
hanging on the broken device. The device has to be quiesced, not just kept
quiet.
Validated by churning balloon inflate/deflate from the host while
kexec-rebooting the guest in a loop under panic_on_warn: the unpatched
module reproduces the WARN within a couple of cycles, while the patched
module survives many consecutive kexec cycles cleanly (12/12 in the final
run, 0 WARNs). checkpatch is clean on both patches.
Denis V. Lunev (2):
virtio_balloon: factor out virtballoon_quiesce()
virtio_balloon: quiesce balloon work before device shutdown
drivers/virtio/virtio_balloon.c | 37 ++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH 1/2] virtio_balloon: factor out virtballoon_quiesce()
From: Denis V. Lunev @ 2026-06-22 13:37 UTC (permalink / raw)
To: mst, david; +Cc: virtualization, linux-kernel, Denis V. Lunev
In-Reply-To: <20260622133715.3707707-1-den@openvz.org>
virtballoon_remove() stops all of the balloon's asynchronous work (the
free page reporting worker, the inflate/deflate and stats workers, the
OOM notifier and the free page shrinker) before tearing the device
down. A following change needs the same teardown from a .shutdown
handler, so move it into a virtballoon_quiesce() helper.
No functional change.
Signed-off-by: Denis V. Lunev <den@openvz.org>
---
drivers/virtio/virtio_balloon.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 088b3a0e6ce6..5b02d9191ac6 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1098,26 +1098,39 @@ static void remove_common(struct virtio_balloon *vb)
vb->vdev->config->del_vqs(vb->vdev);
}
-static void virtballoon_remove(struct virtio_device *vdev)
+/*
+ * Stop all asynchronous balloon work. The device must still be alive so that
+ * in-flight requests can drain via the host before it is reset or freed.
+ */
+static void virtballoon_quiesce(struct virtio_balloon *vb)
{
- struct virtio_balloon *vb = vdev->priv;
+ struct virtio_device *vdev = vb->vdev;
- if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+ if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_REPORTING))
page_reporting_unregister(&vb->pr_dev_info);
- if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
+ if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
unregister_oom_notifier(&vb->oom_nb);
- if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
+ if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
virtio_balloon_unregister_shrinker(vb);
+
spin_lock_irq(&vb->stop_update_lock);
vb->stop_update = true;
spin_unlock_irq(&vb->stop_update_lock);
cancel_work_sync(&vb->update_balloon_size_work);
cancel_work_sync(&vb->update_balloon_stats_work);
- if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) {
+ if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
cancel_work_sync(&vb->report_free_page_work);
+}
+
+static void virtballoon_remove(struct virtio_device *vdev)
+{
+ struct virtio_balloon *vb = vdev->priv;
+
+ virtballoon_quiesce(vb);
+
+ if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
destroy_workqueue(vb->balloon_wq);
- }
remove_common(vb);
mutex_destroy(&vb->balloon_lock);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v10 07/37] mm: thread user_addr through page allocator for cache-friendly zeroing
From: David Hildenbrand (Arm) @ 2026-06-22 14:25 UTC (permalink / raw)
To: Gregory Price
Cc: Zi Yan, Michael S. Tsirkin, Matthew Wilcox, Lorenzo Stoakes,
linux-kernel, Jason Wang, Xuan Zhuo, Eugenio Pérez,
Muchun Song, Oscar Salvador, Andrew Morton, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park, Ying Huang,
Alistair Popple, Christoph Lameter, David Rientjes,
Roman Gushchin, Harry Yoo, Axel Rasmussen, Yuanchu Xie, Wei Xu,
Chris Li, Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He,
virtualization, linux-mm, Andrea Arcangeli
In-Reply-To: <aidCGkafm9yUDTuf@gourry-fedora-PF4VCD3F>
On 6/9/26 00:28, Gregory Price wrote:
> On Mon, Jun 08, 2026 at 11:51:47PM +0200, David Hildenbrand (Arm) wrote:
>> On 6/8/26 23:16, Zi Yan wrote:
>>
>> There was Willy's comment in RFC v3 [1], which had 19 patches. Unfortunately, he
>> no longer followed up to my initial push back and Michael's question later [2].
>>
>> That would have probably been the right time to wait for more discussion.
>>
>> RFC v4 had 22 patches with little replies.
>> v5 had 28 patches with little replies.
>> v6 had 30 patches with no replies.
>> v7 had 31 patches with little replies.
>> v8 had 37 patches with no replies.
>>
>> [1] https://lore.kernel.org/lkml/aeu5P1bZW3yEH54t@casper.infradead.org/
>> [2] https://lore.kernel.org/lkml/20260426165330-mutt-send-email-mst@kernel.org/
>>
>
> Hm, rewinding on this back to v3 here:
I'm late ...
> https://lore.kernel.org/lkml/016cc5e5-044c-46c6-a668-200f90a64d85@kernel.org/
>
> You said:
>
> ```
> Exactly, that's why I am saying that vma_alloc_folio() is the only
> external interface people should be using with a user address.
> ```
>
> Going through the list of folio_zero_user references:
>
> Called unconditionally if a folio is acquired:
> fs/hugetlbfs/inode.c: folio_zero_user(folio, addr);
> mm/hugetlb.c: folio_zero_user(folio, vmf->real_address);
> mm/memfd.c: folio_zero_user(folio, 0);
>
> Called when user_alloc_needs_zeroing() and charging passes:
> mm/huge_memory.c: folio_zero_user(folio, addr);
> mm/memory.c: folio_zero_user(folio, vmf->address);
>
> No one outside mm/ should know about this interface at all.
Exactly.
> Arguably none of these should know about this interface either.
>
> The appropriate place for this logic appears to be:
> vma_alloc_folio
> alloc_hugetlb_folio
> alloc_hugetlb_folio_reserve
Yes. And the hugetlb stuff should just be left alone for now. We don't encourage
new hugetlb features, and the same should apply to new optimizations that are
not straight forward.
>
> The reason to sink it into the post_alloc_hook is to let the buddy
> decide whether the page actually needs to be zeroed (like the virtio
> situation) based on PG_zeroed or whatever.
It's a bit related to your private node work .... if we have an interface that
consumes an alloc_context, we could just forward the address easily.
Now, there are different ways of doing it, but having folio allocation sometimes
use GFP_ZERO and sometimes not is just nasty. It's all a big hack.
>
> It seems like at a minimum moving the logic all the way into
> post_alloc_hook lets us actually delete folio_zero_user() as a published
> interface and move it entirely within page_alloc.c.
>
> The catch is user_alloc_needs_zeroing() coming along with it.
Yes. And once there is only one user remaining, we could inline it and get rid
of this horrible helper :)
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 0/2] virtio_balloon: quiesce balloon work on device shutdown
From: David Hildenbrand (Arm) @ 2026-06-22 14:29 UTC (permalink / raw)
To: Denis V. Lunev, mst; +Cc: virtualization, linux-kernel
In-Reply-To: <20260622133715.3707707-1-den@openvz.org>
On 6/22/26 15:37, Denis V. Lunev wrote:
> Since commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
> device_shutdown()") the virtio bus breaks and resets every virtio device
> during device_shutdown(), i.e. on reboot and kexec. virtio_balloon has no
> .shutdown of its own, so that generic path runs while the balloon's
> asynchronous work is still armed: the free page reporting worker, the
> inflate/deflate and stats workers, the OOM notifier and the free page
> shrinker.
>
> Once the device has been broken, virtqueue_add_inbuf() in
> virtballoon_free_page_report() returns -EIO and trips its WARN_ON_ONCE().
> On a kernel booted with panic_on_warn that turns an ordinary reboot into a
> fatal panic in the middle of device_shutdown(), so the machine never
> reaches the new kernel. The inflate/deflate and OOM paths do not warn but
> are no better off: they call wait_event(vb->acked, ...) and would block
> forever on a queue that can no longer complete.
>
> This was hit in the field as an intermittent failure of a virtualization
> cluster upgrade: guest storage nodes were rebooted via kexec into the new
> kernel, and the ones whose free page reporting happened to run during
> device_shutdown() panicked (the guests run with panic_on_warn) and never
> came back, stalling the rolling upgrade. The crash dump showed the WARN at
> virtio_balloon.c:216 in a page_reporting kworker, with all the balloon
> virtqueues already broken.
>
> Patch 1 factors the teardown out of virtballoon_remove() into a
> virtballoon_quiesce() helper (no functional change). Patch 2 adds a
> virtio_balloon .shutdown handler that quiesces via that helper while the
> device is still alive, then breaks and resets it the way the generic
> virtio_dev_shutdown() would.
>
> Relaxing the single WARN_ON_ONCE() instead was considered and rejected: it
> would silence the panic but leave the inflate/deflate and OOM paths
> hanging on the broken device. The device has to be quiesced, not just kept
> quiet.
Do you have a link to that discussion you could add?
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 0/2] virtio_balloon: quiesce balloon work on device shutdown
From: Denis V. Lunev @ 2026-06-22 14:33 UTC (permalink / raw)
To: David Hildenbrand (Arm), Denis V. Lunev, mst; +Cc: virtualization, linux-kernel
In-Reply-To: <de197e27-e56e-48e1-ac2d-e76ba0c3e6b2@kernel.org>
On 6/22/26 16:29, David Hildenbrand (Arm) wrote:
> This email originated from an IP that might not be authorized by the domain it was sent from.
> Do not click links or open attachments unless it is an email you expected to receive.
> On 6/22/26 15:37, Denis V. Lunev wrote:
>> Since commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
>> device_shutdown()") the virtio bus breaks and resets every virtio device
>> during device_shutdown(), i.e. on reboot and kexec. virtio_balloon has no
>> .shutdown of its own, so that generic path runs while the balloon's
>> asynchronous work is still armed: the free page reporting worker, the
>> inflate/deflate and stats workers, the OOM notifier and the free page
>> shrinker.
>>
>> Once the device has been broken, virtqueue_add_inbuf() in
>> virtballoon_free_page_report() returns -EIO and trips its WARN_ON_ONCE().
>> On a kernel booted with panic_on_warn that turns an ordinary reboot into a
>> fatal panic in the middle of device_shutdown(), so the machine never
>> reaches the new kernel. The inflate/deflate and OOM paths do not warn but
>> are no better off: they call wait_event(vb->acked, ...) and would block
>> forever on a queue that can no longer complete.
>>
>> This was hit in the field as an intermittent failure of a virtualization
>> cluster upgrade: guest storage nodes were rebooted via kexec into the new
>> kernel, and the ones whose free page reporting happened to run during
>> device_shutdown() panicked (the guests run with panic_on_warn) and never
>> came back, stalling the rolling upgrade. The crash dump showed the WARN at
>> virtio_balloon.c:216 in a page_reporting kworker, with all the balloon
>> virtqueues already broken.
>>
>> Patch 1 factors the teardown out of virtballoon_remove() into a
>> virtballoon_quiesce() helper (no functional change). Patch 2 adds a
>> virtio_balloon .shutdown handler that quiesces via that helper while the
>> device is still alive, then breaks and resets it the way the generic
>> virtio_dev_shutdown() would.
>>
>> Relaxing the single WARN_ON_ONCE() instead was considered and rejected: it
>> would silence the panic but leave the inflate/deflate and OOM paths
>> hanging on the broken device. The device has to be quiesced, not just kept
>> quiet.
> Do you have a link to that discussion you could add?
>
>
that was internal discussion for that fix. We have faced this
during tests, made the fix and validated it before sending.
Thus - sorry, this is my mistake.
Den
^ permalink raw reply
* Re: [PATCH 2/2] virtio_balloon: quiesce balloon work before device shutdown
From: David Hildenbrand (Arm) @ 2026-06-22 14:38 UTC (permalink / raw)
To: Denis V. Lunev, mst; +Cc: virtualization, linux-kernel
In-Reply-To: <20260622133715.3707707-3-den@openvz.org>
On 6/22/26 15:37, Denis V. Lunev wrote:
> Commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
> device_shutdown()") added a generic virtio bus .shutdown handler that
> breaks and resets every virtio device during device_shutdown(), i.e. on
> reboot and kexec.
>
> virtio_balloon provides no .shutdown of its own, so that generic path
> runs while the balloon's asynchronous work is still armed. Once the
> device has been broken, virtqueue_add_inbuf() in
> virtballoon_free_page_report() returns -EIO and trips its
> WARN_ON_ONCE(). On a kernel booted with panic_on_warn that turns an
> ordinary reboot, for example a kexec based upgrade, into a fatal panic
> in the middle of device_shutdown(), so the machine never reaches the
> new kernel.
>
> Relaxing that single WARN_ON_ONCE() would only hide the symptom: the
> inflate/deflate and OOM paths do not warn, they call
> wait_event(vb->acked, ...) and would instead block forever on a broken
> queue that can no longer complete. The device has to be quiesced, not
> just kept quiet.
Ah, so
/* We should always be able to add one buffer to an empty queue. */
virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
is not actually correct.
Yeah, quiescing sounds cleaner, although I am thinking whether we should also
warn if virtqueue_add_outbuf() fails, similar to what we do in
virtballoon_free_page_report().
>
> Add a .shutdown handler that quiesces the balloon via the shared
> virtballoon_quiesce() helper while the device is still alive, and only
> then breaks and resets it. The break and reset are repeated here rather
> than reused from virtio_dev_shutdown(): drv->shutdown replaces the
> generic handler rather than augmenting it, so that drivers such as
> virtio-gpu can opt out of the reset. Unlike virtballoon_remove() the
> balloon workqueue is not destroyed, as shutdown does not free the
> device and cancel_work_sync() together with stop_update already prevent
> any further work from being queued.
>
> Fixes: 8bd2fa086a04 ("virtio: break and reset virtio devices on device_shutdown()")
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> ---
> drivers/virtio/virtio_balloon.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> index 5b02d9191ac6..e35ada767b4b 100644
> --- a/drivers/virtio/virtio_balloon.c
> +++ b/drivers/virtio/virtio_balloon.c
> @@ -1137,6 +1137,15 @@ static void virtballoon_remove(struct virtio_device *vdev)
> kfree(vb);
> }
>
> +static void virtballoon_shutdown(struct virtio_device *vdev)
> +{
> + virtballoon_quiesce(vdev->priv);
> +
> + virtio_break_device(vdev);
> + virtio_synchronize_cbs(vdev);
> + vdev->config->reset(vdev);
I guess it would be good if we wouldn't have to copy what the default handler
does, but could instead just have it in a reusable core function?
> +}
> +
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 1/2] virtio_balloon: factor out virtballoon_quiesce()
From: David Hildenbrand (Arm) @ 2026-06-22 14:46 UTC (permalink / raw)
To: Denis V. Lunev, mst; +Cc: virtualization, linux-kernel
In-Reply-To: <20260622133715.3707707-2-den@openvz.org>
On 6/22/26 15:37, Denis V. Lunev wrote:
> virtballoon_remove() stops all of the balloon's asynchronous work (the
> free page reporting worker, the inflate/deflate and stats workers, the
> OOM notifier and the free page shrinker) before tearing the device
> down. A following change needs the same teardown from a .shutdown
> handler, so move it into a virtballoon_quiesce() helper.
>
> No functional change.
>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> ---
> drivers/virtio/virtio_balloon.c | 27 ++++++++++++++++++++-------
> 1 file changed, 20 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> index 088b3a0e6ce6..5b02d9191ac6 100644
> --- a/drivers/virtio/virtio_balloon.c
> +++ b/drivers/virtio/virtio_balloon.c
> @@ -1098,26 +1098,39 @@ static void remove_common(struct virtio_balloon *vb)
> vb->vdev->config->del_vqs(vb->vdev);
> }
>
> -static void virtballoon_remove(struct virtio_device *vdev)
> +/*
> + * Stop all asynchronous balloon work. The device must still be alive so that
> + * in-flight requests can drain via the host before it is reset or freed.
> + */
> +static void virtballoon_quiesce(struct virtio_balloon *vb)
> {
> - struct virtio_balloon *vb = vdev->priv;
> + struct virtio_device *vdev = vb->vdev;
>
> - if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
> + if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_REPORTING))
> page_reporting_unregister(&vb->pr_dev_info);
Most functions here grab mutexes and can sleep. I assume that's fine in shutdown
context.
Nothing jumped at me
Sashiko asks whether ->remove (hotunplug/driver-unloading) can follow a
->shutdown callback. I don't know, seems questionable if this could happen and
should probably be handled by the core?
If it's possible you'd have to remember that stuff was already quiesce'ed. Maybe
simply by checking early in virtballoon_quiesce() whether vb->stop_update ==
true already.
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 2/2] virtio_balloon: quiesce balloon work before device shutdown
From: Michael S. Tsirkin @ 2026-06-22 14:58 UTC (permalink / raw)
To: David Hildenbrand (Arm); +Cc: Denis V. Lunev, virtualization, linux-kernel
In-Reply-To: <8b83f251-3a3e-4fc9-8ea9-8d101fb92919@kernel.org>
On Mon, Jun 22, 2026 at 04:38:54PM +0200, David Hildenbrand (Arm) wrote:
> On 6/22/26 15:37, Denis V. Lunev wrote:
> > Commit 8bd2fa086a04 ("virtio: break and reset virtio devices on
> > device_shutdown()") added a generic virtio bus .shutdown handler that
> > breaks and resets every virtio device during device_shutdown(), i.e. on
> > reboot and kexec.
> >
> > virtio_balloon provides no .shutdown of its own, so that generic path
> > runs while the balloon's asynchronous work is still armed. Once the
> > device has been broken, virtqueue_add_inbuf() in
> > virtballoon_free_page_report() returns -EIO and trips its
> > WARN_ON_ONCE(). On a kernel booted with panic_on_warn that turns an
> > ordinary reboot, for example a kexec based upgrade, into a fatal panic
> > in the middle of device_shutdown(), so the machine never reaches the
> > new kernel.
> >
> > Relaxing that single WARN_ON_ONCE() would only hide the symptom: the
> > inflate/deflate and OOM paths do not warn, they call
> > wait_event(vb->acked, ...) and would instead block forever on a broken
> > queue that can no longer complete. The device has to be quiesced, not
> > just kept quiet.
>
> Ah, so
>
> /* We should always be able to add one buffer to an empty queue. */
> virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
>
> is not actually correct.
Yes - we can't if the device is completely gone)
> Yeah, quiescing sounds cleaner, although I am thinking whether we should also
> warn if virtqueue_add_outbuf() fails, similar to what we do in
> virtballoon_free_page_report().
>
> >
> > Add a .shutdown handler that quiesces the balloon via the shared
> > virtballoon_quiesce() helper while the device is still alive, and only
> > then breaks and resets it. The break and reset are repeated here rather
> > than reused from virtio_dev_shutdown(): drv->shutdown replaces the
> > generic handler rather than augmenting it, so that drivers such as
> > virtio-gpu can opt out of the reset. Unlike virtballoon_remove() the
> > balloon workqueue is not destroyed, as shutdown does not free the
> > device and cancel_work_sync() together with stop_update already prevent
> > any further work from being queued.
> >
> > Fixes: 8bd2fa086a04 ("virtio: break and reset virtio devices on device_shutdown()")
> > Signed-off-by: Denis V. Lunev <den@openvz.org>
> > ---
> > drivers/virtio/virtio_balloon.c | 10 ++++++++++
> > 1 file changed, 10 insertions(+)
> >
> > diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> > index 5b02d9191ac6..e35ada767b4b 100644
> > --- a/drivers/virtio/virtio_balloon.c
> > +++ b/drivers/virtio/virtio_balloon.c
> > @@ -1137,6 +1137,15 @@ static void virtballoon_remove(struct virtio_device *vdev)
> > kfree(vb);
> > }
> >
> > +static void virtballoon_shutdown(struct virtio_device *vdev)
> > +{
> > + virtballoon_quiesce(vdev->priv);
> > +
> > + virtio_break_device(vdev);
> > + virtio_synchronize_cbs(vdev);
> > + vdev->config->reset(vdev);
>
> I guess it would be good if we wouldn't have to copy what the default handler
> does, but could instead just have it in a reusable core function?
>
> > +}
> > +
>
> --
> Cheers,
>
> David
^ permalink raw reply
* Re: [PATCH 1/2] virtio_balloon: factor out virtballoon_quiesce()
From: Michael S. Tsirkin @ 2026-06-22 14:59 UTC (permalink / raw)
To: David Hildenbrand (Arm); +Cc: Denis V. Lunev, virtualization, linux-kernel
In-Reply-To: <79aa1649-acbb-4898-b533-4c991a6ca19b@kernel.org>
On Mon, Jun 22, 2026 at 04:46:48PM +0200, David Hildenbrand (Arm) wrote:
> On 6/22/26 15:37, Denis V. Lunev wrote:
> > virtballoon_remove() stops all of the balloon's asynchronous work (the
> > free page reporting worker, the inflate/deflate and stats workers, the
> > OOM notifier and the free page shrinker) before tearing the device
> > down. A following change needs the same teardown from a .shutdown
> > handler, so move it into a virtballoon_quiesce() helper.
> >
> > No functional change.
> >
> > Signed-off-by: Denis V. Lunev <den@openvz.org>
> > ---
> > drivers/virtio/virtio_balloon.c | 27 ++++++++++++++++++++-------
> > 1 file changed, 20 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> > index 088b3a0e6ce6..5b02d9191ac6 100644
> > --- a/drivers/virtio/virtio_balloon.c
> > +++ b/drivers/virtio/virtio_balloon.c
> > @@ -1098,26 +1098,39 @@ static void remove_common(struct virtio_balloon *vb)
> > vb->vdev->config->del_vqs(vb->vdev);
> > }
> >
> > -static void virtballoon_remove(struct virtio_device *vdev)
> > +/*
> > + * Stop all asynchronous balloon work. The device must still be alive so that
> > + * in-flight requests can drain via the host before it is reset or freed.
> > + */
> > +static void virtballoon_quiesce(struct virtio_balloon *vb)
> > {
> > - struct virtio_balloon *vb = vdev->priv;
> > + struct virtio_device *vdev = vb->vdev;
> >
> > - if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
> > + if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_REPORTING))
> > page_reporting_unregister(&vb->pr_dev_info);
>
> Most functions here grab mutexes and can sleep. I assume that's fine in shutdown
> context.
>
> Nothing jumped at me
>
>
> Sashiko asks whether ->remove (hotunplug/driver-unloading) can follow a
> ->shutdown callback. I don't know, seems questionable if this could happen and
> should probably be handled by the core?
>
> If it's possible you'd have to remember that stuff was already quiesce'ed. Maybe
> simply by checking early in virtballoon_quiesce() whether vb->stop_update ==
> true already.
>
> --
> Cheers,
>
> David
I don't think it can happen.
--
MST
^ permalink raw reply
* [PATCH] crypto: virtio - fix missing le64_to_cpu() conversions
From: Ben Dooks @ 2026-06-22 15:03 UTC (permalink / raw)
To: linux-crypto, virtualization, Gonglei
Cc: linux-kernel, Michael S. Tsirkin, Jason Wang, Ben Dooks
There are two cases of sending a __le64 type to a print function
so fix this by adding le64_to_cpu() which fixes the following
(prototype) sparse warnings:
drivers/crypto/virtio/virtio_crypto_skcipher_algs.c:234:17: warning: incorrect type in argument 3 (different base types)
drivers/crypto/virtio/virtio_crypto_skcipher_algs.c:234:17: expected unsigned long long
drivers/crypto/virtio/virtio_crypto_skcipher_algs.c:234:17: got restricted __le64 [usertype] session_id
drivers/crypto/virtio/virtio_crypto_akcipher_algs.c:196:17: warning: incorrect type in argument 3 (different base types)
drivers/crypto/virtio/virtio_crypto_akcipher_algs.c:196:17: expected unsigned long long
drivers/crypto/virtio/virtio_crypto_akcipher_algs.c:196:17: got restricted __le64 [usertype] session_id
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 3 ++-
drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index d8d452cac391..404e33b16db6 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -194,7 +194,8 @@ static int virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
if (ctrl_status->status != VIRTIO_CRYPTO_OK) {
pr_err("virtio_crypto: Close session failed status: %u, session_id: 0x%llx\n",
- ctrl_status->status, destroy_session->session_id);
+ ctrl_status->status,
+ le64_to_cpu(destroy_session->session_id));
err = -EINVAL;
goto out;
}
diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
index e82fc16cab25..3ca441ae2759 100644
--- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
@@ -232,7 +232,8 @@ static int virtio_crypto_alg_skcipher_close_session(
if (ctrl_status->status != VIRTIO_CRYPTO_OK) {
pr_err("virtio_crypto: Close session failed status: %u, session_id: 0x%llx\n",
- ctrl_status->status, destroy_session->session_id);
+ ctrl_status->status,
+ le64_to_cpu(destroy_session->session_id));
err = -EINVAL;
goto out;
--
2.37.2.352.g3c44437643
^ permalink raw reply related
* [PATCH v2 0/4] vhost/vsock: add support for VHOST_RESET_OWNER and CPR migration
From: Andrey Drobyshev @ 2026-06-22 17:58 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
v1 -> v2:
* Patch 2 (suppress EHOSTUNREACH): replace 'cpr_paused' + backend check
with a single 'started' latch;
* Patch 3 (re-scan TX virtqueue): reword commit message;
* Patch 4 (VHOST_RESET_OWNER):
- fix a vhost_worker use-after-free / stuck VHOST_WORK_QUEUED stall
against the lockless send path;
- drop the no-op vsock_for_each_connected_socket() iteration;
* Shuffle the patches, keep RESET_OWNER implementation last to preserve
bisectability;
* Reword the cover letter.
v1: https://lore.kernel.org/virtualization/20260612165718.433546-1-andrey.drobyshev@virtuozzo.com
Host<-->guest connections via AF_VSOCK sockets aren't supposed to
outlive VM migration, since VM is moving to another host. However
there's a special case, which is QEMU live-update, or CPR
(checkpoint-restore) migration. In this case, VM remains on the same
host, and we'd like such connections to persist.
For this to work, we need to be able to transfer device ownership from
source QEMU to dest QEMU. Namely, source needs to reset ownership by
issuing VHOST_RESET_OWNER ioctl, and then target has to claim it by
calling VHOST_SET_OWNER.
Since VHOST_RESET_OWNER isn't yet implemented for vhost-vsock, let's add
such implementation. Patch 1 is a preliminary helper. Patches 2 and 3
fix the pre-existing issues which do manifest during CPR / RESET_OWNER.
Patch 4 is the ioctl's implementation itself - we keep it last to
preserve bisectability.
There's a complementary series for QEMU [0] adding support of vhost-vsock
devices during CPR migration.
I've tested this (patched QEMU + patched kernel) approximately as follows:
* Run listener in the guest:
socat -u VSOCK-LISTEN:9999 - >/tmp/recv.bin
* Run data transfer from host to guest:
socat -u FILE:/root/bigfile.bin VSOCK-CONNECT:CID:9999
* Perform CPR migration during transfer (either cpr-exec or cpr-transfer)
* Check that file hash sum matches
[0] https://lore.kernel.org/qemu-devel/20260619105514.128812-1-andrey.drobyshev@virtuozzo.com
Andrey Drobyshev (2):
vhost/vsock: suppress EHOSTUNREACH fast-fail during CPR pause
vhost/vsock: re-scan TX virtqueue on device start
Pavel Tikhomirov (2):
vhost/vsock: split out vhost_vsock_drop_backends helper
vhost/vsock: add VHOST_RESET_OWNER ioctl
drivers/vhost/vsock.c | 96 ++++++++++++++++++++++++++++++++++---------
1 file changed, 76 insertions(+), 20 deletions(-)
--
2.47.1
^ permalink raw reply
* [PATCH v2 1/4] vhost/vsock: split out vhost_vsock_drop_backends helper
From: Andrey Drobyshev @ 2026-06-22 17:58 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260622175808.508084-1-andrey.drobyshev@virtuozzo.com>
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Split the actual backend dropping part from vhost_vsock_stop. We're
going to need it for the VHOST_RESET_OWNER implementation in the
following patch, when vsock->dev.mutex is already taken and owner is
checked.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
---
drivers/vhost/vsock.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 9aaab6bb8061..b12221ce6faf 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -664,9 +664,24 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
return ret;
}
-static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+static void vhost_vsock_drop_backends(struct vhost_vsock *vsock)
{
+ struct vhost_virtqueue *vq;
size_t i;
+
+ lockdep_assert_held(&vsock->dev.mutex);
+
+ for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
+ vq = &vsock->vqs[i];
+
+ mutex_lock(&vq->mutex);
+ vhost_vq_set_backend(vq, NULL);
+ mutex_unlock(&vq->mutex);
+ }
+}
+
+static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+{
int ret = 0;
mutex_lock(&vsock->dev.mutex);
@@ -677,14 +692,7 @@ static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
goto err;
}
- for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
- struct vhost_virtqueue *vq = &vsock->vqs[i];
-
- mutex_lock(&vq->mutex);
- vhost_vq_set_backend(vq, NULL);
- mutex_unlock(&vq->mutex);
- }
-
+ vhost_vsock_drop_backends(vsock);
err:
mutex_unlock(&vsock->dev.mutex);
return ret;
--
2.47.1
^ permalink raw reply related
* [PATCH v2 2/4] vhost/vsock: suppress EHOSTUNREACH fast-fail during CPR pause
From: Andrey Drobyshev @ 2026-06-22 17:58 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260622175808.508084-1-andrey.drobyshev@virtuozzo.com>
Earlier commit bb26ed5f3a8b ("vhost/vsock: Refuse the connection
immediately when guest isn't ready") added a fast-fail in
vhost_transport_send_pkt(). It rejects every host send with -EHOSTUNREACH
until the destination calls SET_RUNNING(1). The fast-fail condition checks
whether device's backends are dropped, and if they're, the guest is
considered to be not ready.
However, there might be other reasons for backends to be nulled. In
particular, when QEMU is performing CPR (checkpoint-restore) migration,
device ownership is being RESET and SET again, which leads to backends
drop and reattach. If we end up connecting during this window, an
AF_VSOCK client gets -EHOSTUNREACH, which is wrong.
Add a 'started' flag which is set once in vhost_vsock_start() and is
never cleared. The behaviour changes to:
* When device was never started -> flag is unset -> no listener can
exist yet -> fast-fail;
* Once the device starts -> flag is set -> we don't fast-fail ->
we queue and preserve during any later stop / CPR pause.
Important caveat: after the first start, a connect during any stopped
window is queued instead of fast-failed. That was the behaviour before
the patch bb26ed5f3a8b, and we're restoring it now. However we still
keep the behaviour originally intended by that commit (i.e. fast-fail if
there's no real listener yet) while fixing the CPR path.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
drivers/vhost/vsock.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index b12221ce6faf..bec6bcfd885f 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -61,6 +61,7 @@ struct vhost_vsock {
u32 guest_cid;
bool seqpacket_allow;
+ bool started; /* set on first SET_RUNNING(1); never cleared */
};
static u32 vhost_transport_get_local_cid(void)
@@ -302,17 +303,12 @@ vhost_transport_send_pkt(struct sk_buff *skb, struct net *net)
return -ENODEV;
}
- /* Fast-fail if the guest hasn't enabled the RX vq yet. Queuing the packet
- * and making the caller wait is pointless: even if the guest manages to init
- * within the timeout, it'll immediately reply with RST, because there's no
- * listener on the port yet.
- *
- * vhost_vq_get_backend() without vq->mutex is acceptable here: locking
- * the mutex would be too expensive in this hot path, and we already have
- * all the outcomes covered: if the backend becomes NULL right after the check,
- * vhost_transport_do_send_pkt() will check it under the mutex anyway.
+ /* Fast-fail until the guest first enables the device (SET_RUNNING(1)).
+ * Before that there is no listener, so queuing is pointless. 'started'
+ * is never cleared, so once we're up we keep queuing across later
+ * stop / CPR-pause windows.
*/
- if (unlikely(!data_race(vhost_vq_get_backend(&vsock->vqs[VSOCK_VQ_RX])))) {
+ if (unlikely(!READ_ONCE(vsock->started))) {
rcu_read_unlock();
kfree_skb(skb);
return -EHOSTUNREACH;
@@ -640,6 +636,11 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
mutex_unlock(&vq->mutex);
}
+ /* Set 'started' flag on the first start; never cleared, so send_pkt
+ * keeps queuing (instead of fast-failing) on later stop / CPR pauses.
+ */
+ WRITE_ONCE(vsock->started, true);
+
/* Some packets may have been queued before the device was started,
* let's kick the send worker to send them.
*/
@@ -728,6 +729,7 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
vsock->guest_cid = 0; /* no CID assigned yet */
vsock->seqpacket_allow = false;
+ vsock->started = false;
atomic_set(&vsock->queued_replies, 0);
--
2.47.1
^ permalink raw reply related
* [PATCH v2 3/4] vhost/vsock: re-scan TX virtqueue on device start
From: Andrey Drobyshev @ 2026-06-22 17:58 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260622175808.508084-1-andrey.drobyshev@virtuozzo.com>
During QEMU CPR live-update (and VHOST_RESET_OWNER in general) the guest
keeps running while the host drops and later re-attaches vhost backends.
If the guest adds a buffer to the TX virtqueue (guest->host) and kicks
while the backend is temporarily NULL (between vhost_vsock_drop_backends()
and the next vhost_vsock_start()), then the kick is delivered to the
vhost worker, handle_tx_kick() sees a NULL backend and returns, and the
kick signal is consumed. The buffer is then left in the ring.
Then upon device start vhost_vsock_start() only re-kicks the RX send
worker, never the TX VQ, so the buffer is processed only if the guest
happens to kick again. But if the guest itself is now waiting for data
from the host, it will never kick TX VQ again, and we end up in a
deadlock.
The issue itself is pre-existing, but it only manifests during a brief
pause caused by VHOST_RESET_OWNER. Namely, the deadlock is reproduced
during active host->guest socat data transfer under multiple consecutive
CPR live-update's.
To fix this, in vhost_vsock_start(), after kicking the RX send worker, also
queue the TX vq poll so any buffers the guest enqueued while we were paused
get scanned.
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
drivers/vhost/vsock.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index bec6bcfd885f..81d4f7209719 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -646,6 +646,13 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
*/
vhost_vq_work_queue(&vsock->vqs[VSOCK_VQ_RX], &vsock->send_pkt_work);
+ /*
+ * Some packets might've also been queued in TX VQ. That is the case
+ * during the brief device pause caused by VHOST_RESET_OWNER. Re-scan
+ * the TX VQ here, mirroring the RX send-worker kick above.
+ */
+ vhost_poll_queue(&vsock->vqs[VSOCK_VQ_TX].poll);
+
mutex_unlock(&vsock->dev.mutex);
return 0;
--
2.47.1
^ permalink raw reply related
* [PATCH v2 4/4] vhost/vsock: add VHOST_RESET_OWNER ioctl
From: Andrey Drobyshev @ 2026-06-22 17:58 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260622175808.508084-1-andrey.drobyshev@virtuozzo.com>
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
This ioctl is needed for QEMU's CPR (checkpoint-restore) migration of
the guest with vhost-vsock device. For this to work, we need to reset
the device ownership on the source side by calling RESET_OWNER, and then
claim it on the dest side by calling SET_OWNER. We expect not to lose any
AF_VSOCK connection while this happens.
RESET_OWNER keeps the guest CID hashed, so that connections survive. That
leaves the device reachable by the lockless send path while the worker is
being torn down: a concurrent vhost_transport_send_pkt() can call
vhost_vq_work_queue() as vhost_workers_free() frees the worker. That might
cause a use-after-free of vq->worker. In addition, any work queued onto
the dying worker leaves VHOST_WORK_QUEUED stuck, stalling send_pkt_queue
after resume.
Fence the send path around the teardown: send_pkt() only kicks the worker
while the backend is alive (otherwise the skb stays queued and
vhost_vsock_start() drains it on resume). And reset_owner() calls
synchronize_rcu() after drop_backends() so in-flight senders finish before
the worker is freed.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
drivers/vhost/vsock.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 81d4f7209719..7d0146cd38d2 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -318,7 +318,14 @@ vhost_transport_send_pkt(struct sk_buff *skb, struct net *net)
atomic_inc(&vsock->queued_replies);
virtio_vsock_skb_queue_tail(&vsock->send_pkt_queue, skb);
- vhost_vq_work_queue(&vsock->vqs[VSOCK_VQ_RX], &vsock->send_pkt_work);
+
+ /* Skip the kick once the backend is gone (stop/RESET_OWNER); the skb
+ * stays queued and vhost_vsock_start() drains it. Pairs with the
+ * synchronize_rcu() in vhost_vsock_reset_owner().
+ */
+ if (data_race(vhost_vq_get_backend(&vsock->vqs[VSOCK_VQ_RX])))
+ vhost_vq_work_queue(&vsock->vqs[VSOCK_VQ_RX],
+ &vsock->send_pkt_work);
rcu_read_unlock();
return len;
@@ -903,6 +910,36 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
return -EFAULT;
}
+static int vhost_vsock_reset_owner(struct vhost_vsock *vsock)
+{
+ struct vhost_iotlb *umem;
+ long err;
+
+ mutex_lock(&vsock->dev.mutex);
+ err = vhost_dev_check_owner(&vsock->dev);
+ if (err)
+ goto done;
+ umem = vhost_dev_reset_owner_prepare();
+ if (!umem) {
+ err = -ENOMEM;
+ goto done;
+ }
+ vhost_vsock_drop_backends(vsock);
+
+ /* Let in-flight send_pkt() callers stop touching the worker before the
+ * flush + free below. Pairs with the backend check in
+ * vhost_transport_send_pkt().
+ */
+ synchronize_rcu();
+
+ vhost_vsock_flush(vsock);
+ vhost_dev_stop(&vsock->dev);
+ vhost_dev_reset_owner(&vsock->dev, umem);
+done:
+ mutex_unlock(&vsock->dev.mutex);
+ return err;
+}
+
static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
@@ -946,6 +983,8 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
return -EOPNOTSUPP;
vhost_set_backend_features(&vsock->dev, features);
return 0;
+ case VHOST_RESET_OWNER:
+ return vhost_vsock_reset_owner(vsock);
default:
mutex_lock(&vsock->dev.mutex);
r = vhost_dev_ioctl(&vsock->dev, ioctl, argp);
--
2.47.1
^ permalink raw reply related
* [PATCH v4 0/8] media: add virtio-media driver
From: Brian Daniels @ 2026-06-22 20:43 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: acourbot, adelva, aesteve, changyeon, daniel.almeida, eperezma,
gnurou, gurchetansingh, hverkuil, jasowang, linux-kernel,
linux-media, mst, nicolas.dufresne, virtualization, xuanzhuo,
Brian Daniels
From: Alexandre Courbot <gnurou@gmail.com>
Add the first version of the virtio-media driver.
This driver acts roughly as a V4L2 relay between user-space and the
virtio virtual device on the host, so it is relatively simple, yet
unconventional. It doesn't use VB2 or other frameworks typically used in
a V4L2 driver, and most of its complexity resides in correctly and
efficiently building the virtio descriptor chain to pass to the host,
avoiding copies whenever possible. This is done by
scatterlist_builder.[ch].
This version supports MMAP buffers, while USERPTR buffers can also be
enabled through a driver option. DMABUF support is still pending.
NOTE: This depends on the VIRTIO ID being added in this patch:
https://lore.kernel.org/all/20260310-virtio-media-id-v1-1-be211bcf682b@redhat.com
Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
Co-developed-by: Brian Daniels <briandaniels@google.com>
Signed-off-by: Brian Daniels <briandaniels@google.com>
---
Guest Setup
Tests were ran on a Debian 12 guest running with crosvm. The guest image
was created with:
$ virt-builder debian-12 --root-password password:""
Build crosvm and launch the guest starting at the "Crosvm" section on
this page: https://github.com/chromeos/virtio-media/blob/main/TRY_IT_OUT.md#crosvm
NOTE: Before running v4l2-compliance in the guest, you need to install
v4l-utils and ffmpeg:
$ apt update && apt install v4l-utils ffmpeg
---
Compliance Testing
This was tested using v4l2-compliance. Since virtio-media serves as
a proxy to host devices for the guest VMs, we expect the guest
compliance test to essentially match the host compliance test for the
same device.
NOTE: v4l2-compliance changes its test behavior depending on the driver
name. In the guest, the driver name for virtio-media proxied-devices is
always "virtio-media", even if the actual host device has a driver name
of e.g. "uvcvideo". To ensure the test is consistent between the host
and the guest, I created a patch for the v4l2-compliance tool that
allows you to override the driver name. All test results that follow use
this patch:
https://lore.kernel.org/r/20260528163448.4031965-1-briandaniels@google.com/
All tests used a Logitech USB Webcam C925e.
As tested on the host:
$ v4l2-compliance -d1 -s
v4l2-compliance 1.33.0-5471, 64 bits, 64-bit time_t
v4l2-compliance SHA: 9f2d3ea879ff 2026-05-28 14:45:11
Compliance test for uvcvideo device /dev/video1:
Driver Info:
Driver name : uvcvideo
Card type : Logitech Webcam C925e
Bus info : usb-0000:04:00.1-3
Driver version : 6.18.14
Capabilities : 0x84a00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Media Driver Info:
Driver name : uvcvideo
Model : Logitech Webcam C925e
Serial : 686F371F
Bus info : usb-0000:04:00.1-3
Media version : 6.18.14
Hardware revision: 0x00000016 (22)
Driver version : 6.18.14
Interface Info:
ID : 0x03000002
Type : V4L Video
Entity Info:
ID : 0x00000001 (1)
Name : Logitech Webcam C925e
Function : V4L2 I/O
Flags : default
Pad 0x01000007 : 0: Sink
Link 0x0200001f: from remote pad 0x100000a of entity 'Processing 3' (Video Pixel Formatter): Data, Enabled, Immutable
Required ioctls:
test MC information (see 'Media Driver Info' above): OK
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video1 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
fail: v4l2-test-controls.cpp(983): ret != EINVAL (got 13)
test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 19 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)
test blocking wait: OK
Test input 0:
Streaming ioctls:
test read/write: OK (Not Supported)
Video Capture: Frame #000
Video Capture: Frame #001
Video Capture: Frame #002
Video Capture: Frame #003
Video Capture: Frame #004
Video Capture: Frame #005
Video Capture: Frame #006
Video Capture: Frame #007
Video Capture: Frame #008
Video Capture: Frame #009
Video Capture: Frame #010
Video Capture: Frame #011
Video Capture: Frame #012
Video Capture: Frame #013
Video Capture: Frame #014
Video Capture: Frame #015
Video Capture: Frame #016
Video Capture: Frame #017
Video Capture: Frame #018
Video Capture: Frame #019
Video Capture: Frame #020
Video Capture: Frame #021
Video Capture: Frame #022
Video Capture: Frame #023
Video Capture: Frame #024
Video Capture: Frame #025
Video Capture: Frame #026
Video Capture: Frame #027
Video Capture: Frame #028
Video Capture: Frame #029
Video Capture: Frame #030
Video Capture: Frame #031
Video Capture: Frame #032
Video Capture: Frame #033
Video Capture: Frame #034
Video Capture: Frame #035
Video Capture: Frame #036
Video Capture: Frame #037
Video Capture: Frame #038
Video Capture: Frame #039
Video Capture: Frame #040
Video Capture: Frame #041
Video Capture: Frame #042
Video Capture: Frame #043
Video Capture: Frame #044
Video Capture: Frame #045
Video Capture: Frame #046
Video Capture: Frame #047
Video Capture: Frame #048
Video Capture: Frame #049
Video Capture: Frame #050
Video Capture: Frame #051
Video Capture: Frame #052
Video Capture: Frame #053
Video Capture: Frame #054
Video Capture: Frame #055
Video Capture: Frame #056
Video Capture: Frame #057
Video Capture: Frame #058
Video Capture: Frame #059
test MMAP (no poll, REQBUFS): OK
Video Capture: Frame #000 (select)
Video Capture: Frame #001 (select)
Video Capture: Frame #002 (select)
Video Capture: Frame #003 (select)
Video Capture: Frame #004 (select)
Video Capture: Frame #005 (select)
Video Capture: Frame #006 (select)
Video Capture: Frame #007 (select)
Video Capture: Frame #008 (select)
Video Capture: Frame #009 (select)
Video Capture: Frame #010 (select)
Video Capture: Frame #011 (select)
Video Capture: Frame #012 (select)
Video Capture: Frame #013 (select)
Video Capture: Frame #014 (select)
Video Capture: Frame #015 (select)
Video Capture: Frame #016 (select)
Video Capture: Frame #017 (select)
Video Capture: Frame #018 (select)
Video Capture: Frame #019 (select)
Video Capture: Frame #020 (select)
Video Capture: Frame #021 (select)
Video Capture: Frame #022 (select)
Video Capture: Frame #023 (select)
Video Capture: Frame #024 (select)
Video Capture: Frame #025 (select)
Video Capture: Frame #026 (select)
Video Capture: Frame #027 (select)
Video Capture: Frame #028 (select)
Video Capture: Frame #029 (select)
Video Capture: Frame #030 (select)
Video Capture: Frame #031 (select)
Video Capture: Frame #032 (select)
Video Capture: Frame #033 (select)
Video Capture: Frame #034 (select)
Video Capture: Frame #035 (select)
Video Capture: Frame #036 (select)
Video Capture: Frame #037 (select)
Video Capture: Frame #038 (select)
Video Capture: Frame #039 (select)
Video Capture: Frame #040 (select)
Video Capture: Frame #041 (select)
Video Capture: Frame #042 (select)
Video Capture: Frame #043 (select)
Video Capture: Frame #044 (select)
Video Capture: Frame #045 (select)
Video Capture: Frame #046 (select)
Video Capture: Frame #047 (select)
Video Capture: Frame #048 (select)
Video Capture: Frame #049 (select)
Video Capture: Frame #050 (select)
Video Capture: Frame #051 (select)
Video Capture: Frame #052 (select)
Video Capture: Frame #053 (select)
Video Capture: Frame #054 (select)
Video Capture: Frame #055 (select)
Video Capture: Frame #056 (select)
Video Capture: Frame #057 (select)
Video Capture: Frame #058 (select)
Video Capture: Frame #059 (select)
test MMAP (select, REQBUFS): OK
Video Capture: Frame #000 (epoll)
Video Capture: Frame #001 (epoll)
Video Capture: Frame #002 (epoll)
Video Capture: Frame #003 (epoll)
Video Capture: Frame #004 (epoll)
Video Capture: Frame #005 (epoll)
Video Capture: Frame #006 (epoll)
Video Capture: Frame #007 (epoll)
Video Capture: Frame #008 (epoll)
Video Capture: Frame #009 (epoll)
Video Capture: Frame #010 (epoll)
Video Capture: Frame #011 (epoll)
Video Capture: Frame #012 (epoll)
Video Capture: Frame #013 (epoll)
Video Capture: Frame #014 (epoll)
Video Capture: Frame #015 (epoll)
Video Capture: Frame #016 (epoll)
Video Capture: Frame #017 (epoll)
Video Capture: Frame #018 (epoll)
Video Capture: Frame #019 (epoll)
Video Capture: Frame #020 (epoll)
Video Capture: Frame #021 (epoll)
Video Capture: Frame #022 (epoll)
Video Capture: Frame #023 (epoll)
Video Capture: Frame #024 (epoll)
Video Capture: Frame #025 (epoll)
Video Capture: Frame #026 (epoll)
Video Capture: Frame #027 (epoll)
Video Capture: Frame #028 (epoll)
Video Capture: Frame #029 (epoll)
Video Capture: Frame #030 (epoll)
Video Capture: Frame #031 (epoll)
Video Capture: Frame #032 (epoll)
Video Capture: Frame #033 (epoll)
Video Capture: Frame #034 (epoll)
Video Capture: Frame #035 (epoll)
Video Capture: Frame #036 (epoll)
Video Capture: Frame #037 (epoll)
Video Capture: Frame #038 (epoll)
Video Capture: Frame #039 (epoll)
Video Capture: Frame #040 (epoll)
Video Capture: Frame #041 (epoll)
Video Capture: Frame #042 (epoll)
Video Capture: Frame #043 (epoll)
Video Capture: Frame #044 (epoll)
Video Capture: Frame #045 (epoll)
Video Capture: Frame #046 (epoll)
Video Capture: Frame #047 (epoll)
Video Capture: Frame #048 (epoll)
Video Capture: Frame #049 (epoll)
Video Capture: Frame #050 (epoll)
Video Capture: Frame #051 (epoll)
Video Capture: Frame #052 (epoll)
Video Capture: Frame #053 (epoll)
Video Capture: Frame #054 (epoll)
Video Capture: Frame #055 (epoll)
Video Capture: Frame #056 (epoll)
Video Capture: Frame #057 (epoll)
Video Capture: Frame #058 (epoll)
Video Capture: Frame #059 (epoll)
test MMAP (epoll, REQBUFS): OK
Video Capture: Frame #000
Video Capture: Frame #001
Video Capture: Frame #002
Video Capture: Frame #003
Video Capture: Frame #004
Video Capture: Frame #005
Video Capture: Frame #006
Video Capture: Frame #007
Video Capture: Frame #008
Video Capture: Frame #009
Video Capture: Frame #010
Video Capture: Frame #011
Video Capture: Frame #012
Video Capture: Frame #013
Video Capture: Frame #014
Video Capture: Frame #015
Video Capture: Frame #016
Video Capture: Frame #017
Video Capture: Frame #018
Video Capture: Frame #019
Video Capture: Frame #020
Video Capture: Frame #021
Video Capture: Frame #022
Video Capture: Frame #023
Video Capture: Frame #024
Video Capture: Frame #025
Video Capture: Frame #026
Video Capture: Frame #027
Video Capture: Frame #028
Video Capture: Frame #029
Video Capture: Frame #030
Video Capture: Frame #031
Video Capture: Frame #032
Video Capture: Frame #033
Video Capture: Frame #034
Video Capture: Frame #035
Video Capture: Frame #036
Video Capture: Frame #037
Video Capture: Frame #038
Video Capture: Frame #039
Video Capture: Frame #040
Video Capture: Frame #041
Video Capture: Frame #042
Video Capture: Frame #043
Video Capture: Frame #044
Video Capture: Frame #045
Video Capture: Frame #046
Video Capture: Frame #047
Video Capture: Frame #048
Video Capture: Frame #049
Video Capture: Frame #050
Video Capture: Frame #051
Video Capture: Frame #052
Video Capture: Frame #053
Video Capture: Frame #054
Video Capture: Frame #055
Video Capture: Frame #056
Video Capture: Frame #057
Video Capture: Frame #058
Video Capture: Frame #059
test MMAP (no poll, CREATE_BUFS): OK
Video Capture: Frame #000 (select)
Video Capture: Frame #001 (select)
Video Capture: Frame #002 (select)
Video Capture: Frame #003 (select)
Video Capture: Frame #004 (select)
Video Capture: Frame #005 (select)
Video Capture: Frame #006 (select)
Video Capture: Frame #007 (select)
Video Capture: Frame #008 (select)
Video Capture: Frame #009 (select)
Video Capture: Frame #010 (select)
Video Capture: Frame #011 (select)
Video Capture: Frame #012 (select)
Video Capture: Frame #013 (select)
Video Capture: Frame #014 (select)
Video Capture: Frame #015 (select)
Video Capture: Frame #016 (select)
Video Capture: Frame #017 (select)
Video Capture: Frame #018 (select)
Video Capture: Frame #019 (select)
Video Capture: Frame #020 (select)
Video Capture: Frame #021 (select)
Video Capture: Frame #022 (select)
Video Capture: Frame #023 (select)
Video Capture: Frame #024 (select)
Video Capture: Frame #025 (select)
Video Capture: Frame #026 (select)
Video Capture: Frame #027 (select)
Video Capture: Frame #028 (select)
Video Capture: Frame #029 (select)
Video Capture: Frame #030 (select)
Video Capture: Frame #031 (select)
Video Capture: Frame #032 (select)
Video Capture: Frame #033 (select)
Video Capture: Frame #034 (select)
Video Capture: Frame #035 (select)
Video Capture: Frame #036 (select)
Video Capture: Frame #037 (select)
Video Capture: Frame #038 (select)
Video Capture: Frame #039 (select)
Video Capture: Frame #040 (select)
Video Capture: Frame #041 (select)
Video Capture: Frame #042 (select)
Video Capture: Frame #043 (select)
Video Capture: Frame #044 (select)
Video Capture: Frame #045 (select)
Video Capture: Frame #046 (select)
Video Capture: Frame #047 (select)
Video Capture: Frame #048 (select)
Video Capture: Frame #049 (select)
Video Capture: Frame #050 (select)
Video Capture: Frame #051 (select)
Video Capture: Frame #052 (select)
Video Capture: Frame #053 (select)
Video Capture: Frame #054 (select)
Video Capture: Frame #055 (select)
Video Capture: Frame #056 (select)
Video Capture: Frame #057 (select)
Video Capture: Frame #058 (select)
Video Capture: Frame #059 (select)
test MMAP (select, CREATE_BUFS): OK
Video Capture: Frame #000 (epoll)
Video Capture: Frame #001 (epoll)
Video Capture: Frame #002 (epoll)
Video Capture: Frame #003 (epoll)
Video Capture: Frame #004 (epoll)
Video Capture: Frame #005 (epoll)
Video Capture: Frame #006 (epoll)
Video Capture: Frame #007 (epoll)
Video Capture: Frame #008 (epoll)
Video Capture: Frame #009 (epoll)
Video Capture: Frame #010 (epoll)
Video Capture: Frame #011 (epoll)
Video Capture: Frame #012 (epoll)
Video Capture: Frame #013 (epoll)
Video Capture: Frame #014 (epoll)
Video Capture: Frame #015 (epoll)
Video Capture: Frame #016 (epoll)
Video Capture: Frame #017 (epoll)
Video Capture: Frame #018 (epoll)
Video Capture: Frame #019 (epoll)
Video Capture: Frame #020 (epoll)
Video Capture: Frame #021 (epoll)
Video Capture: Frame #022 (epoll)
Video Capture: Frame #023 (epoll)
Video Capture: Frame #024 (epoll)
Video Capture: Frame #025 (epoll)
Video Capture: Frame #026 (epoll)
Video Capture: Frame #027 (epoll)
Video Capture: Frame #028 (epoll)
Video Capture: Frame #029 (epoll)
Video Capture: Frame #030 (epoll)
Video Capture: Frame #031 (epoll)
Video Capture: Frame #032 (epoll)
Video Capture: Frame #033 (epoll)
Video Capture: Frame #034 (epoll)
Video Capture: Frame #035 (epoll)
Video Capture: Frame #036 (epoll)
Video Capture: Frame #037 (epoll)
Video Capture: Frame #038 (epoll)
Video Capture: Frame #039 (epoll)
Video Capture: Frame #040 (epoll)
Video Capture: Frame #041 (epoll)
Video Capture: Frame #042 (epoll)
Video Capture: Frame #043 (epoll)
Video Capture: Frame #044 (epoll)
Video Capture: Frame #045 (epoll)
Video Capture: Frame #046 (epoll)
Video Capture: Frame #047 (epoll)
Video Capture: Frame #048 (epoll)
Video Capture: Frame #049 (epoll)
Video Capture: Frame #050 (epoll)
Video Capture: Frame #051 (epoll)
Video Capture: Frame #052 (epoll)
Video Capture: Frame #053 (epoll)
Video Capture: Frame #054 (epoll)
Video Capture: Frame #055 (epoll)
Video Capture: Frame #056 (epoll)
Video Capture: Frame #057 (epoll)
Video Capture: Frame #058 (epoll)
Video Capture: Frame #059 (epoll)
test MMAP (epoll, CREATE_BUFS): OK
Video Capture: Frame #000
Video Capture: Frame #001
Video Capture: Frame #002
Video Capture: Frame #003
Video Capture: Frame #004
Video Capture: Frame #005
Video Capture: Frame #006
Video Capture: Frame #007
Video Capture: Frame #008
Video Capture: Frame #009
Video Capture: Frame #010
Video Capture: Frame #011
Video Capture: Frame #012
Video Capture: Frame #013
Video Capture: Frame #014
Video Capture: Frame #015
Video Capture: Frame #016
Video Capture: Frame #017
Video Capture: Frame #018
Video Capture: Frame #019
Video Capture: Frame #020
Video Capture: Frame #021
Video Capture: Frame #022
Video Capture: Frame #023
Video Capture: Frame #024
Video Capture: Frame #025
Video Capture: Frame #026
Video Capture: Frame #027
Video Capture: Frame #028
Video Capture: Frame #029
Video Capture: Frame #030
Video Capture: Frame #031
Video Capture: Frame #032
Video Capture: Frame #033
Video Capture: Frame #034
Video Capture: Frame #035
Video Capture: Frame #036
Video Capture: Frame #037
Video Capture: Frame #038
Video Capture: Frame #039
Video Capture: Frame #040
Video Capture: Frame #041
Video Capture: Frame #042
Video Capture: Frame #043
Video Capture: Frame #044
Video Capture: Frame #045
Video Capture: Frame #046
Video Capture: Frame #047
Video Capture: Frame #048
Video Capture: Frame #049
Video Capture: Frame #050
Video Capture: Frame #051
Video Capture: Frame #052
Video Capture: Frame #053
Video Capture: Frame #054
Video Capture: Frame #055
Video Capture: Frame #056
Video Capture: Frame #057
Video Capture: Frame #058
Video Capture: Frame #059
test USERPTR (no poll): OK
Video Capture: Frame #000 (select)
Video Capture: Frame #001 (select)
Video Capture: Frame #002 (select)
Video Capture: Frame #003 (select)
Video Capture: Frame #004 (select)
Video Capture: Frame #005 (select)
Video Capture: Frame #006 (select)
Video Capture: Frame #007 (select)
Video Capture: Frame #008 (select)
Video Capture: Frame #009 (select)
Video Capture: Frame #010 (select)
Video Capture: Frame #011 (select)
Video Capture: Frame #012 (select)
Video Capture: Frame #013 (select)
Video Capture: Frame #014 (select)
Video Capture: Frame #015 (select)
Video Capture: Frame #016 (select)
Video Capture: Frame #017 (select)
Video Capture: Frame #018 (select)
Video Capture: Frame #019 (select)
Video Capture: Frame #020 (select)
Video Capture: Frame #021 (select)
Video Capture: Frame #022 (select)
Video Capture: Frame #023 (select)
Video Capture: Frame #024 (select)
Video Capture: Frame #025 (select)
Video Capture: Frame #026 (select)
Video Capture: Frame #027 (select)
Video Capture: Frame #028 (select)
Video Capture: Frame #029 (select)
Video Capture: Frame #030 (select)
Video Capture: Frame #031 (select)
Video Capture: Frame #032 (select)
Video Capture: Frame #033 (select)
Video Capture: Frame #034 (select)
Video Capture: Frame #035 (select)
Video Capture: Frame #036 (select)
Video Capture: Frame #037 (select)
Video Capture: Frame #038 (select)
Video Capture: Frame #039 (select)
Video Capture: Frame #040 (select)
Video Capture: Frame #041 (select)
Video Capture: Frame #042 (select)
Video Capture: Frame #043 (select)
Video Capture: Frame #044 (select)
Video Capture: Frame #045 (select)
Video Capture: Frame #046 (select)
Video Capture: Frame #047 (select)
Video Capture: Frame #048 (select)
Video Capture: Frame #049 (select)
Video Capture: Frame #050 (select)
Video Capture: Frame #051 (select)
Video Capture: Frame #052 (select)
Video Capture: Frame #053 (select)
Video Capture: Frame #054 (select)
Video Capture: Frame #055 (select)
Video Capture: Frame #056 (select)
Video Capture: Frame #057 (select)
Video Capture: Frame #058 (select)
Video Capture: Frame #059 (select)
test USERPTR (select): OK
test DMABUF: Cannot test, specify --expbuf-device
Total for uvcvideo device /dev/video1: 58, Succeeded: 57, Failed: 1, Warnings: 0
As tested on the guest:
$ v4l2-compliance -d0 -s --driver-name uvcvideo
v4l2-compliance 1.33.0-5457, 64 bits, 64-bit time_t
v4l2-compliance SHA: e7e240f546f3 2026-05-28 17:06:12
Compliance test for uvcvideo device (overridden from virtio-media) /dev/video0:
Driver Info:
Driver name : uvcvideo
Card type : Logitech Webcam C925e
Bus info : platform:virtio-media
Driver version : 7.1.0
Capabilities : 0x84200001
Video Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
fail: v4l2-test-controls.cpp(981): ret (got 22)
test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 19 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK (Not Supported)
test Requests: OK (Not Supported)
test blocking wait: OK
Test input 0:
Streaming ioctls:
test read/write: OK (Not Supported)
Video Capture: Frame #000
Video Capture: Frame #001
Video Capture: Frame #002
Video Capture: Frame #003
Video Capture: Frame #004
Video Capture: Frame #005
Video Capture: Frame #006
Video Capture: Frame #007
Video Capture: Frame #008
Video Capture: Frame #009
Video Capture: Frame #010
Video Capture: Frame #011
Video Capture: Frame #012
Video Capture: Frame #013
Video Capture: Frame #014
Video Capture: Frame #015
Video Capture: Frame #016
Video Capture: Frame #017
Video Capture: Frame #018
Video Capture: Frame #019
Video Capture: Frame #020
Video Capture: Frame #021
Video Capture: Frame #022
Video Capture: Frame #023
Video Capture: Frame #024
Video Capture: Frame #025
Video Capture: Frame #026
Video Capture: Frame #027
Video Capture: Frame #028
Video Capture: Frame #029
Video Capture: Frame #030
Video Capture: Frame #031
Video Capture: Frame #032
Video Capture: Frame #033
Video Capture: Frame #034
Video Capture: Frame #035
Video Capture: Frame #036
Video Capture: Frame #037
Video Capture: Frame #038
Video Capture: Frame #039
Video Capture: Frame #040
Video Capture: Frame #041
Video Capture: Frame #042
Video Capture: Frame #043
Video Capture: Frame #044
Video Capture: Frame #045
Video Capture: Frame #046
Video Capture: Frame #047
Video Capture: Frame #048
Video Capture: Frame #049
Video Capture: Frame #050
Video Capture: Frame #051
Video Capture: Frame #052
Video Capture: Frame #053
Video Capture: Frame #054
Video Capture: Frame #055
Video Capture: Frame #056
Video Capture: Frame #057
Video Capture: Frame #058
Video Capture: Frame #059
test MMAP (no poll, REQBUFS): OK
Video Capture: Frame #000 (select)
Video Capture: Frame #001 (select)
Video Capture: Frame #002 (select)
Video Capture: Frame #003 (select)
Video Capture: Frame #004 (select)
Video Capture: Frame #005 (select)
Video Capture: Frame #006 (select)
Video Capture: Frame #007 (select)
Video Capture: Frame #008 (select)
Video Capture: Frame #009 (select)
Video Capture: Frame #010 (select)
Video Capture: Frame #011 (select)
Video Capture: Frame #012 (select)
Video Capture: Frame #013 (select)
Video Capture: Frame #014 (select)
Video Capture: Frame #015 (select)
Video Capture: Frame #016 (select)
Video Capture: Frame #017 (select)
Video Capture: Frame #018 (select)
Video Capture: Frame #019 (select)
Video Capture: Frame #020 (select)
Video Capture: Frame #021 (select)
Video Capture: Frame #022 (select)
Video Capture: Frame #023 (select)
Video Capture: Frame #024 (select)
Video Capture: Frame #025 (select)
Video Capture: Frame #026 (select)
Video Capture: Frame #027 (select)
Video Capture: Frame #028 (select)
Video Capture: Frame #029 (select)
Video Capture: Frame #030 (select)
Video Capture: Frame #031 (select)
Video Capture: Frame #032 (select)
Video Capture: Frame #033 (select)
Video Capture: Frame #034 (select)
Video Capture: Frame #035 (select)
Video Capture: Frame #036 (select)
Video Capture: Frame #037 (select)
Video Capture: Frame #038 (select)
Video Capture: Frame #039 (select)
Video Capture: Frame #040 (select)
Video Capture: Frame #041 (select)
Video Capture: Frame #042 (select)
Video Capture: Frame #043 (select)
Video Capture: Frame #044 (select)
Video Capture: Frame #045 (select)
Video Capture: Frame #046 (select)
Video Capture: Frame #047 (select)
Video Capture: Frame #048 (select)
Video Capture: Frame #049 (select)
Video Capture: Frame #050 (select)
Video Capture: Frame #051 (select)
Video Capture: Frame #052 (select)
Video Capture: Frame #053 (select)
Video Capture: Frame #054 (select)
Video Capture: Frame #055 (select)
Video Capture: Frame #056 (select)
Video Capture: Frame #057 (select)
Video Capture: Frame #058 (select)
Video Capture: Frame #059 (select)
test MMAP (select, REQBUFS): OK
Video Capture: Frame #000 (epoll)
Video Capture: Frame #001 (epoll)
Video Capture: Frame #002 (epoll)
Video Capture: Frame #003 (epoll)
Video Capture: Frame #004 (epoll)
Video Capture: Frame #005 (epoll)
Video Capture: Frame #006 (epoll)
Video Capture: Frame #007 (epoll)
Video Capture: Frame #008 (epoll)
Video Capture: Frame #009 (epoll)
Video Capture: Frame #010 (epoll)
Video Capture: Frame #011 (epoll)
Video Capture: Frame #012 (epoll)
Video Capture: Frame #013 (epoll)
Video Capture: Frame #014 (epoll)
Video Capture: Frame #015 (epoll)
Video Capture: Frame #016 (epoll)
Video Capture: Frame #017 (epoll)
Video Capture: Frame #018 (epoll)
Video Capture: Frame #019 (epoll)
Video Capture: Frame #020 (epoll)
Video Capture: Frame #021 (epoll)
Video Capture: Frame #022 (epoll)
Video Capture: Frame #023 (epoll)
Video Capture: Frame #024 (epoll)
Video Capture: Frame #025 (epoll)
Video Capture: Frame #026 (epoll)
Video Capture: Frame #027 (epoll)
Video Capture: Frame #028 (epoll)
Video Capture: Frame #029 (epoll)
Video Capture: Frame #030 (epoll)
Video Capture: Frame #031 (epoll)
Video Capture: Frame #032 (epoll)
Video Capture: Frame #033 (epoll)
Video Capture: Frame #034 (epoll)
Video Capture: Frame #035 (epoll)
Video Capture: Frame #036 (epoll)
Video Capture: Frame #037 (epoll)
Video Capture: Frame #038 (epoll)
Video Capture: Frame #039 (epoll)
Video Capture: Frame #040 (epoll)
Video Capture: Frame #041 (epoll)
Video Capture: Frame #042 (epoll)
Video Capture: Frame #043 (epoll)
Video Capture: Frame #044 (epoll)
Video Capture: Frame #045 (epoll)
Video Capture: Frame #046 (epoll)
Video Capture: Frame #047 (epoll)
Video Capture: Frame #048 (epoll)
Video Capture: Frame #049 (epoll)
Video Capture: Frame #050 (epoll)
Video Capture: Frame #051 (epoll)
Video Capture: Frame #052 (epoll)
Video Capture: Frame #053 (epoll)
Video Capture: Frame #054 (epoll)
Video Capture: Frame #055 (epoll)
Video Capture: Frame #056 (epoll)
Video Capture: Frame #057 (epoll)
Video Capture: Frame #058 (epoll)
Video Capture: Frame #059 (epoll)
test MMAP (epoll, REQBUFS): OK
Video Capture: Frame #000
Video Capture: Frame #001
Video Capture: Frame #002
Video Capture: Frame #003
Video Capture: Frame #004
Video Capture: Frame #005
Video Capture: Frame #006
Video Capture: Frame #007
Video Capture: Frame #008
Video Capture: Frame #009
Video Capture: Frame #010
Video Capture: Frame #011
Video Capture: Frame #012
Video Capture: Frame #013
Video Capture: Frame #014
Video Capture: Frame #015
Video Capture: Frame #016
Video Capture: Frame #017
Video Capture: Frame #018
Video Capture: Frame #019
Video Capture: Frame #020
Video Capture: Frame #021
Video Capture: Frame #022
Video Capture: Frame #023
Video Capture: Frame #024
Video Capture: Frame #025
Video Capture: Frame #026
Video Capture: Frame #027
Video Capture: Frame #028
Video Capture: Frame #029
Video Capture: Frame #030
Video Capture: Frame #031
Video Capture: Frame #032
Video Capture: Frame #033
Video Capture: Frame #034
Video Capture: Frame #035
Video Capture: Frame #036
Video Capture: Frame #037
Video Capture: Frame #038
Video Capture: Frame #039
Video Capture: Frame #040
Video Capture: Frame #041
Video Capture: Frame #042
Video Capture: Frame #043
Video Capture: Frame #044
Video Capture: Frame #045
Video Capture: Frame #046
Video Capture: Frame #047
Video Capture: Frame #048
Video Capture: Frame #049
Video Capture: Frame #050
Video Capture: Frame #051
Video Capture: Frame #052
Video Capture: Frame #053
Video Capture: Frame #054
Video Capture: Frame #055
Video Capture: Frame #056
Video Capture: Frame #057
Video Capture: Frame #058
Video Capture: Frame #059
test MMAP (no poll, CREATE_BUFS): OK
Video Capture: Frame #000 (select)
Video Capture: Frame #001 (select)
Video Capture: Frame #002 (select)
Video Capture: Frame #003 (select)
Video Capture: Frame #004 (select)
Video Capture: Frame #005 (select)
Video Capture: Frame #006 (select)
Video Capture: Frame #007 (select)
Video Capture: Frame #008 (select)
Video Capture: Frame #009 (select)
Video Capture: Frame #010 (select)
Video Capture: Frame #011 (select)
Video Capture: Frame #012 (select)
Video Capture: Frame #013 (select)
Video Capture: Frame #014 (select)
Video Capture: Frame #015 (select)
Video Capture: Frame #016 (select)
Video Capture: Frame #017 (select)
Video Capture: Frame #018 (select)
Video Capture: Frame #019 (select)
Video Capture: Frame #020 (select)
Video Capture: Frame #021 (select)
Video Capture: Frame #022 (select)
Video Capture: Frame #023 (select)
Video Capture: Frame #024 (select)
Video Capture: Frame #025 (select)
Video Capture: Frame #026 (select)
Video Capture: Frame #027 (select)
Video Capture: Frame #028 (select)
Video Capture: Frame #029 (select)
Video Capture: Frame #030 (select)
Video Capture: Frame #031 (select)
Video Capture: Frame #032 (select)
Video Capture: Frame #033 (select)
Video Capture: Frame #034 (select)
Video Capture: Frame #035 (select)
Video Capture: Frame #036 (select)
Video Capture: Frame #037 (select)
Video Capture: Frame #038 (select)
Video Capture: Frame #039 (select)
Video Capture: Frame #040 (select)
Video Capture: Frame #041 (select)
Video Capture: Frame #042 (select)
Video Capture: Frame #043 (select)
Video Capture: Frame #044 (select)
Video Capture: Frame #045 (select)
Video Capture: Frame #046 (select)
Video Capture: Frame #047 (select)
Video Capture: Frame #048 (select)
Video Capture: Frame #049 (select)
Video Capture: Frame #050 (select)
Video Capture: Frame #051 (select)
Video Capture: Frame #052 (select)
Video Capture: Frame #053 (select)
Video Capture: Frame #054 (select)
Video Capture: Frame #055 (select)
Video Capture: Frame #056 (select)
Video Capture: Frame #057 (select)
Video Capture: Frame #058 (select)
Video Capture: Frame #059 (select)
test MMAP (select, CREATE_BUFS): OK
Video Capture: Frame #000 (epoll)
Video Capture: Frame #001 (epoll)
Video Capture: Frame #002 (epoll)
Video Capture: Frame #003 (epoll)
Video Capture: Frame #004 (epoll)
Video Capture: Frame #005 (epoll)
Video Capture: Frame #006 (epoll)
Video Capture: Frame #007 (epoll)
Video Capture: Frame #008 (epoll)
Video Capture: Frame #009 (epoll)
Video Capture: Frame #010 (epoll)
Video Capture: Frame #011 (epoll)
Video Capture: Frame #012 (epoll)
Video Capture: Frame #013 (epoll)
Video Capture: Frame #014 (epoll)
Video Capture: Frame #015 (epoll)
Video Capture: Frame #016 (epoll)
Video Capture: Frame #017 (epoll)
Video Capture: Frame #018 (epoll)
Video Capture: Frame #019 (epoll)
Video Capture: Frame #020 (epoll)
Video Capture: Frame #021 (epoll)
Video Capture: Frame #022 (epoll)
Video Capture: Frame #023 (epoll)
Video Capture: Frame #024 (epoll)
Video Capture: Frame #025 (epoll)
Video Capture: Frame #026 (epoll)
Video Capture: Frame #027 (epoll)
Video Capture: Frame #028 (epoll)
Video Capture: Frame #029 (epoll)
Video Capture: Frame #030 (epoll)
Video Capture: Frame #031 (epoll)
Video Capture: Frame #032 (epoll)
Video Capture: Frame #033 (epoll)
Video Capture: Frame #034 (epoll)
Video Capture: Frame #035 (epoll)
Video Capture: Frame #036 (epoll)
Video Capture: Frame #037 (epoll)
Video Capture: Frame #038 (epoll)
Video Capture: Frame #039 (epoll)
Video Capture: Frame #040 (epoll)
Video Capture: Frame #041 (epoll)
Video Capture: Frame #042 (epoll)
Video Capture: Frame #043 (epoll)
Video Capture: Frame #044 (epoll)
Video Capture: Frame #045 (epoll)
Video Capture: Frame #046 (epoll)
Video Capture: Frame #047 (epoll)
Video Capture: Frame #048 (epoll)
Video Capture: Frame #049 (epoll)
Video Capture: Frame #050 (epoll)
Video Capture: Frame #051 (epoll)
Video Capture: Frame #052 (epoll)
Video Capture: Frame #053 (epoll)
Video Capture: Frame #054 (epoll)
Video Capture: Frame #055 (epoll)
Video Capture: Frame #056 (epoll)
Video Capture: Frame #057 (epoll)
Video Capture: Frame #058 (epoll)
Video Capture: Frame #059 (epoll)
test MMAP (epoll, CREATE_BUFS): OK
test USERPTR (no poll): OK (Not Supported)
test USERPTR (select): OK (Not Supported)
test DMABUF (no poll): OK (Not Supported)
test DMABUF (select): OK (Not Supported)
Total for uvcvideo device /dev/video0: 59, Succeeded: 58, Failed: 1, Warnings: 0
---
Changes in v4:
- Rebased on top of v7.1-rc1
- Replace usages of filep->private_data with file_to_v4l2_fh()
throughout the driver
- Link to v3: https://lore.kernel.org/r/20250412-virtio-media-v3-1-97dc94c18398@gmail.com
Changes in v3:
- Rebased on top of v6.15-rc1 and removes obsolete control callbacks.
- Link to v2: https://lore.kernel.org/r/20250201-virtio-media-v2-1-ac840681452d@gmail.com
Changes in v2:
- Fixed kernel test robot and media CI warnings (ignored a few false
positives).
- Changed in-driver email address to personal one since my Google one
will soon become invalid.
- Link to v1: https://lore.kernel.org/r/20250123-virtio-media-v1-1-81e2549b86b9@gmail.com
Brian Daniels (8):
media: virtio: Add protocol
media: virtio: Add virtio-media driver structs and function
declarations
media: virtio: Add virtio-media session related structures
media: virtio: Add scatterlist_builder
media: virtio: Add virtio_media_ioctls
media: virtio: Add virtio_media_driver
media: virtio: Add virtio-media to the build system
media: virtio: Add MAINTAINERS entry
MAINTAINERS | 6 +
drivers/media/Kconfig | 13 +
drivers/media/Makefile | 2 +
drivers/media/virtio/Makefile | 8 +
drivers/media/virtio/protocol.h | 287 +++++
drivers/media/virtio/scatterlist_builder.c | 574 +++++++++
drivers/media/virtio/scatterlist_builder.h | 112 ++
drivers/media/virtio/session.h | 130 ++
drivers/media/virtio/virtio_media.h | 95 ++
drivers/media/virtio/virtio_media_driver.c | 959 ++++++++++++++
drivers/media/virtio/virtio_media_ioctls.c | 1338 ++++++++++++++++++++
11 files changed, 3524 insertions(+)
create mode 100644 drivers/media/virtio/Makefile
create mode 100644 drivers/media/virtio/protocol.h
create mode 100644 drivers/media/virtio/scatterlist_builder.c
create mode 100644 drivers/media/virtio/scatterlist_builder.h
create mode 100644 drivers/media/virtio/session.h
create mode 100644 drivers/media/virtio/virtio_media.h
create mode 100644 drivers/media/virtio/virtio_media_driver.c
create mode 100644 drivers/media/virtio/virtio_media_ioctls.c
base-commit: 06cb687a5132fcffe624c0070576ab852ac6b568
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply
* [PATCH v4 1/8] media: virtio: Add protocol
From: Brian Daniels @ 2026-06-22 20:43 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: acourbot, adelva, aesteve, changyeon, daniel.almeida, eperezma,
gnurou, gurchetansingh, hverkuil, jasowang, linux-kernel,
linux-media, mst, nicolas.dufresne, virtualization, xuanzhuo,
Brian Daniels
In-Reply-To: <20260622204343.1994418-1-briandaniels@google.com>
From: Alexandre Courbot <gnurou@gmail.com>
Add the identifiers and structs used to implement the virtio interface
as described in section 5.22 of version 1.4 of the virtio spec:
https://docs.oasis-open.org/virtio/virtio/v1.4/csprd01/virtio-v1.4-csprd01-diff-from-v1.2-cs01.html#x1-82200022
Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
Co-developed-by: Brian Daniels <briandaniels@google.com>
Signed-off-by: Brian Daniels <briandaniels@google.com>
---
drivers/media/virtio/protocol.h | 287 ++++++++++++++++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 drivers/media/virtio/protocol.h
diff --git a/drivers/media/virtio/protocol.h b/drivers/media/virtio/protocol.h
new file mode 100644
index 000000000..5878d107c
--- /dev/null
+++ b/drivers/media/virtio/protocol.h
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+ */
+
+/*
+ * Definitions of virtio-media protocol structures.
+ *
+ * Copyright (c) 2024-2025 Google LLC.
+ */
+
+#ifndef __VIRTIO_MEDIA_PROTOCOL_H
+#define __VIRTIO_MEDIA_PROTOCOL_H
+
+#include <linux/videodev2.h>
+#include <linux/bits.h>
+
+/*
+ * Virtio protocol definition.
+ */
+
+/**
+ * struct virtio_media_cmd_header - Header for all virtio-media commands.
+ * @cmd: one of VIRTIO_MEDIA_CMD_*.
+ * @__reserved: must be set to zero by the driver.
+ *
+ * This header starts all commands from the driver to the device on the
+ * commandq.
+ */
+struct virtio_media_cmd_header {
+ u32 cmd;
+ u32 __reserved;
+};
+
+/**
+ * struct virtio_media_resp_header - Header for all virtio-media responses.
+ * @status: 0 if the command was successful, or one of the standard Linux error
+ * codes.
+ * @__reserved: must be set to zero by the device.
+ *
+ * This header starts all responses from the device to the driver on the
+ * commandq.
+ */
+struct virtio_media_resp_header {
+ u32 status;
+ u32 __reserved;
+};
+
+/**
+ * VIRTIO_MEDIA_CMD_OPEN - Command for creating a new session.
+ *
+ * This is the equivalent of calling `open` on a V4L2 device node. Upon
+ * success, a session id is returned which can be used to perform other
+ * commands on the session, notably ioctls.
+ */
+#define VIRTIO_MEDIA_CMD_OPEN 1
+
+/**
+ * struct virtio_media_cmd_open - Driver command for VIRTIO_MEDIA_CMD_OPEN.
+ * @hdr: header with cmd member set to VIRTIO_MEDIA_CMD_OPEN.
+ */
+struct virtio_media_cmd_open {
+ struct virtio_media_cmd_header hdr;
+};
+
+/**
+ * struct virtio_media_resp_open - Device response for VIRTIO_MEDIA_CMD_OPEN.
+ * @hdr: header containing the status of the command.
+ * @session_id: if hdr.status == 0, contains the id of the newly created
+ * session.
+ * @__reserved: must be set to zero by the device.
+ */
+struct virtio_media_resp_open {
+ struct virtio_media_resp_header hdr;
+ u32 session_id;
+ u32 __reserved;
+};
+
+/**
+ * VIRTIO_MEDIA_CMD_CLOSE - Command for closing an active session.
+ *
+ * This is the equivalent of calling `close` on a previously opened V4L2
+ * session. All resources associated with this session will be freed and the
+ * session ID shall not be used again after queueing this command.
+ *
+ * This command does not require a response from the device.
+ */
+#define VIRTIO_MEDIA_CMD_CLOSE 2
+
+/**
+ * struct virtio_media_cmd_close - Driver command for VIRTIO_MEDIA_CMD_CLOSE.
+ * @hdr: header with cmd member set to VIRTIO_MEDIA_CMD_CLOSE.
+ * @session_id: id of the session to close.
+ * @__reserved: must be set to zero by the driver.
+ */
+struct virtio_media_cmd_close {
+ struct virtio_media_cmd_header hdr;
+ u32 session_id;
+ u32 __reserved;
+};
+
+/**
+ * VIRTIO_MEDIA_CMD_IOCTL - Driver command for executing an ioctl.
+ *
+ * This command asks the device to run one of the `VIDIOC_*` ioctls on the
+ * active session.
+ *
+ * The code of the ioctl is extracted from the VIDIOC_* definitions in
+ * `videodev2.h`, and consists of the second argument of the `_IO*` macro.
+ *
+ * Each ioctl has a payload, which is defined by the third argument of the
+ * `_IO*` macro defining it. It can be writable by the driver (`_IOW`), the
+ * device (`_IOR`), or both (`_IOWR`).
+ *
+ * If an ioctl is writable by the driver, it must be followed by a
+ * driver-writable descriptor containing the payload.
+ *
+ * If an ioctl is writable by the device, it must be followed by a
+ * device-writable descriptor of the size of the payload that the device will
+ * write into.
+ *
+ */
+#define VIRTIO_MEDIA_CMD_IOCTL 3
+
+/**
+ * struct virtio_media_cmd_ioctl - Driver command for VIRTIO_MEDIA_CMD_IOCTL.
+ * @hdr: header with cmd member set to VIRTIO_MEDIA_CMD_IOCTL.
+ * @session_id: id of the session to run the ioctl on.
+ * @code: code of the ioctl to run.
+ */
+struct virtio_media_cmd_ioctl {
+ struct virtio_media_cmd_header hdr;
+ u32 session_id;
+ u32 code;
+};
+
+/**
+ * struct virtio_media_resp_ioctl - Device response for VIRTIO_MEDIA_CMD_IOCTL.
+ * @hdr: header containing the status of the ioctl.
+ */
+struct virtio_media_resp_ioctl {
+ struct virtio_media_resp_header hdr;
+};
+
+/**
+ * struct virtio_media_sg_entry - Description of part of a scattered guest
+ * memory.
+ * @start: start guest address of the memory segment.
+ * @len: length of this memory segment.
+ * @__reserved: must be set to zero by the driver.
+ */
+struct virtio_media_sg_entry {
+ u64 start;
+ u32 len;
+ u32 __reserved;
+};
+
+#define VIRTIO_MEDIA_MMAP_FLAG_RW BIT(0)
+
+/**
+ * VIRTIO_MEDIA_CMD_MMAP - Command for mapping a MMAP buffer into the driver's
+ * address space.
+ *
+ */
+#define VIRTIO_MEDIA_CMD_MMAP 4
+
+/**
+ * struct virtio_media_cmd_mmap - Driver command for VIRTIO_MEDIA_CMD_MMAP.
+ * @hdr: header with cmd member set to VIRTIO_MEDIA_CMD_MMAP.
+ * @session_id: ID of the session we are mapping for.
+ * @flags: combination of VIRTIO_MEDIA_MMAP_FLAG_*.
+ * @offset: mem_offset field of the plane to map, as returned by
+ * VIDIOC_QUERYBUF.
+ */
+struct virtio_media_cmd_mmap {
+ struct virtio_media_cmd_header hdr;
+ u32 session_id;
+ u32 flags;
+ u32 offset;
+};
+
+/**
+ * struct virtio_media_resp_mmap - Device response for VIRTIO_MEDIA_CMD_MMAP.
+ * @hdr: header containing the status of the command.
+ * @driver_addr: offset into SHM region 0 of the start of the mapping.
+ * @len: length of the mapping.
+ */
+struct virtio_media_resp_mmap {
+ struct virtio_media_resp_header hdr;
+ u64 driver_addr;
+ u64 len;
+};
+
+/**
+ * VIRTIO_MEDIA_CMD_MUNMAP - Unmap a MMAP buffer previously mapped using
+ * VIRTIO_MEDIA_CMD_MMAP.
+ */
+#define VIRTIO_MEDIA_CMD_MUNMAP 5
+
+/**
+ * struct virtio_media_cmd_munmap - Driver command for VIRTIO_MEDIA_CMD_MUNMAP.
+ * @hdr: header with cmd member set to VIRTIO_MEDIA_CMD_MUNMAP.
+ * @driver_addr: offset into SHM region 0 at which the buffer has been
+ * previously
+ * mapped.
+ */
+struct virtio_media_cmd_munmap {
+ struct virtio_media_cmd_header hdr;
+ u64 driver_addr;
+};
+
+/**
+ * struct virtio_media_resp_munmap - Device response for
+ * VIRTIO_MEDIA_CMD_MUNMAP.
+ * @hdr: header containing the status of the command.
+ */
+struct virtio_media_resp_munmap {
+ struct virtio_media_resp_header hdr;
+};
+
+/* The values for these events are set by the virtio-media specification. */
+#define VIRTIO_MEDIA_EVT_ERROR 0
+#define VIRTIO_MEDIA_EVT_DQBUF 1
+#define VIRTIO_MEDIA_EVT_EVENT 2
+
+/**
+ * struct virtio_media_event_header - Header for events on the eventq.
+ * @event: one of VIRTIO_MEDIA_EVT_*
+ * @session_id: ID of the session the event applies to.
+ */
+struct virtio_media_event_header {
+ u32 event;
+ u32 session_id;
+};
+
+/**
+ * struct virtio_media_event_error - Unrecoverable device-side error.
+ * @hdr: header for the event.
+ * @errno: error code describing the kind of error that occurred.
+ * @__reserved: must to set to zero by the device.
+ *
+ * Upon receiving this event, the session mentioned in the header is considered
+ * corrupted and closed.
+ *
+ */
+struct virtio_media_event_error {
+ struct virtio_media_event_header hdr;
+ u32 errno;
+ u32 __reserved;
+};
+
+/* This is set to VIDEO_MAX_PLANES defined in include/uapi/linux/videodev2.h.
+ * It is renamed here to match the constant that is defined in the virtio-media
+ * specification.
+ */
+#define VIRTIO_MEDIA_MAX_PLANES VIDEO_MAX_PLANES
+
+/**
+ * struct virtio_media_event_dqbuf - Dequeued buffer event.
+ * @hdr: header for the event.
+ * @buffer: struct v4l2_buffer describing the buffer that has been dequeued.
+ * @planes: plane information for the dequeued buffer.
+ *
+ * This event is used to signal that a buffer is not being used anymore by the
+ * device and is returned to the driver.
+ */
+struct virtio_media_event_dqbuf {
+ struct virtio_media_event_header hdr;
+ struct v4l2_buffer buffer;
+ struct v4l2_plane planes[VIRTIO_MEDIA_MAX_PLANES];
+};
+
+/**
+ * struct virtio_media_event_event - V4L2 event.
+ * @hdr: header for the event.
+ * @event: description of the event that occurred.
+ *
+ * This event signals that a V4L2 event has been emitted for a session.
+ */
+struct virtio_media_event_event {
+ struct virtio_media_event_header hdr;
+ struct v4l2_event event;
+};
+
+/* Maximum size of an event. We will queue descriptors of this size on the
+ * eventq.
+ */
+#define VIRTIO_MEDIA_EVENT_MAX_SIZE sizeof(struct virtio_media_event_dqbuf)
+
+#endif // __VIRTIO_MEDIA_PROTOCOL_H
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH v4 2/8] media: virtio: Add virtio-media driver structs and function declarations
From: Brian Daniels @ 2026-06-22 20:43 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: acourbot, adelva, aesteve, changyeon, daniel.almeida, eperezma,
gnurou, gurchetansingh, hverkuil, jasowang, linux-kernel,
linux-media, mst, nicolas.dufresne, virtualization, xuanzhuo,
Brian Daniels
In-Reply-To: <20260622204343.1994418-1-briandaniels@google.com>
From: Alexandre Courbot <gnurou@gmail.com>
Add the structs and function declarations for the new virtio-media drvier.
Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
Co-developed-by: Brian Daniels <briandaniels@google.com>
Signed-off-by: Brian Daniels <briandaniels@google.com>
---
drivers/media/virtio/virtio_media.h | 95 +++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
create mode 100644 drivers/media/virtio/virtio_media.h
diff --git a/drivers/media/virtio/virtio_media.h b/drivers/media/virtio/virtio_media.h
new file mode 100644
index 000000000..52809d4e9
--- /dev/null
+++ b/drivers/media/virtio/virtio_media.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+ */
+
+/*
+ * Virtio-media structures & functions declarations.
+ *
+ * Copyright (c) 2024-2025 Google LLC.
+ */
+
+#ifndef __VIRTIO_MEDIA_H
+#define __VIRTIO_MEDIA_H
+
+#include <linux/virtio_config.h>
+#include <media/v4l2-device.h>
+
+#include "protocol.h"
+
+#define DESC_CHAIN_MAX_LEN SG_MAX_SINGLE_ALLOC
+
+#define VIRTIO_MEDIA_DEFAULT_DRIVER_NAME "virtio-media"
+
+extern char *virtio_media_driver_name;
+extern bool virtio_media_allow_userptr;
+
+/**
+ * struct virtio_media - Virtio-media device.
+ * @v4l2_dev: v4l2_device for the media device.
+ * @video_dev: video_device for the media device.
+ * @virtio_dev: virtio device for the media device.
+ * @commandq: virtio command queue.
+ * @eventq: virtio event queue.
+ * @eventq_work: work to run when events are received on @eventq.
+ * @mmap_region: region into which MMAP buffers are mapped by the host.
+ * @event_buffer: buffer for event descriptors.
+ * @sessions: list of active sessions on the device.
+ * @sessions_lock: protects @sessions and ``virtio_media_session::list``.
+ * @events_lock: prevents concurrent processing of events.
+ * @cmd: union of the device commands "open" and "munmap". The other
+ * commands are handled by @struct virtio_media_session
+ * @resp: union of responses.to device commands "open" and "munmap". The
+ * other responses are handled by @struct virtio_media_session
+ * @vlock: serializes access to the command queue.
+ * @wq: waitqueue for host responses on the command queue.
+ */
+struct virtio_media {
+ struct v4l2_device v4l2_dev;
+ struct video_device video_dev;
+
+ struct virtio_device *virtio_dev;
+ struct virtqueue *commandq;
+ struct virtqueue *eventq;
+ struct work_struct eventq_work;
+
+ struct virtio_shm_region mmap_region;
+
+ void *event_buffer;
+
+ struct list_head sessions;
+ struct mutex sessions_lock; /* protects sessions list */
+
+ struct mutex events_lock; /* prevents concurrent event processing */
+
+ union {
+ struct virtio_media_cmd_open open;
+ struct virtio_media_cmd_munmap munmap;
+ } cmd;
+
+ union {
+ struct virtio_media_resp_open open;
+ struct virtio_media_resp_munmap munmap;
+ } resp;
+
+ struct mutex vlock; /* serializes command queue access */
+ wait_queue_head_t wq;
+};
+
+static inline struct virtio_media *
+to_virtio_media(struct video_device *video_dev)
+{
+ return container_of(video_dev, struct virtio_media, video_dev);
+}
+
+/* virtio_media_driver.c */
+
+int virtio_media_send_command(struct virtio_media *vv, struct scatterlist **sgs,
+ const size_t out_sgs, const size_t in_sgs,
+ size_t minimum_resp_len, size_t *resp_len);
+void virtio_media_process_events(struct virtio_media *vv);
+
+/* virtio_media_ioctls.c */
+
+long virtio_media_device_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+extern const struct v4l2_ioctl_ops virtio_media_ioctl_ops;
+
+#endif // __VIRTIO_MEDIA_H
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH v4 3/8] media: virtio: Add virtio-media session related structures
From: Brian Daniels @ 2026-06-22 20:43 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: acourbot, adelva, aesteve, changyeon, daniel.almeida, eperezma,
gnurou, gurchetansingh, hverkuil, jasowang, linux-kernel,
linux-media, mst, nicolas.dufresne, virtualization, xuanzhuo,
Brian Daniels
In-Reply-To: <20260622204343.1994418-1-briandaniels@google.com>
From: Alexandre Courbot <gnurou@gmail.com>
Add the structs and functions to manage sessions. A session is
equivalent to opening the /dev/videoX device file of the V4L2 device.
Depending on the type of device, it may be possible to open several
sessions concurrently.
While the session is opened, its ID can be used to perform actions on
it, most commonly V4L2 ioctls.
Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
Co-developed-by: Brian Daniels <briandaniels@google.com>
Signed-off-by: Brian Daniels <briandaniels@google.com>
---
drivers/media/virtio/session.h | 130 +++++++++++++++++++++++++++++++++
1 file changed, 130 insertions(+)
create mode 100644 drivers/media/virtio/session.h
diff --git a/drivers/media/virtio/session.h b/drivers/media/virtio/session.h
new file mode 100644
index 000000000..4a2360625
--- /dev/null
+++ b/drivers/media/virtio/session.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+ */
+
+/*
+ * Definitions of virtio-media session related structures.
+ *
+ * Copyright (c) 2024-2025 Google LLC.
+ */
+
+#ifndef __VIRTIO_MEDIA_SESSION_H
+#define __VIRTIO_MEDIA_SESSION_H
+
+#include <linux/scatterlist.h>
+#include <media/v4l2-fh.h>
+
+#include "protocol.h"
+
+#define VIRTIO_MEDIA_LAST_QUEUE (V4L2_BUF_TYPE_META_OUTPUT)
+
+/*
+ * Size of the per-session virtio shadow and event buffers. 16K should be
+ * enough to contain everything we need.
+ */
+#define VIRTIO_SHADOW_BUF_SIZE 0x4000
+
+/**
+ * struct virtio_media_buffer - Current state of a buffer.
+ * @buffer: ``struct v4l2_buffer`` with current information about the buffer.
+ * @planes: backing planes array for @buffer.
+ * @list: link into the list of buffers pending dequeue.
+ */
+struct virtio_media_buffer {
+ struct v4l2_buffer buffer;
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ struct list_head list;
+};
+
+/**
+ * struct virtio_media_queue_state - Represents the state of a V4L2 queue.
+ * @streaming: Whether the queue is currently streaming.
+ * @allocated_bufs: How many buffers are currently allocated.
+ * @is_capture_last: set to true when the last buffer has been received on a
+ * capture queue, so we can return -EPIPE on subsequent DQBUF requests.
+ * @buffers: Buffer state array of size @allocated_bufs.
+ * @queued_bufs: How many buffers are currently queued on the device.
+ * @pending_dqbufs: Buffers that are available for being dequeued.
+ */
+struct virtio_media_queue_state {
+ bool streaming;
+ size_t allocated_bufs;
+ bool is_capture_last;
+
+ struct virtio_media_buffer *buffers;
+ size_t queued_bufs;
+ struct list_head pending_dqbufs;
+};
+
+/**
+ * struct virtio_media_session - A session on a virtio_media device.
+ * @fh: file handler for the session.
+ * @file: file pointer associated with the session's file handler.
+ * @id: session ID used to communicate with the device.
+ * @nonblocking_dequeue: whether dequeue should block or not (nonblocking if
+ * file opened with O_NONBLOCK).
+ * @uses_mplane: whether the queues for this session use the MPLANE API or not.
+ * @cmd: union of session commands "close", "ioctl", and "mmap". A session can
+ * have one command currently running. The rest of the commands are
+ * handled by @struct virtio_media.
+ * @resp: union of responses to session commands "close", "ioctl", and "mmap".
+ * A session can wait on one command only.The rest of the responses are
+ * handled by @struct virtio_media.
+ * @shadow_buf: shadow buffer where data to be added to the descriptor chain can
+ * be staged before being sent to the device.
+ * @command_sgs: SG table gathering descriptors for a given command and its
+ * response.
+ * @queues: state of all the queues for this session.
+ * @queues_lock: protects all members for the queues for this session.
+ * virtio_media_queue_state`.
+ * @dqbuf_wait: waitqueue for dequeued buffers, if ``VIDIOC_DQBUF`` needs to
+ * block or when polling.
+ * @list: link into the list of sessions for the device.
+ */
+struct virtio_media_session {
+ struct v4l2_fh fh;
+ struct file *file;
+ u32 id;
+ bool nonblocking_dequeue;
+ bool uses_mplane;
+
+ union {
+ struct virtio_media_cmd_close close;
+ struct virtio_media_cmd_ioctl ioctl;
+ struct virtio_media_cmd_mmap mmap;
+ } cmd;
+
+ union {
+ struct virtio_media_resp_ioctl ioctl;
+ struct virtio_media_resp_mmap mmap;
+ } resp;
+
+ void *shadow_buf;
+
+ struct sg_table command_sgs;
+
+ struct virtio_media_queue_state queues[VIRTIO_MEDIA_LAST_QUEUE + 1];
+ struct mutex queues_lock; /* protects queues array and states */
+ wait_queue_head_t dqbuf_wait;
+
+ struct list_head list;
+};
+
+static inline struct virtio_media_session *fh_to_session(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct virtio_media_session, fh);
+}
+
+static inline void
+virtio_media_session_fh_add(struct virtio_media_session *session,
+ struct file *file)
+{
+ v4l2_fh_add(&session->fh, file);
+ session->file = file;
+}
+
+static inline void
+virtio_media_session_fh_del(struct virtio_media_session *session)
+{
+ v4l2_fh_del(&session->fh, session->file);
+}
+
+#endif // __VIRTIO_MEDIA_SESSION_H
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox