Patchwork [2/3] hw/virtio-net.c: set config size using host features

login
register
mail settings
Submitter Jesse Larrew
Date Feb. 5, 2013, 10:48 p.m.
Message ID <1360104530-7776-3-git-send-email-jlarrew@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/218457/
State New
Headers show

Comments

Jesse Larrew - Feb. 5, 2013, 10:48 p.m.
Currently, the config size for virtio devices is hard coded. When a new
feature is added that changes the config size, drivers that assume a static
config size will break. For purposes of backward compatability, there needs
to be a way to inform drivers of the config size needed to accomodate the
set of features enabled.

Signed-off-by: Jesse Larrew <jlarrew@linux.vnet.ibm.com>
---
 hw/virtio-net.c | 44 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 36 insertions(+), 8 deletions(-)
Eric Blake - Feb. 5, 2013, 11:07 p.m.
On 02/05/2013 03:48 PM, Jesse Larrew wrote:
> Currently, the config size for virtio devices is hard coded. When a new
> feature is added that changes the config size, drivers that assume a static
> config size will break. For purposes of backward compatability, there needs

s/compatability/compatibility/

> to be a way to inform drivers of the config size needed to accomodate the

s/accomodate/accommodate/

> set of features enabled.
> 
> Signed-off-by: Jesse Larrew <jlarrew@linux.vnet.ibm.com>

>  
> +/*
> + * Calculate the number of bytes up to and including the given 'field' of
> + * 'container'.
> + */
> +#define endof(container, field) \
> +    (intptr_t)(&(((container *)0)->field)+1)

Insufficient (); should be:
((intptr_t)(&(((container *)0)->field)+1))

Patch

diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index f1c2884..2956bc1 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -73,8 +73,31 @@  typedef struct VirtIONet
     int multiqueue;
     uint16_t max_queues;
     uint16_t curr_queues;
+    int config_size;
 } VirtIONet;
 
+/*
+ * Calculate the number of bytes up to and including the given 'field' of
+ * 'container'.
+ */
+#define endof(container, field) \
+    (intptr_t)(&(((container *)0)->field)+1)
+
+typedef struct VirtIOFeature {
+    uint32_t flags;
+    size_t end;
+} VirtIOFeature;
+
+static VirtIOFeature feature_sizes[] = {
+    {.flags = 1 << VIRTIO_NET_F_MAC,
+     .end = endof(struct virtio_net_config, mac)},
+    {.flags = 1 << VIRTIO_NET_F_STATUS,
+     .end = endof(struct virtio_net_config, status)},
+    {.flags = 1 << VIRTIO_NET_F_MQ,
+     .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
+    {}
+};
+
 static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
 {
     VirtIONet *n = qemu_get_nic_opaque(nc);
@@ -104,15 +127,15 @@  static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
     stw_p(&netcfg.status, n->status);
     stw_p(&netcfg.max_virtqueue_pairs, n->max_queues);
     memcpy(netcfg.mac, n->mac, ETH_ALEN);
-    memcpy(config, &netcfg, sizeof(netcfg));
+    memcpy(config, &netcfg, n->config_size);
 }
 
 static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
 {
     VirtIONet *n = to_virtio_net(vdev);
-    struct virtio_net_config netcfg;
+    struct virtio_net_config netcfg = {};
 
-    memcpy(&netcfg, config, sizeof(netcfg));
+    memcpy(&netcfg, config, n->config_size);
 
     if (!(n->vdev.guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) &&
         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
@@ -1279,16 +1302,21 @@  static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
 }
 
 VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
-                              virtio_net_conf *net,
-                              uint32_t host_features)
+                              virtio_net_conf *net, uint32_t host_features)
 {
     VirtIONet *n;
-    int i;
+    int i, config_size = 0;
+
+    for (i = 0; feature_sizes[i].flags != 0; i++) {
+        if (host_features & feature_sizes[i].flags) {
+            config_size = MAX(feature_sizes[i].end, config_size);
+        }
+    }
 
     n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET,
-                                        sizeof(struct virtio_net_config),
-                                        sizeof(VirtIONet));
+                                        config_size, sizeof(VirtIONet));
 
+    n->config_size = config_size;
     n->vdev.get_config = virtio_net_get_config;
     n->vdev.set_config = virtio_net_set_config;
     n->vdev.get_features = virtio_net_get_features;