diff options
Diffstat (limited to 'hw/net/virtio-net.c')
-rw-r--r-- | hw/net/virtio-net.c | 133 |
1 files changed, 88 insertions, 45 deletions
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 01f1351554..5009533cfa 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -31,6 +31,11 @@ #define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ +/* previously fixed value */ +#define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 +/* for now, only allow larger queues; with virtio-1, guest can downsize */ +#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE + /* * Calculate the number of bytes up to and including the given 'field' of * 'container'. @@ -503,6 +508,10 @@ static void virtio_net_set_queues(VirtIONet *n) int i; int r; + if (n->nic->peer_deleted) { + return; + } + for (i = 0; i < n->max_queues; i++) { if (i < n->curr_queues) { r = peer_attach(n, i); @@ -875,6 +884,7 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } + static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = VIRTIO_NET(vdev); @@ -892,8 +902,10 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) } if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { - error_report("virtio-net ctrl missing headers"); - exit(1); + virtio_error(vdev, "virtio-net ctrl missing headers"); + virtqueue_detach_element(vq, elem, 0); + g_free(elem); + break; } iov_cnt = elem->out_num; @@ -1122,21 +1134,24 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); if (!elem) { - if (i == 0) - 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%" PRIx64, - i, n->mergeable_rx_bufs, offset, size, - n->guest_hdr_len, n->host_hdr_len, - vdev->guest_features); - exit(1); + if (i) { + virtio_error(vdev, "virtio-net unexpected empty queue: " + "i %zd mergeable %d offset %zd, size %zd, " + "guest hdr len %zd, host hdr len %zd " + "guest features 0x%" PRIx64, + i, n->mergeable_rx_bufs, offset, size, + n->guest_hdr_len, n->host_hdr_len, + vdev->guest_features); + } + return -1; } if (elem->in_num < 1) { - error_report("virtio-net receive queue contains no in buffers"); - exit(1); + virtio_error(vdev, + "virtio-net receive queue contains no in buffers"); + virtqueue_detach_element(q->rx_vq, elem, 0); + g_free(elem); + return -1; } sg = elem->in_sg; @@ -1166,7 +1181,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t * must have consumed the complete packet. * Otherwise, drop it. */ if (!n->mergeable_rx_bufs && offset < size) { - virtqueue_discard(q->rx_vq, elem, total); + virtqueue_unpop(q->rx_vq, elem, total); g_free(elem); return size; } @@ -1238,15 +1253,19 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) out_num = elem->out_num; out_sg = elem->out_sg; if (out_num < 1) { - error_report("virtio-net header not in first element"); - exit(1); + virtio_error(vdev, "virtio-net header not in first element"); + virtqueue_detach_element(q->tx_vq, elem, 0); + g_free(elem); + return -EINVAL; } if (n->has_vnet_hdr) { if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < n->guest_hdr_len) { - error_report("virtio-net header incorrect"); - exit(1); + virtio_error(vdev, "virtio-net header incorrect"); + virtqueue_detach_element(q->tx_vq, elem, 0); + g_free(elem); + return -EINVAL; } if (n->needs_vnet_hdr_swap) { virtio_net_hdr_swap(vdev, (void *) &mhdr); @@ -1314,7 +1333,9 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) virtio_queue_set_notification(vq, 1); timer_del(q->tx_timer); q->tx_waiting = 0; - virtio_net_flush_tx(q); + if (virtio_net_flush_tx(q) == -EINVAL) { + return; + } } else { timer_mod(q->tx_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); @@ -1385,8 +1406,9 @@ static void virtio_net_tx_bh(void *opaque) } ret = virtio_net_flush_tx(q); - if (ret == -EBUSY) { - return; /* Notification re-enable handled by tx_complete */ + if (ret == -EBUSY || ret == -EINVAL) { + return; /* Notification re-enable handled by tx_complete or device + * broken */ } /* If we flush a full burst of packets, assume there are @@ -1401,7 +1423,10 @@ static void virtio_net_tx_bh(void *opaque) * 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(q->tx_vq, 1); - if (virtio_net_flush_tx(q) > 0) { + ret = virtio_net_flush_tx(q); + if (ret == -EINVAL) { + return; + } else if (ret > 0) { virtio_queue_set_notification(q->tx_vq, 0); qemu_bh_schedule(q->tx_bh); q->tx_waiting = 1; @@ -1412,7 +1437,8 @@ static void virtio_net_add_queue(VirtIONet *n, int index) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - n->vqs[index].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); + n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size, + virtio_net_handle_rx); if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { n->vqs[index].tx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); @@ -1492,17 +1518,6 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) virtio_net_set_queues(n); } -static void virtio_net_save(QEMUFile *f, void *opaque, size_t size) -{ - VirtIONet *n = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - /* At this point, backend must be stopped, otherwise - * it might keep writing to memory. */ - assert(!n->vhost_started); - virtio_save(vdev, f); -} - static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) { VirtIONet *n = VIRTIO_NET(vdev); @@ -1538,14 +1553,6 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } -static int virtio_net_load(QEMUFile *f, void *opaque, size_t size) -{ - VirtIONet *n = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - return virtio_load(vdev, f, VIRTIO_NET_VM_VERSION); -} - static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, int version_id) { @@ -1720,6 +1727,22 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_net_set_config_size(n, n->host_features); virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); + /* + * We set a lower limit on RX queue size to what it always was. + * Guests that want a smaller ring can always resize it without + * help from us (using virtio 1 and up). + */ + if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || + n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || + (n->net_conf.rx_queue_size & (n->net_conf.rx_queue_size - 1))) { + error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " + "must be a power of 2 between %d and %d.", + n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, + VIRTQUEUE_MAX_SIZE); + virtio_cleanup(vdev); + return; + } + n->max_queues = MAX(n->nic_conf.peers.queues, 1); if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " @@ -1832,8 +1855,25 @@ static void virtio_net_instance_init(Object *obj) DEVICE(n), NULL); } -VMSTATE_VIRTIO_DEVICE(net, VIRTIO_NET_VM_VERSION, virtio_net_load, - virtio_net_save); +static void virtio_net_pre_save(void *opaque) +{ + VirtIONet *n = opaque; + + /* At this point, backend must be stopped, otherwise + * it might keep writing to memory. */ + assert(!n->vhost_started); +} + +static const VMStateDescription vmstate_virtio_net = { + .name = "virtio-net", + .minimum_version_id = VIRTIO_NET_VM_VERSION, + .version_id = VIRTIO_NET_VM_VERSION, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, + .pre_save = virtio_net_pre_save, +}; static Property virtio_net_properties[] = { DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true), @@ -1880,6 +1920,8 @@ static Property virtio_net_properties[] = { TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), + DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, + VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), DEFINE_PROP_END_OF_LIST(), }; @@ -1904,6 +1946,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; vdc->load = virtio_net_load_device; vdc->save = virtio_net_save_device; + vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO); } static const TypeInfo virtio_net_info = { |