virtio-net: More configurability and bh handling for tx
v2 cover virtio-net: More configurability and bh handling for tx
Incorporated feedback. txburst= and txtimer= are now "x-" prefixed developer options. I added a tx= option, because I do want there to be a supported way to switch between TX strategies. This also drops the magic value of txtimer= 1 or 0 (setting default timeout or switching modes). I also dropped the trickiness around only enabling the bottom half for the iothread since Anthony indicates we don't care about performance for the non-iothread case.
New performance data against both qemu.git and qemu-kvm.git:
This shows the base performance versus patched with new default for various packet sizes and netperf test loads. The guest is a 2-way SMP, connected directly to the host via a tap (no bridge). Thanks,
Alex
---
Alex Williamson (4):
virtio-net: Introduce a new bottom half packet TX
virtio-net: Rename tx_timer_active to tx_waiting
virtio-net: Limit number of packets sent per TX flush
virtio-net: Make tx_timer timeout configurable
hw/s390-virtio-bus.c | 8 +++ hw/s390-virtio-bus.h | 1 hw/syborg_virtio.c | 8 +++ hw/virtio-net.c | 129 +++++++++++++++++++++++++++++++++++++++++--------- hw/virtio-net.h | 14 +++++ hw/virtio-pci.c | 8 +++ hw/virtio.h | 4 +- 7 files changed, 144 insertions(+), 28 deletions(-)
PATCH v2 1/4 virtio-net: Make tx_timer timeout configurable
Add an option to make the TX mitigation timer adjustable as a device option. The 150us hard coded default used currently is reasonable, but may not be suitable for all workloads, this gives us a way to adjust it using a single binary. We can't support any random option though, so use the "x-" prefix to indicate this is a developer option. Usage:
-device virtio-net-pci,x-txtimer=500000,... # .5ms timeout
---
hw/s390-virtio-bus.c | 5 ++++- hw/s390-virtio-bus.h | 1 + hw/syborg_virtio.c | 5 ++++- hw/virtio-net.c | 9 ++++++--- hw/virtio-net.h | 5 +++++ hw/virtio-pci.c | 5 ++++- hw/virtio.h | 4 +++- 7 files changed, 27 insertions(+), 7 deletions(-)
hw/s390-virtio-bus.c +4 -1
@@ -27,6 +27,7 @@#include "elf.h" #include "hw/virtio.h" #include "hw/virtio-serial.h" +#include "hw/virtio-net.h" #include "hw/sysbus.h" #include "kvm.h"
@@ -110,7 +111,7 @@ static int s390_virtio_net_init(VirtIOS390Device *dev) {VirtIODevice *vdev; - vdev =virtio_net_init ((DeviceState *)dev, &dev->nic); + vdev =virtio_net_init ((DeviceState *)dev, &dev->nic, &dev->net);if (!vdev) {return -1 ; }
@@ -327,6 +328,8 @@ static VirtIOS390DeviceInfo s390_virtio_net = { .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property []) {DEFINE_NIC_PROPERTIES (VirtIOS390Device, nic), +DEFINE_PROP_UINT32 ("x-txtimer" , VirtIOS390Device, + net.txtimer,TX_TIMER_INTERVAL ),DEFINE_PROP_END_OF_LIST (), }, };
hw/s390-virtio-bus.h +1 -0
@@ -43,6 +43,7 @@ typedef struct VirtIOS390Device {uint32_t host_features;/* Max. number of ports we can have for a the virtio-serial device */ uint32_t max_virtserial_ports; +virtio_net_conf net; } VirtIOS390Device; typedef struct VirtIOS390Bus {
hw/syborg_virtio.c +4 -1
@@ -68,6 +68,7 @@ typedef struct {uint32_t id;NICConf nic;uint32_t host_features; +virtio_net_conf net; } SyborgVirtIOProxy;static uint32_t syborg_virtio_readl (void *opaque,target_phys_addr_t offset)
@@ -284,7 +285,7 @@ static int syborg_virtio_net_init(SysBusDevice *dev)VirtIODevice *vdev;SyborgVirtIOProxy *proxy =FROM_SYSBUS (SyborgVirtIOProxy, dev); - vdev =virtio_net_init (&dev->qdev, &proxy->nic); + vdev =virtio_net_init (&dev->qdev, &proxy->nic, &proxy->net);return syborg_virtio_init (proxy, vdev); }
@@ -295,6 +296,8 @@ static SysBusDeviceInfo syborg_virtio_net_info = { .qdev.props = (Property[]) {DEFINE_NIC_PROPERTIES (SyborgVirtIOProxy, nic),DEFINE_VIRTIO_NET_FEATURES (SyborgVirtIOProxy, host_features), +DEFINE_PROP_UINT32 ("x-txtimer" , SyborgVirtIOProxy, + net.txtimer,TX_TIMER_INTERVAL ),DEFINE_PROP_END_OF_LIST (), } };
hw/virtio-net.c +6 -3
@@ -36,6 +36,7 @@ typedef struct VirtIONetVirtQueue *ctrl_vq;NICState *nic;QEMUTimer *tx_timer; +uint32_t tx_timeout;int tx_timer_active;uint32_t has_vnet_hdr;uint8_t has_ufo;
@@ -702,7 +703,7 @@ static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)virtio_net_flush_tx (n, vq); }else {qemu_mod_timer (n->tx_timer, -qemu_get_clock (vm_clock) +TX_TIMER_INTERVAL ); +qemu_get_clock (vm_clock) + n->tx_timeout); n->tx_timer_active =1 ;virtio_queue_set_notification (vq,0 ); }
@@ -842,7 +843,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)if (n->tx_timer_active) {qemu_mod_timer (n->tx_timer, -qemu_get_clock (vm_clock) +TX_TIMER_INTERVAL ); +qemu_get_clock (vm_clock) + n->tx_timeout); }return 0 ; }
@@ -903,7 +904,8 @@ static void virtio_net_vmstate_change(void *opaque, int running, int reason)virtio_net_set_status (&n->vdev, n->vdev.status & status); } -VirtIODevice *virtio_net_init (DeviceState *dev,NICConf *conf) +VirtIODevice *virtio_net_init (DeviceState *dev,NICConf *conf, +virtio_net_conf *net) {VirtIONet *n;
@@ -931,6 +933,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf) n->tx_timer =qemu_new_timer (vm_clock, virtio_net_tx_timer, n); n->tx_timer_active =0 ; + n->tx_timeout = net->txtimer; n->mergeable_rx_bufs =0 ; n->promisc =1 ;/* for compatibility */
hw/virtio-net.h +5 -0
@@ -49,6 +49,11 @@#define TX_TIMER_INTERVAL 150000/* 150 us */ +typedef struct virtio_net_conf +{ +uint32_t txtimer; +}virtio_net_conf ; +/* Maximum packet size we can receive from tap device: header + 64k */ #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
hw/virtio-pci.c +4 -1
@@ -107,6 +107,7 @@ typedef struct {#endif /* Max. number of ports we can have for a the virtio-serial device */ uint32_t max_virtserial_ports; +virtio_net_conf net; } VirtIOPCIProxy;/* virtio device */
@@ -613,7 +614,7 @@ static int virtio_net_init_pci(PCIDevice *pci_dev)VirtIOPCIProxy *proxy =DO_UPCAST (VirtIOPCIProxy, pci_dev, pci_dev);VirtIODevice *vdev; - vdev =virtio_net_init (&pci_dev->qdev, &proxy->nic); + vdev =virtio_net_init (&pci_dev->qdev, &proxy->nic, &proxy->net); vdev->nvectors = proxy->nvectors; virtio_init_pci(proxy, vdev,
@@ -690,6 +691,8 @@ static PCIDeviceInfo virtio_info[] = {DEFINE_PROP_UINT32 ("vectors" , VirtIOPCIProxy, nvectors,3 ),DEFINE_VIRTIO_NET_FEATURES (VirtIOPCIProxy, host_features),DEFINE_NIC_PROPERTIES (VirtIOPCIProxy, nic), +DEFINE_PROP_UINT32 ("x-txtimer" , VirtIOPCIProxy, + net.txtimer,TX_TIMER_INTERVAL ), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset,
hw/virtio.h +3 -1
@@ -185,7 +185,9 @@ void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,/* Base devices. */ VirtIODevice *virtio_blk_init (DeviceState *dev,BlockConf *conf); -VirtIODevice *virtio_net_init (DeviceState *dev,NICConf *conf); +struct virtio_net_conf ; +VirtIODevice *virtio_net_init (DeviceState *dev,NICConf *conf, +struct virtio_net_conf *net);VirtIODevice *virtio_serial_init (DeviceState *dev,uint32_t max_nr_ports);VirtIODevice *virtio_balloon_init (DeviceState *dev);#ifdef CONFIG_LINUX
PATCH v2 2/4 virtio-net: Limit number of packets sent per TX flush
In reply to Alex WilliamsonIf virtio_net_flush_tx() is called with notification disabled, we can race with the guest, processing packets at the same rate as they get produced. The trouble is that this means we have no guaranteed exit condition from the function and can spend minutes in there. Currently flush_tx is only called with notification on, which seems to limit us to one pass through the queue per call. An upcoming patch changes this.
Also add an option to set this value on the command line as different workloads may wish to use different values. We can't necessarily support any random value, so this is a developer option: x-txburst= Usage:
-device virtio-net-pci,x-txburst=64 # 64 packets per tx flush
One pass through the queue (256) seems to be a good default value for this, balancing latency with throughput. We use a signed int for x-txburst because 2^31 packets in a burst would take many, many minutes to process and it allows us to easily return a negative value value from virtio_net_flush_tx() to indicate a back-off or error condition.
---
hw/s390-virtio-bus.c | 2 ++ hw/syborg_virtio.c | 2 ++ hw/virtio-net.c | 21 +++++++++++++++------ hw/virtio-net.h | 8 ++++++++ hw/virtio-pci.c | 2 ++ 5 files changed, 29 insertions(+), 6 deletions(-)
hw/s390-virtio-bus.c +2 -0
@@ -330,6 +330,8 @@ static VirtIOS390DeviceInfo s390_virtio_net = { DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic), DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device, net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", VirtIOS390Device, + net.txburst, TX_BURST), DEFINE_PROP_END_OF_LIST(), }, };
hw/syborg_virtio.c +2 -0
@@ -298,6 +298,8 @@ static SysBusDeviceInfo syborg_virtio_net_info = { DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features), DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy, net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy, + net.txburst, TX_BURST), DEFINE_PROP_END_OF_LIST(), } };
hw/virtio-net.c +15 -6
@@ -37,6 +37,7 @@ typedef struct VirtIONetNICState *nic;QEMUTimer *tx_timer;uint32_t tx_timeout; +int32_t tx_burst;int tx_timer_active;uint32_t has_vnet_hdr;uint8_t has_ufo;
@@ -620,7 +621,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_return size; } -static void virtio_net_flush_tx (VirtIONet *n,VirtQueue *vq); +static int32_t virtio_net_flush_tx (VirtIONet *n,VirtQueue *vq);static void virtio_net_tx_complete (VLANClientState *nc,ssize_t len) {
@@ -636,16 +637,18 @@ static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) }/* TX */ -static void virtio_net_flush_tx (VirtIONet *n,VirtQueue *vq) +static int32_t virtio_net_flush_tx (VirtIONet *n,VirtQueue *vq) {VirtQueueElement elem; +int32_t num_packets =0 ; -if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK )) -return ; +if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK )) { +return num_packets; + }if (n->async_tx.elem.out_num) {virtio_queue_set_notification (n->tx_vq,0 ); -return ; +return num_packets; }while (virtqueue_pop (vq, &elem)) {
@@ -682,14 +685,19 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)virtio_queue_set_notification (n->tx_vq,0 ); n->async_tx.elem = elem; n->async_tx.len = len; -return ; +return -EBUSY ; } len += ret;virtqueue_push (vq, &elem, len);virtio_notify (&n->vdev, vq); + +if (++num_packets >= n->tx_burst) { +break ; + } } +return num_packets; }static void virtio_net_handle_tx (VirtIODevice *vdev,VirtQueue *vq)
@@ -934,6 +942,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->tx_timer =qemu_new_timer (vm_clock, virtio_net_tx_timer, n); n->tx_timer_active =0 ; n->tx_timeout = net->txtimer; + n->tx_burst = net->txburst; n->mergeable_rx_bufs =0 ; n->promisc =1 ;/* for compatibility */
hw/virtio-net.h +8 -0
@@ -49,9 +49,17 @@#define TX_TIMER_INTERVAL 150000/* 150 us */ +/* Limit the number of packets that can be sent via a single flush +* of the TX queue. This gives us a guaranteed exit condition and +* ensures fairness in the io path. 256 conveniently matches the +* length of the TX queue and shows a good balance of performance +* and latency. */ +#define TX_BURST 256 +typedef struct virtio_net_conf {uint32_t txtimer; +int32_t txburst; }virtio_net_conf ;/* Maximum packet size we can receive from tap device: header + 64k */
hw/virtio-pci.c +2 -0
@@ -693,6 +693,8 @@ static PCIDeviceInfo virtio_info[] = {DEFINE_NIC_PROPERTIES (VirtIOPCIProxy, nic),DEFINE_PROP_UINT32 ("x-txtimer" , VirtIOPCIProxy, net.txtimer,TX_TIMER_INTERVAL ), +DEFINE_PROP_INT32 ("x-txburst" , VirtIOPCIProxy, + net.txburst,TX_BURST ), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset,
PATCH v2 3/4 virtio-net: Rename tx_timer_active to tx_waiting
In reply to Alex WilliamsonDe-couple this from the timer since we might want to use different backends to send the packet.
---
hw/virtio-net.c | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-)
hw/virtio-net.c +9 -9
@@ -38,7 +38,7 @@ typedef struct VirtIONetQEMUTimer *tx_timer;uint32_t tx_timeout;int32_t tx_burst; -int tx_timer_active; +int tx_waiting;uint32_t has_vnet_hdr;uint8_t has_ufo;struct {
@@ -704,15 +704,15 @@ static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq) {VirtIONet *n =to_virtio_net (vdev); -if (n->tx_timer_active) { +if (n->tx_waiting) {virtio_queue_set_notification (vq,1 );qemu_del_timer (n->tx_timer); - n->tx_timer_active =0 ; + n->tx_waiting =0 ;virtio_net_flush_tx (n, vq); }else {qemu_mod_timer (n->tx_timer,qemu_get_clock (vm_clock) + n->tx_timeout); - n->tx_timer_active =1 ; + n->tx_waiting =1 ;virtio_queue_set_notification (vq,0 ); } }
@@ -721,7 +721,7 @@ static void virtio_net_tx_timer(void *opaque) {VirtIONet *n = opaque; - n->tx_timer_active =0 ; + n->tx_waiting =0 ;/* Just in case the driver is not ready on more */ if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))
@@ -744,7 +744,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)virtio_save (&n->vdev, f);qemu_put_buffer (f, n->mac,ETH_ALEN ); -qemu_put_be32 (f, n->tx_timer_active); +qemu_put_be32 (f, n->tx_waiting);qemu_put_be32 (f, n->mergeable_rx_bufs);qemu_put_be16 (f, n->status);qemu_put_byte (f, n->promisc);
@@ -773,7 +773,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)virtio_load (&n->vdev, f);qemu_get_buffer (f, n->mac,ETH_ALEN ); - n->tx_timer_active =qemu_get_be32 (f); + n->tx_waiting =qemu_get_be32 (f); n->mergeable_rx_bufs =qemu_get_be32 (f);if (version_id >=3 )
@@ -849,7 +849,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } n->mac_table.first_multi = i; -if (n->tx_timer_active) { +if (n->tx_waiting) {qemu_mod_timer (n->tx_timer,qemu_get_clock (vm_clock) + n->tx_timeout); }
@@ -940,7 +940,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,qemu_format_nic_info_str (&n->nic->nc, conf->macaddr.a); n->tx_timer =qemu_new_timer (vm_clock, virtio_net_tx_timer, n); - n->tx_timer_active =0 ; + n->tx_waiting =0 ; n->tx_timeout = net->txtimer; n->tx_burst = net->txburst; n->mergeable_rx_bufs =0 ;
PATCH v2 4/4 virtio-net: Introduce a new bottom half packet TX
In reply to Alex WilliamsonBased on a patch from Mark McLoughlin, this patch introduces a new bottom half packet transmitter that avoids the latency imposed by the tx_timer approach. Rather than scheduling a timer when a TX packet comes in, schedule a bottom half to be run from the iothread. The bottom half handler first attempts to flush the queue with notification disabled (this is where we could race with a guest without txburst). If we flush a full burst, reschedule immediately. If we send short of a full burst, try to re-enable notification. To avoid a race with TXs that may have occurred, we must then flush again. If we find some packets to send, the guest it probably active, so we can reschedule again.
tx_timer and tx_bh are mutually exclusive, so we can re-use the tx_waiting flag to indicate one or the other needs to be setup. This allows us to seamlessly migrate between timer and bh TX handling.
The bottom half handler becomes the new default and we add a new tx= option to virtio-net-pci. Usage:
-device virtio-net-pci,tx=timer # select timer mitigation vs "bh"
---
hw/s390-virtio-bus.c | 1 + hw/syborg_virtio.c | 1 + hw/virtio-net.c | 85 +++++++++++++++++++++++++++++++++++++++++++++----- hw/virtio-net.h | 1 + hw/virtio-pci.c | 1 + 5 files changed, 81 insertions(+), 8 deletions(-)
hw/s390-virtio-bus.c +1 -0
@@ -332,6 +332,7 @@ static VirtIOS390DeviceInfo s390_virtio_net = { net.txtimer, TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIOS390Device, net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx), DEFINE_PROP_END_OF_LIST(), }, };
hw/syborg_virtio.c +1 -0
@@ -300,6 +300,7 @@ static SysBusDeviceInfo syborg_virtio_net_info = { net.txtimer, TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy, net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx), DEFINE_PROP_END_OF_LIST(), } };
hw/virtio-net.c +77 -8
134 lines not on this page · raw message
hw/virtio-net.h +1 -0
@@ -60,6 +60,7 @@ typedef struct virtio_net_conf {uint32_t txtimer;int32_t txburst; +char *tx; } virtio_net_conf;/* Maximum packet size we can receive from tap device: header + 64k */
hw/virtio-pci.c +1 -0
@@ -695,6 +695,7 @@ static PCIDeviceInfo virtio_info[] = { net.txtimer, TX_TIMER_INTERVAL),DEFINE_PROP_INT32 ("x-txburst" , VirtIOPCIProxy, net.txburst,TX_BURST ), +DEFINE_PROP_STRING ("tx" , VirtIOPCIProxy, net.tx), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset,
Quoted 10 lines · Alex Williamson
Incorporated feedback. txburst= and txtimer= are now "x-" prefixed developer options. I added a tx= option, because I do want there to be a supported way to switch between TX strategies. This also drops the magic value of txtimer= 1 or 0 (setting default timeout or switching modes). I also dropped the trickiness around only enabling the bottom half for the iothread since Anthony indicates we don't care about performance for the non-iothread case.
New performance data against both qemu.git and qemu-kvm.git:
Quoted 7 lines
This shows the base performance versus patched with new default for various packet sizes and netperf test loads. The guest is a 2-way SMP, connected directly to the host via a tap (no bridge). Thanks,
Alex
ACK the series.
Please remember to clone bugzilla into libvirt and ask them to support the new option on zstream/0day, so that we do have a working backup plan.
Quoted 17 lines
---
Alex Williamson (4):
virtio-net: Introduce a new bottom half packet TX
virtio-net: Renametx_timer_activetotx_waiting
virtio-net: Limit number of packets sent per TX flush
virtio-net: Maketx_timertimeout configurablehw/s390-virtio-bus.c | 8 +++ hw/s390-virtio-bus.h | 1 hw/syborg_virtio.c | 8 +++ hw/virtio-net.c | 129 +++++++++++++++++++++++++++++++++++++++++--------- hw/virtio-net.h | 14 +++++ hw/virtio-pci.c | 8 +++ hw/virtio.h | 4 +- 7 files changed, 144 insertions(+), 28 deletions(-)
Quoted 10 lines · Alex Williamson
Incorporated feedback. txburst= and txtimer= are now "x-" prefixed developer options. I added a tx= option, because I do want there to be a supported way to switch between TX strategies. This also drops the magic value of txtimer= 1 or 0 (setting default timeout or switching modes). I also dropped the trickiness around only enabling the bottom half for the iothread since Anthony indicates we don't care about performance for the non-iothread case.
New performance data against both qemu.git and qemu-kvm.git:
Quoted 7 lines
This shows the base performance versus patched with new default for various packet sizes and netperf test loads. The guest is a 2-way SMP, connected directly to the host via a tap (no bridge). Thanks,
Alex
(whole series)
Thanks very much for the cleanups, I think it is clearer nicer now.
Later, Juan.