* Re: [PATCH] virtio_net: validate device stats reply records before use
From: Michael Bommarito @ 2026-07-11 15:29 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Jason Wang, Xuan Zhuo, Eugenio Pérez, Andrew Lunn,
Jakub Kicinski, Paolo Abeni, virtualization, netdev, linux-kernel,
stable
In-Reply-To: <20260711111503-mutt-send-email-mst@kernel.org>
On Sat, Jul 11, 2026 at 11:20 AM Michael S. Tsirkin <mst@redhat.com> wrote:
> Why does it "matter most", or at all, there?
> Host can always deny guest service. In fact, this is how cloud vendors
> charge their clients, by denying service to whoever did not pay them.
...
> I'm all for making things easier to debug even when the device is buggy.
> But I'm not inclined to add tons of hard to maintain code to
> that end, and I would be worried broken hosts will come to
> rely on drivers working around them.
I am always confused by the CoCo threat model to be honest, since it
seems like some people care a lot about maximalist reliance on the
contract and other people are more practical about how many other
vectors exist anyway. No hard feelings if you want to NACK, but at
least it's documented publicly now for people to consider.
Thanks,
Mike
^ permalink raw reply
* Re: [PATCH] virtio_net: validate device stats reply records before use
From: Michael S. Tsirkin @ 2026-07-11 15:20 UTC (permalink / raw)
To: Michael Bommarito
Cc: Jason Wang, Xuan Zhuo, Eugenio Pérez, Andrew Lunn,
Jakub Kicinski, Paolo Abeni, virtualization, netdev, linux-kernel,
stable
In-Reply-To: <20260711150754.2918392-1-michael.bommarito@gmail.com>
On Sat, Jul 11, 2026 at 11:07:54AM -0400, Michael Bommarito wrote:
> __virtnet_get_hw_stats() walks the device statistics reply buffer with
> "for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size))",
> using each record's device-supplied hdr->size as the stride without
> checking that a full struct virtio_net_stats_reply_hdr remains, that
> hdr->size is nonzero and matches the expected size for hdr->type, or that
> the record fits within res_size. A backend that returns hdr->size == 0
> spins the loop forever; a short or oversized size drives out-of-bounds
> reads in virtnet_fill_stats().
>
> Impact: a malicious or compromised virtio-net backend hangs the CPU
> running the guest's device-statistics query in an infinite loop
> (hdr->size == 0), or drives an out-of-bounds read of the reply buffer.
> This matters most for a confidential guest, where the host is outside the
> trust boundary.
Why does it "matter most", or at all, there?
Host can always deny guest service. In fact, this is how cloud vendors
charge their clients, by denying service to whoever did not pay them.
> Validate each record before use: require a full header in the remaining
> bytes, a nonzero hdr->size that is at least the header size and matches the
> size expected for hdr->type, and that the record fits within res_size; stop
> the walk otherwise. Add virtnet_stats_reply_size() for the per-type size.
I'm all for making things easier to debug even when the device is buggy.
But I'm not inclined to add tons of hard to maintain code to
that end, and I would be worried broken hosts will come to
rely on drivers working around them.
>
> Fixes: 941168f8b40e ("virtio_net: support device stats")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
> drivers/net/virtio_net.c | 42 ++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 40 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 3e2a5876c6c8c..9cbe40d218cc4 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -3532,6 +3532,7 @@ static int virtnet_tx_resize(struct virtnet_info *vi, struct send_queue *sq,
> return err;
> }
>
> +
> /*
> * Send command via the control virtqueue and check status. Commands
> * supported by the hypervisor, as indicated by feature bits, should
> @@ -3546,6 +3547,7 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd
> bool ok;
> int ret;
>
> +
> /* Caller should know better */
> BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ));
>
we don't need this.
> @@ -4927,6 +4929,32 @@ static void virtnet_fill_stats(struct virtnet_info *vi, u32 qid,
> }
> }
>
> +static int virtnet_stats_reply_size(u8 type)
> +{
> + switch (type) {
> + case VIRTIO_NET_STATS_TYPE_REPLY_CVQ:
> + return sizeof(struct virtio_net_stats_cvq);
> + case VIRTIO_NET_STATS_TYPE_REPLY_RX_BASIC:
> + return sizeof(struct virtio_net_stats_rx_basic);
> + case VIRTIO_NET_STATS_TYPE_REPLY_RX_CSUM:
> + return sizeof(struct virtio_net_stats_rx_csum);
> + case VIRTIO_NET_STATS_TYPE_REPLY_RX_GSO:
> + return sizeof(struct virtio_net_stats_rx_gso);
> + case VIRTIO_NET_STATS_TYPE_REPLY_RX_SPEED:
> + return sizeof(struct virtio_net_stats_rx_speed);
> + case VIRTIO_NET_STATS_TYPE_REPLY_TX_BASIC:
> + return sizeof(struct virtio_net_stats_tx_basic);
> + case VIRTIO_NET_STATS_TYPE_REPLY_TX_CSUM:
> + return sizeof(struct virtio_net_stats_tx_csum);
> + case VIRTIO_NET_STATS_TYPE_REPLY_TX_GSO:
> + return sizeof(struct virtio_net_stats_tx_gso);
> + case VIRTIO_NET_STATS_TYPE_REPLY_TX_SPEED:
> + return sizeof(struct virtio_net_stats_tx_speed);
> + default:
> + return sizeof(struct virtio_net_stats_reply_hdr);
> + }
> +}
> +
> static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> struct virtnet_stats_ctx *ctx,
> struct virtio_net_ctrl_queue_stats *req,
> @@ -4936,7 +4964,7 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> struct scatterlist sgs_in, sgs_out;
> void *p;
> u32 qid;
> - int ok;
> + int hdr_size, ok, remaining;
>
> sg_init_one(&sgs_out, req, req_size);
> sg_init_one(&sgs_in, reply, res_size);
> @@ -4948,8 +4976,17 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> if (!ok)
> return ok;
>
> - for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size)) {
> + for (p = reply; p - reply < res_size; p += hdr_size) {
> + remaining = res_size - (p - reply);
> + if (remaining < sizeof(*hdr))
> + return -EINVAL;
> +
> hdr = p;
> + hdr_size = le16_to_cpu(hdr->size);
> + if (hdr_size < virtnet_stats_reply_size(hdr->type) ||
> + hdr_size > remaining)
> + return -EINVAL;
> +
> qid = le16_to_cpu(hdr->vq_index);
> virtnet_fill_stats(vi, qid, ctx, p, false, hdr->type);
> }
That's a lot of fragile code for unclear benefit.
> @@ -7305,3 +7342,4 @@ module_exit(virtio_net_driver_exit);
> MODULE_DEVICE_TABLE(virtio, id_table);
> MODULE_DESCRIPTION("Virtio network driver");
> MODULE_LICENSE("GPL");
> +
> --
> 2.53.0
^ permalink raw reply
* [PATCH] virtio_net: validate device stats reply records before use
From: Michael Bommarito @ 2026-07-11 15:07 UTC (permalink / raw)
To: Michael S . Tsirkin, Jason Wang, Xuan Zhuo, Eugenio Pérez
Cc: Andrew Lunn, Jakub Kicinski, Paolo Abeni, virtualization, netdev,
linux-kernel, stable
__virtnet_get_hw_stats() walks the device statistics reply buffer with
"for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size))",
using each record's device-supplied hdr->size as the stride without
checking that a full struct virtio_net_stats_reply_hdr remains, that
hdr->size is nonzero and matches the expected size for hdr->type, or that
the record fits within res_size. A backend that returns hdr->size == 0
spins the loop forever; a short or oversized size drives out-of-bounds
reads in virtnet_fill_stats().
Impact: a malicious or compromised virtio-net backend hangs the CPU
running the guest's device-statistics query in an infinite loop
(hdr->size == 0), or drives an out-of-bounds read of the reply buffer.
This matters most for a confidential guest, where the host is outside the
trust boundary.
Validate each record before use: require a full header in the remaining
bytes, a nonzero hdr->size that is at least the header size and matches the
size expected for hdr->type, and that the record fits within res_size; stop
the walk otherwise. Add virtnet_stats_reply_size() for the per-type size.
Fixes: 941168f8b40e ("virtio_net: support device stats")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
drivers/net/virtio_net.c | 42 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 3e2a5876c6c8c..9cbe40d218cc4 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -3532,6 +3532,7 @@ static int virtnet_tx_resize(struct virtnet_info *vi, struct send_queue *sq,
return err;
}
+
/*
* Send command via the control virtqueue and check status. Commands
* supported by the hypervisor, as indicated by feature bits, should
@@ -3546,6 +3547,7 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd
bool ok;
int ret;
+
/* Caller should know better */
BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ));
@@ -4927,6 +4929,32 @@ static void virtnet_fill_stats(struct virtnet_info *vi, u32 qid,
}
}
+static int virtnet_stats_reply_size(u8 type)
+{
+ switch (type) {
+ case VIRTIO_NET_STATS_TYPE_REPLY_CVQ:
+ return sizeof(struct virtio_net_stats_cvq);
+ case VIRTIO_NET_STATS_TYPE_REPLY_RX_BASIC:
+ return sizeof(struct virtio_net_stats_rx_basic);
+ case VIRTIO_NET_STATS_TYPE_REPLY_RX_CSUM:
+ return sizeof(struct virtio_net_stats_rx_csum);
+ case VIRTIO_NET_STATS_TYPE_REPLY_RX_GSO:
+ return sizeof(struct virtio_net_stats_rx_gso);
+ case VIRTIO_NET_STATS_TYPE_REPLY_RX_SPEED:
+ return sizeof(struct virtio_net_stats_rx_speed);
+ case VIRTIO_NET_STATS_TYPE_REPLY_TX_BASIC:
+ return sizeof(struct virtio_net_stats_tx_basic);
+ case VIRTIO_NET_STATS_TYPE_REPLY_TX_CSUM:
+ return sizeof(struct virtio_net_stats_tx_csum);
+ case VIRTIO_NET_STATS_TYPE_REPLY_TX_GSO:
+ return sizeof(struct virtio_net_stats_tx_gso);
+ case VIRTIO_NET_STATS_TYPE_REPLY_TX_SPEED:
+ return sizeof(struct virtio_net_stats_tx_speed);
+ default:
+ return sizeof(struct virtio_net_stats_reply_hdr);
+ }
+}
+
static int __virtnet_get_hw_stats(struct virtnet_info *vi,
struct virtnet_stats_ctx *ctx,
struct virtio_net_ctrl_queue_stats *req,
@@ -4936,7 +4964,7 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
struct scatterlist sgs_in, sgs_out;
void *p;
u32 qid;
- int ok;
+ int hdr_size, ok, remaining;
sg_init_one(&sgs_out, req, req_size);
sg_init_one(&sgs_in, reply, res_size);
@@ -4948,8 +4976,17 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
if (!ok)
return ok;
- for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size)) {
+ for (p = reply; p - reply < res_size; p += hdr_size) {
+ remaining = res_size - (p - reply);
+ if (remaining < sizeof(*hdr))
+ return -EINVAL;
+
hdr = p;
+ hdr_size = le16_to_cpu(hdr->size);
+ if (hdr_size < virtnet_stats_reply_size(hdr->type) ||
+ hdr_size > remaining)
+ return -EINVAL;
+
qid = le16_to_cpu(hdr->vq_index);
virtnet_fill_stats(vi, qid, ctx, p, false, hdr->type);
}
@@ -7305,3 +7342,4 @@ module_exit(virtio_net_driver_exit);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio network driver");
MODULE_LICENSE("GPL");
+
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v4 0/8] media: add virtio-media driver
From: Michael S. Tsirkin @ 2026-07-11 9:02 UTC (permalink / raw)
To: Brian Daniels
Cc: Mauro Carvalho Chehab, acourbot, adelva, aesteve, changyeon,
daniel.almeida, eperezma, gnurou, gurchetansingh, hverkuil,
jasowang, linux-kernel, linux-media, nicolas.dufresne,
virtualization, xuanzhuo
In-Reply-To: <CAD4i_GTPO2vEoCtDM1p1APapWv5TYqP_Hhn41kOCodTYJB5Yiw@mail.gmail.com>
On Fri, Jul 10, 2026 at 04:44:14PM -0400, Brian Daniels wrote:
> Hi Michael,
>
> Would you prefer more time to review these changes, or should I go
> ahead and upload a v5 based on the existing comments?
>
> Thanks,
> Brian
Pls send v5. Thanks!
> On Thu, Jun 25, 2026 at 4:21 PM Brian Daniels <briandaniels@google.com> wrote:
> >
> > > > 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
> >
> > I saw some CI build failures come back, but that should be resolved if the
> > patch above is merged. If you'd rather me add it to this patch series let me
> > know.
> >
> > > > Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
> > > > Co-developed-by: Brian Daniels <briandaniels@google.com>
> > > > Signed-off-by: Brian Daniels <briandaniels@google.com>
> > >
> > > Thanks for the patches!
> > > Sent some comments on individual patches.
> >
> > Thanks for the feedback!
> >
> > I've started to prepare v5 based on the comments so far, I'll hold off on
> > sending it until we've resolved the other open threads.
> >
> > > > ---
> > > > 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
> >
> >
^ permalink raw reply
* Re: [PATCH v4 0/8] media: add virtio-media driver
From: Brian Daniels @ 2026-07-10 20:44 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Mauro Carvalho Chehab, acourbot, adelva, aesteve, changyeon,
daniel.almeida, eperezma, gnurou, gurchetansingh, hverkuil,
jasowang, linux-kernel, linux-media, nicolas.dufresne,
virtualization, xuanzhuo
In-Reply-To: <20260625202130.2983760-1-briandaniels@google.com>
Hi Michael,
Would you prefer more time to review these changes, or should I go
ahead and upload a v5 based on the existing comments?
Thanks,
Brian
On Thu, Jun 25, 2026 at 4:21 PM Brian Daniels <briandaniels@google.com> wrote:
>
> > > 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
>
> I saw some CI build failures come back, but that should be resolved if the
> patch above is merged. If you'd rather me add it to this patch series let me
> know.
>
> > > Signed-off-by: Alexandre Courbot <gnurou@gmail.com>
> > > Co-developed-by: Brian Daniels <briandaniels@google.com>
> > > Signed-off-by: Brian Daniels <briandaniels@google.com>
> >
> > Thanks for the patches!
> > Sent some comments on individual patches.
>
> Thanks for the feedback!
>
> I've started to prepare v5 based on the comments so far, I'll hold off on
> sending it until we've resolved the other open threads.
>
> > > ---
> > > 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
>
>
^ permalink raw reply
* Re: [PATCH v2] drm/qxl: remove dependency on DRM simple helpers
From: Thomas Zimmermann @ 2026-07-10 13:11 UTC (permalink / raw)
To: Diogo Silva, Dave Airlie, Gerd Hoffmann, Maarten Lankhorst,
Maxime Ripard, David Airlie, Simona Vetter
Cc: virtualization, spice-devel, dri-devel, linux-kernel
In-Reply-To: <18186cbc-a60c-4a1a-95f5-46e392faa997@suse.de>
Merged into drm-misc-next
Am 08.07.26 um 14:33 schrieb Thomas Zimmermann:
>
>
> Am 07.07.26 um 18:14 schrieb Diogo Silva:
>> Simple KMS helper are deprecated since they only add an intermediate
>> layer between drivers and the atomic modesetting.
>> This patch removes the drm_simple_encoder_init() helper usage in the
>> qxl display driver by open coding it and using the encoder atomic
>> helpers directly. This is a step to eventually get rid of this simple
>> KMS helper, once all drivers that use it have been converted.
>>
>> Signed-off-by: Diogo Silva <diogompaissilva@gmail.com>
>
> Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
>
>> ---
>> Changes in v2:
>> - fix type error
>> - Link to v1:
>> https://lore.kernel.org/r/20260707-qxl-simple-v1-1-524f6316b5d5@gmail.com
>> ---
>> drivers/gpu/drm/qxl/qxl_display.c | 12 ++++++++----
>> 1 file changed, 8 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/qxl/qxl_display.c
>> b/drivers/gpu/drm/qxl/qxl_display.c
>> index a026bd35ef485..7f4178800afd7 100644
>> --- a/drivers/gpu/drm/qxl/qxl_display.c
>> +++ b/drivers/gpu/drm/qxl/qxl_display.c
>> @@ -31,12 +31,12 @@
>> #include <drm/drm_atomic.h>
>> #include <drm/drm_atomic_helper.h>
>> #include <drm/drm_edid.h>
>> +#include <drm/drm_encoder.h>
>> #include <drm/drm_framebuffer.h>
>> #include <drm/drm_gem_framebuffer_helper.h>
>> #include <drm/drm_plane_helper.h>
>> #include <drm/drm_print.h>
>> #include <drm/drm_probe_helper.h>
>> -#include <drm/drm_simple_kms_helper.h>
>> #include <drm/drm_gem_atomic_helper.h>
>> #include <drm/drm_vblank.h>
>> #include <drm/drm_vblank_helper.h>
>> @@ -1095,6 +1095,10 @@ static const struct drm_connector_helper_funcs
>> qxl_connector_helper_funcs = {
>> .best_encoder = qxl_best_encoder,
>> };
>> +static const struct drm_encoder_funcs qxl_encoder_funcs = {
>> + .destroy = drm_encoder_cleanup,
>> +};
>> +
>> static enum drm_connector_status qxl_conn_detect(
>> struct drm_connector *connector,
>> bool force)
>> @@ -1169,10 +1173,10 @@ static int qdev_output_init(struct drm_device
>> *dev, int num_output)
>> drm_connector_init(dev, &qxl_output->base,
>> &qxl_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
>> - ret = drm_simple_encoder_init(dev, &qxl_output->enc,
>> - DRM_MODE_ENCODER_VIRTUAL);
>> + ret = drm_encoder_init(dev, &qxl_output->enc, &qxl_encoder_funcs,
>> + DRM_MODE_ENCODER_VIRTUAL, NULL);
>> if (ret) {
>> - drm_err(dev, "drm_simple_encoder_init() failed, error %d\n",
>> + drm_err(dev, "drm_encoder_init() failed, error %d\n",
>> ret);
>> goto err_drm_connector_cleanup;
>> }
>>
>> ---
>> base-commit: ee2867b79f9bac3a6fc3221139b09598ce79099c
>> change-id: 20260707-qxl-simple-c01aa1a5c4eb
>>
>> Best regards,
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, (HRB 36809, AG Nürnberg)
^ permalink raw reply
* Re: [PATCH v3] vdpa/mlx5: Fix buffer length in create_direct_keys()
From: Dragos Tatulea @ 2026-07-10 11:37 UTC (permalink / raw)
To: Christian Borntraeger
Cc: Michael S . Tsirkin, Jason Wang, virtualization, linux-kernel
In-Reply-To: <21e944c6-2039-4e34-a2f9-711ea4769294@nvidia.com>
On 10.07.26 10:23, Dragos Tatulea wrote:
>
>
> On 10.07.26 09:18, Christian Borntraeger wrote:
>> Am 07.07.26 um 09:17 schrieb Christian Borntraeger:
>>> Am 06.07.26 um 16:15 schrieb Christian Borntraeger:
>>>> We have seen in our CI the following KASAN message:
>>>> BUG: KASAN: slab-out-of-bounds in cmd_exec+0x550/0xca0 [mlx5_core]
>>>> Read of size 272 at addr 0000000176795020 by task qemu-system-s39/82764
>>>> [...]
>>>> [<000011388ab3a7a0>] cmd_exec+0x550/0xca0 [mlx5_core]
>>>> [<000011388ab3b61c>] mlx5_cmd_exec_cb+0x25c/0x4f0 [mlx5_core]
>>>> [<000011388b21e82e>] mlx5_vdpa_exec_async_cmds+0x22e/0x5e0 [mlx5_vdpa]
>>>> [<000011388b21fd44>] create_direct_keys+0x954/0xef0 [mlx5_vdpa]
>>>> [...]
>>>> The buggy address is located 4128 bytes inside of
>>>> allocated 4384-byte region [0000000176794000, 0000000176795120)
>>>>
>>>> So in essence we read 16 bytes beyond 4384-byte allocation.
>>>> create_direct_keys calculates the pointer and length for in and out
>>>> buffers.
>>>> The size calculation for in includes the entire structure
>>>> size (out + in + mtt[]) but the pointer passed to cmd_exec points only
>>>> to the 'in' field, skipping the 'out' field.
>>>>
>>>> This causes mlx5_copy_to_msg() to read beyond the allocated buffer
>>>> by sizeof(out) bytes when copying command data.
>>>>
>>>> Properly calculate the input size to match the pointer and allocation size.
>>>>
>>>> Fixes: 0071b138d44a ("vdpa/mlx5: Create direct MKEYs in parallel")
>>>> Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
>>>
>>>
>>> Dragos,
>>>
>>> With this fix our nighly CI did not result in a kasan message. As I only have
>>> limited test coverage a full regression on your side might still be the right
>>> thing to do.
>>
>> Any feedback?
>>
> Sorry, got carried away with other stuff. Yes, I will check it on our side.
>
> Until then:
> Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
>
I was able to reproduce the issue with KASAN during our live migration tests.
With this patch the issue is gone. Thanks for the fix!
Tested-by: Dragos Tatulea <dtatulea@nvidia.com>
Thanks,
Dragos
^ permalink raw reply
* Re: [PATCH v3] vdpa/mlx5: Fix buffer length in create_direct_keys()
From: Dragos Tatulea @ 2026-07-10 8:23 UTC (permalink / raw)
To: Christian Borntraeger
Cc: Michael S . Tsirkin, Jason Wang, virtualization, linux-kernel
In-Reply-To: <9f799ce2-f84c-4224-b91d-464dc767eb00@linux.ibm.com>
On 10.07.26 09:18, Christian Borntraeger wrote:
> Am 07.07.26 um 09:17 schrieb Christian Borntraeger:
>> Am 06.07.26 um 16:15 schrieb Christian Borntraeger:
>>> We have seen in our CI the following KASAN message:
>>> BUG: KASAN: slab-out-of-bounds in cmd_exec+0x550/0xca0 [mlx5_core]
>>> Read of size 272 at addr 0000000176795020 by task qemu-system-s39/82764
>>> [...]
>>> [<000011388ab3a7a0>] cmd_exec+0x550/0xca0 [mlx5_core]
>>> [<000011388ab3b61c>] mlx5_cmd_exec_cb+0x25c/0x4f0 [mlx5_core]
>>> [<000011388b21e82e>] mlx5_vdpa_exec_async_cmds+0x22e/0x5e0 [mlx5_vdpa]
>>> [<000011388b21fd44>] create_direct_keys+0x954/0xef0 [mlx5_vdpa]
>>> [...]
>>> The buggy address is located 4128 bytes inside of
>>> allocated 4384-byte region [0000000176794000, 0000000176795120)
>>>
>>> So in essence we read 16 bytes beyond 4384-byte allocation.
>>> create_direct_keys calculates the pointer and length for in and out
>>> buffers.
>>> The size calculation for in includes the entire structure
>>> size (out + in + mtt[]) but the pointer passed to cmd_exec points only
>>> to the 'in' field, skipping the 'out' field.
>>>
>>> This causes mlx5_copy_to_msg() to read beyond the allocated buffer
>>> by sizeof(out) bytes when copying command data.
>>>
>>> Properly calculate the input size to match the pointer and allocation size.
>>>
>>> Fixes: 0071b138d44a ("vdpa/mlx5: Create direct MKEYs in parallel")
>>> Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
>>
>>
>> Dragos,
>>
>> With this fix our nighly CI did not result in a kasan message. As I only have
>> limited test coverage a full regression on your side might still be the right
>> thing to do.
>
> Any feedback?
>
Sorry, got carried away with other stuff. Yes, I will check it on our side.
Until then:
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Thanks,
Dragos
^ permalink raw reply
* Re: [PATCH net-next 1/3] net: busy-poll: introduce sk_tx_busy_loop()
From: Menglong Dong @ 2026-07-10 7:39 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: menglong8.dong, Jakub Kicinski, jasowang, mst, xuanzhuo, eperezma,
andrew+netdev, davem, edumazet, pabeni, magnus.karlsson, sdf,
horms, ast, daniel, hawk, john.fastabend, bjorn, kerneljasonxing,
netdev, virtualization, linux-kernel, bpf
In-Reply-To: <ajJrckiXEUztBQDz@boxer>
On 2026/6/17 17:40 Maciej Fijalkowski <maciej.fijalkowski@intel.com> write:
> On Sun, Jun 14, 2026 at 06:12:46PM +0800, Menglong Dong wrote:
> > On 2026/6/14 02:21, Jakub Kicinski wrote:
> > > On Thu, 11 Jun 2026 15:12:40 +0800 menglong8.dong@gmail.com wrote:
> > > > For now, we use sk_busy_loop() for both rx and tx path. The sk_busy_loop()
> > > > will call napi_busy_loop() for the specified napi_id. However, some
> > > > nic drivers have tx napi, such as virtio-net. In this case, sk_busy_loop()
> > > > doesn't work, as it can only schedule the NAPI for the rx queue.
> > > >
> > > > Therefore, introduce sk_tx_busy_loop() for the nic drivers that support tx
> > > > napi, which will schedule the tx napi if available.
> > >
> > > First, I thought the only difference with Tx NAPI is that it can't be
> > > busy polled. So if you want to poll an instance don't register it as
> > > a Tx one instead of adding all this "tx polling" stuff in the core?
> >
> > I see. Register the tx NAPI with netif_napi_add_config() allow us
> > busy poll it. But we still have two NAPI instance: rx NAPI and tx NAPI.
> > sk_busy_loop() can only busy poll on one of them.
> >
> > Before AF_XDP, we don't have the need to send packet via tx NAPI, which
> > means that we don't need to busy poll it.
> >
> > I analyst some nic drivers on the implement of AF_XDP. Some of them
> > will check xsk tx ring of current queue and send the data in it in the
> > rx NAPI, such as mlx5. Some of them will allocate a extra "rxtx" NAPI
> > for the AF_XDP zero-copy queue, which will poll both the data receiving
> > and sending.
> >
> > In the case about, they will do the data sending and receiving for the
> > AF_XDP in a single NAPI instance.
> >
> > However, some driver receiving the data in rx NAPI and send data in
> > tx NAPI for AF_XDP. In this case, we can't use sk_busy_loop() for both
> > rx path and tx path, as we need to wake different NAPI instance.
> >
> > >
> > > Second, can this problem happen for any other NIC or is it purely
> > > an artifact of virtio's delayed Tx completion handling?
> >
> > According to my analysis, only virtio-net and ICSSG driver have
> > split NAPI for AF_XDP. I don't have a ICSSG nic, but the codex tell
> > me that it does have the same problem.
> >
> > I'm not sure if it is a good idea to introduce the sk_tx_busy_loop().
> > Maybe we can modify the driver instead by using the same NAPI
> > for both data sending and receiving, just like others do. The
> > advantage of introduce sk_tx_busy_loop() is that we can split the
> > data sending and receiving, which maybe more efficient.
>
> Would be good if you back your changes by any performance numbers. I
> believe that drivers do tx processing via rx napi as before AF_XDP it was
> only about cleaning up writebacks, AF_XDP added more weight via actual tx
> descriptors submission.
>
> Maybe you can vibe-code virtio-net to work only with rx napi and see what
> are the results.
According to my testing, there is no obvious performance improvement
with tx napi. I'm going to implement the xsk tx queue consume in rx napi
instead for virtio-net.
Thanks!
Menglong Dong
>
> Side note/question - Do you have a tx-only use case for AF_XDP ? I am
> planning (for a long time actually) to implement asymmetric AF_XDP
> sockets. Currently for ZC scenarios xsk socket occupies both rx and tx
> queues even when you do rx or tx only.
>
> >
> > >
> > > Third, this series does not apply.
> >
> > Ah, I'll rebase this series if a V2 is acceptable.
> >
> > Thanks!
> > Menglong Dong
> >
> > >
> > >
> >
> >
> >
> >
>
^ permalink raw reply
* Re: [PATCH v3] vdpa/mlx5: Fix buffer length in create_direct_keys()
From: Christian Borntraeger @ 2026-07-10 7:18 UTC (permalink / raw)
To: Dragos Tatulea
Cc: Michael S . Tsirkin, Jason Wang, virtualization, linux-kernel
In-Reply-To: <3508dbb8-d110-42dc-aa25-4b3e73057422@linux.ibm.com>
Am 07.07.26 um 09:17 schrieb Christian Borntraeger:
> Am 06.07.26 um 16:15 schrieb Christian Borntraeger:
>> We have seen in our CI the following KASAN message:
>> BUG: KASAN: slab-out-of-bounds in cmd_exec+0x550/0xca0 [mlx5_core]
>> Read of size 272 at addr 0000000176795020 by task qemu-system-s39/82764
>> [...]
>> [<000011388ab3a7a0>] cmd_exec+0x550/0xca0 [mlx5_core]
>> [<000011388ab3b61c>] mlx5_cmd_exec_cb+0x25c/0x4f0 [mlx5_core]
>> [<000011388b21e82e>] mlx5_vdpa_exec_async_cmds+0x22e/0x5e0 [mlx5_vdpa]
>> [<000011388b21fd44>] create_direct_keys+0x954/0xef0 [mlx5_vdpa]
>> [...]
>> The buggy address is located 4128 bytes inside of
>> allocated 4384-byte region [0000000176794000, 0000000176795120)
>>
>> So in essence we read 16 bytes beyond 4384-byte allocation.
>> create_direct_keys calculates the pointer and length for in and out
>> buffers.
>> The size calculation for in includes the entire structure
>> size (out + in + mtt[]) but the pointer passed to cmd_exec points only
>> to the 'in' field, skipping the 'out' field.
>>
>> This causes mlx5_copy_to_msg() to read beyond the allocated buffer
>> by sizeof(out) bytes when copying command data.
>>
>> Properly calculate the input size to match the pointer and allocation size.
>>
>> Fixes: 0071b138d44a ("vdpa/mlx5: Create direct MKEYs in parallel")
>> Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
>
>
> Dragos,
>
> With this fix our nighly CI did not result in a kasan message. As I only have
> limited test coverage a full regression on your side might still be the right
> thing to do.
Any feedback?
^ permalink raw reply
* Re: [PATCH v2] virtio-pmem: allocate flush bio from a driver-private bio_set
From: Christoph Hellwig @ 2026-07-10 5:45 UTC (permalink / raw)
To: Joseph Qi
Cc: Pankaj Gupta, Christoph Hellwig, Baokun Li, virtualization,
linux-kernel
In-Reply-To: <20260709124455.1547912-1-joseph.qi@linux.alibaba.com>
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply
* Re: [RFC] virtio_balloon: fix Use-After-Free in page reporting during PM freeze
From: Link Lin @ 2026-07-10 0:45 UTC (permalink / raw)
To: Andrew Morton
Cc: Vlastimil Babka, Michael S . Tsirkin, David Hildenbrand,
virtualization, linux-mm, linux-kernel, prasin, rientjes, duenwen,
jasowang, xuanzhuo, Ammar Faizi, jiaqiyan, ahwilkins, Greg Thelen,
Alexander Duyck, stable
In-Reply-To: <20260709161253.6b5e9ba349f70a3ebfb8180f@linux-foundation.org>
On Thu, 9 Jul 2026 at 16:12, Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Thu, 9 Jul 2026 22:43:30 +0000 Link Lin <linkl@google.com> wrote:
>
> > Fix this by:
> > 1. Unregistering page reporting in virtballoon_freeze() prior to calling
> > remove_common(). This clears the RCU pr_dev_info pointer and flushes/
> > cancels prdev->work on system_wq via cancel_delayed_work_sync().
> > 2. Re-registering page reporting in virtballoon_restore() after the
> > virtqueues are re-initialized and virtio_device_ready() has been called.
> > 3. Unwinding virtqueue initialization via remove_common() in
> > virtballoon_restore() if page_reporting_register() fails.
>
> AI review thinks the patch didn't do the above:
> https://sashiko.dev/#/patchset/20260709224330.946683-1-linkl@google.com
The AI reviewer might not have parsed the entirety of the fix I proposed.
The patch submitted definitely includes the changes to virtballoon_restore()
for steps 2 and 3 (re-registering page reporting and unwinding init_vqs
on failure). It seems the AI failed to parse the diff correctly. See:
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
+ ret = page_reporting_register(&vb->pr_dev_info);
+ if (ret)
+ goto out_remove_vqs;
+ }
+
if (towards_target(vb))
virtballoon_changed(vdev);
update_balloon_size(vb);
return 0;
+
+out_remove_vqs:
+ remove_common(vb);
+ return ret;
}
> It also might have found a couple of pre-existing bugs in there.
Indeed. Regarding the first pre-existing bug found by the AI (leaving the
OOM notifier registered during suspend, leading to a UAF if memory pressure
spikes during S4 hibernation):
I actually addressed this in the commit message:
"(Note: The OOM Notifier and Shrinker/Free Page Hinting features suffer
from an identical lifecycle flaw and are also vulnerable to UAFs during
S4 hibernation when memory pressure spikes. This patch focuses on Free
Page Reporting, which runs periodically, to ensure clean backports to
stable kernels)."
Regarding the second pre-existing bug the AI flagged (leaving uncancelled
works on system_freezable_wq if virtballoon_restore fails on the cold path):
the AI is correct that this asynchronous work cancellation failure exists.
Since these are separate, pre-existing lifecycle bugs, would you prefer I
roll fixes for the OOM notifier, shrinker/free page hinting, and work
cancellations into a v2 of this patch, or submit them as a separate patch
series to keep the stable backports clean?
Thanks,
Link
^ permalink raw reply
* Re: [RFC] virtio_balloon: fix Use-After-Free in page reporting during PM freeze
From: Andrew Morton @ 2026-07-09 23:12 UTC (permalink / raw)
To: Link Lin
Cc: Vlastimil Babka, Michael S . Tsirkin, David Hildenbrand,
virtualization, linux-mm, linux-kernel, prasin, rientjes, duenwen,
jasowang, xuanzhuo, Ammar Faizi, jiaqiyan, ahwilkins, Greg Thelen,
Alexander Duyck, stable
In-Reply-To: <20260709224330.946683-1-linkl@google.com>
On Thu, 9 Jul 2026 22:43:30 +0000 Link Lin <linkl@google.com> wrote:
> During system power management freeze (e.g. ACPI S3 suspend or S4
> hibernation), virtballoon_freeze() calls remove_common() to reset the
> virtio device and delete all virtqueues via vdev->config->del_vqs().
> However, unlike virtballoon_remove(), virtballoon_freeze() fails to call
> page_reporting_unregister(&vb->pr_dev_info).
>
> ...
>
> If memory is freed into the buddy allocator or a delayed work timer
> expires while the device is being frozen, page_reporting_process() fires
> on system_wq and calls virtballoon_free_page_report(). This function
> passes vb->reporting_vq into virtqueue_add_inbuf() / virtqueue_add_split().
> Because the virtqueues were already destroyed by del_vqs(), this results
> in a Use-After-Free / General Protection Fault:
>
> [ 250.709271] general protection fault, probably for non-canonical address 0x7f728084daf08d5e: 0000 [#1] SMP PTI
> [ 250.732967] CPU: 2 PID: 38 Comm: kworker/2:1 Not tainted 5.10.0-44-cloud-amd64 #1 Debian 5.10.257-1
> [ 250.751575] Workqueue: events page_reporting_process
> [ 250.756665] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring]
> ...
> [ 250.867678] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon]
> [ 250.883446] page_reporting_process+0x225/0x4f0
whoops
> Fix this by:
> 1. Unregistering page reporting in virtballoon_freeze() prior to calling
> remove_common(). This clears the RCU pr_dev_info pointer and flushes/
> cancels prdev->work on system_wq via cancel_delayed_work_sync().
> 2. Re-registering page reporting in virtballoon_restore() after the
> virtqueues are re-initialized and virtio_device_ready() has been called.
> 3. Unwinding virtqueue initialization via remove_common() in
> virtballoon_restore() if page_reporting_register() fails.
AI review thinks the patch didn't do the above:
https://sashiko.dev/#/patchset/20260709224330.946683-1-linkl@google.com
It also might have found a couple of pre-existing bugs in there.
^ permalink raw reply
* [RFC] virtio_balloon: fix Use-After-Free in page reporting during PM freeze
From: Link Lin @ 2026-07-09 22:43 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Michael S . Tsirkin,
David Hildenbrand
Cc: virtualization, linux-mm, linux-kernel, prasin, rientjes, duenwen,
jasowang, xuanzhuo, Ammar Faizi, jiaqiyan, ahwilkins, Greg Thelen,
Alexander Duyck, Link Lin, stable
During system power management freeze (e.g. ACPI S3 suspend or S4
hibernation), virtballoon_freeze() calls remove_common() to reset the
virtio device and delete all virtqueues via vdev->config->del_vqs().
However, unlike virtballoon_remove(), virtballoon_freeze() fails to call
page_reporting_unregister(&vb->pr_dev_info).
The comment in virtballoon_freeze() states:
/*
* The workqueue is already frozen by the PM core before this
* function is called.
*/
While this comment was accurate in 2011 for balloon-internal workqueues
(such as balloon_wq, which was created with WQ_FREEZABLE and is paused
by the PM freezer), it is invalid for Free Page Reporting.
Free Page Reporting (mm/page_reporting.c) schedules its delayed work
(prdev->work) on the global system_wq. Because system_wq lacks the
WQ_FREEZABLE flag, the PM freezer (freeze_workqueues_busy()) explicitly
skips it. Consequently, page_reporting_process() on system_wq remains
active and unfrozen throughout device suspend.
If memory is freed into the buddy allocator or a delayed work timer
expires while the device is being frozen, page_reporting_process() fires
on system_wq and calls virtballoon_free_page_report(). This function
passes vb->reporting_vq into virtqueue_add_inbuf() / virtqueue_add_split().
Because the virtqueues were already destroyed by del_vqs(), this results
in a Use-After-Free / General Protection Fault:
[ 250.709271] general protection fault, probably for non-canonical address 0x7f728084daf08d5e: 0000 [#1] SMP PTI
[ 250.732967] CPU: 2 PID: 38 Comm: kworker/2:1 Not tainted 5.10.0-44-cloud-amd64 #1 Debian 5.10.257-1
[ 250.751575] Workqueue: events page_reporting_process
[ 250.756665] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring]
...
[ 250.867678] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon]
[ 250.883446] page_reporting_process+0x225/0x4f0
(Note: The OOM Notifier and Shrinker/Free Page Hinting features suffer
from an identical lifecycle flaw and are also vulnerable to UAFs during
S4 hibernation when memory pressure spikes. This patch focuses on Free
Page Reporting, which runs periodically, to ensure clean backports to
stable kernels).
Fix this by:
1. Unregistering page reporting in virtballoon_freeze() prior to calling
remove_common(). This clears the RCU pr_dev_info pointer and flushes/
cancels prdev->work on system_wq via cancel_delayed_work_sync().
2. Re-registering page reporting in virtballoon_restore() after the
virtqueues are re-initialized and virtio_device_ready() has been called.
3. Unwinding virtqueue initialization via remove_common() in
virtballoon_restore() if page_reporting_register() fails.
Fixes: 924a663f75e2 ("virtio-balloon: Reporting free page reservations")
Cc: stable@vger.kernel.org
Cc: jasowang@redhat.com
Cc: xuanzhuo@linux.alibaba.com
Cc: Ammar Faizi <ammarfaizi2@openresty.com>
Cc: jiaqiyan@google.com
Cc: ahwilkins@google.com
Cc: Greg Thelen <gthelen@google.com>
Cc: Alexander Duyck <alexander.duyck@gmail.com>
Signed-off-by: Link Lin <linkl@google.com>
---
drivers/virtio/virtio_balloon.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index a1b2c3d4e5f6..45a90fb3abf8 100640
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1055,6 +1055,9 @@ static int virtballoon_freeze(struct virtio_device *vdev)
* The workqueue is already frozen by the PM core before this
* function is called.
*/
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+ page_reporting_unregister(&vb->pr_dev_info);
+
remove_common(vb);
return 0;
}
static int virtballoon_restore(struct virtio_device *vdev)
{
struct virtio_balloon *vb = vdev->priv;
int ret;
ret = init_vqs(vdev->priv);
if (ret)
return ret;
virtio_device_ready(vdev);
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
+ ret = page_reporting_register(&vb->pr_dev_info);
+ if (ret)
+ goto out_remove_vqs;
+ }
+
if (towards_target(vb))
virtballoon_changed(vdev);
update_balloon_size(vb);
return 0;
+
+out_remove_vqs:
+ remove_common(vb);
+ return ret;
}
--
2.45.0
^ permalink raw reply related
* Re: [PATCH net v2 2/2] vsock/test: add test for small packets under pressure
From: Bobby Eshleman @ 2026-07-09 19:48 UTC (permalink / raw)
To: Stefano Garzarella
Cc: netdev, Jason Wang, Xuan Zhuo, Eric Dumazet, Eugenio Pérez,
Simon Horman, Stefan Hajnoczi, David S. Miller, linux-kernel,
Michael S. Tsirkin, kvm, Paolo Abeni, virtualization,
Jakub Kicinski, Jason Wang
In-Reply-To: <20260708102904.50732-3-sgarzare@redhat.com>
On Wed, Jul 08, 2026 at 12:29:04PM +0200, Stefano Garzarella wrote:
> From: Stefano Garzarella <sgarzare@redhat.com>
>
> Add a test that sends 2 MB of data using randomly sized small packets
> (129-512 bytes) over a SOCK_STREAM connection. Packets above
> GOOD_COPY_LEN (128) bypass the in-place coalescing in recv_enqueue(),
> forcing each one into its own skb.
>
> Without receive queue collapsing, the per-skb overhead eventually
> exceeds buf_alloc and the connection is reset. The test verifies
> that all data arrives and that content integrity is preserved.
>
> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
> ---
> tools/testing/vsock/vsock_test.c | 87 ++++++++++++++++++++++++++++++++
> 1 file changed, 87 insertions(+)
>
> diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
> index 76be0e4a7f0e..b4ff9f946565 100644
> --- a/tools/testing/vsock/vsock_test.c
> +++ b/tools/testing/vsock/vsock_test.c
> @@ -2347,6 +2347,88 @@ static void test_stream_tx_credit_bounds_server(const struct test_opts *opts)
> close(fd);
> }
>
> +/* Test that many small packets don't cause a connection reset under pressure
> + * and that data integrity is preserved. Packet sizes vary randomly between
> + * 129 and 512 bytes, above GOOD_COPY_LEN (128) to bypass in-place coalescing
> + * in recv_enqueue, forcing each one into its own skb. Without receive queue
> + * collapsing, the per-skb overhead eventually exceeds buf_alloc and the
> + * connection is reset.
> + */
> +#define COLLAPSE_PKT_MIN 129
> +#define COLLAPSE_PKT_MAX 512
> +#define COLLAPSE_TOTAL (2 * 1024 * 1024)
> +
> +static void test_stream_collapse_client(const struct test_opts *opts)
> +{
> + unsigned char *data;
> + unsigned long hash;
> + size_t offset = 0;
> + int i, fd;
> +
> + data = malloc(COLLAPSE_TOTAL);
> + if (!data) {
> + perror("malloc");
> + exit(EXIT_FAILURE);
> + }
> +
> + for (i = 0; i < COLLAPSE_TOTAL; i++)
> + data[i] = rand() & 0xff;
> +
> + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
> + if (fd < 0) {
> + perror("connect");
> + exit(EXIT_FAILURE);
> + }
> +
> + while (offset < COLLAPSE_TOTAL) {
> + size_t pkt_size = COLLAPSE_PKT_MIN +
> + rand() % (COLLAPSE_PKT_MAX - COLLAPSE_PKT_MIN + 1);
> +
> + pkt_size = min(pkt_size, COLLAPSE_TOTAL - offset);
> +
> + send_buf(fd, data + offset, pkt_size, 0, pkt_size);
> + offset += pkt_size;
> + }
> +
> + hash = hash_djb2(data, COLLAPSE_TOTAL);
> + control_writeulong(hash);
> +
> + free(data);
> + close(fd);
> +}
> +
> +static void test_stream_collapse_server(const struct test_opts *opts)
> +{
> + unsigned long hash, remote_hash;
> + unsigned char *data;
> + int fd;
> +
> + data = malloc(COLLAPSE_TOTAL);
> + if (!data) {
> + perror("malloc");
> + exit(EXIT_FAILURE);
> + }
> +
> + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
> + if (fd < 0) {
> + perror("accept");
> + exit(EXIT_FAILURE);
> + }
> +
> + recv_buf(fd, data, COLLAPSE_TOTAL, 0, COLLAPSE_TOTAL);
> +
> + hash = hash_djb2(data, COLLAPSE_TOTAL);
> + remote_hash = control_readulong();
> + if (hash != remote_hash) {
> + fprintf(stderr, "hash mismatch: local %lu remote %lu\n",
> + hash, remote_hash);
> + exit(EXIT_FAILURE);
> + }
> +
> + free(data);
> + close(fd);
> +}
> +
> static struct test_case test_cases[] = {
> {
> .name = "SOCK_STREAM connection reset",
> @@ -2546,6 +2628,11 @@ static struct test_case test_cases[] = {
> .run_client = test_stream_msg_peek_client,
> .run_server = test_stream_peek_after_recv_server,
> },
> + {
> + .name = "SOCK_STREAM small packets backpressure",
> + .run_client = test_stream_collapse_client,
> + .run_server = test_stream_collapse_server,
> + },
> {},
> };
>
> --
> 2.55.0
>
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply
* Re: [PATCH net v2 1/2] vsock/virtio: collapse receive queue under memory pressure
From: Bobby Eshleman @ 2026-07-09 19:07 UTC (permalink / raw)
To: Stefano Garzarella
Cc: netdev, Jason Wang, Xuan Zhuo, Eric Dumazet, Eugenio Pérez,
Simon Horman, Stefan Hajnoczi, David S. Miller, linux-kernel,
Michael S. Tsirkin, kvm, Paolo Abeni, virtualization,
Jakub Kicinski, Jason Wang, stable, Brien Oberstein
In-Reply-To: <20260708102904.50732-2-sgarzare@redhat.com>
On Wed, Jul 08, 2026 at 12:29:03PM +0200, Stefano Garzarella wrote:
> From: Stefano Garzarella <sgarzare@redhat.com>
>
> When many small packets accumulate in the receive queue, the skb overhead
> can exceed buf_alloc even while the payload is within bounds. This causes
> virtio_transport_inc_rx_pkt() to reject packets, leading to connection
> resets during large transfers under backpressure.
>
> The issue was reported by Brien, who has a reproducer, but it is also
> easily reproducible with iperf-vsock [1] using a small packet size:
>
> iperf3 --vsock -c $CID -l 129
>
> which fails immediately without this patch but with commit 059b7dbd20a6
> ("vsock/virtio: fix potential unbounded skb queue").
>
> Inspired by TCP's tcp_collapse() which solves a similar problem, add
> virtio_transport_collapse_rx_queue() that walks the receive queue and
> re-copies data into compact linear skbs to reduce the overhead.
>
> The collapse is triggered proactively from when the number of skb queued
> is close to exceeding the overhead budget.
>
> A pre-scan counts the eligible bytes to size each allocation precisely,
> avoiding waste for isolated small packets. Partially consumed skbs are
> kept as-is to preserve buf_used/fwd_cnt accounting, EOM-marked skbs to
> maintain SEQPACKET message boundaries, and skbs already larger than the
> collapse target because they already have a good data-to-overhead ratio.
>
> Walking a large queue may take a significant amount of time and cache
> misses, causing traffic burstiness. To limit this, the collapse stops
> once enough room is freed for this packet and the next one, but may
> opportunistically free more to fill each collapsed skb to capacity.
>
> [1] https://github.com/stefano-garzarella/iperf-vsock
>
> Fixes: 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue")
> Cc: stable@vger.kernel.org
> Reported-by: Brien Oberstein <brienpub@gmail.com>
> Closes: https://lore.kernel.org/netdev/618701dd023e$063de350$12b9a9f0$@gmail.com/
> Tested-by: Brien Oberstein <brienpub@gmail.com>
> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
> ---
> v2:
> - defined MAX_COLLAPSE_LEN macro instead of using a variable [Paolo]
> - added a threshold to avoid walking all the queue while collapsing
> [Paolo]
> - collapsed the queue before calling virtio_transport_inc_rx_pkt().
> While working on the threshold, I figured out that the check I was
> introducing can also be used to proactively trigger the collapse, so I
> moved the call to virtio_transport_collapse_rx_queue() before acquiring
> the rx_lock to have also a better diff to simplify backports
> - improved code readability (removed `out` label, `keep` initialization,
> etc.) [Paolo + other small stuff]
> - Brien kindly retested this version as well (thank you so much)
> ---
> net/vmw_vsock/virtio_transport_common.c | 165 +++++++++++++++++++++++-
> 1 file changed, 164 insertions(+), 1 deletion(-)
>
> diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
> index 09475007165b..8becad81279c 100644
> --- a/net/vmw_vsock/virtio_transport_common.c
> +++ b/net/vmw_vsock/virtio_transport_common.c
> @@ -26,6 +26,13 @@
> /* Threshold for detecting small packets to copy */
> #define GOOD_COPY_LEN 128
>
> +/* Max payload that can be collapsed into a single linear skb, using the same
> + * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure
> + * on the page allocator.
> + */
> +#define MAX_COLLAPSE_LEN \
> + SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER)
> +
> static void virtio_transport_cancel_close_work(struct vsock_sock *vsk,
> bool cancel_timeout);
> static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs);
> @@ -420,6 +427,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
> return ret;
> }
>
> +static bool virtio_transport_can_collapse(struct sk_buff *skb)
> +{
> + /* skbs that are partially consumed, mark a SEQPACKET message boundary,
> + * or are already large enough should not be collapsed: they either
> + * need special accounting, carry protocol state, or already have a
> + * good data-to-overhead ratio.
> + */
> + if (VIRTIO_VSOCK_SKB_CB(skb)->offset)
> + return false;
> + if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM)
> + return false;
> + if (skb->len >= MAX_COLLAPSE_LEN)
> + return false;
> + return true;
> +}
> +
> +/* Iterate through the packets in the queue starting from the current skb to
> + * count the number of bytes we can collapse.
> + */
> +static unsigned int
> +virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue)
> +{
> + unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
> +
> + while ((skb = skb_peek_next(skb, queue)) &&
> + virtio_transport_can_collapse(skb)) {
> + unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
> +
> + if (len > MAX_COLLAPSE_LEN - target)
> + return target;
> +
> + target += len;
> + }
> +
> + return target;
> +}
> +
> +/* Called under lock_sock to compact the receive queue by merging small skbs.
> + * @min_to_free: minimum number of skbs to eliminate from the queue. May free
> + * more to fill each collapsed skb to capacity.
> + */
> +static void
> +virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs,
> + u32 min_to_free)
> +{
> + struct sk_buff *skb, *next_skb, *new_skb = NULL;
> + struct sk_buff_head new_queue;
> + u32 saved = 0;
> +
> + __skb_queue_head_init(&new_queue);
> +
> + skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) {
> + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb);
> + u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset;
> + u32 src_len = skb->len - src_off;
> + bool keep;
> +
> + keep = !virtio_transport_can_collapse(skb);
> + if (keep) {
> + /* Finalize pending collapsed skb to preserve packet
> + * ordering.
> + */
> + if (new_skb) {
> + __skb_queue_tail(&new_queue, new_skb);
> + new_skb = NULL;
> + saved--;
> + }
> + goto next;
> + }
> +
> + /* Finalize if this packet won't fit in the remaining tailroom,
> + * so we can allocate a right-sized new_skb.
> + */
> + if (new_skb && src_len > skb_tailroom(new_skb)) {
> + __skb_queue_tail(&new_queue, new_skb);
> + new_skb = NULL;
> + saved--;
> + }
> +
> + if (!new_skb) {
> + unsigned int alloc_size;
> +
> + /* Check after finalizing to opportunistically fill
> + * each collapsed skb to capacity, merging more skbs
> + * than strictly required.
> + */
> + if (saved >= min_to_free)
> + break;
> +
> + alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue);
> +
> + /* Only this skb's data is eligible, nothing to merge
> + * with. Keep as-is.
> + */
> + if (alloc_size <= src_len) {
> + keep = true;
> + goto next;
> + }
> +
> + new_skb = virtio_vsock_alloc_linear_skb(alloc_size +
> + VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL);
> + if (!new_skb)
> + break;
> +
> + memcpy(virtio_vsock_hdr(new_skb), hdr,
> + sizeof(struct virtio_vsock_hdr));
> + virtio_vsock_hdr(new_skb)->len = 0;
> + }
> +
> + /* Cannot fail since src_off/src_len are within bounds, but if
> + * it does, discard new_skb to avoid queuing corrupted data.
> + */
> + if (WARN_ON_ONCE(skb_copy_bits(skb, src_off,
> + skb_put(new_skb, src_len),
> + src_len))) {
> + kfree_skb(new_skb);
> + new_skb = NULL;
> + break;
> + }
> +
> + le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len);
> + virtio_vsock_hdr(new_skb)->flags |= hdr->flags;
> +
> +next:
> + __skb_unlink(skb, &vvs->rx_queue);
> + if (keep) {
> + __skb_queue_tail(&new_queue, skb);
> + } else {
> + consume_skb(skb);
> + saved++;
> + }
> + }
> +
> + if (new_skb)
> + __skb_queue_tail(&new_queue, new_skb);
> +
> + skb_queue_splice(&new_queue, &vvs->rx_queue);
> +}
> +
> static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
> u32 len)
> {
> @@ -1354,12 +1500,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
> {
> struct virtio_vsock_sock *vvs = vsk->trans;
> bool can_enqueue, free_pkt = false;
> + u32 len, queue_max, queue_len;
> struct virtio_vsock_hdr *hdr;
> - u32 len;
>
> hdr = virtio_vsock_hdr(skb);
> len = le32_to_cpu(hdr->len);
>
> + /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb
> + * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc.
> + * Proactively collapse the queue before that happens.
> + * No rx_lock needed: lock_sock is held by caller, preventing
> + * concurrent enqueue or dequeue.
> + */
> + queue_max = vvs->buf_alloc / SKB_TRUESIZE(0);
> + queue_len = skb_queue_len(&vvs->rx_queue);
> + if (queue_len >= queue_max) {
> + /* Walking a large queue may take a significant amount of time
> + * and cache misses, causing traffic burstiness. Limit the
> + * collapse to freeing room for this packet and the next one.
> + * It may free more to fill each collapsed skb to capacity.
> + */
> + virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max);
> + }
> +
> spin_lock_bh(&vvs->rx_lock);
>
> can_enqueue = virtio_transport_inc_rx_pkt(vvs, len);
The changes still look good to me.
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
Thanks,
Bobby
^ permalink raw reply
* Re: [PATCH] drm/virtio: Don't detach GEM from a non-created context
From: Dmitry Osipenko @ 2026-07-09 16:23 UTC (permalink / raw)
To: Yiwei Zhang
Cc: Jason Macnak, David Airlie, Gerd Hoffmann, Gurchetan Singh,
dri-devel, virtualization, linux-kernel, stable
In-Reply-To: <CAKT=dD=YecudFK1L9wJ22BO41qXt7V4Qm=nNQQiuwheKejoHdg@mail.gmail.com>
On 7/7/26 22:01, Yiwei Zhang wrote:
> On Mon, Jun 29, 2026 at 2:39 PM Dmitry Osipenko
> <dmitry.osipenko@collabora.com> wrote:
>>
>> Hi,
>>
>> On 6/25/26 20:08, Jason Macnak wrote:
>>> Applies the same treatment as commit 7cf6dd467e87 ("drm/virtio:
>>> Don't attach GEM to a non-created context in gem_object_open()")
>>> to virtio_gpu_gem_object_close() to avoid trying to detach
>>> a resource that was never attached due to a context
>>> never being created when context_init is supported.
>>>
>>> Fixes: 086b9f27f0ab ("drm/virtio: Don't create a context with default param if context_init is supported")
>>> Cc: <stable@vger.kernel.org> # v6.14+
>>> Signed-off-by: Jason Macnak <natsu@google.com>
>>> ---
>>> drivers/gpu/drm/virtio/virtgpu_gem.c | 14 ++++++++------
>>> 1 file changed, 8 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
>>> index 435d37d36034..66c3f6f74e9c 100644
>>> --- a/drivers/gpu/drm/virtio/virtgpu_gem.c
>>> +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
>>> @@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
>>> if (!vgdev->has_virgl_3d)
>>> return;
>>>
>>> - objs = virtio_gpu_array_alloc(1);
>>> - if (!objs)
>>> - return;
>>> - virtio_gpu_array_add_obj(objs, obj);
>>> + if (vfpriv->context_created) {
>>> + objs = virtio_gpu_array_alloc(1);
>>> + if (!objs)
>>> + return;
>>> + virtio_gpu_array_add_obj(objs, obj);
>>>
>>> - virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
>>> - objs);
>>> + virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
>>> + objs);
>>> + }
>>> virtio_gpu_notify(vgdev);
>>> }
>>
>> The following scenario still will be troubling:
>>
>> 1. vgdev->has_context_init = true
>> 2. virtio_gpu_gem_object_open() invoked, GEM created and not attached to ctx
>> 3. virtio_gpu_context_init_ioctl() invoked, now vfpriv->context_created
>> = true
>> 4. virtio_gpu_gem_object_close() will detach resource that wasn't attached
>>
>> Add obj->ctx_attached member to struct virtio_gpu_object. See
>> virtio_gpu_object_attach() that uses obj->attached, do the same for
>> virtio_gpu_cmd_context_attach_resource().
>>
>> --
>> Best regards,
>> Dmitry
>
> Hi Dmitry,
>
> WIth context_init, resource attach/detach is per-context based. So a
> simple obj->ctx_attached won't work. One would have to track in the
> guest context_init ctx for whether a bo has been attached or not.
>
> Another option is to accept this patch and live with the case you
> mentioned. We can consider that "invalid" user behavior.
Indeed, obj->ctx_attached shouldn't work for a shared/exported BO. Will
think on it for a couple days more and then merge this version if no
better ideas will appear.
--
Best regards,
Dmitry
^ permalink raw reply
* Re: [PATCH net-next v20 05/12] virtio_net: Query and set flow filter caps
From: Michael S. Tsirkin @ 2026-07-09 14:18 UTC (permalink / raw)
To: Shahar Shitrit
Cc: Daniel Jurgens, netdev, jasowang, pabeni, virtualization, parav,
yohadt, xuanzhuo, eperezma, jgg, kevin.tian, kuba, andrew+netdev,
edumazet
In-Reply-To: <a7f724e5-7648-4362-bfc0-ef4cd87211a8@nvidia.com>
On Thu, Jul 09, 2026 at 04:37:11PM +0300, Shahar Shitrit wrote:
>
>
> On 08/02/2026 13:51, Michael S. Tsirkin wrote:
> > On Thu, Feb 05, 2026 at 04:47:00PM -0600, Daniel Jurgens wrote:
> >> When probing a virtnet device, attempt to read the flow filter
> >> capabilities. In order to use the feature the caps must also
> >> be set. For now setting what was read is sufficient.
> >>
> >> This patch adds uapi definitions virtio_net flow filters define in
> >> version 1.4 of the VirtIO spec.
> >>
> >> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
> >> Reviewed-by: Parav Pandit <parav@nvidia.com>
> >> Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
> >>
> >> ---
> >> v4:
> >> - Validate the length in the selector caps
> >> - Removed __free usage.
> >> - Removed for(int.
> >> v5:
> >> - Remove unneed () after MAX_SEL_LEN macro (test bot)
> >> v6:
> >> - Fix sparse warning "array of flexible structures" Jakub K/Simon H
> >> - Use new variable and validate ff_mask_size before set_cap. MST
> >> v7:
> >> - Set ff->ff_{caps, mask, actions} NULL in error path. Paolo Abeni
> >> - Return errors from virtnet_ff_init, -ENOTSUPP is not fatal. Xuan
> >>
> >> v8:
> >> - Use real_ff_mask_size when setting the selector caps. Jason Wang
> >>
> >> v9:
> >> - Set err after failed memory allocations. Simon Horman
> >>
> >> v10:
> >> - Return -EOPNOTSUPP in virnet_ff_init before allocing any memory.
> >> Jason/Paolo.
> >>
> >> v11:
> >> - Return -EINVAL if any resource limit is 0. Simon Horman
> >> - Ensure we don't overrun alloced space of ff->ff_mask by moving the
> >> real_ff_mask_size > ff_mask_size check into the loop. Simon Horman
> >>
> >> v12:
> >> - Move uapi includes to virtio_net.c vs header file. MST
> >> - Remove kernel.h header in virtio_net_ff uapi. MST
> >> - WARN_ON_ONCE in error paths validating selectors. MST
> >> - Move includes from .h to .c files. MST
> >> - Add WARN_ON_ONCE if obj_destroy fails. MST
> >> - Comment cleanup in virito_net_ff.h uapi. MST
> >> - Add 2 byte pad to the end of virtio_net_ff_cap_data.
> >> https://lore.kernel.org/virtio-comment/20251119044029-mutt-send-email-mst@kernel.org/T/#m930988a5d3db316c68546d8b61f4b94f6ebda030
> >> - Cleanup and reinit in the freeze/restore path. MST
> >>
> >> v13:
> >> - Added /* private: */ comment before reserved field. Jakub
> >> - Change ff_mask validation to break at unkonwn selector type. This
> >> will allow compatability with newer controllers if the types of
> >> selectors is expanded. MST
> >>
> >> v14:
> >> - Handle err from virtnet_ff_init in virtnet_restore_up. MST
> >>
> >> v15:
> >> - In virtnet_restore_up only call virtnet_close in err path if
> >> netif_runnig. AI
> >>
> >> v16:
> >> - Return 0 from virtnet_restore_up if virtnet_init_ff return not
> >> supported. AI
> >>
> >> v17:
> >> - During restore freeze_down on error during ff_init. AI
> >>
> >> v18:
> >> - Changed selector cap validation to verify size for each type
> >> instead of just checking they weren't bigger than max size. AI
> >> - Added __count_by attribute to flexible members in uapi. Paolo A
> >>
> >> v19:
> >> - Fixed ;; and incorrect plural in comment. AI
> >>
> >> v20:
> >> - include uapi/linux/stddef.h for __counted_by. AI
> >
> > AI has led you astray, sadly (
> will fix in v21
> >
> >
> >
> >
> >> ---
> >> drivers/net/virtio_net.c | 231 ++++++++++++++++++++++++++++-
> >> include/uapi/linux/virtio_net_ff.h | 91 ++++++++++++
> >> 2 files changed, 321 insertions(+), 1 deletion(-)
> >> create mode 100644 include/uapi/linux/virtio_net_ff.h
> >>
> >> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> >> index db88dcaefb20..2cfa37e2f83f 100644
> >> --- a/drivers/net/virtio_net.c
> >> +++ b/drivers/net/virtio_net.c
> >> @@ -26,6 +26,11 @@
> >> #include <net/netdev_rx_queue.h>
> >> #include <net/netdev_queues.h>
> >> #include <net/xdp_sock_drv.h>
> >> +#include <linux/virtio_admin.h>
> >> +#include <net/ipv6.h>
> >> +#include <net/ip.h>
> >> +#include <uapi/linux/virtio_pci.h>
> >> +#include <uapi/linux/virtio_net_ff.h>
> >>
> >> static int napi_weight = NAPI_POLL_WEIGHT;
> >> module_param(napi_weight, int, 0444);
> >> @@ -281,6 +286,14 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
> >> VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
> >> };
> >>
> >> +struct virtnet_ff {
> >> + struct virtio_device *vdev;
> >> + bool ff_supported;
> >> + struct virtio_net_ff_cap_data *ff_caps;
> >> + struct virtio_net_ff_cap_mask_data *ff_mask;
> >> + struct virtio_net_ff_actions *ff_actions;
> >> +};
> >> +
> >> #define VIRTNET_Q_TYPE_RX 0
> >> #define VIRTNET_Q_TYPE_TX 1
> >> #define VIRTNET_Q_TYPE_CQ 2
> >> @@ -488,6 +501,7 @@ struct virtnet_info {
> >> TRAILING_OVERLAP(struct virtio_net_rss_config_trailer, rss_trailer, hash_key_data,
> >> u8 rss_hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE];
> >> );
> >> + struct virtnet_ff ff;
> >> };
> >> static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) ==
> >> offsetof(struct virtnet_info, rss_hash_key_data));
> >> @@ -526,6 +540,7 @@ static struct sk_buff *virtnet_skb_append_frag(struct sk_buff *head_skb,
> >> struct page *page, void *buf,
> >> int len, int truesize);
> >> static void virtnet_xsk_completed(struct send_queue *sq, int num);
> >> +static void remove_vq_common(struct virtnet_info *vi);
> >>
> >> enum virtnet_xmit_type {
> >> VIRTNET_XMIT_TYPE_SKB,
> >> @@ -5684,6 +5699,192 @@ static const struct netdev_stat_ops virtnet_stat_ops = {
> >> .get_base_stats = virtnet_get_base_stats,
> >> };
> >>
> >> +static size_t get_mask_size(u16 type)
> >> +{
> >> + switch (type) {
> >> + case VIRTIO_NET_FF_MASK_TYPE_ETH:
> >> + return sizeof(struct ethhdr);
> >> + case VIRTIO_NET_FF_MASK_TYPE_IPV4:
> >> + return sizeof(struct iphdr);
> >> + case VIRTIO_NET_FF_MASK_TYPE_IPV6:
> >> + return sizeof(struct ipv6hdr);
> >> + case VIRTIO_NET_FF_MASK_TYPE_TCP:
> >> + return sizeof(struct tcphdr);
> >> + case VIRTIO_NET_FF_MASK_TYPE_UDP:
> >> + return sizeof(struct udphdr);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
> >> +{
> >> + size_t ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data) +
> >> + sizeof(struct virtio_net_ff_selector) *
> >> + VIRTIO_NET_FF_MASK_TYPE_MAX;
> >> + struct virtio_admin_cmd_query_cap_id_result *cap_id_list;
> >> + struct virtio_net_ff_selector *sel;
> >> + unsigned long sel_types = 0;
> >> + size_t real_ff_mask_size;
> >> + int err;
> >> + int i;
> >> +
> >> + if (!vdev->config->admin_cmd_exec)
> >> + return -EOPNOTSUPP;
> >> +
> >> + cap_id_list = kzalloc(sizeof(*cap_id_list), GFP_KERNEL);
> >> + if (!cap_id_list)
> >> + return -ENOMEM;
> >> +
> >> + err = virtio_admin_cap_id_list_query(vdev, cap_id_list);
> >> + if (err)
> >> + goto err_cap_list;
> >> +
> >> + if (!(VIRTIO_CAP_IN_LIST(cap_id_list,
> >> + VIRTIO_NET_FF_RESOURCE_CAP) &&
> >> + VIRTIO_CAP_IN_LIST(cap_id_list,
> >> + VIRTIO_NET_FF_SELECTOR_CAP) &&
> >> + VIRTIO_CAP_IN_LIST(cap_id_list,
> >> + VIRTIO_NET_FF_ACTION_CAP))) {
> >> + err = -EOPNOTSUPP;
> >> + goto err_cap_list;
> >> + }
> >> +
> >> + ff->ff_caps = kzalloc(sizeof(*ff->ff_caps), GFP_KERNEL);
> >> + if (!ff->ff_caps) {
> >> + err = -ENOMEM;
> >> + goto err_cap_list;
> >> + }
> >> +
> >> + err = virtio_admin_cap_get(vdev,
> >> + VIRTIO_NET_FF_RESOURCE_CAP,
> >> + ff->ff_caps,
> >> + sizeof(*ff->ff_caps));
> >> +
> >> + if (err)
> >> + goto err_ff;
> >> +
> >> + if (!ff->ff_caps->groups_limit ||
> >> + !ff->ff_caps->classifiers_limit ||
> >> + !ff->ff_caps->rules_limit ||
> >> + !ff->ff_caps->rules_per_group_limit) {
> >> + err = -EINVAL;
> >> + goto err_ff;
> >> + }
> >> +
> >> + /* VIRTIO_NET_FF_MASK_TYPE start at 1 */
> >> + for (i = 1; i <= VIRTIO_NET_FF_MASK_TYPE_MAX; i++)
> >> + ff_mask_size += get_mask_size(i);
> >> +
> >> + ff->ff_mask = kzalloc(ff_mask_size, GFP_KERNEL);
> >> + if (!ff->ff_mask) {
> >> + err = -ENOMEM;
> >> + goto err_ff;
> >> + }
> >> +
> >> + err = virtio_admin_cap_get(vdev,
> >> + VIRTIO_NET_FF_SELECTOR_CAP,
> >> + ff->ff_mask,
> >> + ff_mask_size);
> >
> > So ff_actions is from device and ff_actions->count does not seem to be checked.
> >
> > If device somehow gains a larger mask down the road, can it not then overflow?
> > or malicious?
> see below
> >
> >
> >> +
> >> + if (err)
> >> + goto err_ff_mask;
> >> +
> >> + ff->ff_actions = kzalloc(sizeof(*ff->ff_actions) +
> >> + VIRTIO_NET_FF_ACTION_MAX,
> >> + GFP_KERNEL);
> >> + if (!ff->ff_actions) {
> >> + err = -ENOMEM;
> >> + goto err_ff_mask;
> >> + }
> >> +
> >> + err = virtio_admin_cap_get(vdev,
> >> + VIRTIO_NET_FF_ACTION_CAP,
> >> + ff->ff_actions,
> >> + sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
> >
> > So ff_actions is from device and ff_actions->count is not checked.
> >
> > If device gains a ton of actions down the road, can it not then overflow?
> > or malicious?
>
> it can't overflow, as it can write up to sizeof(*ff->ff_actions) +
> VIRTIO_NET_FF_ACTION_MAX bytes. But it is a valid concern in case of
> someone in the future iterates on count. Better to handle it now so it
> won't be forgotten. Checking that ff_actions->count doesn't exceed
> VIRTIO_NET_FF_ACTION_MAX can break backward compatibility.
These are admin commands right? They are truncated silently.
All you have to do is just check the length returned.
At least that's my vague understanding, don't have the
time to re-review now. HTH
> If this max
> value is extended in a new spec/controller then this driver would fail
> to load. Instead, I will assign ff->ff_actions->count with the minimum
> of what we got and the max.
> >
> >> +
> >> + if (err)
> >> + goto err_ff_action;
> >> +
> >> + err = virtio_admin_cap_set(vdev,
> >> + VIRTIO_NET_FF_RESOURCE_CAP,
> >> + ff->ff_caps,
> >> + sizeof(*ff->ff_caps));
> >> + if (err)
> >> + goto err_ff_action;
> >> +
> >> + real_ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data);
> >> + sel = (void *)&ff->ff_mask->selectors;
> >> +
> >> + for (i = 0; i < ff->ff_mask->count; i++) {
> >> + /* If the selector type is unknown it may indicate the spec
> >> + * has been revised to include new types of selectors
> >> + */
> >> + if (sel->type > VIRTIO_NET_FF_MASK_TYPE_MAX)
> >
> > do you want to check sel->type 0 too?
> yes, will add in v21.
> >
> >> + break;
> >
> > but count remains unchanged? should we not to reduce count here
> > so device knows what driver can drive?
>
> we should. will add it in v21.
> >
> >
> >> +
> >> + if (sel->length != get_mask_size(sel->type) ||
> >> + test_and_set_bit(sel->type, &sel_types)) {
> >> + WARN_ON_ONCE(true);
> >> + err = -EINVAL;
> >> + goto err_ff_action;
> >> + }
> >> + real_ff_mask_size += sizeof(struct virtio_net_ff_selector) + sel->length;
> >> + if (real_ff_mask_size > ff_mask_size) {
> >> + WARN_ON_ONCE(true);
> >> + err = -EINVAL;
> >> + goto err_ff_action;
> >> + }
> >> + sel = (void *)sel + sizeof(*sel) + sel->length;
> >> + }
> >> +
> >> + err = virtio_admin_cap_set(vdev,
> >> + VIRTIO_NET_FF_SELECTOR_CAP,
> >> + ff->ff_mask,
> >> + real_ff_mask_size);
> >> + if (err)
> >> + goto err_ff_action;
> >> +
> >> + err = virtio_admin_cap_set(vdev,
> >> + VIRTIO_NET_FF_ACTION_CAP,
> >> + ff->ff_actions,
> >> + sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
> >> + if (err)
> >> + goto err_ff_action;
> >> +
> >> + ff->vdev = vdev;
> >> + ff->ff_supported = true;
> >> +
> >> + kfree(cap_id_list);
> >> +
> >> + return 0;
> >> +
> >> +err_ff_action:
> >> + kfree(ff->ff_actions);
> >> + ff->ff_actions = NULL;
> >> +err_ff_mask:
> >> + kfree(ff->ff_mask);
> >> + ff->ff_mask = NULL;
> >> +err_ff:
> >> + kfree(ff->ff_caps);
> >> + ff->ff_caps = NULL;
> >> +err_cap_list:
> >> + kfree(cap_id_list);
> >> +
> >> + return err;
> >> +}
> >> +
> >> +static void virtnet_ff_cleanup(struct virtnet_ff *ff)
> >> +{
> >> + if (!ff->ff_supported)
> >> + return;
> >> +
> >> + kfree(ff->ff_actions);
> >> + kfree(ff->ff_mask);
> >> + kfree(ff->ff_caps);
> >> + ff->ff_supported = false;
> >> +}
> >> +
> >> static void virtnet_freeze_down(struct virtio_device *vdev)
> >> {
> >> struct virtnet_info *vi = vdev->priv;
> >> @@ -5702,6 +5903,10 @@ static void virtnet_freeze_down(struct virtio_device *vdev)
> >> netif_tx_lock_bh(vi->dev);
> >> netif_device_detach(vi->dev);
> >> netif_tx_unlock_bh(vi->dev);
> >> +
> >> + rtnl_lock();
> >> + virtnet_ff_cleanup(&vi->ff);
> >> + rtnl_unlock();
> >> }
> >>
> >> static int init_vqs(struct virtnet_info *vi);
> >> @@ -5727,10 +5932,23 @@ static int virtnet_restore_up(struct virtio_device *vdev)
> >> return err;
> >> }
> >>
> >> + /* Initialize flow filters. Not supported is an acceptable and common
> >> + * return code
> >> + */
> >> + rtnl_lock();
> >> + err = virtnet_ff_init(&vi->ff, vi->vdev);
> >> + if (err && err != -EOPNOTSUPP) {
> >> + rtnl_unlock();
> >> + virtnet_freeze_down(vi->vdev);
> >> + remove_vq_common(vi);
> >> + return err;
> >> + }
> >> + rtnl_unlock();
> >> +
> >> netif_tx_lock_bh(vi->dev);
> >> netif_device_attach(vi->dev);
> >> netif_tx_unlock_bh(vi->dev);
> >> - return err;
> >> + return 0;
> >> }
> >>
> >> static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads)
> >> @@ -7058,6 +7276,15 @@ static int virtnet_probe(struct virtio_device *vdev)
> >> }
> >> vi->guest_offloads_capable = vi->guest_offloads;
> >>
> >> + /* Initialize flow filters. Not supported is an acceptable and common
> >> + * return code
> >> + */
> >> + err = virtnet_ff_init(&vi->ff, vi->vdev);
> >> + if (err && err != -EOPNOTSUPP) {
> >> + rtnl_unlock();
> >> + goto free_unregister_netdev;
> >> + }
> >> +
> >> rtnl_unlock();
> >>
> >> err = virtnet_cpu_notif_add(vi);
> >> @@ -7073,6 +7300,7 @@ static int virtnet_probe(struct virtio_device *vdev)
> >>
> >> free_unregister_netdev:
> >> unregister_netdev(dev);
> >> + virtnet_ff_cleanup(&vi->ff);
> >> free_failover:
> >> net_failover_destroy(vi->failover);
> >> free_vqs:
> >> @@ -7121,6 +7349,7 @@ static void virtnet_remove(struct virtio_device *vdev)
> >> virtnet_free_irq_moder(vi);
> >>
> >> unregister_netdev(vi->dev);
> >> + virtnet_ff_cleanup(&vi->ff);
> >>
> >> net_failover_destroy(vi->failover);
> >>
> >> diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
> >> new file mode 100644
> >> index 000000000000..552a6b3a8a91
> >> --- /dev/null
> >> +++ b/include/uapi/linux/virtio_net_ff.h
> >> @@ -0,0 +1,91 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
> >> + *
> >> + * Header file for virtio_net flow filters
> >> + */
> >> +#ifndef _LINUX_VIRTIO_NET_FF_H
> >> +#define _LINUX_VIRTIO_NET_FF_H
> >> +
> >> +#include <linux/types.h>
> >> +#include <uapi/linux/stddef.h>
> >> +
> >> +#define VIRTIO_NET_FF_RESOURCE_CAP 0x800
> >> +#define VIRTIO_NET_FF_SELECTOR_CAP 0x801
> >> +#define VIRTIO_NET_FF_ACTION_CAP 0x802
> >> +
> >> +/**
> >> + * struct virtio_net_ff_cap_data - Flow filter resource capability limits
> >> + * @groups_limit: maximum number of flow filter groups supported by the device
> >> + * @classifiers_limit: maximum number of classifiers supported by the device
> >> + * @rules_limit: maximum number of rules supported device-wide across all groups
> >> + * @rules_per_group_limit: maximum number of rules allowed in a single group
> >> + * @last_rule_priority: priority value associated with the lowest-priority rule
> >> + * @selectors_per_classifier_limit: maximum selectors allowed in one classifier
> >> + */
> >> +struct virtio_net_ff_cap_data {
> >> + __le32 groups_limit;
> >> + __le32 classifiers_limit;
> >> + __le32 rules_limit;
> >> + __le32 rules_per_group_limit;
> >> + __u8 last_rule_priority;
> >> + __u8 selectors_per_classifier_limit;
> >> + /* private: */
> >> + __u8 reserved[2];
> >> +};
> >> +
> >> +/**
> >> + * struct virtio_net_ff_selector - Selector mask descriptor
> >> + * @type: selector type, one of VIRTIO_NET_FF_MASK_TYPE_* constants
> >> + * @flags: selector flags, see VIRTIO_NET_FF_MASK_F_* constants
> >> + * @reserved: must be set to 0 by the driver and ignored by the device
> >> + * @length: size in bytes of @mask
> >> + * @reserved1: must be set to 0 by the driver and ignored by the device
> >> + * @mask: variable-length mask payload for @type, length given by @length
> >> + *
> >> + * A selector describes a header mask that a classifier can apply. The format
> >> + * of @mask depends on @type.
> >> + */
> >> +struct virtio_net_ff_selector {
> >> + __u8 type;
> >> + __u8 flags;
> >> + __u8 reserved[2];
> >> + __u8 length;
> >> + __u8 reserved1[3];
> >> + __u8 mask[] __counted_by(length);
> >> +};
> >> +
> >> +#define VIRTIO_NET_FF_MASK_TYPE_ETH 1
> >> +#define VIRTIO_NET_FF_MASK_TYPE_IPV4 2
> >> +#define VIRTIO_NET_FF_MASK_TYPE_IPV6 3
> >> +#define VIRTIO_NET_FF_MASK_TYPE_TCP 4
> >> +#define VIRTIO_NET_FF_MASK_TYPE_UDP 5
> >> +#define VIRTIO_NET_FF_MASK_TYPE_MAX VIRTIO_NET_FF_MASK_TYPE_UDP
> >> +
> >> +/**
> >> + * struct virtio_net_ff_cap_mask_data - Supported selector mask formats
> >> + * @count: number of entries in @selectors
> >> + * @reserved: must be set to 0 by the driver and ignored by the device
> >> + * @selectors: packed array of struct virtio_net_ff_selector.
> >> + */
> >> +struct virtio_net_ff_cap_mask_data {
> >> + __u8 count;
> >> + __u8 reserved[7];
> >> + __u8 selectors[] __counted_by(count);
> >
> > This looks wrong to me. count is # of selectors (packed entries) not
> > bytes.
> right, will remove it.
> > >
> >
> >
> >> +};
> >> +
> >> +#define VIRTIO_NET_FF_MASK_F_PARTIAL_MASK (1 << 0)
> >> +
> >> +#define VIRTIO_NET_FF_ACTION_DROP 1
> >> +#define VIRTIO_NET_FF_ACTION_RX_VQ 2
> >> +#define VIRTIO_NET_FF_ACTION_MAX VIRTIO_NET_FF_ACTION_RX_VQ
> >> +/**
> >> + * struct virtio_net_ff_actions - Supported flow actions
> >> + * @count: number of supported actions in @actions
> >> + * @reserved: must be set to 0 by the driver and ignored by the device
> >> + * @actions: array of action identifiers (VIRTIO_NET_FF_ACTION_*)
> >> + */
> >> +struct virtio_net_ff_actions {
> >> + __u8 count;
> >> + __u8 reserved[7];
> >> + __u8 actions[] __counted_by(count);
> >
> >
> > this too.
> count directly represents the number of __u8 elements in actions[], so
> the size of the flexible array is count * sizeof(__u8). Therefore, here
> it is correct.
> >
> >> +};
> >> +#endif
> >> --
> >> 2.50.1
> >
> >
^ permalink raw reply
* Re: [PATCH net-next v20 07/12] virtio_net: Implement layer 2 ethtool flow rules
From: Shahar Shitrit @ 2026-07-09 13:44 UTC (permalink / raw)
To: Michael S. Tsirkin, Daniel Jurgens
Cc: netdev, jasowang, pabeni, virtualization, parav, yohadt, xuanzhuo,
eperezma, jgg, kevin.tian, kuba, andrew+netdev, edumazet
In-Reply-To: <20260208062637-mutt-send-email-mst@kernel.org>
On 08/02/2026 13:35, Michael S. Tsirkin wrote:
> On Thu, Feb 05, 2026 at 04:47:02PM -0600, Daniel Jurgens wrote:
>> Filtering a flow requires a classifier to match the packets, and a rule
>> to filter on the matches.
>>
>> A classifier consists of one or more selectors. There is one selector
>> per header type. A selector must only use fields set in the selector
>> capability. If partial matching is supported, the classifier mask for a
>> particular field can be a subset of the mask for that field in the
>> capability.
>>
>> The rule consists of a priority, an action and a key. The key is a byte
>> array containing headers corresponding to the selectors in the
>> classifier.
>>
>> This patch implements ethtool rules for ethernet headers.
>>
>> Example:
>> $ ethtool -U ens9 flow-type ether dst 08:11:22:33:44:54 action 30
>> Added rule with ID 1
>>
>> The rule in the example directs received packets with the specified
>> destination MAC address to rq 30.
>>
>> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
>> Reviewed-by: Parav Pandit <parav@nvidia.com>
>> Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
>> Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
>> ---
>> v4:
>> - Fixed double free bug in error flows
>> - Build bug on for classifier struct ordering.
>> - (u8 *) to (void *) casting.
>> - Documentation in UAPI
>> - Answered questions about overflow with no changes.
>> v6:
>> - Fix sparse warning "array of flexible structures" Jakub K/Simon H
>> v7:
>> - Move for (int i -> for (i hunk from next patch. Paolo Abeni
>>
>> v12:
>> - Make key_size u8. MST
>> - Free key in insert_rule when it's successful. MST
>>
>> v17:
>> - Fix memory leak if validate_classifier_selector fails. AI
>>
>> ---
>> ---
>> drivers/net/virtio_net.c | 464 +++++++++++++++++++++++++++++
>> include/uapi/linux/virtio_net_ff.h | 50 ++++
>> 2 files changed, 514 insertions(+)
>>
>> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
>> index 340104b22a59..27833ba1abee 100644
>> --- a/drivers/net/virtio_net.c
>> +++ b/drivers/net/virtio_net.c
>> @@ -31,6 +31,7 @@
>> #include <net/ip.h>
>> #include <uapi/linux/virtio_pci.h>
>> #include <uapi/linux/virtio_net_ff.h>
>> +#include <linux/xarray.h>
>>
>> static int napi_weight = NAPI_POLL_WEIGHT;
>> module_param(napi_weight, int, 0444);
>> @@ -286,6 +287,11 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
>> VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
>> };
>>
>> +struct virtnet_ethtool_ff {
>> + struct xarray rules;
>> + int num_rules;
>> +};
>> +
>> #define VIRTNET_FF_ETHTOOL_GROUP_PRIORITY 1
>> #define VIRTNET_FF_MAX_GROUPS 1
>>
>> @@ -295,8 +301,16 @@ struct virtnet_ff {
>> struct virtio_net_ff_cap_data *ff_caps;
>> struct virtio_net_ff_cap_mask_data *ff_mask;
>> struct virtio_net_ff_actions *ff_actions;
>> + struct xarray classifiers;
>> + int num_classifiers;
>> + struct virtnet_ethtool_ff ethtool;
>> };
>>
>> +static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff,
>> + struct ethtool_rx_flow_spec *fs,
>> + u16 curr_queue_pairs);
>> +static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location);
>> +
>> #define VIRTNET_Q_TYPE_RX 0
>> #define VIRTNET_Q_TYPE_TX 1
>> #define VIRTNET_Q_TYPE_CQ 2
>> @@ -5579,6 +5593,21 @@ static u32 virtnet_get_rx_ring_count(struct net_device *dev)
>> return vi->curr_queue_pairs;
>> }
>>
>> +static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
>> +{
>> + struct virtnet_info *vi = netdev_priv(dev);
>> +
>> + switch (info->cmd) {
>> + case ETHTOOL_SRXCLSRLINS:
>> + return virtnet_ethtool_flow_insert(&vi->ff, &info->fs,
>> + vi->curr_queue_pairs);
>> + case ETHTOOL_SRXCLSRLDEL:
>> + return virtnet_ethtool_flow_remove(&vi->ff, info->fs.location);
>> + }
>> +
>> + return -EOPNOTSUPP;
>> +}
>> +
>> static const struct ethtool_ops virtnet_ethtool_ops = {
>> .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
>> ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
>> @@ -5605,6 +5634,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
>> .get_rxfh_fields = virtnet_get_hashflow,
>> .set_rxfh_fields = virtnet_set_hashflow,
>> .get_rx_ring_count = virtnet_get_rx_ring_count,
>> + .set_rxnfc = virtnet_set_rxnfc,
>> };
>>
>> static void virtnet_get_queue_stats_rx(struct net_device *dev, int i,
>> @@ -5702,6 +5732,429 @@ static const struct netdev_stat_ops virtnet_stat_ops = {
>> .get_base_stats = virtnet_get_base_stats,
>> };
>>
>> +struct virtnet_ethtool_rule {
>> + struct ethtool_rx_flow_spec flow_spec;
>> + u32 classifier_id;
>> +};
>> +
>> +/* The classifier struct must be the last field in this struct */
>> +struct virtnet_classifier {
>> + size_t size;
>> + u32 id;
>> + struct virtio_net_resource_obj_ff_classifier classifier;
>> +};
>> +
>> +static_assert(sizeof(struct virtnet_classifier) ==
>> + ALIGN(offsetofend(struct virtnet_classifier, classifier),
>> + __alignof__(struct virtnet_classifier)),
>> + "virtnet_classifier: classifier must be the last member");
>> +
>> +static bool check_mask_vs_cap(const void *m, const void *c,
>> + u16 len, bool partial)
>> +{
>> + const u8 *mask = m;
>> + const u8 *cap = c;
>> + int i;
>> +
>> + for (i = 0; i < len; i++) {
>> + if (partial && ((mask[i] & cap[i]) != mask[i]))
>> + return false;
>> + if (!partial && mask[i] != cap[i])
>> + return false;
>> + }
>> +
>> + return true;
>> +}
>> +
>> +static
>> +struct virtio_net_ff_selector *get_selector_cap(const struct virtnet_ff *ff,
>> + u8 selector_type)
>> +{
>> + struct virtio_net_ff_selector *sel;
>> + void *buf;
>> + int i;
>> +
>> + buf = &ff->ff_mask->selectors;
>> + sel = buf;
>> +
>> + for (i = 0; i < ff->ff_mask->count; i++) {
>> + if (sel->type == selector_type)
>> + return sel;
>> +
>> + buf += sizeof(struct virtio_net_ff_selector) + sel->length;
>> + sel = buf;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static bool validate_eth_mask(const struct virtnet_ff *ff,
>> + const struct virtio_net_ff_selector *sel,
>> + const struct virtio_net_ff_selector *sel_cap)
>> +{
>> + bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
>> + struct ethhdr *cap, *mask;
>> + struct ethhdr zeros = {};
>> +
>> + cap = (struct ethhdr *)&sel_cap->mask;
>> + mask = (struct ethhdr *)&sel->mask;
>> +
>> + if (memcmp(&zeros.h_dest, mask->h_dest, sizeof(zeros.h_dest)) &&
>> + !check_mask_vs_cap(mask->h_dest, cap->h_dest,
>> + sizeof(mask->h_dest), partial_mask))
>> + return false;
>> +
>> + if (memcmp(&zeros.h_source, mask->h_source, sizeof(zeros.h_source)) &&
>> + !check_mask_vs_cap(mask->h_source, cap->h_source,
>> + sizeof(mask->h_source), partial_mask))
>> + return false;
>> +
>> + if (mask->h_proto &&
>> + !check_mask_vs_cap(&mask->h_proto, &cap->h_proto,
>> + sizeof(__be16), partial_mask))
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> +static bool validate_mask(const struct virtnet_ff *ff,
>> + const struct virtio_net_ff_selector *sel)
>> +{
>> + struct virtio_net_ff_selector *sel_cap = get_selector_cap(ff, sel->type);
>> +
>> + if (!sel_cap)
>> + return false;
>> +
>> + switch (sel->type) {
>> + case VIRTIO_NET_FF_MASK_TYPE_ETH:
>> + return validate_eth_mask(ff, sel, sel_cap);
>> + }
>> +
>> + return false;
>> +}
>> +
>> +static int setup_classifier(struct virtnet_ff *ff, struct virtnet_classifier *c)
>> +{
>> + int err;
>> +
>> + err = xa_alloc(&ff->classifiers, &c->id, c,
>> + XA_LIMIT(0, le32_to_cpu(ff->ff_caps->classifiers_limit) - 1),
>> + GFP_KERNEL);
>> + if (err)
>> + return err;
>> +
>> + err = virtio_admin_obj_create(ff->vdev,
>> + VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
>> + c->id,
>> + VIRTIO_ADMIN_GROUP_TYPE_SELF,
>> + 0,
>> + &c->classifier,
>> + c->size);
>> + if (err)
>> + goto err_xarray;
>> +
>> + return 0;
>> +
>> +err_xarray:
>> + xa_erase(&ff->classifiers, c->id);
>> +
>> + return err;
>> +}
>> +
>> +static void destroy_classifier(struct virtnet_ff *ff,
>> + u32 classifier_id)
>> +{
>> + struct virtnet_classifier *c;
>> +
>> + c = xa_load(&ff->classifiers, classifier_id);
>> + if (c) {
>> + virtio_admin_obj_destroy(ff->vdev,
>> + VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
>> + c->id,
>> + VIRTIO_ADMIN_GROUP_TYPE_SELF,
>> + 0);
>> +
>> + xa_erase(&ff->classifiers, c->id);
>> + kfree(c);
>> + }
>> +}
>> +
>> +static void destroy_ethtool_rule(struct virtnet_ff *ff,
>> + struct virtnet_ethtool_rule *eth_rule)
>> +{
>> + ff->ethtool.num_rules--;
>> +
>> + virtio_admin_obj_destroy(ff->vdev,
>> + VIRTIO_NET_RESOURCE_OBJ_FF_RULE,
>> + eth_rule->flow_spec.location,
>> + VIRTIO_ADMIN_GROUP_TYPE_SELF,
>> + 0);
>> +
>> + xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
>> + destroy_classifier(ff, eth_rule->classifier_id);
>> + kfree(eth_rule);
>> +}
>> +
>> +static int insert_rule(struct virtnet_ff *ff,
>> + struct virtnet_ethtool_rule *eth_rule,
>> + u32 classifier_id,
>> + const u8 *key,
>> + u8 key_size)
>> +{
>> + struct ethtool_rx_flow_spec *fs = ð_rule->flow_spec;
>> + struct virtio_net_resource_obj_ff_rule *ff_rule;
>> + int err;
>> +
>> + ff_rule = kzalloc(sizeof(*ff_rule) + key_size, GFP_KERNEL);
>> + if (!ff_rule)
>> + return -ENOMEM;
>> +
>> + /* Intentionally leave the priority as 0. All rules have the same
>> + * priority.
>> + */
>> + ff_rule->group_id = cpu_to_le32(VIRTNET_FF_ETHTOOL_GROUP_PRIORITY);
>> + ff_rule->classifier_id = cpu_to_le32(classifier_id);
>> + ff_rule->key_length = key_size;
>> + ff_rule->action = fs->ring_cookie == RX_CLS_FLOW_DISC ?
>> + VIRTIO_NET_FF_ACTION_DROP :
>> + VIRTIO_NET_FF_ACTION_RX_VQ;
>
> btw driver does not validate these actions are supported or did i miss the
> check? the spec does not say it must but it also does not
> say how device will behave if not. better not to take risks.
>
>
>> + ff_rule->vq_index = fs->ring_cookie != RX_CLS_FLOW_DISC ?
>> + cpu_to_le16(fs->ring_cookie) : 0;
>
>
> So here ring_cookie is a vq index? vq index is what matches the spec. but below ...
here we miss rxq2vq conversion of fs->ring_cookie. I'll fix in v21.
>
>
>> + memcpy(&ff_rule->keys, key, key_size);
>> +
>> + err = virtio_admin_obj_create(ff->vdev,
>> + VIRTIO_NET_RESOURCE_OBJ_FF_RULE,
>> + fs->location,
>> + VIRTIO_ADMIN_GROUP_TYPE_SELF,
>> + 0,
>> + ff_rule,
>> + sizeof(*ff_rule) + key_size);
>> + if (err)
>> + goto err_ff_rule;
>> +
>> + eth_rule->classifier_id = classifier_id;
>> + ff->ethtool.num_rules++;
>> + kfree(ff_rule);
>> + kfree(key);
>> +
>> + return 0;
>> +
>> +err_ff_rule:
>> + kfree(ff_rule);
>> +
>> + return err;
>> +}
>> +
>> +static u32 flow_type_mask(u32 flow_type)
>> +{
>> + return flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
>> +}
>> +
>> +static bool supported_flow_type(const struct ethtool_rx_flow_spec *fs)
>> +{
>> + switch (fs->flow_type) {
>> + case ETHER_FLOW:
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +
>> +static int validate_flow_input(struct virtnet_ff *ff,
>> + const struct ethtool_rx_flow_spec *fs,
>> + u16 curr_queue_pairs)
>> +{
>> + /* Force users to use RX_CLS_LOC_ANY - don't allow specific locations */
>> + if (fs->location != RX_CLS_LOC_ANY)
>> + return -EOPNOTSUPP;
>> +
>> + if (fs->ring_cookie != RX_CLS_FLOW_DISC &&
>> + fs->ring_cookie >= curr_queue_pairs)
>> + return -EINVAL;
>
>
> here ring cookie seems to be a queue pair index?
>
>> +
>> + if (fs->flow_type != flow_type_mask(fs->flow_type))
>> + return -EOPNOTSUPP;
>> +
>> + if (!supported_flow_type(fs))
>> + return -EOPNOTSUPP;
>> +
>> + return 0;
>> +}
>> +
>> +static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
>> + u8 *key_size, size_t *classifier_size,
>> + int *num_hdrs)
>> +{
>> + *num_hdrs = 1;
>> + *key_size = sizeof(struct ethhdr);
>> + /*
>> + * The classifier size is the size of the classifier header, a selector
>> + * header for each type of header in the match criteria, and each header
>> + * providing the mask for matching against.
>> + */
>> + *classifier_size = *key_size +
>> + sizeof(struct virtio_net_resource_obj_ff_classifier) +
>> + sizeof(struct virtio_net_ff_selector) * (*num_hdrs);
>> +}
>> +
>> +static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector,
>> + u8 *key,
>> + const struct ethtool_rx_flow_spec *fs)
>> +{
>> + struct ethhdr *eth_m = (struct ethhdr *)&selector->mask;
>> + struct ethhdr *eth_k = (struct ethhdr *)key;
>> +
>> + selector->type = VIRTIO_NET_FF_MASK_TYPE_ETH;
>> + selector->length = sizeof(struct ethhdr);
>> +
>> + memcpy(eth_m, &fs->m_u.ether_spec, sizeof(*eth_m));
>> + memcpy(eth_k, &fs->h_u.ether_spec, sizeof(*eth_k));
>> +}
>> +
>> +static int
>> +validate_classifier_selectors(struct virtnet_ff *ff,
>> + struct virtio_net_resource_obj_ff_classifier *classifier,
>> + int num_hdrs)
>> +{
>> + struct virtio_net_ff_selector *selector = (void *)classifier->selectors;
>> + int i;
>> +
>> + for (i = 0; i < num_hdrs; i++) {
>> + if (!validate_mask(ff, selector))
>> + return -EINVAL;
>> +
>> + selector = (((void *)selector) + sizeof(*selector) +
>> + selector->length);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int build_and_insert(struct virtnet_ff *ff,
>> + struct virtnet_ethtool_rule *eth_rule)
>> +{
>> + struct virtio_net_resource_obj_ff_classifier *classifier;
>> + struct ethtool_rx_flow_spec *fs = ð_rule->flow_spec;
>> + struct virtio_net_ff_selector *selector;
>> + struct virtnet_classifier *c;
>> + size_t classifier_size;
>> + int num_hdrs;
>> + u8 key_size;
>> + u8 *key;
>> + int err;
>> +
>> + calculate_flow_sizes(fs, &key_size, &classifier_size, &num_hdrs);
>> +
>> + key = kzalloc(key_size, GFP_KERNEL);
>> + if (!key)
>> + return -ENOMEM;
>> +
>> + /*
>> + * virtio_net_ff_obj_ff_classifier is already included in the
>> + * classifier_size.
>> + */
>> + c = kzalloc(classifier_size +
>> + sizeof(struct virtnet_classifier) -
>> + sizeof(struct virtio_net_resource_obj_ff_classifier),
>> + GFP_KERNEL);
>> + if (!c) {
>> + kfree(key);
>> + return -ENOMEM;
>> + }
>> +
>> + c->size = classifier_size;
>> + classifier = &c->classifier;
>> + classifier->count = num_hdrs;
>> + selector = (void *)&classifier->selectors[0];
>> +
>> + setup_eth_hdr_key_mask(selector, key, fs);
>> +
>> + err = validate_classifier_selectors(ff, classifier, num_hdrs);
>> + if (err)
>> + goto err_classifier;
>> +
>> + err = setup_classifier(ff, c);
>> + if (err)
>> + goto err_classifier;
>> +
>> + err = insert_rule(ff, eth_rule, c->id, key, key_size);
>> + if (err) {
>> + /* destroy_classifier will free the classifier */
>> + destroy_classifier(ff, c->id);
>> + goto err_key;
>> + }
>> +
>> + return 0;
>> +
>> +err_classifier:
>> + kfree(c);
>> +err_key:
>> + kfree(key);
>> +
>> + return err;
>> +}
>> +
>> +static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff,
>> + struct ethtool_rx_flow_spec *fs,
>> + u16 curr_queue_pairs)
>> +{
>> + struct virtnet_ethtool_rule *eth_rule;
>> + int err;
>> +
>> + if (!ff->ff_supported)
>> + return -EOPNOTSUPP;
>> +
>> + err = validate_flow_input(ff, fs, curr_queue_pairs);
>> + if (err)
>> + return err;
>> +
>> + eth_rule = kzalloc(sizeof(*eth_rule), GFP_KERNEL);
>> + if (!eth_rule)
>> + return -ENOMEM;
>> +
>> + err = xa_alloc(&ff->ethtool.rules, &fs->location, eth_rule,
>> + XA_LIMIT(0, le32_to_cpu(ff->ff_caps->rules_limit) - 1),
>> + GFP_KERNEL);
>> + if (err)
>> + goto err_rule;
>> +
>> + eth_rule->flow_spec = *fs;
>> +
>> + err = build_and_insert(ff, eth_rule);
>> + if (err)
>> + goto err_xa;
>> +
>> + return err;
>> +
>> +err_xa:
>> + xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
>> +
>> +err_rule:
>> + fs->location = RX_CLS_LOC_ANY;
>> + kfree(eth_rule);
>> +
>> + return err;
>> +}
>> +
>> +static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location)
>> +{
>> + struct virtnet_ethtool_rule *eth_rule;
>> + int err = 0;
>> +
>> + if (!ff->ff_supported)
>> + return -EOPNOTSUPP;
>> +
>> + eth_rule = xa_load(&ff->ethtool.rules, location);
>> + if (!eth_rule) {
>> + err = -ENOENT;
>> + goto out;
>> + }
>> +
>> + destroy_ethtool_rule(ff, eth_rule);
>> +out:
>> + return err;
>> +}
>> +
>> static size_t get_mask_size(u16 type)
>> {
>> switch (type) {
>> @@ -5875,6 +6328,8 @@ static int virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
>> if (err)
>> goto err_ff_action;
>>
>> + xa_init_flags(&ff->classifiers, XA_FLAGS_ALLOC);
>> + xa_init_flags(&ff->ethtool.rules, XA_FLAGS_ALLOC);
>> ff->vdev = vdev;
>> ff->ff_supported = true;
>>
>> @@ -5899,9 +6354,18 @@ static int virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
>>
>> static void virtnet_ff_cleanup(struct virtnet_ff *ff)
>> {
>> + struct virtnet_ethtool_rule *eth_rule;
>> + unsigned long i;
>> +
>> if (!ff->ff_supported)
>> return;
>>
>> + xa_for_each(&ff->ethtool.rules, i, eth_rule)
>> + destroy_ethtool_rule(ff, eth_rule);
>> +
>> + xa_destroy(&ff->ethtool.rules);
>> + xa_destroy(&ff->classifiers);
>> +
>> virtio_admin_obj_destroy(ff->vdev,
>> VIRTIO_NET_RESOURCE_OBJ_FF_GROUP,
>> VIRTNET_FF_ETHTOOL_GROUP_PRIORITY,
>> diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
>> index c7d01e3edc80..aa0619f9c63b 100644
>> --- a/include/uapi/linux/virtio_net_ff.h
>> +++ b/include/uapi/linux/virtio_net_ff.h
>> @@ -13,6 +13,8 @@
>> #define VIRTIO_NET_FF_ACTION_CAP 0x802
>>
>> #define VIRTIO_NET_RESOURCE_OBJ_FF_GROUP 0x0200
>> +#define VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER 0x0201
>> +#define VIRTIO_NET_RESOURCE_OBJ_FF_RULE 0x0202
>>
>> /**
>> * struct virtio_net_ff_cap_data - Flow filter resource capability limits
>> @@ -103,4 +105,52 @@ struct virtio_net_resource_obj_ff_group {
>> __le16 group_priority;
>> };
>>
>> +/**
>> + * struct virtio_net_resource_obj_ff_classifier - Flow filter classifier object
>> + * @count: number of selector entries in @selectors
>> + * @reserved: must be set to 0 by the driver and ignored by the device
>> + * @selectors: array of selector descriptors that define match masks
>> + *
>> + * Payload for the VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER administrative object.
>> + * Each selector describes a header mask used to match packets
>> + * (see struct virtio_net_ff_selector). Selectors appear in the order they are
>> + * to be applied.
>> + */
>> +struct virtio_net_resource_obj_ff_classifier {
>> + __u8 count;
>> + __u8 reserved[7];
>> + __u8 selectors[];
>> +};
>> +
>> +/**
>> + * struct virtio_net_resource_obj_ff_rule - Flow filter rule object
>> + * @group_id: identifier of the target flow filter group
>> + * @classifier_id: identifier of the classifier referenced by this rule
>> + * @rule_priority: relative priority of this rule within the group
>> + * @key_length: number of bytes in @keys
>> + * @action: action to perform, one of VIRTIO_NET_FF_ACTION_*
>> + * @reserved: must be set to 0 by the driver and ignored by the device
>> + * @vq_index: RX virtqueue index for VIRTIO_NET_FF_ACTION_RX_VQ, 0 otherwise
>> + * @reserved1: must be set to 0 by the driver and ignored by the device
>> + * @keys: concatenated key bytes matching the classifier's selectors order
>> + *
>> + * Payload for the VIRTIO_NET_RESOURCE_OBJ_FF_RULE administrative object.
>> + * @group_id and @classifier_id refer to previously created objects of types
>> + * VIRTIO_NET_RESOURCE_OBJ_FF_GROUP and VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER
>> + * respectively. The key bytes are compared against packet headers using the
>> + * masks provided by the classifier's selectors. Multi-byte fields are
>> + * little-endian.
>> + */
>> +struct virtio_net_resource_obj_ff_rule {
>> + __le32 group_id;
>> + __le32 classifier_id;
>> + __u8 rule_priority;
>> + __u8 key_length; /* length of key in bytes */
>> + __u8 action;
>> + __u8 reserved;
>> + __le16 vq_index;
>> + __u8 reserved1[2];
>> + __u8 keys[];
>> +};
>> +
>> #endif
>> --
>> 2.50.1
>
>
^ permalink raw reply
* Re: [PATCH net-next v20 05/12] virtio_net: Query and set flow filter caps
From: Shahar Shitrit @ 2026-07-09 13:37 UTC (permalink / raw)
To: Michael S. Tsirkin, Daniel Jurgens
Cc: netdev, jasowang, pabeni, virtualization, parav, yohadt, xuanzhuo,
eperezma, jgg, kevin.tian, kuba, andrew+netdev, edumazet
In-Reply-To: <20260208063807-mutt-send-email-mst@kernel.org>
On 08/02/2026 13:51, Michael S. Tsirkin wrote:
> On Thu, Feb 05, 2026 at 04:47:00PM -0600, Daniel Jurgens wrote:
>> When probing a virtnet device, attempt to read the flow filter
>> capabilities. In order to use the feature the caps must also
>> be set. For now setting what was read is sufficient.
>>
>> This patch adds uapi definitions virtio_net flow filters define in
>> version 1.4 of the VirtIO spec.
>>
>> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
>> Reviewed-by: Parav Pandit <parav@nvidia.com>
>> Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
>>
>> ---
>> v4:
>> - Validate the length in the selector caps
>> - Removed __free usage.
>> - Removed for(int.
>> v5:
>> - Remove unneed () after MAX_SEL_LEN macro (test bot)
>> v6:
>> - Fix sparse warning "array of flexible structures" Jakub K/Simon H
>> - Use new variable and validate ff_mask_size before set_cap. MST
>> v7:
>> - Set ff->ff_{caps, mask, actions} NULL in error path. Paolo Abeni
>> - Return errors from virtnet_ff_init, -ENOTSUPP is not fatal. Xuan
>>
>> v8:
>> - Use real_ff_mask_size when setting the selector caps. Jason Wang
>>
>> v9:
>> - Set err after failed memory allocations. Simon Horman
>>
>> v10:
>> - Return -EOPNOTSUPP in virnet_ff_init before allocing any memory.
>> Jason/Paolo.
>>
>> v11:
>> - Return -EINVAL if any resource limit is 0. Simon Horman
>> - Ensure we don't overrun alloced space of ff->ff_mask by moving the
>> real_ff_mask_size > ff_mask_size check into the loop. Simon Horman
>>
>> v12:
>> - Move uapi includes to virtio_net.c vs header file. MST
>> - Remove kernel.h header in virtio_net_ff uapi. MST
>> - WARN_ON_ONCE in error paths validating selectors. MST
>> - Move includes from .h to .c files. MST
>> - Add WARN_ON_ONCE if obj_destroy fails. MST
>> - Comment cleanup in virito_net_ff.h uapi. MST
>> - Add 2 byte pad to the end of virtio_net_ff_cap_data.
>> https://lore.kernel.org/virtio-comment/20251119044029-mutt-send-email-mst@kernel.org/T/#m930988a5d3db316c68546d8b61f4b94f6ebda030
>> - Cleanup and reinit in the freeze/restore path. MST
>>
>> v13:
>> - Added /* private: */ comment before reserved field. Jakub
>> - Change ff_mask validation to break at unkonwn selector type. This
>> will allow compatability with newer controllers if the types of
>> selectors is expanded. MST
>>
>> v14:
>> - Handle err from virtnet_ff_init in virtnet_restore_up. MST
>>
>> v15:
>> - In virtnet_restore_up only call virtnet_close in err path if
>> netif_runnig. AI
>>
>> v16:
>> - Return 0 from virtnet_restore_up if virtnet_init_ff return not
>> supported. AI
>>
>> v17:
>> - During restore freeze_down on error during ff_init. AI
>>
>> v18:
>> - Changed selector cap validation to verify size for each type
>> instead of just checking they weren't bigger than max size. AI
>> - Added __count_by attribute to flexible members in uapi. Paolo A
>>
>> v19:
>> - Fixed ;; and incorrect plural in comment. AI
>>
>> v20:
>> - include uapi/linux/stddef.h for __counted_by. AI
>
> AI has led you astray, sadly (
will fix in v21
>
>
>
>
>> ---
>> drivers/net/virtio_net.c | 231 ++++++++++++++++++++++++++++-
>> include/uapi/linux/virtio_net_ff.h | 91 ++++++++++++
>> 2 files changed, 321 insertions(+), 1 deletion(-)
>> create mode 100644 include/uapi/linux/virtio_net_ff.h
>>
>> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
>> index db88dcaefb20..2cfa37e2f83f 100644
>> --- a/drivers/net/virtio_net.c
>> +++ b/drivers/net/virtio_net.c
>> @@ -26,6 +26,11 @@
>> #include <net/netdev_rx_queue.h>
>> #include <net/netdev_queues.h>
>> #include <net/xdp_sock_drv.h>
>> +#include <linux/virtio_admin.h>
>> +#include <net/ipv6.h>
>> +#include <net/ip.h>
>> +#include <uapi/linux/virtio_pci.h>
>> +#include <uapi/linux/virtio_net_ff.h>
>>
>> static int napi_weight = NAPI_POLL_WEIGHT;
>> module_param(napi_weight, int, 0444);
>> @@ -281,6 +286,14 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
>> VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
>> };
>>
>> +struct virtnet_ff {
>> + struct virtio_device *vdev;
>> + bool ff_supported;
>> + struct virtio_net_ff_cap_data *ff_caps;
>> + struct virtio_net_ff_cap_mask_data *ff_mask;
>> + struct virtio_net_ff_actions *ff_actions;
>> +};
>> +
>> #define VIRTNET_Q_TYPE_RX 0
>> #define VIRTNET_Q_TYPE_TX 1
>> #define VIRTNET_Q_TYPE_CQ 2
>> @@ -488,6 +501,7 @@ struct virtnet_info {
>> TRAILING_OVERLAP(struct virtio_net_rss_config_trailer, rss_trailer, hash_key_data,
>> u8 rss_hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE];
>> );
>> + struct virtnet_ff ff;
>> };
>> static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) ==
>> offsetof(struct virtnet_info, rss_hash_key_data));
>> @@ -526,6 +540,7 @@ static struct sk_buff *virtnet_skb_append_frag(struct sk_buff *head_skb,
>> struct page *page, void *buf,
>> int len, int truesize);
>> static void virtnet_xsk_completed(struct send_queue *sq, int num);
>> +static void remove_vq_common(struct virtnet_info *vi);
>>
>> enum virtnet_xmit_type {
>> VIRTNET_XMIT_TYPE_SKB,
>> @@ -5684,6 +5699,192 @@ static const struct netdev_stat_ops virtnet_stat_ops = {
>> .get_base_stats = virtnet_get_base_stats,
>> };
>>
>> +static size_t get_mask_size(u16 type)
>> +{
>> + switch (type) {
>> + case VIRTIO_NET_FF_MASK_TYPE_ETH:
>> + return sizeof(struct ethhdr);
>> + case VIRTIO_NET_FF_MASK_TYPE_IPV4:
>> + return sizeof(struct iphdr);
>> + case VIRTIO_NET_FF_MASK_TYPE_IPV6:
>> + return sizeof(struct ipv6hdr);
>> + case VIRTIO_NET_FF_MASK_TYPE_TCP:
>> + return sizeof(struct tcphdr);
>> + case VIRTIO_NET_FF_MASK_TYPE_UDP:
>> + return sizeof(struct udphdr);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
>> +{
>> + size_t ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data) +
>> + sizeof(struct virtio_net_ff_selector) *
>> + VIRTIO_NET_FF_MASK_TYPE_MAX;
>> + struct virtio_admin_cmd_query_cap_id_result *cap_id_list;
>> + struct virtio_net_ff_selector *sel;
>> + unsigned long sel_types = 0;
>> + size_t real_ff_mask_size;
>> + int err;
>> + int i;
>> +
>> + if (!vdev->config->admin_cmd_exec)
>> + return -EOPNOTSUPP;
>> +
>> + cap_id_list = kzalloc(sizeof(*cap_id_list), GFP_KERNEL);
>> + if (!cap_id_list)
>> + return -ENOMEM;
>> +
>> + err = virtio_admin_cap_id_list_query(vdev, cap_id_list);
>> + if (err)
>> + goto err_cap_list;
>> +
>> + if (!(VIRTIO_CAP_IN_LIST(cap_id_list,
>> + VIRTIO_NET_FF_RESOURCE_CAP) &&
>> + VIRTIO_CAP_IN_LIST(cap_id_list,
>> + VIRTIO_NET_FF_SELECTOR_CAP) &&
>> + VIRTIO_CAP_IN_LIST(cap_id_list,
>> + VIRTIO_NET_FF_ACTION_CAP))) {
>> + err = -EOPNOTSUPP;
>> + goto err_cap_list;
>> + }
>> +
>> + ff->ff_caps = kzalloc(sizeof(*ff->ff_caps), GFP_KERNEL);
>> + if (!ff->ff_caps) {
>> + err = -ENOMEM;
>> + goto err_cap_list;
>> + }
>> +
>> + err = virtio_admin_cap_get(vdev,
>> + VIRTIO_NET_FF_RESOURCE_CAP,
>> + ff->ff_caps,
>> + sizeof(*ff->ff_caps));
>> +
>> + if (err)
>> + goto err_ff;
>> +
>> + if (!ff->ff_caps->groups_limit ||
>> + !ff->ff_caps->classifiers_limit ||
>> + !ff->ff_caps->rules_limit ||
>> + !ff->ff_caps->rules_per_group_limit) {
>> + err = -EINVAL;
>> + goto err_ff;
>> + }
>> +
>> + /* VIRTIO_NET_FF_MASK_TYPE start at 1 */
>> + for (i = 1; i <= VIRTIO_NET_FF_MASK_TYPE_MAX; i++)
>> + ff_mask_size += get_mask_size(i);
>> +
>> + ff->ff_mask = kzalloc(ff_mask_size, GFP_KERNEL);
>> + if (!ff->ff_mask) {
>> + err = -ENOMEM;
>> + goto err_ff;
>> + }
>> +
>> + err = virtio_admin_cap_get(vdev,
>> + VIRTIO_NET_FF_SELECTOR_CAP,
>> + ff->ff_mask,
>> + ff_mask_size);
>
> So ff_actions is from device and ff_actions->count does not seem to be checked.
>
> If device somehow gains a larger mask down the road, can it not then overflow?
> or malicious?
see below
>
>
>> +
>> + if (err)
>> + goto err_ff_mask;
>> +
>> + ff->ff_actions = kzalloc(sizeof(*ff->ff_actions) +
>> + VIRTIO_NET_FF_ACTION_MAX,
>> + GFP_KERNEL);
>> + if (!ff->ff_actions) {
>> + err = -ENOMEM;
>> + goto err_ff_mask;
>> + }
>> +
>> + err = virtio_admin_cap_get(vdev,
>> + VIRTIO_NET_FF_ACTION_CAP,
>> + ff->ff_actions,
>> + sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
>
> So ff_actions is from device and ff_actions->count is not checked.
>
> If device gains a ton of actions down the road, can it not then overflow?
> or malicious?
it can't overflow, as it can write up to sizeof(*ff->ff_actions) +
VIRTIO_NET_FF_ACTION_MAX bytes. But it is a valid concern in case of
someone in the future iterates on count. Better to handle it now so it
won't be forgotten. Checking that ff_actions->count doesn't exceed
VIRTIO_NET_FF_ACTION_MAX can break backward compatibility. If this max
value is extended in a new spec/controller then this driver would fail
to load. Instead, I will assign ff->ff_actions->count with the minimum
of what we got and the max.
>
>> +
>> + if (err)
>> + goto err_ff_action;
>> +
>> + err = virtio_admin_cap_set(vdev,
>> + VIRTIO_NET_FF_RESOURCE_CAP,
>> + ff->ff_caps,
>> + sizeof(*ff->ff_caps));
>> + if (err)
>> + goto err_ff_action;
>> +
>> + real_ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data);
>> + sel = (void *)&ff->ff_mask->selectors;
>> +
>> + for (i = 0; i < ff->ff_mask->count; i++) {
>> + /* If the selector type is unknown it may indicate the spec
>> + * has been revised to include new types of selectors
>> + */
>> + if (sel->type > VIRTIO_NET_FF_MASK_TYPE_MAX)
>
> do you want to check sel->type 0 too?
yes, will add in v21.
>
>> + break;
>
> but count remains unchanged? should we not to reduce count here
> so device knows what driver can drive?
we should. will add it in v21.
>
>
>> +
>> + if (sel->length != get_mask_size(sel->type) ||
>> + test_and_set_bit(sel->type, &sel_types)) {
>> + WARN_ON_ONCE(true);
>> + err = -EINVAL;
>> + goto err_ff_action;
>> + }
>> + real_ff_mask_size += sizeof(struct virtio_net_ff_selector) + sel->length;
>> + if (real_ff_mask_size > ff_mask_size) {
>> + WARN_ON_ONCE(true);
>> + err = -EINVAL;
>> + goto err_ff_action;
>> + }
>> + sel = (void *)sel + sizeof(*sel) + sel->length;
>> + }
>> +
>> + err = virtio_admin_cap_set(vdev,
>> + VIRTIO_NET_FF_SELECTOR_CAP,
>> + ff->ff_mask,
>> + real_ff_mask_size);
>> + if (err)
>> + goto err_ff_action;
>> +
>> + err = virtio_admin_cap_set(vdev,
>> + VIRTIO_NET_FF_ACTION_CAP,
>> + ff->ff_actions,
>> + sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
>> + if (err)
>> + goto err_ff_action;
>> +
>> + ff->vdev = vdev;
>> + ff->ff_supported = true;
>> +
>> + kfree(cap_id_list);
>> +
>> + return 0;
>> +
>> +err_ff_action:
>> + kfree(ff->ff_actions);
>> + ff->ff_actions = NULL;
>> +err_ff_mask:
>> + kfree(ff->ff_mask);
>> + ff->ff_mask = NULL;
>> +err_ff:
>> + kfree(ff->ff_caps);
>> + ff->ff_caps = NULL;
>> +err_cap_list:
>> + kfree(cap_id_list);
>> +
>> + return err;
>> +}
>> +
>> +static void virtnet_ff_cleanup(struct virtnet_ff *ff)
>> +{
>> + if (!ff->ff_supported)
>> + return;
>> +
>> + kfree(ff->ff_actions);
>> + kfree(ff->ff_mask);
>> + kfree(ff->ff_caps);
>> + ff->ff_supported = false;
>> +}
>> +
>> static void virtnet_freeze_down(struct virtio_device *vdev)
>> {
>> struct virtnet_info *vi = vdev->priv;
>> @@ -5702,6 +5903,10 @@ static void virtnet_freeze_down(struct virtio_device *vdev)
>> netif_tx_lock_bh(vi->dev);
>> netif_device_detach(vi->dev);
>> netif_tx_unlock_bh(vi->dev);
>> +
>> + rtnl_lock();
>> + virtnet_ff_cleanup(&vi->ff);
>> + rtnl_unlock();
>> }
>>
>> static int init_vqs(struct virtnet_info *vi);
>> @@ -5727,10 +5932,23 @@ static int virtnet_restore_up(struct virtio_device *vdev)
>> return err;
>> }
>>
>> + /* Initialize flow filters. Not supported is an acceptable and common
>> + * return code
>> + */
>> + rtnl_lock();
>> + err = virtnet_ff_init(&vi->ff, vi->vdev);
>> + if (err && err != -EOPNOTSUPP) {
>> + rtnl_unlock();
>> + virtnet_freeze_down(vi->vdev);
>> + remove_vq_common(vi);
>> + return err;
>> + }
>> + rtnl_unlock();
>> +
>> netif_tx_lock_bh(vi->dev);
>> netif_device_attach(vi->dev);
>> netif_tx_unlock_bh(vi->dev);
>> - return err;
>> + return 0;
>> }
>>
>> static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads)
>> @@ -7058,6 +7276,15 @@ static int virtnet_probe(struct virtio_device *vdev)
>> }
>> vi->guest_offloads_capable = vi->guest_offloads;
>>
>> + /* Initialize flow filters. Not supported is an acceptable and common
>> + * return code
>> + */
>> + err = virtnet_ff_init(&vi->ff, vi->vdev);
>> + if (err && err != -EOPNOTSUPP) {
>> + rtnl_unlock();
>> + goto free_unregister_netdev;
>> + }
>> +
>> rtnl_unlock();
>>
>> err = virtnet_cpu_notif_add(vi);
>> @@ -7073,6 +7300,7 @@ static int virtnet_probe(struct virtio_device *vdev)
>>
>> free_unregister_netdev:
>> unregister_netdev(dev);
>> + virtnet_ff_cleanup(&vi->ff);
>> free_failover:
>> net_failover_destroy(vi->failover);
>> free_vqs:
>> @@ -7121,6 +7349,7 @@ static void virtnet_remove(struct virtio_device *vdev)
>> virtnet_free_irq_moder(vi);
>>
>> unregister_netdev(vi->dev);
>> + virtnet_ff_cleanup(&vi->ff);
>>
>> net_failover_destroy(vi->failover);
>>
>> diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
>> new file mode 100644
>> index 000000000000..552a6b3a8a91
>> --- /dev/null
>> +++ b/include/uapi/linux/virtio_net_ff.h
>> @@ -0,0 +1,91 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>> + *
>> + * Header file for virtio_net flow filters
>> + */
>> +#ifndef _LINUX_VIRTIO_NET_FF_H
>> +#define _LINUX_VIRTIO_NET_FF_H
>> +
>> +#include <linux/types.h>
>> +#include <uapi/linux/stddef.h>
>> +
>> +#define VIRTIO_NET_FF_RESOURCE_CAP 0x800
>> +#define VIRTIO_NET_FF_SELECTOR_CAP 0x801
>> +#define VIRTIO_NET_FF_ACTION_CAP 0x802
>> +
>> +/**
>> + * struct virtio_net_ff_cap_data - Flow filter resource capability limits
>> + * @groups_limit: maximum number of flow filter groups supported by the device
>> + * @classifiers_limit: maximum number of classifiers supported by the device
>> + * @rules_limit: maximum number of rules supported device-wide across all groups
>> + * @rules_per_group_limit: maximum number of rules allowed in a single group
>> + * @last_rule_priority: priority value associated with the lowest-priority rule
>> + * @selectors_per_classifier_limit: maximum selectors allowed in one classifier
>> + */
>> +struct virtio_net_ff_cap_data {
>> + __le32 groups_limit;
>> + __le32 classifiers_limit;
>> + __le32 rules_limit;
>> + __le32 rules_per_group_limit;
>> + __u8 last_rule_priority;
>> + __u8 selectors_per_classifier_limit;
>> + /* private: */
>> + __u8 reserved[2];
>> +};
>> +
>> +/**
>> + * struct virtio_net_ff_selector - Selector mask descriptor
>> + * @type: selector type, one of VIRTIO_NET_FF_MASK_TYPE_* constants
>> + * @flags: selector flags, see VIRTIO_NET_FF_MASK_F_* constants
>> + * @reserved: must be set to 0 by the driver and ignored by the device
>> + * @length: size in bytes of @mask
>> + * @reserved1: must be set to 0 by the driver and ignored by the device
>> + * @mask: variable-length mask payload for @type, length given by @length
>> + *
>> + * A selector describes a header mask that a classifier can apply. The format
>> + * of @mask depends on @type.
>> + */
>> +struct virtio_net_ff_selector {
>> + __u8 type;
>> + __u8 flags;
>> + __u8 reserved[2];
>> + __u8 length;
>> + __u8 reserved1[3];
>> + __u8 mask[] __counted_by(length);
>> +};
>> +
>> +#define VIRTIO_NET_FF_MASK_TYPE_ETH 1
>> +#define VIRTIO_NET_FF_MASK_TYPE_IPV4 2
>> +#define VIRTIO_NET_FF_MASK_TYPE_IPV6 3
>> +#define VIRTIO_NET_FF_MASK_TYPE_TCP 4
>> +#define VIRTIO_NET_FF_MASK_TYPE_UDP 5
>> +#define VIRTIO_NET_FF_MASK_TYPE_MAX VIRTIO_NET_FF_MASK_TYPE_UDP
>> +
>> +/**
>> + * struct virtio_net_ff_cap_mask_data - Supported selector mask formats
>> + * @count: number of entries in @selectors
>> + * @reserved: must be set to 0 by the driver and ignored by the device
>> + * @selectors: packed array of struct virtio_net_ff_selector.
>> + */
>> +struct virtio_net_ff_cap_mask_data {
>> + __u8 count;
>> + __u8 reserved[7];
>> + __u8 selectors[] __counted_by(count);
>
> This looks wrong to me. count is # of selectors (packed entries) not
> bytes.
right, will remove it.
> >
>
>
>> +};
>> +
>> +#define VIRTIO_NET_FF_MASK_F_PARTIAL_MASK (1 << 0)
>> +
>> +#define VIRTIO_NET_FF_ACTION_DROP 1
>> +#define VIRTIO_NET_FF_ACTION_RX_VQ 2
>> +#define VIRTIO_NET_FF_ACTION_MAX VIRTIO_NET_FF_ACTION_RX_VQ
>> +/**
>> + * struct virtio_net_ff_actions - Supported flow actions
>> + * @count: number of supported actions in @actions
>> + * @reserved: must be set to 0 by the driver and ignored by the device
>> + * @actions: array of action identifiers (VIRTIO_NET_FF_ACTION_*)
>> + */
>> +struct virtio_net_ff_actions {
>> + __u8 count;
>> + __u8 reserved[7];
>> + __u8 actions[] __counted_by(count);
>
>
> this too.
count directly represents the number of __u8 elements in actions[], so
the size of the flexible array is count * sizeof(__u8). Therefore, here
it is correct.
>
>> +};
>> +#endif
>> --
>> 2.50.1
>
>
^ permalink raw reply
* Re: [PATCH] drm/virtio: defer hotplug event from dequeue worker to avoid deadlock
From: Dmitry Osipenko @ 2026-07-09 12:55 UTC (permalink / raw)
To: Ryosuke Yasuoka, David Airlie, Gerd Hoffmann, Gurchetan Singh,
Chia-I Wu, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Simona Vetter, Dmitry Baryshkov, Javier Martinez Canillas
Cc: dri-devel, virtualization, linux-kernel
In-Reply-To: <18c09efbacafbff3.b25d31827bed23c9.829612c1dfc3d1e4@ryasuoka-thinkpadx1carbongen9.tokyo.csb>
On 7/9/26 15:30, Ryosuke Yasuoka wrote:
>
>
> On 07/07/2026 18:10, Dmitry Osipenko wrote:
>> On 7/3/26 08:58, Ryosuke Yasuoka wrote:
>>>
>>>
>>> On 02/07/2026 14:26, Dmitry Osipenko wrote:
>>>> On 7/1/26 12:23, Ryosuke Yasuoka wrote:
>>>>>
>>>>>
>>>>> On 30/06/2026 16:46, Dmitry Osipenko wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 6/30/26 12:16, Ryosuke Yasuoka wrote:
>>>>>>> A probe-time deadlock can occur between the dequeue worker and
>>>>>>> drm_client_register(). During probe, drm_client_register() holds
>>>>>>> clientlist_mutex and calls the fbdev hotplug callback, which triggers an
>>>>>>> atomic commit that ends up sleeping in virtio_gpu_queue_ctrl_sgs()
>>>>>>> waiting for virtqueue space. The dequeue worker that would free that
>>>>>>> space calls virtio_gpu_cmd_get_display_info_cb(), which invokes
>>>>>>> drm_kms_helper_hotplug_event() -> drm_client_dev_hotplug(), attempting
>>>>>>> to acquire the same clientlist_mutex. Since wake_up() is only called
>>>>>>> after the resp_cb loop, the probe thread is never woken and both threads
>>>>>>> deadlock.
>>>>>>>
>>>>>>> Fix this by deferring the hotplug notification from
>>>>>>> virtio_gpu_cmd_get_display_info_cb() to a separate work item. The
>>>>>>> display data (outputs[i].info) is still updated synchronously in the
>>>>>>> callback, and the deferred work only triggers a re-probe notification to
>>>>>>> DRM clients.
>>>>>>>
>>>>>>> Fixes: 27655b9bb9f0 ("drm/client: Send hotplug event after registering a client")
>>>>>>> Closes: https://syzkaller.appspot.com/bug?id=d6dd6f86d3aaf7eebe7406e45c1c6e549453f224
>>>>>>> Closes: https://syzkaller.appspot.com/bug?id=908bd910da5dd79b88de4cf7baf376cc873a922e
>>>>>>> Signed-off-by: Ryosuke Yasuoka <ryasuoka@redhat.com>
>>>>>>> ---
>>>>>>> drivers/gpu/drm/virtio/virtgpu_drv.h | 3 +++
>>>>>>> drivers/gpu/drm/virtio/virtgpu_kms.c | 3 +++
>>>>>>> drivers/gpu/drm/virtio/virtgpu_vq.c | 12 ++++++++++--
>>>>>>> 3 files changed, 16 insertions(+), 2 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>>> index 7449907754a4..27ffa4697ae9 100644
>>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>>> @@ -264,6 +264,8 @@ struct virtio_gpu_device {
>>>>>>>
>>>>>>> struct work_struct config_changed_work;
>>>>>>>
>>>>>>> + struct work_struct hotplug_work;
>>>>>>> +
>>>>>>> struct work_struct obj_free_work;
>>>>>>> spinlock_t obj_free_lock;
>>>>>>> struct list_head obj_free_list;
>>>>>>> @@ -350,6 +352,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
>>>>>>> uint32_t x, uint32_t y,
>>>>>>> struct virtio_gpu_object_array *objs,
>>>>>>> struct virtio_gpu_fence *fence);
>>>>>>> +void virtio_gpu_hotplug_work_func(struct work_struct *work);
>>>>>>> void virtio_gpu_panic_cmd_resource_flush(struct virtio_gpu_device *vgdev,
>>>>>>> uint32_t resource_id,
>>>>>>> uint32_t x, uint32_t y,
>>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>>> index cfde9f573df6..cfb532ba43a4 100644
>>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>>> @@ -154,6 +154,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
>>>>>>> INIT_WORK(&vgdev->config_changed_work,
>>>>>>> virtio_gpu_config_changed_work_func);
>>>>>>>
>>>>>>> + INIT_WORK(&vgdev->hotplug_work, virtio_gpu_hotplug_work_func);
>>>>>>> +
>>>>>>> INIT_WORK(&vgdev->obj_free_work,
>>>>>>> virtio_gpu_array_put_free_work);
>>>>>>> INIT_LIST_HEAD(&vgdev->obj_free_list);
>>>>>>> @@ -293,6 +295,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
>>>>>>> flush_work(&vgdev->obj_free_work);
>>>>>>> flush_work(&vgdev->ctrlq.dequeue_work);
>>>>>>> flush_work(&vgdev->cursorq.dequeue_work);
>>>>>>> + flush_work(&vgdev->hotplug_work);
>>>>>>> flush_work(&vgdev->config_changed_work);
>>>>>>> virtio_reset_device(vgdev->vdev);
>>>>>>> vgdev->vdev->config->del_vqs(vgdev->vdev);
>>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>>> index 67865810a2e7..084d98f5dc7b 100644
>>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>>> @@ -816,6 +816,15 @@ virtio_gpu_cmd_resource_detach_backing(struct virtio_gpu_device *vgdev,
>>>>>>> virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence);
>>>>>>> }
>>>>>>>
>>>>>>> +void virtio_gpu_hotplug_work_func(struct work_struct *work)
>>>>>>> +{
>>>>>>> + struct virtio_gpu_device *vgdev =
>>>>>>> + container_of(work, struct virtio_gpu_device, hotplug_work);
>>>>>>> +
>>>>>>> + if (!drm_helper_hpd_irq_event(vgdev->ddev))
>>>>>>> + drm_kms_helper_hotplug_event(vgdev->ddev);
>>>>>>> +}
>>>>>>> +
>>>>>>> static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>>>>>>> struct virtio_gpu_vbuffer *vbuf)
>>>>>>> {
>>>>>>> @@ -841,8 +850,7 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>>>>>>> spin_unlock(&vgdev->display_info_lock);
>>>>>>> wake_up(&vgdev->resp_wq);
>>>>>>>
>>>>>>> - if (!drm_helper_hpd_irq_event(vgdev->ddev))
>>>>>>> - drm_kms_helper_hotplug_event(vgdev->ddev);
>>>>>>> + schedule_work(&vgdev->hotplug_work);
>>>>>>> }
>>>>>>>
>>>>>>> static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
>>>>>>
>>>>>
>>>>> Hi,
>>>>> Thank you for your review.
>>>>>
>>>>>> Could you please move drm_kms_helper_hotplug_event() to virtio_gpu_init(), placing it after wait_event_timeout(display_info_pending)? This will avoid additional work_struct that otherwise needs to be cancelled in virtio_gpu_init() on the timeout.
>>>>>
>>>>> IIUC, moving the drm_kms_helper_hotplug_event() and _hpd_irq_event()
>>>>> into virtio_gpu_init() after wait_event_timeout() would not prevent the
>>>>> issue.
>>>>>
>>>>> Looking at the syzbot call traces[1][2], the deadlock occurs during
>>>>> drm_client_setup(), which runs after virtio_gpu_init() has already
>>>>> returned. The display_info_cb that triggers the deadlock is called from
>>>>> the dequeue worker while drm_client_register() holds clientlist_mutex.
>>>>>
>>>>> Thread A:
>>>>> virtio_gpu_probe()
>>>>> -> virtio_gpu_init() // sends GET_DISPLAY_INFO and waits up to 5s
>>>>
>>>> You mean that the timeout happens and it's again syzkaller report for a
>>>> broken host. A day ago I applied [1] that should fix this "bogus"
>>>> syzkaller report.
>>>>
>>>> [1] https://patchwork.freedesktop.org/patch/735301/
>>>
>>> Thank you for [1] and the clarificaiton. I agree that [1] addresses the
>>> broken host scenario where virtio_gpu_init() times out and probe
>>> continues with a pending display_info response. I think there is another
>>> scenario causing the same deadlock reported by syzbot and my patch
>>> addresses both cases.
>>>
>>> The deadlock can also occur with the following scenario:
>>> after virtio_gpu_init() completes successfully (wait_event_timeout
>>> returns), if a config_changed event arrives before drm_client_setup()
>>> completes, config_changed_work_func() sends a new GET_DISPLAY_INFO. Its
>>> display_info_cb fires in the dequeue worker and blocks on
>>> clientlist_mutex held by drm_client_register(). This is the same
>>> deadlock path reported by syzbot.
>>>
>>> Since [1] only prevents the timeout case, I believe the schedule_work()
>>> approach is still needed to prevent display_info_cb from blocking on
>>> clientlist_mutex in the dequeue worker context, regardless of the
>>> trigger.
>>
>> The config_changed_work_func() already invokes
>> drm_helper_hpd_irq_event() by itself [1].
>>
>> [1]
>> https://elixir.bootlin.com/linux/v7.1.2/source/drivers/gpu/drm/virtio/virtgpu_kms.c#L52
>>
>> The drm_helper_hpd_irq_event() itself calls
>> drm_kms_helper_hotplug_event() [2].
>>
>> [2]
>> https://elixir.bootlin.com/linux/v7.1.2/source/drivers/gpu/drm/drm_probe_helper.c#L1113
>>
>> Isn't drm_helper_hpd_irq_event() call done by
>> virtio_gpu_cmd_get_display_info_cb() already partially redundant? I.e.
>> only it's needed by virtio_gpu_init() and not by
>> config_changed_work_func(). And then we can move
>> drm_helper_hpd_irq_event() to virtio_gpu_init():
>>
>> ```
>> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c
>> b/drivers/gpu/drm/virtio/virtgpu_kms.c
>> index 922782bdc9cd..9805283683c1 100644
>> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
>> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
>> @@ -303,6 +303,7 @@ int virtio_gpu_init(struct virtio_device *vdev,
>> struct drm_device *dev)
>> virtio_gpu_notify(vgdev);
>> wait_event_timeout(vgdev->resp_wq,
>> !vgdev->display_info_pending,
>> 5 * HZ);
>> + drm_helper_hpd_irq_event(vgdev->ddev);
>> }
>>
>> vgdev->pm_nb.notifier_call = virtio_gpu_pm_notifier;
>> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c
>> b/drivers/gpu/drm/virtio/virtgpu_vq.c
>> index 68d097ad9d1d..aa6440a9b81e 100644
>> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
>> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
>> @@ -880,9 +880,6 @@ static void
>> virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>> vgdev->display_info_pending = false;
>> spin_unlock(&vgdev->display_info_lock);
>> wake_up(&vgdev->resp_wq);
>> -
>> - if (!drm_helper_hpd_irq_event(vgdev->ddev))
>> - drm_kms_helper_hotplug_event(vgdev->ddev);
>> }
>>
>> static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device
>> *vgdev,
>> ```
>>
>> Isn't it a bug that config_changed_work_func() doesn't wait for
>> display_info_pending event?
>>
>>
>> ```
>> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c
>> b/drivers/gpu/drm/virtio/virtgpu_kms.c
>> index 922782bdc9cd..dd2d5c896c39 100644
>> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
>> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
>> @@ -50,6 +50,8 @@ static void virtio_gpu_config_changed_work_func(struct
>> work_struct *work)
>> virtio_gpu_cmd_get_edids(vgdev);
>> virtio_gpu_cmd_get_display_info(vgdev);
>> virtio_gpu_notify(vgdev);
>> + wait_event_timeout(vgdev->resp_wq,
>> !vgdev->display_info_pending,
>> + 5 * HZ);
>> drm_helper_hpd_irq_event(vgdev->ddev);
>> }
>> events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
>>
>> ```
>
> Thank you for your suggestion. I agree that removing the hotplug calls
> from display_info_cb and waiting for the response in
> config_changed_work_func() and virtio_gpu_init() is a cleaner approach.
> I'll send a v2 based on this.
Thanks, please also check whether drm_helper_hpd_irq_event() needed at
all in in virtio_gpu_init(). Suppose the hotplug event does nothing
before DRM device/client has been registered.
--
Best regards,
Dmitry
^ permalink raw reply
* [PATCH v2] virtio-pmem: allocate flush bio from a driver-private bio_set
From: Joseph Qi @ 2026-07-09 12:44 UTC (permalink / raw)
To: Pankaj Gupta; +Cc: Christoph Hellwig, Baokun Li, virtualization, linux-kernel
async_pmem_flush() allocates a child bio for the flush with GFP_ATOMIC.
This runs from pmem_submit_bio(), a ->submit_bio callback that executes
in a sleepable context, so there is no atomicity requirement here.
bio_alloc() only guarantees success when __GFP_DIRECT_RECLAIM is set,
because that is what lets it fall back to the mempool reserve. With
GFP_ATOMIC the reclaim bit is absent, so the allocation can fail and
return -ENOMEM whenever the fast paths (percpu cache and slab) are
exhausted, which is common right after boot. A flush is issued from
filesystem writeback and must not fail on a transient allocation
shortage, otherwise the device can appear unmountable:
Buffer I/O error on dev pmem0, logical block 0, lost sync page write
Switch to GFP_NOIO so __GFP_DIRECT_RECLAIM is set and the allocation can
make forward progress. However, bio_alloc() draws from the shared
fs_bio_set, and the incoming bio being flushed may itself have come from
fs_bio_set; allocating a second bio from the same set while submitting
underneath ->submit_bio can deadlock the mempool. Add a driver-private
bio_set for the flush and allocate from it via bio_alloc_bioset(), so
the flush bio has an independent reserve.
With a dedicated mempool-backed bio_set and GFP_NOIO the allocation
cannot fail, so drop the now-redundant NULL check.
Fixes: 6e84200c0a29 ("virtio-pmem: Add virtio pmem driver")
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
---
drivers/nvdimm/nd_virtio.c | 11 ++++++-----
drivers/nvdimm/virtio_pmem.c | 11 ++++++++++-
drivers/nvdimm/virtio_pmem.h | 4 ++++
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c
index 4176046627beb..b4bd21edf5c1c 100644
--- a/drivers/nvdimm/nd_virtio.c
+++ b/drivers/nvdimm/nd_virtio.c
@@ -110,17 +110,18 @@ static int virtio_pmem_flush(struct nd_region *nd_region)
/* The asynchronous flush callback function */
int async_pmem_flush(struct nd_region *nd_region, struct bio *bio)
{
+ struct virtio_device *vdev = nd_region->provider_data;
+ struct virtio_pmem *vpmem = vdev->priv;
+
/*
* Create child bio for asynchronous flush and chain with
* parent bio. Otherwise directly call nd_region flush.
*/
if (bio && bio->bi_iter.bi_sector != -1) {
- struct bio *child = bio_alloc(bio->bi_bdev, 0,
- REQ_OP_WRITE | REQ_PREFLUSH,
- GFP_ATOMIC);
+ struct bio *child = bio_alloc_bioset(bio->bi_bdev, 0,
+ REQ_OP_WRITE | REQ_PREFLUSH, GFP_NOIO,
+ &vpmem->flush_bio_set);
- if (!child)
- return -ENOMEM;
bio_clone_blkg_association(child, bio);
child->bi_iter.bi_sector = -1;
bio_chain(child, bio);
diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c
index 77b1966619059..136179506b478 100644
--- a/drivers/nvdimm/virtio_pmem.c
+++ b/drivers/nvdimm/virtio_pmem.c
@@ -65,12 +65,17 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
}
mutex_init(&vpmem->flush_lock);
+ err = bioset_init(&vpmem->flush_bio_set, BIO_POOL_SIZE, 0, 0);
+ if (err) {
+ dev_err(&vdev->dev, "failed to initialize flush bio_set\n");
+ goto out_err;
+ }
vpmem->vdev = vdev;
vdev->priv = vpmem;
err = init_vq(vpmem);
if (err) {
dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n");
- goto out_err;
+ goto out_bioset;
}
if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION)) {
@@ -131,6 +136,8 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
nvdimm_bus_unregister(vpmem->nvdimm_bus);
out_vq:
vdev->config->del_vqs(vdev);
+out_bioset:
+ bioset_exit(&vpmem->flush_bio_set);
out_err:
return err;
}
@@ -138,10 +145,12 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
static void virtio_pmem_remove(struct virtio_device *vdev)
{
struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev);
+ struct virtio_pmem *vpmem = vdev->priv;
nvdimm_bus_unregister(nvdimm_bus);
vdev->config->del_vqs(vdev);
virtio_reset_device(vdev);
+ bioset_exit(&vpmem->flush_bio_set);
}
static int virtio_pmem_freeze(struct virtio_device *vdev)
diff --git a/drivers/nvdimm/virtio_pmem.h b/drivers/nvdimm/virtio_pmem.h
index f72cf17f9518f..4ff2076f75047 100644
--- a/drivers/nvdimm/virtio_pmem.h
+++ b/drivers/nvdimm/virtio_pmem.h
@@ -15,6 +15,7 @@
#include <linux/libnvdimm.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
+#include <linux/bio.h>
struct virtio_pmem_request {
struct virtio_pmem_req req;
@@ -39,6 +40,9 @@ struct virtio_pmem {
/* Serialize flush requests to the device. */
struct mutex flush_lock;
+ /* bio_set for allocating flush child bios */
+ struct bio_set flush_bio_set;
+
/* nvdimm bus registers virtio pmem device */
struct nvdimm_bus *nvdimm_bus;
struct nvdimm_bus_descriptor nd_desc;
--
2.39.3
^ permalink raw reply related
* Re: [PATCH] drm/virtio: defer hotplug event from dequeue worker to avoid deadlock
From: Ryosuke Yasuoka @ 2026-07-09 12:30 UTC (permalink / raw)
To: Dmitry Osipenko, David Airlie, Gerd Hoffmann, Gurchetan Singh,
Chia-I Wu, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Simona Vetter, Dmitry Baryshkov, Javier Martinez Canillas
Cc: dri-devel, virtualization, linux-kernel
In-Reply-To: <8b02c456-fb23-4c17-ad20-a2bab2d588b1@collabora.com>
On 07/07/2026 18:10, Dmitry Osipenko wrote:
> On 7/3/26 08:58, Ryosuke Yasuoka wrote:
>>
>>
>> On 02/07/2026 14:26, Dmitry Osipenko wrote:
>>> On 7/1/26 12:23, Ryosuke Yasuoka wrote:
>>>>
>>>>
>>>> On 30/06/2026 16:46, Dmitry Osipenko wrote:
>>>>> Hi,
>>>>>
>>>>> On 6/30/26 12:16, Ryosuke Yasuoka wrote:
>>>>>> A probe-time deadlock can occur between the dequeue worker and
>>>>>> drm_client_register(). During probe, drm_client_register() holds
>>>>>> clientlist_mutex and calls the fbdev hotplug callback, which triggers an
>>>>>> atomic commit that ends up sleeping in virtio_gpu_queue_ctrl_sgs()
>>>>>> waiting for virtqueue space. The dequeue worker that would free that
>>>>>> space calls virtio_gpu_cmd_get_display_info_cb(), which invokes
>>>>>> drm_kms_helper_hotplug_event() -> drm_client_dev_hotplug(), attempting
>>>>>> to acquire the same clientlist_mutex. Since wake_up() is only called
>>>>>> after the resp_cb loop, the probe thread is never woken and both threads
>>>>>> deadlock.
>>>>>>
>>>>>> Fix this by deferring the hotplug notification from
>>>>>> virtio_gpu_cmd_get_display_info_cb() to a separate work item. The
>>>>>> display data (outputs[i].info) is still updated synchronously in the
>>>>>> callback, and the deferred work only triggers a re-probe notification to
>>>>>> DRM clients.
>>>>>>
>>>>>> Fixes: 27655b9bb9f0 ("drm/client: Send hotplug event after registering a client")
>>>>>> Closes: https://syzkaller.appspot.com/bug?id=d6dd6f86d3aaf7eebe7406e45c1c6e549453f224
>>>>>> Closes: https://syzkaller.appspot.com/bug?id=908bd910da5dd79b88de4cf7baf376cc873a922e
>>>>>> Signed-off-by: Ryosuke Yasuoka <ryasuoka@redhat.com>
>>>>>> ---
>>>>>> drivers/gpu/drm/virtio/virtgpu_drv.h | 3 +++
>>>>>> drivers/gpu/drm/virtio/virtgpu_kms.c | 3 +++
>>>>>> drivers/gpu/drm/virtio/virtgpu_vq.c | 12 ++++++++++--
>>>>>> 3 files changed, 16 insertions(+), 2 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>> index 7449907754a4..27ffa4697ae9 100644
>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
>>>>>> @@ -264,6 +264,8 @@ struct virtio_gpu_device {
>>>>>>
>>>>>> struct work_struct config_changed_work;
>>>>>>
>>>>>> + struct work_struct hotplug_work;
>>>>>> +
>>>>>> struct work_struct obj_free_work;
>>>>>> spinlock_t obj_free_lock;
>>>>>> struct list_head obj_free_list;
>>>>>> @@ -350,6 +352,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
>>>>>> uint32_t x, uint32_t y,
>>>>>> struct virtio_gpu_object_array *objs,
>>>>>> struct virtio_gpu_fence *fence);
>>>>>> +void virtio_gpu_hotplug_work_func(struct work_struct *work);
>>>>>> void virtio_gpu_panic_cmd_resource_flush(struct virtio_gpu_device *vgdev,
>>>>>> uint32_t resource_id,
>>>>>> uint32_t x, uint32_t y,
>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>> index cfde9f573df6..cfb532ba43a4 100644
>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
>>>>>> @@ -154,6 +154,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
>>>>>> INIT_WORK(&vgdev->config_changed_work,
>>>>>> virtio_gpu_config_changed_work_func);
>>>>>>
>>>>>> + INIT_WORK(&vgdev->hotplug_work, virtio_gpu_hotplug_work_func);
>>>>>> +
>>>>>> INIT_WORK(&vgdev->obj_free_work,
>>>>>> virtio_gpu_array_put_free_work);
>>>>>> INIT_LIST_HEAD(&vgdev->obj_free_list);
>>>>>> @@ -293,6 +295,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
>>>>>> flush_work(&vgdev->obj_free_work);
>>>>>> flush_work(&vgdev->ctrlq.dequeue_work);
>>>>>> flush_work(&vgdev->cursorq.dequeue_work);
>>>>>> + flush_work(&vgdev->hotplug_work);
>>>>>> flush_work(&vgdev->config_changed_work);
>>>>>> virtio_reset_device(vgdev->vdev);
>>>>>> vgdev->vdev->config->del_vqs(vgdev->vdev);
>>>>>> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>> index 67865810a2e7..084d98f5dc7b 100644
>>>>>> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
>>>>>> @@ -816,6 +816,15 @@ virtio_gpu_cmd_resource_detach_backing(struct virtio_gpu_device *vgdev,
>>>>>> virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence);
>>>>>> }
>>>>>>
>>>>>> +void virtio_gpu_hotplug_work_func(struct work_struct *work)
>>>>>> +{
>>>>>> + struct virtio_gpu_device *vgdev =
>>>>>> + container_of(work, struct virtio_gpu_device, hotplug_work);
>>>>>> +
>>>>>> + if (!drm_helper_hpd_irq_event(vgdev->ddev))
>>>>>> + drm_kms_helper_hotplug_event(vgdev->ddev);
>>>>>> +}
>>>>>> +
>>>>>> static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>>>>>> struct virtio_gpu_vbuffer *vbuf)
>>>>>> {
>>>>>> @@ -841,8 +850,7 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
>>>>>> spin_unlock(&vgdev->display_info_lock);
>>>>>> wake_up(&vgdev->resp_wq);
>>>>>>
>>>>>> - if (!drm_helper_hpd_irq_event(vgdev->ddev))
>>>>>> - drm_kms_helper_hotplug_event(vgdev->ddev);
>>>>>> + schedule_work(&vgdev->hotplug_work);
>>>>>> }
>>>>>>
>>>>>> static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
>>>>>
>>>>
>>>> Hi,
>>>> Thank you for your review.
>>>>
>>>>> Could you please move drm_kms_helper_hotplug_event() to virtio_gpu_init(), placing it after wait_event_timeout(display_info_pending)? This will avoid additional work_struct that otherwise needs to be cancelled in virtio_gpu_init() on the timeout.
>>>>
>>>> IIUC, moving the drm_kms_helper_hotplug_event() and _hpd_irq_event()
>>>> into virtio_gpu_init() after wait_event_timeout() would not prevent the
>>>> issue.
>>>>
>>>> Looking at the syzbot call traces[1][2], the deadlock occurs during
>>>> drm_client_setup(), which runs after virtio_gpu_init() has already
>>>> returned. The display_info_cb that triggers the deadlock is called from
>>>> the dequeue worker while drm_client_register() holds clientlist_mutex.
>>>>
>>>> Thread A:
>>>> virtio_gpu_probe()
>>>> -> virtio_gpu_init() // sends GET_DISPLAY_INFO and waits up to 5s
>>>
>>> You mean that the timeout happens and it's again syzkaller report for a
>>> broken host. A day ago I applied [1] that should fix this "bogus"
>>> syzkaller report.
>>>
>>> [1] https://patchwork.freedesktop.org/patch/735301/
>>
>> Thank you for [1] and the clarificaiton. I agree that [1] addresses the
>> broken host scenario where virtio_gpu_init() times out and probe
>> continues with a pending display_info response. I think there is another
>> scenario causing the same deadlock reported by syzbot and my patch
>> addresses both cases.
>>
>> The deadlock can also occur with the following scenario:
>> after virtio_gpu_init() completes successfully (wait_event_timeout
>> returns), if a config_changed event arrives before drm_client_setup()
>> completes, config_changed_work_func() sends a new GET_DISPLAY_INFO. Its
>> display_info_cb fires in the dequeue worker and blocks on
>> clientlist_mutex held by drm_client_register(). This is the same
>> deadlock path reported by syzbot.
>>
>> Since [1] only prevents the timeout case, I believe the schedule_work()
>> approach is still needed to prevent display_info_cb from blocking on
>> clientlist_mutex in the dequeue worker context, regardless of the
>> trigger.
>
> The config_changed_work_func() already invokes
> drm_helper_hpd_irq_event() by itself [1].
>
> [1]
> https://elixir.bootlin.com/linux/v7.1.2/source/drivers/gpu/drm/virtio/virtgpu_kms.c#L52
>
> The drm_helper_hpd_irq_event() itself calls
> drm_kms_helper_hotplug_event() [2].
>
> [2]
> https://elixir.bootlin.com/linux/v7.1.2/source/drivers/gpu/drm/drm_probe_helper.c#L1113
>
> Isn't drm_helper_hpd_irq_event() call done by
> virtio_gpu_cmd_get_display_info_cb() already partially redundant? I.e.
> only it's needed by virtio_gpu_init() and not by
> config_changed_work_func(). And then we can move
> drm_helper_hpd_irq_event() to virtio_gpu_init():
>
> ```
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c
> b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 922782bdc9cd..9805283683c1 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -303,6 +303,7 @@ int virtio_gpu_init(struct virtio_device *vdev,
> struct drm_device *dev)
> virtio_gpu_notify(vgdev);
> wait_event_timeout(vgdev->resp_wq,
> !vgdev->display_info_pending,
> 5 * HZ);
> + drm_helper_hpd_irq_event(vgdev->ddev);
> }
>
> vgdev->pm_nb.notifier_call = virtio_gpu_pm_notifier;
> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c
> b/drivers/gpu/drm/virtio/virtgpu_vq.c
> index 68d097ad9d1d..aa6440a9b81e 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_vq.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
> @@ -880,9 +880,6 @@ static void
> virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
> vgdev->display_info_pending = false;
> spin_unlock(&vgdev->display_info_lock);
> wake_up(&vgdev->resp_wq);
> -
> - if (!drm_helper_hpd_irq_event(vgdev->ddev))
> - drm_kms_helper_hotplug_event(vgdev->ddev);
> }
>
> static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device
> *vgdev,
> ```
>
> Isn't it a bug that config_changed_work_func() doesn't wait for
> display_info_pending event?
>
>
> ```
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c
> b/drivers/gpu/drm/virtio/virtgpu_kms.c
> index 922782bdc9cd..dd2d5c896c39 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_kms.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -50,6 +50,8 @@ static void virtio_gpu_config_changed_work_func(struct
> work_struct *work)
> virtio_gpu_cmd_get_edids(vgdev);
> virtio_gpu_cmd_get_display_info(vgdev);
> virtio_gpu_notify(vgdev);
> + wait_event_timeout(vgdev->resp_wq,
> !vgdev->display_info_pending,
> + 5 * HZ);
> drm_helper_hpd_irq_event(vgdev->ddev);
> }
> events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
>
> ```
Thank you for your suggestion. I agree that removing the hotplug calls
from display_info_cb and waiting for the response in
config_changed_work_func() and virtio_gpu_init() is a cleaner approach.
I'll send a v2 based on this.
Best regards,
Ryosuke
^ permalink raw reply
* [PATCH 36/60] drm/virtio: Convert to atomic_create_state
From: Maxime Ripard @ 2026-07-09 11:51 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter
Cc: dri-devel, Maxime Ripard, airlied, dmitry.osipenko,
gurchetansingh, kraxel, olvaffe, virtualization
In-Reply-To: <20260709-drm-no-more-plane-reset-v1-0-302d986fe5f0@kernel.org>
The plane only initializes a pristine state in its reset hook
using drm_atomic_helper_plane_reset(), which is equivalent to what
atomic_create_state expects. Convert to it.
The conversion was done using the following Coccinelle semantic patch:
@@
identifier funcs;
symbol drm_atomic_helper_plane_reset;
symbol drm_atomic_helper_plane_create_state;
@@
struct drm_plane_funcs funcs = {
...,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
...,
};
@match_struct_reset@
identifier funcs, reset_func;
@@
struct drm_plane_funcs funcs = {
...,
.reset = reset_func,
...,
};
@reset_uses_helpers depends on match_struct_reset@
identifier match_struct_reset.reset_func;
@@
void reset_func(...)
{
<+...
(
__drm_atomic_helper_plane_reset(...);
|
__drm_gem_reset_shadow_plane(...);
)
...+>
}
@match_struct_destroy@
identifier funcs, destroy_func;
@@
struct drm_plane_funcs funcs = {
...,
.atomic_destroy_state = destroy_func,
...,
};
@script:python renamed_func@
old_name << match_struct_reset.reset_func;
new_name;
@@
if old_name.endswith("_reset"):
coccinelle.new_name = old_name.replace("_reset", "_create_state")
else:
coccinelle.new_name = old_name
@update_struct depends on match_struct_reset && reset_uses_helpers@
identifier match_struct_reset.funcs, match_struct_reset.reset_func;
identifier renamed_func.new_name;
@@
struct drm_plane_funcs funcs = {
...,
- .reset = reset_func,
+ .atomic_create_state = new_name,
...,
};
@drop_destroy depends on update_struct && match_struct_destroy@
identifier match_struct_reset.reset_func;
identifier match_struct_destroy.destroy_func;
identifier container_func;
identifier P;
symbol drm_atomic_helper_plane_destroy_state;
symbol __drm_atomic_helper_plane_destroy_state;
@@
void reset_func(struct drm_plane *P)
{
...
(
- if (P->state) {
- <+...
(
- drm_atomic_helper_plane_destroy_state(P, P->state);
|
- __drm_atomic_helper_plane_destroy_state(P->state);
|
- P->funcs->atomic_destroy_state(P, P->state);
|
- destroy_func(P, P->state);
)
- ...+>
- }
|
- drm_WARN_ON_ONCE(P->dev, P->state);
|
- WARN_ON(P->state);
)
...
(
- kfree(P->state);
|
- kfree(container_func(P->state));
|
// kfree is optional
)
(
- P->state = NULL;
|
// plane->state clearing is optional
)
...
}
@drop_destroy_mtk depends on update_struct@
identifier P;
symbol __drm_atomic_helper_plane_destroy_state;
symbol to_mtk_plane_state;
@@
void mtk_plane_reset(struct drm_plane *P)
{
...
- if (P->state) {
- __drm_atomic_helper_plane_destroy_state(P->state);
- ...
- } else {
...
- }
...
}
@transform_nv50_wndw depends on update_struct@
identifier S;
@@
void nv50_wndw_reset(...)
{
...
- if (WARN_ON(!(S = kzalloc_obj(*S))))
+ S = kzalloc_obj(*S);
+ if (WARN_ON(!S))
return;
...
}
@transform_kzalloc depends on update_struct@
identifier match_struct_reset.reset_func;
identifier P, S;
statement ST;
statement list STL;
@@
void reset_func(struct drm_plane *P)
{
<...
S = kzalloc_obj(*S);
(
- if (S)
- {
- STL
- }
+ if (!S) return;
+
+ STL
|
- if (S) ST
+ if (!S) return;
+
+ ST
)
...>
}
@transform_body depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier S, P;
expression PS;
@@
- void reset_func(struct drm_plane *P)
+ struct drm_plane_state *new_name(struct drm_plane *P)
{
...
S = kzalloc_obj(*S);
...
(
if (!S) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
|
if (WARN_ON(!S)) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
|
if (S == NULL) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
)
...
(
- __drm_atomic_helper_plane_reset(P, PS);
+ __drm_atomic_helper_plane_state_init(PS, P);
|
- __drm_gem_reset_shadow_plane(P, PS);
+ __drm_gem_shadow_plane_state_init(P, PS);
)
...
}
@update_early_return depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
<+...
- return;
+ return ERR_PTR(-EINVAL);
...+>
}
@update_return_plane depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
...
__drm_atomic_helper_plane_state_init(PS, P);
...
+
+ return PS;
}
@update_return_shadow depends on update_struct@
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
...
__drm_gem_shadow_plane_state_init(P, PS);
...
+
+ return &PS->base;
}
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
Cc: airlied@redhat.com
Cc: dmitry.osipenko@collabora.com
Cc: gurchetansingh@chromium.org
Cc: kraxel@redhat.com
Cc: olvaffe@gmail.com
Cc: virtualization@lists.linux.dev
---
drivers/gpu/drm/virtio/virtgpu_plane.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 1d1b27ece62a..52971864db50 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -89,11 +89,11 @@ drm_plane_state *virtio_gpu_plane_duplicate_state(struct drm_plane *plane)
}
static const struct drm_plane_funcs virtio_gpu_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = virtio_gpu_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static int virtio_gpu_plane_atomic_check(struct drm_plane *plane,
--
2.54.0
^ permalink raw reply related
* [PATCH 27/60] drm/qxl: Convert to atomic_create_state
From: Maxime Ripard @ 2026-07-09 11:50 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter
Cc: dri-devel, Maxime Ripard, airlied, kraxel, spice-devel,
virtualization
In-Reply-To: <20260709-drm-no-more-plane-reset-v1-0-302d986fe5f0@kernel.org>
The plane only initializes a pristine state in its reset hook
using drm_atomic_helper_plane_reset(), which is equivalent to what
atomic_create_state expects. Convert to it.
The conversion was done using the following Coccinelle semantic patch:
@@
identifier funcs;
symbol drm_atomic_helper_plane_reset;
symbol drm_atomic_helper_plane_create_state;
@@
struct drm_plane_funcs funcs = {
...,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
...,
};
@match_struct_reset@
identifier funcs, reset_func;
@@
struct drm_plane_funcs funcs = {
...,
.reset = reset_func,
...,
};
@reset_uses_helpers depends on match_struct_reset@
identifier match_struct_reset.reset_func;
@@
void reset_func(...)
{
<+...
(
__drm_atomic_helper_plane_reset(...);
|
__drm_gem_reset_shadow_plane(...);
)
...+>
}
@match_struct_destroy@
identifier funcs, destroy_func;
@@
struct drm_plane_funcs funcs = {
...,
.atomic_destroy_state = destroy_func,
...,
};
@script:python renamed_func@
old_name << match_struct_reset.reset_func;
new_name;
@@
if old_name.endswith("_reset"):
coccinelle.new_name = old_name.replace("_reset", "_create_state")
else:
coccinelle.new_name = old_name
@update_struct depends on match_struct_reset && reset_uses_helpers@
identifier match_struct_reset.funcs, match_struct_reset.reset_func;
identifier renamed_func.new_name;
@@
struct drm_plane_funcs funcs = {
...,
- .reset = reset_func,
+ .atomic_create_state = new_name,
...,
};
@drop_destroy depends on update_struct && match_struct_destroy@
identifier match_struct_reset.reset_func;
identifier match_struct_destroy.destroy_func;
identifier container_func;
identifier P;
symbol drm_atomic_helper_plane_destroy_state;
symbol __drm_atomic_helper_plane_destroy_state;
@@
void reset_func(struct drm_plane *P)
{
...
(
- if (P->state) {
- <+...
(
- drm_atomic_helper_plane_destroy_state(P, P->state);
|
- __drm_atomic_helper_plane_destroy_state(P->state);
|
- P->funcs->atomic_destroy_state(P, P->state);
|
- destroy_func(P, P->state);
)
- ...+>
- }
|
- drm_WARN_ON_ONCE(P->dev, P->state);
|
- WARN_ON(P->state);
)
...
(
- kfree(P->state);
|
- kfree(container_func(P->state));
|
// kfree is optional
)
(
- P->state = NULL;
|
// plane->state clearing is optional
)
...
}
@drop_destroy_mtk depends on update_struct@
identifier P;
symbol __drm_atomic_helper_plane_destroy_state;
symbol to_mtk_plane_state;
@@
void mtk_plane_reset(struct drm_plane *P)
{
...
- if (P->state) {
- __drm_atomic_helper_plane_destroy_state(P->state);
- ...
- } else {
...
- }
...
}
@transform_nv50_wndw depends on update_struct@
identifier S;
@@
void nv50_wndw_reset(...)
{
...
- if (WARN_ON(!(S = kzalloc_obj(*S))))
+ S = kzalloc_obj(*S);
+ if (WARN_ON(!S))
return;
...
}
@transform_kzalloc depends on update_struct@
identifier match_struct_reset.reset_func;
identifier P, S;
statement ST;
statement list STL;
@@
void reset_func(struct drm_plane *P)
{
<...
S = kzalloc_obj(*S);
(
- if (S)
- {
- STL
- }
+ if (!S) return;
+
+ STL
|
- if (S) ST
+ if (!S) return;
+
+ ST
)
...>
}
@transform_body depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier S, P;
expression PS;
@@
- void reset_func(struct drm_plane *P)
+ struct drm_plane_state *new_name(struct drm_plane *P)
{
...
S = kzalloc_obj(*S);
...
(
if (!S) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
|
if (WARN_ON(!S)) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
|
if (S == NULL) {
...
- return;
+ return ERR_PTR(-ENOMEM);
}
)
...
(
- __drm_atomic_helper_plane_reset(P, PS);
+ __drm_atomic_helper_plane_state_init(PS, P);
|
- __drm_gem_reset_shadow_plane(P, PS);
+ __drm_gem_shadow_plane_state_init(P, PS);
)
...
}
@update_early_return depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
<+...
- return;
+ return ERR_PTR(-EINVAL);
...+>
}
@update_return_plane depends on update_struct@
identifier match_struct_reset.reset_func;
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
...
__drm_atomic_helper_plane_state_init(PS, P);
...
+
+ return PS;
}
@update_return_shadow depends on update_struct@
identifier renamed_func.new_name;
identifier P;
expression PS;
@@
struct drm_plane_state *new_name(struct drm_plane *P)
{
...
__drm_gem_shadow_plane_state_init(P, PS);
...
+
+ return &PS->base;
}
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
Cc: airlied@redhat.com
Cc: kraxel@redhat.com
Cc: spice-devel@lists.freedesktop.org
Cc: virtualization@lists.linux.dev
---
drivers/gpu/drm/qxl/qxl_display.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index a026bd35ef48..d0e47be7febd 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -927,11 +927,11 @@ static const struct drm_plane_helper_funcs qxl_cursor_helper_funcs = {
static const struct drm_plane_funcs qxl_cursor_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_helper_destroy,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const uint32_t qxl_primary_plane_formats[] = {
@@ -949,11 +949,11 @@ static const struct drm_plane_helper_funcs primary_helper_funcs = {
static const struct drm_plane_funcs qxl_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_helper_destroy,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static struct drm_plane *qxl_create_plane(struct qxl_device *qdev,
--
2.54.0
^ 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