Michael S. Tsirkin
2011-May-19 23:24 UTC
[PATCHv2 0/2] virtio-net: 64 bit features, event index
OK, here's a patch that implements the virtio spec update that I sent earlier. It supercedes the PUBLISH_USED_IDX patches I sent out earlier. Support is added in both userspace and vhost-net. If you see issues or are just curious, you can turn the new feature off. For example: -global virtio-net-pci.event_idx=on -global virtio-blk-pci.event_idx=off Also, it's possible to try both vhost-net and virtio-net. Another part is adding support for 64 bit features in place. The high bits are actually unused, to test hack qemu to set some high bit. linux code is here: git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git vhost-net-next-event-idx-v3 git://git.kernel.org/pub/scm/linux/kernel/git/mst/qemu-kvm.git virtio-net-event-idx-v3 Changes from v1: - unify used and avail ring handling in a single feature bit - copy avail event idx fix from vhost-net Michael S. Tsirkin (2): virtio/vhost: support 64 bit features virtio+vhost: event idx feature hw/qdev-properties.c | 39 +++++++++++++--- hw/qdev.h | 10 ++++ hw/s390-virtio-bus.c | 5 +- hw/s390-virtio-bus.h | 2 +- hw/syborg_virtio.c | 7 ++- hw/vhost_net.c | 14 ++++-- hw/vhost_net.h | 4 +- hw/virtio-9p.c | 2 +- hw/virtio-balloon.c | 2 +- hw/virtio-blk.c | 2 +- hw/virtio-blk.h | 2 +- hw/virtio-net.c | 11 +++-- hw/virtio-net.h | 34 +++++++------- hw/virtio-pci.c | 91 +++++++++++++++++++++++++++---------- hw/virtio-serial-bus.c | 2 +- hw/virtio.c | 116 ++++++++++++++++++++++++++++++++++++++++++------ hw/virtio.h | 24 +++++++--- 17 files changed, 275 insertions(+), 92 deletions(-) -- 1.7.5.53.gc233e
Michael S. Tsirkin
2011-May-19 23:24 UTC
[PATCHv2 1/2] virtio/vhost: support 64 bit features
Add support for extended feature bits: up to 64 bit. Only virtio-pci is actually implemented, s390 and syborg are stubbed out (and untested). Signed-off-by: Michael S. Tsirkin <mst at redhat.com> --- hw/qdev-properties.c | 39 ++++++++++++++++---- hw/qdev.h | 10 +++++ hw/s390-virtio-bus.c | 5 ++- hw/s390-virtio-bus.h | 2 +- hw/syborg_virtio.c | 7 ++-- hw/vhost_net.c | 8 ++-- hw/vhost_net.h | 4 +- hw/virtio-9p.c | 2 +- hw/virtio-balloon.c | 2 +- hw/virtio-blk.c | 2 +- hw/virtio-blk.h | 2 +- hw/virtio-net.c | 11 +++--- hw/virtio-net.h | 34 +++++++++--------- hw/virtio-pci.c | 91 +++++++++++++++++++++++++++++++++++------------- hw/virtio-serial-bus.c | 2 +- hw/virtio.c | 24 ++++++++++--- hw/virtio.h | 17 +++++---- 17 files changed, 179 insertions(+), 83 deletions(-) diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 1088a26..a933c9e 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -10,20 +10,35 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) return ptr; } -static uint32_t qdev_get_prop_mask(Property *prop) +static uint64_t qdev_get_prop_mask(Property *prop) { assert(prop->info->type == PROP_TYPE_BIT); - return 0x1 << prop->bitnr; + return 0x1ULL << prop->bitnr; +} + +static uint64_t qdev_get_prop_val(DeviceState *dev, Property *prop) +{ + assert(prop->info->type == PROP_TYPE_BIT); + if (prop->info->size == sizeof(uint32_t)) { + return *(uint32_t *)qdev_get_prop_ptr(dev, prop); + } else { + return *(uint64_t *)qdev_get_prop_ptr(dev, prop); + } } static void bit_prop_set(DeviceState *dev, Property *props, bool val) { - uint32_t *p = qdev_get_prop_ptr(dev, props); - uint32_t mask = qdev_get_prop_mask(props); + uint64_t p = qdev_get_prop_val(dev, props); + uint64_t mask = qdev_get_prop_mask(props); if (val) - *p |= mask; + p |= mask; else - *p &= ~mask; + p &= ~mask; + if (props->info->size == sizeof(uint32_t)) { + *(uint32_t *)qdev_get_prop_ptr(dev, props) = p; + } else { + *(uint64_t *)qdev_get_prop_ptr(dev, props) = p; + } } static void qdev_prop_cpy(DeviceState *dev, Property *props, void *src) @@ -51,8 +66,8 @@ static int parse_bit(DeviceState *dev, Property *prop, const char *str) static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) { - uint32_t *p = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off"); + uint64_t val = qdev_get_prop_val(dev, prop); + return snprintf(dest, len, (val & qdev_get_prop_mask(prop)) ? "on" : "off"); } PropertyInfo qdev_prop_bit = { @@ -63,6 +78,14 @@ PropertyInfo qdev_prop_bit = { .print = print_bit, }; +PropertyInfo qdev_prop_bit64 = { + .name = "on/off", + .type = PROP_TYPE_BIT, + .size = sizeof(uint64_t), + .parse = parse_bit, + .print = print_bit, +}; + /* --- 8bit integer --- */ static int parse_uint8(DeviceState *dev, Property *prop, const char *str) diff --git a/hw/qdev.h b/hw/qdev.h index 8a13ec9..e65cab0 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -219,6 +219,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data); /*** qdev-properties.c ***/ extern PropertyInfo qdev_prop_bit; +extern PropertyInfo qdev_prop_bit64; extern PropertyInfo qdev_prop_uint8; extern PropertyInfo qdev_prop_uint16; extern PropertyInfo qdev_prop_uint32; @@ -257,6 +258,15 @@ extern PropertyInfo qdev_prop_pci_devfn; .defval = (bool[]) { (_defval) }, \ } +#define DEFINE_PROP_BIT64(_name, _state, _field, _bit, _defval) { \ + .name = (_name), \ + .info = &(qdev_prop_bit64), \ + .bitnr = (_bit), \ + .offset = offsetof(_state, _field) \ + + type_check(uint64_t,typeof_field(_state, _field)), \ + .defval = (bool[]) { (_defval) }, \ + } + #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) #define DEFINE_PROP_UINT16(_n, _s, _f, _d) \ diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 175e5cb..89e8c8e 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -310,10 +310,11 @@ static void virtio_s390_notify(void *opaque, uint16_t vector) kvm_s390_virtio_irq(s390_cpu_addr2state(0), 0, token); } -static unsigned virtio_s390_get_features(void *opaque) +static uint64_t virtio_s390_get_features(void *opaque) { VirtIOS390Device *dev = (VirtIOS390Device*)opaque; - return dev->host_features; + /* TODO: support high 32 bit features */ + return dev->host_features & 0xFFFFFFFFULL; } /**************** S390 Virtio Bus Device Descriptions *******************/ diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index edf6d04..5c851e3 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390-virtio-bus.h @@ -43,7 +43,7 @@ typedef struct VirtIOS390Device { VirtIODevice *vdev; BlockConf block; NICConf nic; - uint32_t host_features; + uint64_t host_features; virtio_serial_conf serial; virtio_net_conf net; } VirtIOS390Device; diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c index ee08c49..b64c357 100644 --- a/hw/syborg_virtio.c +++ b/hw/syborg_virtio.c @@ -67,7 +67,7 @@ typedef struct { uint32_t int_enable; uint32_t id; NICConf nic; - uint32_t host_features; + uint64_t host_features; virtio_net_conf net; } SyborgVirtIOProxy; @@ -244,10 +244,11 @@ static void syborg_virtio_update_irq(void *opaque, uint16_t vector) qemu_set_irq(proxy->irq, level != 0); } -static unsigned syborg_virtio_get_features(void *opaque) +static uint32_t syborg_virtio_get_features(void *opaque) { SyborgVirtIOProxy *proxy = opaque; - return proxy->host_features; + /* TODO: support high 32 bit features */ + return proxy->host_features & 0xFFFFFFFFULL; } static VirtIOBindings syborg_virtio_bindings = { diff --git a/hw/vhost_net.c b/hw/vhost_net.c index 420e05f..7e94f61 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -41,7 +41,7 @@ struct vhost_net { VLANClientState *vc; }; -unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { /* Clear features not supported by host kernel. */ if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) { @@ -56,7 +56,7 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) return features; } -void vhost_net_ack_features(struct vhost_net *net, unsigned features) +void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { net->dev.acked_features = net->dev.backend_features; if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) { @@ -219,11 +219,11 @@ void vhost_net_cleanup(struct vhost_net *net) { } -unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { return features; } -void vhost_net_ack_features(struct vhost_net *net, unsigned features) +void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { } #endif diff --git a/hw/vhost_net.h b/hw/vhost_net.h index 91e40b1..4069258 100644 --- a/hw/vhost_net.h +++ b/hw/vhost_net.h @@ -14,7 +14,7 @@ void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); void vhost_net_cleanup(VHostNetState *net); -unsigned vhost_net_get_features(VHostNetState *net, unsigned features); -void vhost_net_ack_features(VHostNetState *net, unsigned features); +uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features); +void vhost_net_ack_features(VHostNetState *net, uint64_t features); #endif diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c index 7e29535..184691a 100644 --- a/hw/virtio-9p.c +++ b/hw/virtio-9p.c @@ -3624,7 +3624,7 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) free_pdu(s, pdu); } -static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) +static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features) { features |= 1 << VIRTIO_9P_MOUNT_TAG; return features; diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index f9add1c..b34adc1 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -195,7 +195,7 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, dev->actual = le32_to_cpu(config.actual); } -static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) +static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f) { f |= (1 << VIRTIO_BALLOON_F_STATS_VQ); return f; diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 91e0394..442ce11 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -466,7 +466,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); } -static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) +static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features) { VirtIOBlock *s = to_virtio_blk(vdev); diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index fff46da..61104ea 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -98,7 +98,7 @@ struct virtio_scsi_inhdr #ifdef __linux__ #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ - DEFINE_PROP_BIT("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true) + DEFINE_PROP_BIT64("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true) #else #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 6997e02..c242fd1 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -223,7 +223,7 @@ static int peer_has_ufo(VirtIONet *n) return n->has_ufo; } -static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) +static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features) { VirtIONet *n = to_virtio_net(vdev); @@ -258,9 +258,9 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); } -static uint32_t virtio_net_bad_features(VirtIODevice *vdev) +static uint64_t virtio_net_bad_features(VirtIODevice *vdev) { - uint32_t features = 0; + uint64_t features = 0; /* Linux kernel 2.6.25. It understood MAC (as everyone must), * but also these: */ @@ -273,7 +273,7 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) return features; } -static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) +static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) { VirtIONet *n = to_virtio_net(vdev); @@ -628,7 +628,8 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ return -1; error_report("virtio-net unexpected empty queue: " "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd guest features 0x%x", + "guest hdr len %zd, host hdr len %zd " + "guest features 0x%" PRIx64, i, n->mergeable_rx_bufs, offset, size, guest_hdr_len, host_hdr_len, n->vdev.guest_features); exit(1); diff --git a/hw/virtio-net.h b/hw/virtio-net.h index 8af9a1c..8f8ac7f 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -169,21 +169,21 @@ struct virtio_net_ctrl_mac { #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ - DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ - DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ - DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ - DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \ - DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \ - DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \ - DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \ - DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \ - DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \ - DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \ - DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \ - DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \ - DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \ - DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ - DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ - DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ - DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) + DEFINE_PROP_BIT64("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ + DEFINE_PROP_BIT64("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ + DEFINE_PROP_BIT64("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ + DEFINE_PROP_BIT64("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \ + DEFINE_PROP_BIT64("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \ + DEFINE_PROP_BIT64("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \ + DEFINE_PROP_BIT64("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \ + DEFINE_PROP_BIT64("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \ + DEFINE_PROP_BIT64("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \ + DEFINE_PROP_BIT64("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \ + DEFINE_PROP_BIT64("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \ + DEFINE_PROP_BIT64("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \ + DEFINE_PROP_BIT64("status", _state, _field, VIRTIO_NET_F_STATUS, true), \ + DEFINE_PROP_BIT64("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ + DEFINE_PROP_BIT64("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ + DEFINE_PROP_BIT64("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ + DEFINE_PROP_BIT64("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) #endif diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 5236470..eb86de2 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -64,15 +64,28 @@ /* Config space size */ #define VIRTIO_PCI_CONFIG_NOMSI 20 #define VIRTIO_PCI_CONFIG_MSI 24 -#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) +#define VIRTIO_PCI_CONFIG_HI 32 +/* An extended 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES_HI 24 + +/* An extended 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES_HI 28 + +#define VIRTIO_PCI_REGION_SIZE(proxy) (((proxy)->host_features & \ + (1ULL << VIRTIO_F_FEATURES_HI)) ? \ + VIRTIO_PCI_CONFIG_HI : \ + (msix_present(&(proxy)->pci_dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI)) /* The remaining space is defined by each driver as the per-driver * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) +#define VIRTIO_PCI_CONFIG(proxy) (((proxy)->vdev->guest_features & \ + (1ULL << VIRTIO_F_FEATURES_HI)) ? \ + VIRTIO_PCI_CONFIG_HI : \ + (msix_enabled(&(proxy)->pci_dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI)) /* Virtio ABI version, if we increment this, we break the guest driver. */ #define VIRTIO_PCI_ABI_VERSION 0 @@ -106,7 +119,7 @@ typedef struct { uint32_t nvectors; BlockConf block; NICConf nic; - uint32_t host_features; + uint64_t host_features; #ifdef CONFIG_LINUX V9fsConf fsconf; #endif @@ -314,11 +327,22 @@ static void virtio_pci_reset(DeviceState *d) proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } +static inline uint64_t virtio_replace_hi(uint64_t features, uint32_t hi) +{ + return (features & 0xffffffffull) | ((uint64_t)hi) << 32; +} + +static inline uint64_t virtio_replace_lo(uint64_t features, uint32_t lo) +{ + return (features & 0xffffffff00000000ull) | lo; +} + static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = proxy->vdev; target_phys_addr_t pa; + uint64_t f; switch (addr) { case VIRTIO_PCI_GUEST_FEATURES: @@ -329,9 +353,15 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) else val = 0; } + /* Clearing VIRTIO_F_FEATURES_HI clears high 32 bit. */ + if (val & (1ULL << VIRTIO_F_FEATURES_HI)) { + f = virtio_replace_lo(vdev->guest_features, val); + } else { + f = val; + } if (vdev->set_features) - vdev->set_features(vdev, val); - vdev->guest_features = val; + vdev->set_features(vdev, f); + vdev->guest_features = f; break; case VIRTIO_PCI_QUEUE_PFN: pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; @@ -389,6 +419,12 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) val = VIRTIO_NO_VECTOR; virtio_queue_set_vector(vdev, vdev->queue_sel, val); break; + case VIRTIO_PCI_GUEST_FEATURES_HI: + f = virtio_replace_hi(vdev->guest_features, val); + if (vdev->set_features) + vdev->set_features(vdev, f); + vdev->guest_features = f; + break; default: error_report("%s: unexpected address 0x%x value 0x%x", __func__, addr, val); @@ -433,6 +469,11 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) case VIRTIO_MSI_QUEUE_VECTOR: ret = virtio_queue_vector(vdev, vdev->queue_sel); break; + case VIRTIO_PCI_HOST_FEATURES_HI: + ret = proxy->host_features >> 32; + break; + case VIRTIO_PCI_GUEST_FEATURES_HI: + ret = vdev->guest_features >> 32; default: break; } @@ -443,7 +484,7 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) return virtio_ioport_read(proxy, addr); @@ -454,7 +495,7 @@ static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) return virtio_ioport_read(proxy, addr); @@ -465,7 +506,7 @@ static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) return virtio_ioport_read(proxy, addr); @@ -476,7 +517,7 @@ static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) { virtio_ioport_write(proxy, addr, val); @@ -489,7 +530,7 @@ static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) { virtio_ioport_write(proxy, addr, val); @@ -502,7 +543,7 @@ static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG(proxy); addr -= proxy->addr; if (addr < config) { virtio_ioport_write(proxy, addr, val); @@ -517,7 +558,7 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, { VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev); VirtIODevice *vdev = proxy->vdev; - unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len; + unsigned config_len = VIRTIO_PCI_REGION_SIZE(proxy) + vdev->config_len; proxy->addr = addr; @@ -551,7 +592,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, msix_write_config(pci_dev, address, val, len); } -static unsigned virtio_pci_get_features(void *opaque) +static uint64_t virtio_pci_get_features(void *opaque) { VirtIOPCIProxy *proxy = opaque; return proxy->host_features; @@ -788,13 +829,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->pci_dev.config_write = virtio_write_config; - size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len; - if (size & (size-1)) - size = 1 << qemu_fls(size); - - pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO, - virtio_map); - if (!kvm_has_many_ioeventfds()) { proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; } @@ -803,6 +837,15 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; proxy->host_features = vdev->get_features(vdev, proxy->host_features); + if (proxy->host_features & 0xffffffff00000000ull) { + proxy->host_features |= 0x1ULL << VIRTIO_F_FEATURES_HI; + } + size = VIRTIO_PCI_REGION_SIZE(proxy) + vdev->config_len; + if (size & (size-1)) + size = 1 << qemu_fls(size); + + pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO, + virtio_map); } static int virtio_blk_init_pci(PCIDevice *pci_dev) diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 6227379..76acd02 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -461,7 +461,7 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) { } -static uint32_t get_features(VirtIODevice *vdev, uint32_t features) +static uint64_t get_features(VirtIODevice *vdev, uint64_t features) { VirtIOSerial *vser; diff --git a/hw/virtio.c b/hw/virtio.c index 31bd9e3..b9acbd4 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -653,6 +653,8 @@ void virtio_notify_config(VirtIODevice *vdev) void virtio_save(VirtIODevice *vdev, QEMUFile *f) { + uint32_t guest_features_hi = vdev->guest_features >> 32; + uint32_t guest_features_lo = vdev->guest_features; int i; if (vdev->binding->save_config) @@ -661,7 +663,10 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) qemu_put_8s(f, &vdev->status); qemu_put_8s(f, &vdev->isr); qemu_put_be16s(f, &vdev->queue_sel); - qemu_put_be32s(f, &vdev->guest_features); + qemu_put_be32s(f, &guest_features_lo); + if (guest_features_lo & (1ULL << VIRTIO_F_FEATURES_HI)) { + qemu_put_be32s(f, &guest_features_hi); + } qemu_put_be32(f, vdev->config_len); qemu_put_buffer(f, vdev->config, vdev->config_len); @@ -687,8 +692,9 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) int virtio_load(VirtIODevice *vdev, QEMUFile *f) { int num, i, ret; - uint32_t features; - uint32_t supported_features + uint32_t features_hi, features_lo; + uint64_t features; + uint64_t supported_features vdev->binding->get_features(vdev->binding_opaque); if (vdev->binding->load_config) { @@ -700,9 +706,17 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) qemu_get_8s(f, &vdev->status); qemu_get_8s(f, &vdev->isr); qemu_get_be16s(f, &vdev->queue_sel); - qemu_get_be32s(f, &features); + qemu_get_be32s(f, &features_lo); + if (features_lo & (1ULL << VIRTIO_F_FEATURES_HI)) { + qemu_get_be32s(f, &features_hi); + } else { + features_hi = 0; + } + features = ((uint64_t)features_hi) << 32 | features_lo; + if (features & ~supported_features) { - error_report("Features 0x%x unsupported. Allowed features: 0x%x", + error_report("Features 0x%" PRIx64 " unsupported. " + "Allowed features: 0x%" PRIx64, features, supported_features); return -1; } diff --git a/hw/virtio.h b/hw/virtio.h index bc72289..9517b97 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -49,6 +49,9 @@ /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 +/* Enables feature bits 32 to 63 (only really required for virtio_pci). */ +#define VIRTIO_F_FEATURES_HI 31 + /* from Linux's linux/virtio_ring.h */ /* This marks a buffer as continuing via the next field. */ @@ -93,7 +96,7 @@ typedef struct { int (*load_config)(void * opaque, QEMUFile *f); int (*load_queue)(void * opaque, int n, QEMUFile *f); int (*load_done)(void * opaque, QEMUFile *f); - unsigned (*get_features)(void * opaque); + uint64_t (*get_features)(void * opaque); bool (*query_guest_notifiers)(void * opaque); int (*set_guest_notifiers)(void * opaque, bool assigned); int (*set_host_notifier)(void * opaque, int n, bool assigned); @@ -110,14 +113,14 @@ struct VirtIODevice uint8_t status; uint8_t isr; uint16_t queue_sel; - uint32_t guest_features; + uint64_t guest_features; size_t config_len; void *config; uint16_t config_vector; int nvectors; - uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); - uint32_t (*bad_features)(VirtIODevice *vdev); - void (*set_features)(VirtIODevice *vdev, uint32_t val); + uint64_t (*get_features)(VirtIODevice *vdev, uint64_t requested_features); + uint64_t (*bad_features)(VirtIODevice *vdev); + void (*set_features)(VirtIODevice *vdev, uint64_t val); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); @@ -209,8 +212,8 @@ void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ - DEFINE_PROP_BIT("indirect_desc", _state, _field, \ - VIRTIO_RING_F_INDIRECT_DESC, true) + DEFINE_PROP_BIT64("indirect_desc", _state, _field, \ + VIRTIO_RING_F_INDIRECT_DESC, true) target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); -- 1.7.5.53.gc233e
Add support for used_event feature, and utilize it to reduce the number of interrupts and exits for the guest. Signed-off-by: Michael S. Tsirkin <mst at redhat.com> --- hw/vhost_net.c | 6 ++++ hw/virtio.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++----- hw/virtio.h | 9 +++++- 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/hw/vhost_net.c b/hw/vhost_net.c index 7e94f61..41841d9 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -50,6 +50,9 @@ uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (!(net->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); + } if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) { features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); } @@ -65,6 +68,9 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features) if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + net->dev.acked_features |= (1 << VIRTIO_RING_F_EVENT_IDX); + } if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF); } diff --git a/hw/virtio.c b/hw/virtio.c index b9acbd4..d51ac93 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -72,7 +72,17 @@ struct VirtQueue VRing vring; target_phys_addr_t pa; uint16_t last_avail_idx; + /* Last used index value we have signalled on */ + uint16_t signalled_used; + + /* Last used index value we have signalled on */ + bool signalled_used_valid; + + /* Notification enabled? */ + bool notification; + int inuse; + uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); VirtIODevice *vdev; @@ -141,6 +151,11 @@ static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) return lduw_phys(pa); } +static inline uint16_t vring_used_event(VirtQueue *vq) +{ + return vring_avail_ring(vq, vq->vring.num); +} + static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) { target_phys_addr_t pa; @@ -162,11 +177,11 @@ static uint16_t vring_used_idx(VirtQueue *vq) return lduw_phys(pa); } -static inline void vring_used_idx_increment(VirtQueue *vq, uint16_t val) +static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) { target_phys_addr_t pa; pa = vq->vring.used + offsetof(VRingUsed, idx); - stw_phys(pa, vring_used_idx(vq) + val); + stw_phys(pa, val); } static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) @@ -183,12 +198,26 @@ static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) stw_phys(pa, lduw_phys(pa) & ~mask); } +static inline void vring_avail_event(VirtQueue *vq, uint16_t val) +{ + target_phys_addr_t pa; + if (!vq->notification) { + return; + } + pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); + stw_phys(pa, val); +} + void virtio_queue_set_notification(VirtQueue *vq, int enable) { - if (enable) + vq->notification = enable; + if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(vq, vring_avail_idx(vq)); + } else if (enable) { vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); - else + } else { vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY); + } } int virtio_queue_ready(VirtQueue *vq) @@ -234,11 +263,16 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, void virtqueue_flush(VirtQueue *vq, unsigned int count) { + uint16_t old, new; /* Make sure buffer is written before we update index. */ wmb(); trace_virtqueue_flush(vq, count); - vring_used_idx_increment(vq, count); + old = vring_used_idx(vq); + new = old + count; + vring_used_idx_set(vq, new); vq->inuse -= count; + if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) + vq->signalled_used_valid = false; } void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, @@ -395,6 +429,9 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); + if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(vq, vring_avail_idx(vq)); + } if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { @@ -478,6 +515,9 @@ void virtio_reset(void *opaque) vdev->vq[i].last_avail_idx = 0; vdev->vq[i].pa = 0; vdev->vq[i].vector = VIRTIO_NO_VECTOR; + vdev->vq[i].signalled_used = 0; + vdev->vq[i].signalled_used_valid = false; + vdev->vq[i].notification = true; } } @@ -629,13 +669,45 @@ void virtio_irq(VirtQueue *vq) virtio_notify_vector(vq->vdev, vq->vector); } -void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) +/* Assuming a given event_idx value from the other size, if + * we have just incremented index from old to new_idx, + * should we trigger an event? */ +static inline int vring_need_event(uint16_t event, uint16_t new, uint16_t old) +{ + /* Note: Xen has similar logic for notification hold-off + * in include/xen/interface/io/ring.h with req_event and req_prod + * corresponding to event_idx + 1 and new respectively. + * Note also that req_event and req_prod in Xen start at 1, + * event indexes in virtio start at 0. */ + return (uint16_t)(new - event - 1) < (uint16_t)(new - old); +} + +static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) { + uint16_t old, new; + bool v; /* Always notify when queue is empty (when feature acknowledge) */ - if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) && - (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || - (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx))) + if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && + !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) { + return true; + } + + if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); + } + + v = vq->signalled_used_valid; + vq->signalled_used_valid = true; + old = vq->signalled_used; + new = vq->signalled_used = vring_used_idx(vq); + return !v || vring_need_event(vring_used_event(vq), new, old); +} + +void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) +{ + if (!vring_notify(vdev, vq)) { return; + } trace_virtio_notify(vdev, vq); vdev->isr |= 0x01; @@ -732,6 +804,8 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) vdev->vq[i].vring.num = qemu_get_be32(f); vdev->vq[i].pa = qemu_get_be64(f); qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); + vdev->vq[i].signalled_used_valid = false; + vdev->vq[i].notification = true; if (vdev->vq[i].pa) { uint16_t nheads; diff --git a/hw/virtio.h b/hw/virtio.h index 9517b97..3438a91 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -46,6 +46,11 @@ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* The Guest publishes the used index for which it expects an interrupt + * at the end of the avail ring. Host should ignore the avail->flags field. */ +/* The Host publishes the avail index for which it expects a kick + * at the end of the used ring. Guest should ignore the used->flags field. */ +#define VIRTIO_RING_F_EVENT_IDX 29 /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 @@ -213,7 +218,9 @@ void virtio_serial_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT64("indirect_desc", _state, _field, \ - VIRTIO_RING_F_INDIRECT_DESC, true) + VIRTIO_RING_F_INDIRECT_DESC, true), \ + DEFINE_PROP_BIT64("event_idx", _state, _field, \ + VIRTIO_RING_F_EVENT_IDX, true) target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); -- 1.7.5.53.gc233e
Apparently Analagous Threads
- [PATCHv2 0/2] virtio-net: 64 bit features, event index
- [PATCH 0/3] virtio-net: 64 bit features, event index
- [PATCH 0/3] virtio-net: 64 bit features, event index
- [PATCH RFC v6 00/20] qemu: towards virtio-1 host support
- [PATCH RFC v6 00/20] qemu: towards virtio-1 host support