diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2010-09-08 14:26:14 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2010-09-08 14:26:14 -0500 |
commit | dccbe6fbab47c9a2589f436e0592933b47cbe40b (patch) | |
tree | e5a851b48a3801dd28282eb17533975cade7ac23 /hw | |
parent | 630c26893d6dc7713c0fcfc3c09d6bfe536a6ce3 (diff) | |
parent | a697a334b3c4d3250e6420f5d38550ea10eb5319 (diff) |
Merge remote branch 'mst/for_anthony' into staging
Diffstat (limited to 'hw')
-rw-r--r-- | hw/e1000.c | 11 | ||||
-rw-r--r-- | hw/s390-virtio-bus.c | 8 | ||||
-rw-r--r-- | hw/s390-virtio-bus.h | 1 | ||||
-rw-r--r-- | hw/syborg_virtio.c | 8 | ||||
-rw-r--r-- | hw/vhost_net.c | 24 | ||||
-rw-r--r-- | hw/virtio-net.c | 129 | ||||
-rw-r--r-- | hw/virtio-net.h | 14 | ||||
-rw-r--r-- | hw/virtio-pci.c | 8 | ||||
-rw-r--r-- | hw/virtio.h | 4 |
9 files changed, 175 insertions, 32 deletions
diff --git a/hw/e1000.c b/hw/e1000.c index 80b78bc618..7d7d14002f 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -345,7 +345,7 @@ is_vlan_txd(uint32_t txd_lower) /* FCS aka Ethernet CRC-32. We don't get it from backends and can't * fill it in, just pad descriptor length by 4 bytes unless guest - * told us to trip it off the packet. */ + * told us to strip it off the packet. */ static inline int fcs_len(E1000State *s) { @@ -690,9 +690,14 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) s->mac_reg[GPRC]++; s->mac_reg[TPR]++; - n = s->mac_reg[TORL]; - if ((s->mac_reg[TORL] += size) < n) + /* TOR - Total Octets Received: + * This register includes bytes received in a packet from the <Destination + * Address> field through the <CRC> field, inclusively. + */ + n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4; + if (n < s->mac_reg[TORL]) s->mac_reg[TORH]++; + s->mac_reg[TORL] = n; n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index fe6884d47d..784dc01b97 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -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,11 @@ 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_INT32("x-txburst", VirtIOS390Device, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index 333fea8963..41558c9c67 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390-virtio-bus.h @@ -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 { diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c index abf0370107..4dfd1a87b9 100644 --- a/hw/syborg_virtio.c +++ b/hw/syborg_virtio.c @@ -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,11 @@ 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_INT32("x-txburst", SyborgVirtIOProxy, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx), DEFINE_PROP_END_OF_LIST(), } }; diff --git a/hw/vhost_net.c b/hw/vhost_net.c index 0c00de272a..4a7b8194f2 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -50,7 +50,9 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); } - features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) { + features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + } return features; } @@ -63,6 +65,9 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features) if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF); + } } static int vhost_net_get_fd(VLANClientState *backend) @@ -97,6 +102,10 @@ struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd) if (r < 0) { goto fail; } + if (!tap_has_vnet_hdr_len(backend, + sizeof(struct virtio_net_hdr_mrg_rxbuf))) { + net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + } if (~net->dev.features & net->dev.backend_features) { fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n", (uint64_t)(~net->dev.features & net->dev.backend_features)); @@ -117,6 +126,10 @@ int vhost_net_start(struct vhost_net *net, { struct vhost_vring_file file = { }; int r; + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, + sizeof(struct virtio_net_hdr_mrg_rxbuf)); + } net->dev.nvqs = 2; net->dev.vqs = net->vqs; @@ -144,6 +157,9 @@ fail: } net->vc->info->poll(net->vc, true); vhost_dev_stop(&net->dev, dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } return r; } @@ -158,11 +174,17 @@ void vhost_net_stop(struct vhost_net *net, } net->vc->info->poll(net->vc, true); vhost_dev_stop(&net->dev, dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } } void vhost_net_cleanup(struct vhost_net *net) { vhost_dev_cleanup(&net->dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } qemu_free(net); } #else diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 075f72df2d..0a9cae22c4 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -36,7 +36,10 @@ typedef struct VirtIONet VirtQueue *ctrl_vq; NICState *nic; QEMUTimer *tx_timer; - int tx_timer_active; + QEMUBH *tx_bh; + uint32_t tx_timeout; + int32_t tx_burst; + int tx_waiting; uint32_t has_vnet_hdr; uint8_t has_ufo; struct { @@ -619,7 +622,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) { @@ -635,16 +638,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)) { @@ -681,38 +686,55 @@ 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) +static void virtio_net_handle_tx_timer(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) + TX_TIMER_INTERVAL); - n->tx_timer_active = 1; + qemu_get_clock(vm_clock) + n->tx_timeout); + n->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } } +static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIONet *n = to_virtio_net(vdev); + + if (unlikely(n->tx_waiting)) { + return; + } + virtio_queue_set_notification(vq, 0); + qemu_bh_schedule(n->tx_bh); + n->tx_waiting = 1; +} + 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)) @@ -722,6 +744,41 @@ static void virtio_net_tx_timer(void *opaque) virtio_net_flush_tx(n, n->tx_vq); } +static void virtio_net_tx_bh(void *opaque) +{ + VirtIONet *n = opaque; + int32_t ret; + + n->tx_waiting = 0; + + /* Just in case the driver is not ready on more */ + if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) + return; + + ret = virtio_net_flush_tx(n, n->tx_vq); + if (ret == -EBUSY) { + return; /* Notification re-enable handled by tx_complete */ + } + + /* If we flush a full burst of packets, assume there are + * more coming and immediately reschedule */ + if (ret >= n->tx_burst) { + qemu_bh_schedule(n->tx_bh); + n->tx_waiting = 1; + return; + } + + /* If less than a full burst, re-enable notification and flush + * anything that may have come in while we weren't looking. If + * we find something, assume the guest is still active and reschedule */ + virtio_queue_set_notification(n->tx_vq, 1); + if (virtio_net_flush_tx(n, n->tx_vq) > 0) { + virtio_queue_set_notification(n->tx_vq, 0); + qemu_bh_schedule(n->tx_bh); + n->tx_waiting = 1; + } +} + static void virtio_net_save(QEMUFile *f, void *opaque) { VirtIONet *n = opaque; @@ -735,7 +792,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); @@ -764,7 +821,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) @@ -840,9 +897,13 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } n->mac_table.first_multi = i; - if (n->tx_timer_active) { - qemu_mod_timer(n->tx_timer, - qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL); + if (n->tx_waiting) { + if (n->tx_timer) { + qemu_mod_timer(n->tx_timer, + qemu_get_clock(vm_clock) + n->tx_timeout); + } else { + qemu_bh_schedule(n->tx_bh); + } } return 0; } @@ -903,7 +964,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; @@ -919,7 +981,22 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf) n->vdev.reset = virtio_net_reset; n->vdev.set_status = virtio_net_set_status; n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); + + if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { + fprintf(stderr, "virtio-net: " + "Unknown option tx=%s, valid options: \"timer\" \"bh\"\n", + net->tx); + fprintf(stderr, "Defaulting to \"bh\"\n"); + } + + if (net->tx && !strcmp(net->tx, "timer")) { + n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); + n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n); + n->tx_timeout = net->txtimer; + } else { + n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); + n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); + } n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&conf->macaddr); memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac)); @@ -929,8 +1006,8 @@ 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_burst = net->txburst; n->mergeable_rx_bufs = 0; n->promisc = 1; /* for compatibility */ @@ -962,8 +1039,12 @@ void virtio_net_exit(VirtIODevice *vdev) qemu_free(n->mac_table.macs); qemu_free(n->vlans); - qemu_del_timer(n->tx_timer); - qemu_free_timer(n->tx_timer); + if (n->tx_timer) { + qemu_del_timer(n->tx_timer); + qemu_free_timer(n->tx_timer); + } else { + qemu_bh_delete(n->tx_bh); + } virtio_cleanup(&n->vdev); qemu_del_vlan_client(&n->nic->nc); diff --git a/hw/virtio-net.h b/hw/virtio-net.h index 235f1a9fa8..8af9a1ce55 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -49,6 +49,20 @@ #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; + char *tx; +} 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)) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 6e8f88a141..86e6b0a561 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -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,11 @@ 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_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, diff --git a/hw/virtio.h b/hw/virtio.h index 5836ab61e7..a60d7355de 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -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 |