[PATCH RFC v3 08/13] qmp: update virtio features map to support extended features

Paolo Abeni posted 13 patches 3 months, 4 weeks ago
Maintainers: Dmitry Fleytman <dmitry.fleytman@gmail.com>, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>, Jason Wang <jasowang@redhat.com>, Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>, "Michael S. Tsirkin" <mst@redhat.com>, Stefano Garzarella <sgarzare@redhat.com>, Cornelia Huck <cohuck@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, Luigi Rizzo <rizzo@iet.unipi.it>, Giuseppe Lettieri <g.lettieri@iet.unipi.it>, Vincenzo Maffione <v.maffione@gmail.com>, Eric Blake <eblake@redhat.com>, Markus Armbruster <armbru@redhat.com>
There is a newer version of this series
[PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Paolo Abeni 3 months, 4 weeks ago
Extend the VirtioDeviceFeatures struct with an additional u64
to track unknown features in the 64-127 bit range and decode
the full virtio features spaces for vhost and virtio devices.

Also add entries for the soon-to-be-supported virtio net GSO over
UDP features.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
v2 -> v3:
  - unknown-dev-features-dword2 -> unknown-dev-features2
  - _array -> _ex
  - fixed typos in entries description

v1 -> v2:
  - uint128_t -> uint64_t[]
---
 hw/virtio/virtio-hmp-cmds.c |  3 +-
 hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
 hw/virtio/virtio-qmp.h      |  3 +-
 qapi/virtio.json            |  8 +++-
 4 files changed, 73 insertions(+), 30 deletions(-)

diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c
index 7d8677bcf0..1daae482d3 100644
--- a/hw/virtio/virtio-hmp-cmds.c
+++ b/hw/virtio/virtio-hmp-cmds.c
@@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
     }
 
     if (features->has_unknown_dev_features) {
-        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
+        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
+                       features->unknown_dev_features2,
                        features->unknown_dev_features);
     }
 }
diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
index 3b6377cf0d..03c6163cf4 100644
--- a/hw/virtio/virtio-qmp.c
+++ b/hw/virtio/virtio-qmp.c
@@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
     FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
             "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
             "negotiation supported"),
+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
+            "UDP tunnel packets"),
+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
+            "UDP tunnel packets requiring checksum offload for the outer "
+            "header"),
+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
+            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
+            "UDP tunnel packets"),
+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
+            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
+            "UDP tunnel packets requiring checksum offload for the outer "
+            "header"),
     { -1, "" }
 };
 #endif
@@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = {
         list;                                            \
     })
 
+#define CONVERT_FEATURES_EX(type, map, bitmap)           \
+    ({                                                   \
+        type *list = NULL;                               \
+        type *node;                                      \
+        for (i = 0; map[i].virtio_bit != -1; i++) {      \
+            bit = map[i].virtio_bit;                     \
+            if (!virtio_has_feature_ex(bitmap, bit)) {   \
+                continue;                                \
+            }                                            \
+            node = g_new0(type, 1);                      \
+            node->value = g_strdup(map[i].feature_desc); \
+            node->next = list;                           \
+            list = node;                                 \
+            virtio_clear_feature_ex(bitmap, bit);        \
+        }                                                \
+        list;                                            \
+    })
+
 VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap)
 {
     VirtioDeviceStatus *status;
@@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap)
     return vhu_protocols;
 }
 
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bmap)
 {
+    uint64_t bitmap[VIRTIO_FEATURES_DWORDS];
     VirtioDeviceFeatures *features;
     uint64_t bit;
     int i;
 
+    virtio_features_copy(bitmap, bmap);
     features = g_new0(VirtioDeviceFeatures, 1);
     features->has_dev_features = true;
 
     /* transport features */
-    features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0,
-                                            bitmap);
+    features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map,
+                                               bitmap);
 
     /* device features */
     switch (device_id) {
 #ifdef CONFIG_VIRTIO_SERIAL
     case VIRTIO_ID_CONSOLE:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_BLK
     case VIRTIO_ID_BLOCK:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_GPU
     case VIRTIO_ID_GPU:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_NET
     case VIRTIO_ID_NET:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_SCSI
     case VIRTIO_ID_SCSI:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_BALLOON
     case VIRTIO_ID_BALLOON:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_IOMMU
     case VIRTIO_ID_IOMMU:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_INPUT
     case VIRTIO_ID_INPUT:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VHOST_USER_FS
     case VIRTIO_ID_FS:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VHOST_VSOCK
     case VIRTIO_ID_VSOCK:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_CRYPTO
     case VIRTIO_ID_CRYPTO:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_MEM
     case VIRTIO_ID_MEM:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_I2C_ADAPTER
     case VIRTIO_ID_I2C_ADAPTER:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VIRTIO_RNG
     case VIRTIO_ID_RNG:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap);
         break;
 #endif
 #ifdef CONFIG_VHOST_USER_GPIO
     case VIRTIO_ID_GPIO:
         features->dev_features =
-            CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap);
+            CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap);
         break;
 #endif
     /* No features */
@@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
         g_assert_not_reached();
     }
 
-    features->has_unknown_dev_features = bitmap != 0;
+    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
     if (features->has_unknown_dev_features) {
-        features->unknown_dev_features = bitmap;
+        features->unknown_dev_features = bitmap[0];
+        features->unknown_dev_features2 = bitmap[1];
     }
 
     return features;
@@ -743,11 +779,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
     status->device_id = vdev->device_id;
     status->vhost_started = vdev->vhost_started;
     status->guest_features = qmp_decode_features(vdev->device_id,
-                                                 vdev->guest_features);
+                                                 vdev->guest_features_ex);
     status->host_features = qmp_decode_features(vdev->device_id,
-                                                vdev->host_features);
+                                                vdev->host_features_ex);
     status->backend_features = qmp_decode_features(vdev->device_id,
-                                                   vdev->backend_features);
+                                                 vdev->backend_features_ex);
 
     switch (vdev->device_endian) {
     case VIRTIO_DEVICE_ENDIAN_LITTLE:
@@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
         status->vhost_dev->nvqs = hdev->nvqs;
         status->vhost_dev->vq_index = hdev->vq_index;
         status->vhost_dev->features =
-            qmp_decode_features(vdev->device_id, hdev->features);
+            qmp_decode_features(vdev->device_id, hdev->features_ex);
         status->vhost_dev->acked_features =
-            qmp_decode_features(vdev->device_id, hdev->acked_features);
+            qmp_decode_features(vdev->device_id, hdev->acked_features_ex);
         status->vhost_dev->backend_features =
-            qmp_decode_features(vdev->device_id, hdev->backend_features);
+            qmp_decode_features(vdev->device_id, hdev->backend_features_ex);
+
         status->vhost_dev->protocol_features =
             qmp_decode_protocols(hdev->protocol_features);
         status->vhost_dev->max_queues = hdev->max_queues;
diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h
index 245a446a56..e0a1e49035 100644
--- a/hw/virtio/virtio-qmp.h
+++ b/hw/virtio/virtio-qmp.h
@@ -18,6 +18,7 @@
 VirtIODevice *qmp_find_virtio_device(const char *path);
 VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
 VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
+                                          const uint64_t *bitmap);
 
 #endif
diff --git a/qapi/virtio.json b/qapi/virtio.json
index 9d652fe4a8..f2e2dd6e97 100644
--- a/qapi/virtio.json
+++ b/qapi/virtio.json
@@ -490,14 +490,18 @@
 #     unique features)
 #
 # @unknown-dev-features: Virtio device features bitmap that have not
-#     been decoded
+#     been decoded (bits 0-63)
+#
+# @unknown-dev-features2: Virtio device features bitmap that have not
+#     been decoded (bits 64-127)
 #
 # Since: 7.2
 ##
 { 'struct': 'VirtioDeviceFeatures',
   'data': { 'transports': [ 'str' ],
             '*dev-features': [ 'str' ],
-            '*unknown-dev-features': 'uint64' } }
+            '*unknown-dev-features': 'uint64',
+            '*unknown-dev-features2': 'uint64' } }
 
 ##
 # @VirtQueueStatus:
-- 
2.50.0
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Akihiko Odaki 3 months, 3 weeks ago
On 2025/07/18 17:52, Paolo Abeni wrote:
> Extend the VirtioDeviceFeatures struct with an additional u64
> to track unknown features in the 64-127 bit range and decode
> the full virtio features spaces for vhost and virtio devices.
> 
> Also add entries for the soon-to-be-supported virtio net GSO over
> UDP features.
> 
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
> v2 -> v3:
>    - unknown-dev-features-dword2 -> unknown-dev-features2
>    - _array -> _ex
>    - fixed typos in entries description
> 
> v1 -> v2:
>    - uint128_t -> uint64_t[]
> ---
>   hw/virtio/virtio-hmp-cmds.c |  3 +-
>   hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
>   hw/virtio/virtio-qmp.h      |  3 +-
>   qapi/virtio.json            |  8 +++-
>   4 files changed, 73 insertions(+), 30 deletions(-)
> 
> diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c
> index 7d8677bcf0..1daae482d3 100644
> --- a/hw/virtio/virtio-hmp-cmds.c
> +++ b/hw/virtio/virtio-hmp-cmds.c
> @@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
>       }
>   
>       if (features->has_unknown_dev_features) {
> -        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
> +        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
> +                       features->unknown_dev_features2,
>                          features->unknown_dev_features);
>       }
>   }
> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
> index 3b6377cf0d..03c6163cf4 100644
> --- a/hw/virtio/virtio-qmp.c
> +++ b/hw/virtio/virtio-qmp.c
> @@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
>       FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
>               "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
>               "negotiation supported"),
> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
> +            "UDP tunnel packets"),
> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
> +            "UDP tunnel packets requiring checksum offload for the outer "
> +            "header"),
> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
> +            "UDP tunnel packets"),
> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
> +            "UDP tunnel packets requiring checksum offload for the outer "
> +            "header"),
>       { -1, "" }
>   };
>   #endif
> @@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = {
>           list;                                            \
>       })
>   
> +#define CONVERT_FEATURES_EX(type, map, bitmap)           \
> +    ({                                                   \
> +        type *list = NULL;                               \
> +        type *node;                                      \
> +        for (i = 0; map[i].virtio_bit != -1; i++) {      \
> +            bit = map[i].virtio_bit;                     \
> +            if (!virtio_has_feature_ex(bitmap, bit)) {   \
> +                continue;                                \
> +            }                                            \
> +            node = g_new0(type, 1);                      \
> +            node->value = g_strdup(map[i].feature_desc); \
> +            node->next = list;                           \
> +            list = node;                                 \
> +            virtio_clear_feature_ex(bitmap, bit);        \
> +        }                                                \
> +        list;                                            \
> +    })
> +
>   VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap)
>   {
>       VirtioDeviceStatus *status;
> @@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap)
>       return vhu_protocols;
>   }
>   
> -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
> +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
> +                                          const uint64_t *bmap)
>   {
> +    uint64_t bitmap[VIRTIO_FEATURES_DWORDS];
>       VirtioDeviceFeatures *features;
>       uint64_t bit;
>       int i;
>   
> +    virtio_features_copy(bitmap, bmap);
>       features = g_new0(VirtioDeviceFeatures, 1);
>       features->has_dev_features = true;
>   
>       /* transport features */
> -    features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0,
> -                                            bitmap);
> +    features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map,
> +                                               bitmap);
>   
>       /* device features */
>       switch (device_id) {
>   #ifdef CONFIG_VIRTIO_SERIAL
>       case VIRTIO_ID_CONSOLE:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_BLK
>       case VIRTIO_ID_BLOCK:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_GPU
>       case VIRTIO_ID_GPU:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_NET
>       case VIRTIO_ID_NET:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_SCSI
>       case VIRTIO_ID_SCSI:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_BALLOON
>       case VIRTIO_ID_BALLOON:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_IOMMU
>       case VIRTIO_ID_IOMMU:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_INPUT
>       case VIRTIO_ID_INPUT:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VHOST_USER_FS
>       case VIRTIO_ID_FS:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VHOST_VSOCK
>       case VIRTIO_ID_VSOCK:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_CRYPTO
>       case VIRTIO_ID_CRYPTO:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_MEM
>       case VIRTIO_ID_MEM:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_I2C_ADAPTER
>       case VIRTIO_ID_I2C_ADAPTER:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VIRTIO_RNG
>       case VIRTIO_ID_RNG:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap);
>           break;
>   #endif
>   #ifdef CONFIG_VHOST_USER_GPIO
>       case VIRTIO_ID_GPIO:
>           features->dev_features =
> -            CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap);
> +            CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap);
>           break;
>   #endif
>       /* No features */
> @@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>           g_assert_not_reached();
>       }
>   
> -    features->has_unknown_dev_features = bitmap != 0;
> +    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
>       if (features->has_unknown_dev_features) {
> -        features->unknown_dev_features = bitmap;
> +        features->unknown_dev_features = bitmap[0];
> +        features->unknown_dev_features2 = bitmap[1];
>       }
>   
>       return features;
> @@ -743,11 +779,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>       status->device_id = vdev->device_id;
>       status->vhost_started = vdev->vhost_started;
>       status->guest_features = qmp_decode_features(vdev->device_id,
> -                                                 vdev->guest_features);
> +                                                 vdev->guest_features_ex);
>       status->host_features = qmp_decode_features(vdev->device_id,
> -                                                vdev->host_features);
> +                                                vdev->host_features_ex);
>       status->backend_features = qmp_decode_features(vdev->device_id,
> -                                                   vdev->backend_features);
> +                                                 vdev->backend_features_ex);
>   
>       switch (vdev->device_endian) {
>       case VIRTIO_DEVICE_ENDIAN_LITTLE:
> @@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>           status->vhost_dev->nvqs = hdev->nvqs;
>           status->vhost_dev->vq_index = hdev->vq_index;
>           status->vhost_dev->features =
> -            qmp_decode_features(vdev->device_id, hdev->features);
> +            qmp_decode_features(vdev->device_id, hdev->features_ex);
>           status->vhost_dev->acked_features =
> -            qmp_decode_features(vdev->device_id, hdev->acked_features);
> +            qmp_decode_features(vdev->device_id, hdev->acked_features_ex);
>           status->vhost_dev->backend_features =
> -            qmp_decode_features(vdev->device_id, hdev->backend_features);
> +            qmp_decode_features(vdev->device_id, hdev->backend_features_ex);
> +
>           status->vhost_dev->protocol_features =
>               qmp_decode_protocols(hdev->protocol_features);
>           status->vhost_dev->max_queues = hdev->max_queues;
> diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h
> index 245a446a56..e0a1e49035 100644
> --- a/hw/virtio/virtio-qmp.h
> +++ b/hw/virtio/virtio-qmp.h
> @@ -18,6 +18,7 @@
>   VirtIODevice *qmp_find_virtio_device(const char *path);
>   VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
>   VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
> -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
> +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
> +                                          const uint64_t *bitmap);
>   
>   #endif
> diff --git a/qapi/virtio.json b/qapi/virtio.json
> index 9d652fe4a8..f2e2dd6e97 100644
> --- a/qapi/virtio.json
> +++ b/qapi/virtio.json
> @@ -490,14 +490,18 @@
>   #     unique features)
>   #
>   # @unknown-dev-features: Virtio device features bitmap that have not
> -#     been decoded
> +#     been decoded (bits 0-63)
> +#
> +# @unknown-dev-features2: Virtio device features bitmap that have not
> +#     been decoded (bits 64-127)

This documentation should contain "(since 10.1)" as described in:
docs/devel/qapi-code-gen.rst

>   #
>   # Since: 7.2
>   ##
>   { 'struct': 'VirtioDeviceFeatures',
>     'data': { 'transports': [ 'str' ],
>               '*dev-features': [ 'str' ],
> -            '*unknown-dev-features': 'uint64' } }
> +            '*unknown-dev-features': 'uint64',
> +            '*unknown-dev-features2': 'uint64' } }
>   
>   ##
>   # @VirtQueueStatus:
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Paolo Abeni 3 months, 3 weeks ago
On 7/21/25 9:23 AM, Akihiko Odaki wrote:
> On 2025/07/18 17:52, Paolo Abeni wrote:
>> Extend the VirtioDeviceFeatures struct with an additional u64
>> to track unknown features in the 64-127 bit range and decode
>> the full virtio features spaces for vhost and virtio devices.
>>
>> Also add entries for the soon-to-be-supported virtio net GSO over
>> UDP features.
>>
>> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>> ---
>> v2 -> v3:
>>    - unknown-dev-features-dword2 -> unknown-dev-features2
>>    - _array -> _ex
>>    - fixed typos in entries description
>>
>> v1 -> v2:
>>    - uint128_t -> uint64_t[]
>> ---
>>   hw/virtio/virtio-hmp-cmds.c |  3 +-
>>   hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
>>   hw/virtio/virtio-qmp.h      |  3 +-
>>   qapi/virtio.json            |  8 +++-
>>   4 files changed, 73 insertions(+), 30 deletions(-)
>>
>> diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c
>> index 7d8677bcf0..1daae482d3 100644
>> --- a/hw/virtio/virtio-hmp-cmds.c
>> +++ b/hw/virtio/virtio-hmp-cmds.c
>> @@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
>>       }
>>   
>>       if (features->has_unknown_dev_features) {
>> -        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
>> +        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
>> +                       features->unknown_dev_features2,
>>                          features->unknown_dev_features);
>>       }
>>   }
>> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
>> index 3b6377cf0d..03c6163cf4 100644
>> --- a/hw/virtio/virtio-qmp.c
>> +++ b/hw/virtio/virtio-qmp.c
>> @@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
>>       FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
>>               "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
>>               "negotiation supported"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>> +            "UDP tunnel packets"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>> +            "UDP tunnel packets requiring checksum offload for the outer "
>> +            "header"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>> +            "UDP tunnel packets"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>> +            "UDP tunnel packets requiring checksum offload for the outer "
>> +            "header"),
>>       { -1, "" }
>>   };
>>   #endif
>> @@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = {
>>           list;                                            \
>>       })
>>   
>> +#define CONVERT_FEATURES_EX(type, map, bitmap)           \
>> +    ({                                                   \
>> +        type *list = NULL;                               \
>> +        type *node;                                      \
>> +        for (i = 0; map[i].virtio_bit != -1; i++) {      \
>> +            bit = map[i].virtio_bit;                     \
>> +            if (!virtio_has_feature_ex(bitmap, bit)) {   \
>> +                continue;                                \
>> +            }                                            \
>> +            node = g_new0(type, 1);                      \
>> +            node->value = g_strdup(map[i].feature_desc); \
>> +            node->next = list;                           \
>> +            list = node;                                 \
>> +            virtio_clear_feature_ex(bitmap, bit);        \
>> +        }                                                \
>> +        list;                                            \
>> +    })
>> +
>>   VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap)
>>   {
>>       VirtioDeviceStatus *status;
>> @@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap)
>>       return vhu_protocols;
>>   }
>>   
>> -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>> +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
>> +                                          const uint64_t *bmap)
>>   {
>> +    uint64_t bitmap[VIRTIO_FEATURES_DWORDS];
>>       VirtioDeviceFeatures *features;
>>       uint64_t bit;
>>       int i;
>>   
>> +    virtio_features_copy(bitmap, bmap);
>>       features = g_new0(VirtioDeviceFeatures, 1);
>>       features->has_dev_features = true;
>>   
>>       /* transport features */
>> -    features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0,
>> -                                            bitmap);
>> +    features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map,
>> +                                               bitmap);
>>   
>>       /* device features */
>>       switch (device_id) {
>>   #ifdef CONFIG_VIRTIO_SERIAL
>>       case VIRTIO_ID_CONSOLE:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_BLK
>>       case VIRTIO_ID_BLOCK:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_GPU
>>       case VIRTIO_ID_GPU:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_NET
>>       case VIRTIO_ID_NET:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_SCSI
>>       case VIRTIO_ID_SCSI:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_BALLOON
>>       case VIRTIO_ID_BALLOON:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_IOMMU
>>       case VIRTIO_ID_IOMMU:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_INPUT
>>       case VIRTIO_ID_INPUT:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VHOST_USER_FS
>>       case VIRTIO_ID_FS:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VHOST_VSOCK
>>       case VIRTIO_ID_VSOCK:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_CRYPTO
>>       case VIRTIO_ID_CRYPTO:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_MEM
>>       case VIRTIO_ID_MEM:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_I2C_ADAPTER
>>       case VIRTIO_ID_I2C_ADAPTER:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VIRTIO_RNG
>>       case VIRTIO_ID_RNG:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap);
>>           break;
>>   #endif
>>   #ifdef CONFIG_VHOST_USER_GPIO
>>       case VIRTIO_ID_GPIO:
>>           features->dev_features =
>> -            CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap);
>> +            CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap);
>>           break;
>>   #endif
>>       /* No features */
>> @@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>>           g_assert_not_reached();
>>       }
>>   
>> -    features->has_unknown_dev_features = bitmap != 0;
>> +    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
>>       if (features->has_unknown_dev_features) {
>> -        features->unknown_dev_features = bitmap;
>> +        features->unknown_dev_features = bitmap[0];
>> +        features->unknown_dev_features2 = bitmap[1];
>>       }
>>   
>>       return features;
>> @@ -743,11 +779,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>>       status->device_id = vdev->device_id;
>>       status->vhost_started = vdev->vhost_started;
>>       status->guest_features = qmp_decode_features(vdev->device_id,
>> -                                                 vdev->guest_features);
>> +                                                 vdev->guest_features_ex);
>>       status->host_features = qmp_decode_features(vdev->device_id,
>> -                                                vdev->host_features);
>> +                                                vdev->host_features_ex);
>>       status->backend_features = qmp_decode_features(vdev->device_id,
>> -                                                   vdev->backend_features);
>> +                                                 vdev->backend_features_ex);
>>   
>>       switch (vdev->device_endian) {
>>       case VIRTIO_DEVICE_ENDIAN_LITTLE:
>> @@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>>           status->vhost_dev->nvqs = hdev->nvqs;
>>           status->vhost_dev->vq_index = hdev->vq_index;
>>           status->vhost_dev->features =
>> -            qmp_decode_features(vdev->device_id, hdev->features);
>> +            qmp_decode_features(vdev->device_id, hdev->features_ex);
>>           status->vhost_dev->acked_features =
>> -            qmp_decode_features(vdev->device_id, hdev->acked_features);
>> +            qmp_decode_features(vdev->device_id, hdev->acked_features_ex);
>>           status->vhost_dev->backend_features =
>> -            qmp_decode_features(vdev->device_id, hdev->backend_features);
>> +            qmp_decode_features(vdev->device_id, hdev->backend_features_ex);
>> +
>>           status->vhost_dev->protocol_features =
>>               qmp_decode_protocols(hdev->protocol_features);
>>           status->vhost_dev->max_queues = hdev->max_queues;
>> diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h
>> index 245a446a56..e0a1e49035 100644
>> --- a/hw/virtio/virtio-qmp.h
>> +++ b/hw/virtio/virtio-qmp.h
>> @@ -18,6 +18,7 @@
>>   VirtIODevice *qmp_find_virtio_device(const char *path);
>>   VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
>>   VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
>> -VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
>> +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
>> +                                          const uint64_t *bitmap);
>>   
>>   #endif
>> diff --git a/qapi/virtio.json b/qapi/virtio.json
>> index 9d652fe4a8..f2e2dd6e97 100644
>> --- a/qapi/virtio.json
>> +++ b/qapi/virtio.json
>> @@ -490,14 +490,18 @@
>>   #     unique features)
>>   #
>>   # @unknown-dev-features: Virtio device features bitmap that have not
>> -#     been decoded
>> +#     been decoded (bits 0-63)
>> +#
>> +# @unknown-dev-features2: Virtio device features bitmap that have not
>> +#     been decoded (bits 64-127)
> 
> This documentation should contain "(since 10.1)" as described in:
> docs/devel/qapi-code-gen.rst

Just to be pedant, since there is a little ambiguity between the
examples in docs/devel/qapi-code-gen.rst and the actual code e.g. in
qapi/qom.json, I'll use:

# @unknown-dev-features2: Virtio device features bitmap that have not
#     been decoded (bits 64-127) (since 10.2)

/P
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Stefano Garzarella 3 months, 3 weeks ago
On Mon, Jul 21, 2025 at 04:23:14PM +0900, Akihiko Odaki wrote:
>On 2025/07/18 17:52, Paolo Abeni wrote:
>>Extend the VirtioDeviceFeatures struct with an additional u64
>>to track unknown features in the 64-127 bit range and decode
>>the full virtio features spaces for vhost and virtio devices.
>>
>>Also add entries for the soon-to-be-supported virtio net GSO over
>>UDP features.
>>
>>Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>>---
>>v2 -> v3:
>>   - unknown-dev-features-dword2 -> unknown-dev-features2
>>   - _array -> _ex
>>   - fixed typos in entries description
>>
>>v1 -> v2:
>>   - uint128_t -> uint64_t[]
>>---
>>  hw/virtio/virtio-hmp-cmds.c |  3 +-
>>  hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
>>  hw/virtio/virtio-qmp.h      |  3 +-
>>  qapi/virtio.json            |  8 +++-
>>  4 files changed, 73 insertions(+), 30 deletions(-)
>>

[...]

>>diff --git a/qapi/virtio.json b/qapi/virtio.json
>>index 9d652fe4a8..f2e2dd6e97 100644
>>--- a/qapi/virtio.json
>>+++ b/qapi/virtio.json
>>@@ -490,14 +490,18 @@
>>  #     unique features)
>>  #
>>  # @unknown-dev-features: Virtio device features bitmap that have not
>>-#     been decoded
>>+#     been decoded (bits 0-63)
>>+#
>>+# @unknown-dev-features2: Virtio device features bitmap that have not
>>+#     been decoded (bits 64-127)
>
>This documentation should contain "(since 10.1)" as described in:
>docs/devel/qapi-code-gen.rst

Good catch!

BTW 10.1 is already in soft-freeze, so I guess this will land in the 
next cycle, so I'd suggest to use 10.2

Thanks,
Stefano
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Markus Armbruster 3 months, 4 weeks ago
Paolo Abeni <pabeni@redhat.com> writes:

> Extend the VirtioDeviceFeatures struct with an additional u64
> to track unknown features in the 64-127 bit range and decode
> the full virtio features spaces for vhost and virtio devices.
>
> Also add entries for the soon-to-be-supported virtio net GSO over
> UDP features.
>
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>

[...]

> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
> index 3b6377cf0d..03c6163cf4 100644
> --- a/hw/virtio/virtio-qmp.c
> +++ b/hw/virtio/virtio-qmp.c

[...]

> @@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>          g_assert_not_reached();
>      }
>  
> -    features->has_unknown_dev_features = bitmap != 0;
> +    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
>      if (features->has_unknown_dev_features) {
> -        features->unknown_dev_features = bitmap;
> +        features->unknown_dev_features = bitmap[0];
> +        features->unknown_dev_features2 = bitmap[1];
>      }

Why not assign unconditionally?

>  
>      return features;

[...]

> diff --git a/qapi/virtio.json b/qapi/virtio.json
> index 9d652fe4a8..f2e2dd6e97 100644
> --- a/qapi/virtio.json
> +++ b/qapi/virtio.json
> @@ -490,14 +490,18 @@
>  #     unique features)
>  #
>  # @unknown-dev-features: Virtio device features bitmap that have not
> -#     been decoded
> +#     been decoded (bits 0-63)
> +#
> +# @unknown-dev-features2: Virtio device features bitmap that have not
> +#     been decoded (bits 64-127)
>  #
>  # Since: 7.2
>  ##
>  { 'struct': 'VirtioDeviceFeatures',
>    'data': { 'transports': [ 'str' ],
>              '*dev-features': [ 'str' ],
> -            '*unknown-dev-features': 'uint64' } }
> +            '*unknown-dev-features': 'uint64',
> +            '*unknown-dev-features2': 'uint64' } }
>  
>  ##
>  # @VirtQueueStatus:

I wish we could simply widen @unknown-dev-features, but we don't have
uint128, and adding it would risk breaking QMP clients.  64 bit integers
are already troublesome in JSON.

Does the example in x-query-virtio-status's doc comment need an update?
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Paolo Abeni 3 months, 3 weeks ago
On 7/19/25 8:57 AM, Markus Armbruster wrote:
> Paolo Abeni <pabeni@redhat.com> writes:
> 
>> Extend the VirtioDeviceFeatures struct with an additional u64
>> to track unknown features in the 64-127 bit range and decode
>> the full virtio features spaces for vhost and virtio devices.
>>
>> Also add entries for the soon-to-be-supported virtio net GSO over
>> UDP features.
>>
>> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> 
> [...]
> 
>> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
>> index 3b6377cf0d..03c6163cf4 100644
>> --- a/hw/virtio/virtio-qmp.c
>> +++ b/hw/virtio/virtio-qmp.c
> 
> [...]
> 
>> @@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>>          g_assert_not_reached();
>>      }
>>  
>> -    features->has_unknown_dev_features = bitmap != 0;
>> +    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
>>      if (features->has_unknown_dev_features) {
>> -        features->unknown_dev_features = bitmap;
>> +        features->unknown_dev_features = bitmap[0];
>> +        features->unknown_dev_features2 = bitmap[1];
>>      }
> 
> Why not assign unconditionally?

I stuck with the old/previous code style. I can move to unconditional
assignment in the next revision.

>>      return features;
> 
> [...]
> 
>> diff --git a/qapi/virtio.json b/qapi/virtio.json
>> index 9d652fe4a8..f2e2dd6e97 100644
>> --- a/qapi/virtio.json
>> +++ b/qapi/virtio.json
>> @@ -490,14 +490,18 @@
>>  #     unique features)
>>  #
>>  # @unknown-dev-features: Virtio device features bitmap that have not
>> -#     been decoded
>> +#     been decoded (bits 0-63)
>> +#
>> +# @unknown-dev-features2: Virtio device features bitmap that have not
>> +#     been decoded (bits 64-127)
>>  #
>>  # Since: 7.2
>>  ##
>>  { 'struct': 'VirtioDeviceFeatures',
>>    'data': { 'transports': [ 'str' ],
>>              '*dev-features': [ 'str' ],
>> -            '*unknown-dev-features': 'uint64' } }
>> +            '*unknown-dev-features': 'uint64',
>> +            '*unknown-dev-features2': 'uint64' } }
>>  
>>  ##
>>  # @VirtQueueStatus:
> 
> I wish we could simply widen @unknown-dev-features, but we don't have
> uint128, and adding it would risk breaking QMP clients.  64 bit integers
> are already troublesome in JSON.
> 
> Does the example in x-query-virtio-status's doc comment need an update?

Yes, you are right, the output for the 'unknown features' field will
include leading zeros for the high 64 bits.

I will update it in the next revision, thanks!

Paolo
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Stefano Garzarella 3 months, 4 weeks ago
On Fri, Jul 18, 2025 at 10:52:34AM +0200, Paolo Abeni wrote:
>Extend the VirtioDeviceFeatures struct with an additional u64
>to track unknown features in the 64-127 bit range and decode
>the full virtio features spaces for vhost and virtio devices.
>
>Also add entries for the soon-to-be-supported virtio net GSO over
>UDP features.
>
>Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>---
>v2 -> v3:
>  - unknown-dev-features-dword2 -> unknown-dev-features2
>  - _array -> _ex
>  - fixed typos in entries description
>
>v1 -> v2:
>  - uint128_t -> uint64_t[]
>---
> hw/virtio/virtio-hmp-cmds.c |  3 +-
> hw/virtio/virtio-qmp.c      | 89 ++++++++++++++++++++++++++-----------
> hw/virtio/virtio-qmp.h      |  3 +-
> qapi/virtio.json            |  8 +++-
> 4 files changed, 73 insertions(+), 30 deletions(-)
>
>diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c
>index 7d8677bcf0..1daae482d3 100644
>--- a/hw/virtio/virtio-hmp-cmds.c
>+++ b/hw/virtio/virtio-hmp-cmds.c
>@@ -74,7 +74,8 @@ static void hmp_virtio_dump_features(Monitor *mon,
>     }
>
>     if (features->has_unknown_dev_features) {
>-        monitor_printf(mon, "  unknown-features(0x%016"PRIx64")\n",
>+        monitor_printf(mon, "  unknown-features(0x%016"PRIx64"%016"PRIx64")\n",
>+                       features->unknown_dev_features2,
>                        features->unknown_dev_features);
>     }
> }
>diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
>index 3b6377cf0d..03c6163cf4 100644
>--- a/hw/virtio/virtio-qmp.c
>+++ b/hw/virtio/virtio-qmp.c
>@@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
>     FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
>             "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
>             "negotiation supported"),
>+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
>+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>+            "UDP tunnel packets"),
>+    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
>+            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>+            "UDP tunnel packets requiring checksum offload for the outer "
>+            "header"),
>+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
>+            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>+            "UDP tunnel packets"),
>+    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
>+            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>+            "UDP tunnel packets requiring checksum offload for the outer "
>+            "header"),

Is this chunk supposed to be here in this patch or better in the last 
patches where you add UPD tunnel features support?

Thanks,
Stefano

>     { -1, "" }
> };
> #endif
>@@ -510,6 +524,24 @@ static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = {
>         list;                                            \
>     })
>
>+#define CONVERT_FEATURES_EX(type, map, bitmap)           \
>+    ({                                                   \
>+        type *list = NULL;                               \
>+        type *node;                                      \
>+        for (i = 0; map[i].virtio_bit != -1; i++) {      \
>+            bit = map[i].virtio_bit;                     \
>+            if (!virtio_has_feature_ex(bitmap, bit)) {   \
>+                continue;                                \
>+            }                                            \
>+            node = g_new0(type, 1);                      \
>+            node->value = g_strdup(map[i].feature_desc); \
>+            node->next = list;                           \
>+            list = node;                                 \
>+            virtio_clear_feature_ex(bitmap, bit);        \
>+        }                                                \
>+        list;                                            \
>+    })
>+
> VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap)
> {
>     VirtioDeviceStatus *status;
>@@ -545,109 +577,112 @@ VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap)
>     return vhu_protocols;
> }
>
>-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
>+                                          const uint64_t *bmap)
> {
>+    uint64_t bitmap[VIRTIO_FEATURES_DWORDS];
>     VirtioDeviceFeatures *features;
>     uint64_t bit;
>     int i;
>
>+    virtio_features_copy(bitmap, bmap);
>     features = g_new0(VirtioDeviceFeatures, 1);
>     features->has_dev_features = true;
>
>     /* transport features */
>-    features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0,
>-                                            bitmap);
>+    features->transports = CONVERT_FEATURES_EX(strList, virtio_transport_map,
>+                                               bitmap);
>
>     /* device features */
>     switch (device_id) {
> #ifdef CONFIG_VIRTIO_SERIAL
>     case VIRTIO_ID_CONSOLE:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_serial_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_BLK
>     case VIRTIO_ID_BLOCK:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_blk_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_GPU
>     case VIRTIO_ID_GPU:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_gpu_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_NET
>     case VIRTIO_ID_NET:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_net_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_SCSI
>     case VIRTIO_ID_SCSI:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_scsi_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_BALLOON
>     case VIRTIO_ID_BALLOON:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_balloon_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_IOMMU
>     case VIRTIO_ID_IOMMU:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_iommu_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_INPUT
>     case VIRTIO_ID_INPUT:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_input_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VHOST_USER_FS
>     case VIRTIO_ID_FS:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_fs_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VHOST_VSOCK
>     case VIRTIO_ID_VSOCK:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_vsock_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_CRYPTO
>     case VIRTIO_ID_CRYPTO:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_crypto_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_MEM
>     case VIRTIO_ID_MEM:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_mem_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_I2C_ADAPTER
>     case VIRTIO_ID_I2C_ADAPTER:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_i2c_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VIRTIO_RNG
>     case VIRTIO_ID_RNG:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_rng_feature_map, bitmap);
>         break;
> #endif
> #ifdef CONFIG_VHOST_USER_GPIO
>     case VIRTIO_ID_GPIO:
>         features->dev_features =
>-            CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap);
>+            CONVERT_FEATURES_EX(strList, virtio_gpio_feature_map, bitmap);
>         break;
> #endif
>     /* No features */
>@@ -680,9 +715,10 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap)
>         g_assert_not_reached();
>     }
>
>-    features->has_unknown_dev_features = bitmap != 0;
>+    features->has_unknown_dev_features = !virtio_features_empty(bitmap);
>     if (features->has_unknown_dev_features) {
>-        features->unknown_dev_features = bitmap;
>+        features->unknown_dev_features = bitmap[0];
>+        features->unknown_dev_features2 = bitmap[1];
>     }
>
>     return features;
>@@ -743,11 +779,11 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>     status->device_id = vdev->device_id;
>     status->vhost_started = vdev->vhost_started;
>     status->guest_features = qmp_decode_features(vdev->device_id,
>-                                                 vdev->guest_features);
>+                                                 vdev->guest_features_ex);
>     status->host_features = qmp_decode_features(vdev->device_id,
>-                                                vdev->host_features);
>+                                                vdev->host_features_ex);
>     status->backend_features = qmp_decode_features(vdev->device_id,
>-                                                   vdev->backend_features);
>+                                                 vdev->backend_features_ex);
>
>     switch (vdev->device_endian) {
>     case VIRTIO_DEVICE_ENDIAN_LITTLE:
>@@ -785,11 +821,12 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
>         status->vhost_dev->nvqs = hdev->nvqs;
>         status->vhost_dev->vq_index = hdev->vq_index;
>         status->vhost_dev->features =
>-            qmp_decode_features(vdev->device_id, hdev->features);
>+            qmp_decode_features(vdev->device_id, hdev->features_ex);
>         status->vhost_dev->acked_features =
>-            qmp_decode_features(vdev->device_id, hdev->acked_features);
>+            qmp_decode_features(vdev->device_id, hdev->acked_features_ex);
>         status->vhost_dev->backend_features =
>-            qmp_decode_features(vdev->device_id, hdev->backend_features);
>+            qmp_decode_features(vdev->device_id, hdev->backend_features_ex);
>+
>         status->vhost_dev->protocol_features =
>             qmp_decode_protocols(hdev->protocol_features);
>         status->vhost_dev->max_queues = hdev->max_queues;
>diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h
>index 245a446a56..e0a1e49035 100644
>--- a/hw/virtio/virtio-qmp.h
>+++ b/hw/virtio/virtio-qmp.h
>@@ -18,6 +18,7 @@
> VirtIODevice *qmp_find_virtio_device(const char *path);
> VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap);
> VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap);
>-VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap);
>+VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id,
>+                                          const uint64_t *bitmap);
>
> #endif
>diff --git a/qapi/virtio.json b/qapi/virtio.json
>index 9d652fe4a8..f2e2dd6e97 100644
>--- a/qapi/virtio.json
>+++ b/qapi/virtio.json
>@@ -490,14 +490,18 @@
> #     unique features)
> #
> # @unknown-dev-features: Virtio device features bitmap that have not
>-#     been decoded
>+#     been decoded (bits 0-63)
>+#
>+# @unknown-dev-features2: Virtio device features bitmap that have not
>+#     been decoded (bits 64-127)
> #
> # Since: 7.2
> ##
> { 'struct': 'VirtioDeviceFeatures',
>   'data': { 'transports': [ 'str' ],
>             '*dev-features': [ 'str' ],
>-            '*unknown-dev-features': 'uint64' } }
>+            '*unknown-dev-features': 'uint64',
>+            '*unknown-dev-features2': 'uint64' } }
>
> ##
> # @VirtQueueStatus:
>-- 
>2.50.0
>
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Paolo Abeni 3 months, 4 weeks ago
On 7/18/25 12:18 PM, Stefano Garzarella wrote:
> On Fri, Jul 18, 2025 at 10:52:34AM +0200, Paolo Abeni wrote:
>> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
>> index 3b6377cf0d..03c6163cf4 100644
>> --- a/hw/virtio/virtio-qmp.c
>> +++ b/hw/virtio/virtio-qmp.c
>> @@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
>>     FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
>>             "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
>>             "negotiation supported"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>> +            "UDP tunnel packets"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>> +            "UDP tunnel packets requiring checksum offload for the outer "
>> +            "header"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>> +            "UDP tunnel packets"),
>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>> +            "UDP tunnel packets requiring checksum offload for the outer "
>> +            "header"),
> 
> Is this chunk supposed to be here in this patch or better in the last 
> patches where you add UPD tunnel features support?

Note that this point was discussed in the previous iteration:

https://lists.gnu.org/archive/html/qemu-devel/2025-07/msg04418.html

I think it could be both way: it could be useful for QMP to be able to
dump the (kernel/backend) features in human readable format even if qemu
does not support them.

No big objection to move the above chunk in patch 13/13, if there is
agreement :)

/P
Re: [PATCH RFC v3 08/13] qmp: update virtio features map to support extended features
Posted by Stefano Garzarella 3 months, 4 weeks ago
On Fri, Jul 18, 2025 at 12:23:21PM +0200, Paolo Abeni wrote:
>On 7/18/25 12:18 PM, Stefano Garzarella wrote:
>> On Fri, Jul 18, 2025 at 10:52:34AM +0200, Paolo Abeni wrote:
>>> diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
>>> index 3b6377cf0d..03c6163cf4 100644
>>> --- a/hw/virtio/virtio-qmp.c
>>> +++ b/hw/virtio/virtio-qmp.c
>>> @@ -325,6 +325,20 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = {
>>>     FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \
>>>             "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features "
>>>             "negotiation supported"),
>>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO, \
>>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>>> +            "UDP tunnel packets"),
>>> +    FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM, \
>>> +            "VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO: Driver can receive GSO over "
>>> +            "UDP tunnel packets requiring checksum offload for the outer "
>>> +            "header"),
>>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO, \
>>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>>> +            "UDP tunnel packets"),
>>> +    FEATURE_ENTRY(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM, \
>>> +            "VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO: Device can receive GSO over "
>>> +            "UDP tunnel packets requiring checksum offload for the outer "
>>> +            "header"),
>>
>> Is this chunk supposed to be here in this patch or better in the last
>> patches where you add UPD tunnel features support?
>
>Note that this point was discussed in the previous iteration:
>
>https://lists.gnu.org/archive/html/qemu-devel/2025-07/msg04418.html

Ops, sorry, I skiped that version and came directly here.
I'll check the v3.

>
>I think it could be both way: it could be useful for QMP to be able to
>dump the (kernel/backend) features in human readable format even if qemu
>does not support them.

I see.

>
>No big objection to move the above chunk in patch 13/13, if there is
>agreement :)

Not a strong opinion here.

Thanks,
Stefano