* Re: [Qemu-devel] [RFC V2 PATCH 4/4] virtio-net: add multiqueue support [not found] ` <20120625100449.8096.65545.stgit@amd-6168-8-1.englab.nay.redhat.com> @ 2012-07-01 9:43 ` Michael S. Tsirkin 2012-07-02 7:04 ` Jason Wang 0 siblings, 1 reply; 3+ messages in thread From: Michael S. Tsirkin @ 2012-07-01 9:43 UTC (permalink / raw) To: Jason Wang Cc: krkumar2, habanero, aliguori, mashirle, kvm, rusty, qemu-devel, virtualization, tahm, jwhan, akong On Mon, Jun 25, 2012 at 06:04:49PM +0800, Jason Wang wrote: > This patch let the virtio-net can transmit and recevie packets through multiuple > VLANClientStates and abstract them as multiple virtqueues to guest. A new > parameter 'queues' were introduced to specify the number of queue pairs. > > The main goal for vhost support is to let the multiqueue could be used without > changes in vhost code. So each vhost_net structure were used to track a single > VLANClientState and two virtqueues in the past. As multiple VLANClientState were > stored in the NICState, we can infer the correspond VLANClientState from this > and queue_index easily. > > Signed-off-by: Jason Wang <jasowang@redhat.com> Can this patch be split up? 1. extend vhost API to allow multiqueue and minimally tweak virtio 2. add real multiqueue for virtio Hmm? > --- > hw/vhost.c | 58 ++++--- > hw/vhost.h | 1 > hw/vhost_net.c | 7 + > hw/vhost_net.h | 2 > hw/virtio-net.c | 461 +++++++++++++++++++++++++++++++++++++------------------ > hw/virtio-net.h | 3 > 6 files changed, 355 insertions(+), 177 deletions(-) > > diff --git a/hw/vhost.c b/hw/vhost.c > index 43664e7..6318bb2 100644 > --- a/hw/vhost.c > +++ b/hw/vhost.c > @@ -620,11 +620,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, > { > target_phys_addr_t s, l, a; > int r; > + int vhost_vq_index = (idx > 2 ? idx - 1 : idx) % dev->nvqs; > struct vhost_vring_file file = { > - .index = idx, > + .index = vhost_vq_index > }; > struct vhost_vring_state state = { > - .index = idx, > + .index = vhost_vq_index > }; > struct VirtQueue *vvq = virtio_get_queue(vdev, idx); > > @@ -670,11 +671,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, > goto fail_alloc_ring; > } > > - r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); > + r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); > if (r < 0) { > r = -errno; > goto fail_alloc; > } > + > file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); > r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); > if (r) { > @@ -715,7 +717,7 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev, > unsigned idx) > { > struct vhost_vring_state state = { > - .index = idx, > + .index = (idx > 2 ? idx - 1 : idx) % dev->nvqs, > }; > int r; > r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); > @@ -829,7 +831,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > } > > for (i = 0; i < hdev->nvqs; ++i) { > - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); > + r = vdev->binding->set_host_notifier(vdev->binding_opaque, > + hdev->start_idx + i, > + true); > if (r < 0) { > fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); > goto fail_vq; > @@ -839,7 +843,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > return 0; > fail_vq: > while (--i >= 0) { > - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); > + r = vdev->binding->set_host_notifier(vdev->binding_opaque, > + hdev->start_idx + i, > + false); > if (r < 0) { > fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); > fflush(stderr); > @@ -860,7 +866,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > int i, r; > > for (i = 0; i < hdev->nvqs; ++i) { > - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); > + r = vdev->binding->set_host_notifier(vdev->binding_opaque, > + hdev->start_idx + i, > + false); > if (r < 0) { > fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); > fflush(stderr); > @@ -874,15 +882,17 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) > { > int i, r; > if (!vdev->binding->set_guest_notifiers) { > - fprintf(stderr, "binding does not support guest notifiers\n"); > + fprintf(stderr, "binding does not support guest notifier\n"); > r = -ENOSYS; > goto fail; > } > > - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); > - if (r < 0) { > - fprintf(stderr, "Error binding guest notifier: %d\n", -r); > - goto fail_notifiers; > + if (hdev->start_idx == 0) { > + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); > + if (r < 0) { > + fprintf(stderr, "Error binding guest notifier: %d\n", -r); > + goto fail_notifiers; > + } > } > > r = vhost_dev_set_features(hdev, hdev->log_enabled); > @@ -898,7 +908,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) > r = vhost_virtqueue_init(hdev, > vdev, > hdev->vqs + i, > - i); > + hdev->start_idx + i); > if (r < 0) { > goto fail_vq; > } > @@ -925,11 +935,13 @@ fail_vq: > vhost_virtqueue_cleanup(hdev, > vdev, > hdev->vqs + i, > - i); > + hdev->start_idx + i); > } > + i = hdev->nvqs; > fail_mem: > fail_features: > - vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > + if (hdev->start_idx == 0) > + vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > fail_notifiers: > fail: > return r; > @@ -944,18 +956,22 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) > vhost_virtqueue_cleanup(hdev, > vdev, > hdev->vqs + i, > - i); > + hdev->start_idx + i); > } > + > for (i = 0; i < hdev->n_mem_sections; ++i) { > vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i], > 0, (target_phys_addr_t)~0x0ull); > } > - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > - if (r < 0) { > - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); > - fflush(stderr); > + > + if (hdev->start_idx == 0) { > + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > + if (r < 0) { > + fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); > + fflush(stderr); > + } > + assert (r >= 0); > } > - assert (r >= 0); > > hdev->started = false; > g_free(hdev->log); > diff --git a/hw/vhost.h b/hw/vhost.h > index 80e64df..fa5357a 100644 > --- a/hw/vhost.h > +++ b/hw/vhost.h > @@ -34,6 +34,7 @@ struct vhost_dev { > MemoryRegionSection *mem_sections; > struct vhost_virtqueue *vqs; > int nvqs; > + int start_idx; > unsigned long long features; > unsigned long long acked_features; > unsigned long long backend_features; > diff --git a/hw/vhost_net.c b/hw/vhost_net.c > index f672e9d..73a72bb 100644 > --- a/hw/vhost_net.c > +++ b/hw/vhost_net.c > @@ -138,13 +138,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) > } > > int vhost_net_start(struct vhost_net *net, > - VirtIODevice *dev) > + VirtIODevice *dev, > + int start_idx) > { > struct vhost_vring_file file = { }; > int r; > > net->dev.nvqs = 2; > net->dev.vqs = net->vqs; > + net->dev.start_idx = start_idx; > > r = vhost_dev_enable_notifiers(&net->dev, dev); > if (r < 0) { > @@ -227,7 +229,8 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) > } > > int vhost_net_start(struct vhost_net *net, > - VirtIODevice *dev) > + VirtIODevice *dev, > + int start_idx) > { > return -ENOSYS; > } > diff --git a/hw/vhost_net.h b/hw/vhost_net.h > index 91e40b1..79a4f09 100644 > --- a/hw/vhost_net.h > +++ b/hw/vhost_net.h > @@ -9,7 +9,7 @@ typedef struct vhost_net VHostNetState; > VHostNetState *vhost_net_init(VLANClientState *backend, int devfd, bool force); > > bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); > -int vhost_net_start(VHostNetState *net, VirtIODevice *dev); > +int vhost_net_start(VHostNetState *net, VirtIODevice *dev, int start_idx); > void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); > > void vhost_net_cleanup(VHostNetState *net); > diff --git a/hw/virtio-net.c b/hw/virtio-net.c > index 3f190d4..d42c4cc 100644 > --- a/hw/virtio-net.c > +++ b/hw/virtio-net.c > @@ -26,34 +26,43 @@ > #define MAC_TABLE_ENTRIES 64 > #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ > > -typedef struct VirtIONet > +struct VirtIONet; > + > +typedef struct VirtIONetQueue > { > - VirtIODevice vdev; > - uint8_t mac[ETH_ALEN]; > - uint16_t status; > VirtQueue *rx_vq; > VirtQueue *tx_vq; > - VirtQueue *ctrl_vq; > - NICState *nic; > QEMUTimer *tx_timer; > QEMUBH *tx_bh; > uint32_t tx_timeout; > - int32_t tx_burst; > int tx_waiting; > - uint32_t has_vnet_hdr; > - uint8_t has_ufo; > struct { > VirtQueueElement elem; > ssize_t len; > } async_tx; > + struct VirtIONet *n; > + uint8_t vhost_started; > +} VirtIONetQueue; > + > +typedef struct VirtIONet > +{ > + VirtIODevice vdev; > + uint8_t mac[ETH_ALEN]; > + uint16_t status; > + VirtIONetQueue vqs[MAX_QUEUE_NUM]; > + VirtQueue *ctrl_vq; > + NICState *nic; > + int32_t tx_burst; > + uint32_t has_vnet_hdr; > + uint8_t has_ufo; > int mergeable_rx_bufs; > + int multiqueue; > uint8_t promisc; > uint8_t allmulti; > uint8_t alluni; > uint8_t nomulti; > uint8_t nouni; > uint8_t nobcast; > - uint8_t vhost_started; > struct { > int in_use; > int first_multi; > @@ -63,6 +72,7 @@ typedef struct VirtIONet > } mac_table; > uint32_t *vlans; > DeviceState *qdev; > + uint32_t queues; > } VirtIONet; > > /* TODO > @@ -74,12 +84,25 @@ static VirtIONet *to_virtio_net(VirtIODevice *vdev) > return (VirtIONet *)vdev; > } > > +static int vq_get_pair_index(VirtIONet *n, VirtQueue *vq) > +{ > + int i; > + for (i = 0; i < n->queues; i++) { > + if (n->vqs[i].tx_vq == vq || n->vqs[i].rx_vq == vq) { > + return i; > + } > + } > + assert(1); > + return -1; > +} > + > static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) > { > VirtIONet *n = to_virtio_net(vdev); > struct virtio_net_config netcfg; > > stw_p(&netcfg.status, n->status); > + netcfg.queues = n->queues * 2; > memcpy(netcfg.mac, n->mac, ETH_ALEN); > memcpy(config, &netcfg, sizeof(netcfg)); > } > @@ -103,78 +126,140 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) > (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; > } > > -static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > +static void nc_vhost_status(VLANClientState *nc, VirtIONet *n, > + uint8_t status) > { > - if (!n->nic->nc.peer) { > + int queue_index = nc->queue_index; > + VLANClientState *peer = nc->peer; > + VirtIONetQueue *netq = &n->vqs[nc->queue_index]; > + > + if (!peer) { > return; > } > - if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > + if (peer->info->type != NET_CLIENT_TYPE_TAP) { > return; > } > > - if (!tap_get_vhost_net(n->nic->nc.peer)) { > + if (!tap_get_vhost_net(peer)) { > return; > } > - if (!!n->vhost_started == virtio_net_started(n, status) && > - !n->nic->nc.peer->link_down) { > + if (!!netq->vhost_started == virtio_net_started(n, status) && > + !peer->link_down) { > return; > } > - if (!n->vhost_started) { > - int r; > - if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { > + if (!netq->vhost_started) { > + /* skip ctrl vq */ > + int r, start_idx = queue_index == 0 ? 0 : queue_index * 2 + 1; > + if (!vhost_net_query(tap_get_vhost_net(peer), &n->vdev)) { > return; > } > - r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); > + r = vhost_net_start(tap_get_vhost_net(peer), &n->vdev, start_idx); > if (r < 0) { > error_report("unable to start vhost net: %d: " > "falling back on userspace virtio", -r); > } else { > - n->vhost_started = 1; > + netq->vhost_started = 1; > } > } else { > - vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); > - n->vhost_started = 0; > + vhost_net_stop(tap_get_vhost_net(peer), &n->vdev); > + netq->vhost_started = 0; > + } > +} > + > +static int peer_attach(VirtIONet *n, int index) > +{ > + if (!n->nic->ncs[index]->peer) { > + return -1; > + } > + > + if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { > + return -1; > + } > + > + return tap_attach(n->nic->ncs[index]->peer); > +} > + > +static int peer_detach(VirtIONet *n, int index) > +{ > + if (!n->nic->ncs[index]->peer) { > + return -1; > + } > + > + if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { > + return -1; > + } > + > + return tap_detach(n->nic->ncs[index]->peer); > +} > + > +static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > +{ > + int i; > + for (i = 0; i < n->queues; i++) { > + if (!n->multiqueue && i != 0) > + status = 0; > + nc_vhost_status(n->nic->ncs[i], n, status); > } > } > > static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) > { > VirtIONet *n = to_virtio_net(vdev); > + int i; > > virtio_net_vhost_status(n, status); > > - if (!n->tx_waiting) { > - return; > - } > + for (i = 0; i < n->queues; i++) { > + VirtIONetQueue *netq = &n->vqs[i]; > + if (!netq->tx_waiting) { > + continue; > + } > + > + if (!n->multiqueue && i != 0) > + status = 0; > > - if (virtio_net_started(n, status) && !n->vhost_started) { > - if (n->tx_timer) { > - qemu_mod_timer(n->tx_timer, > - qemu_get_clock_ns(vm_clock) + n->tx_timeout); > + if (virtio_net_started(n, status) && !netq->vhost_started) { > + if (netq->tx_timer) { > + qemu_mod_timer(netq->tx_timer, > + qemu_get_clock_ns(vm_clock) + netq->tx_timeout); > + } else { > + qemu_bh_schedule(netq->tx_bh); > + } > } else { > - qemu_bh_schedule(n->tx_bh); > + if (netq->tx_timer) { > + qemu_del_timer(netq->tx_timer); > + } else { > + qemu_bh_cancel(netq->tx_bh); > + } > } > - } else { > - if (n->tx_timer) { > - qemu_del_timer(n->tx_timer); > - } else { > - qemu_bh_cancel(n->tx_bh); > + } > +} > + > +static bool virtio_net_is_link_up(VirtIONet *n) > +{ > + int i; > + for (i = 0; i < n->queues; i++) { > + if (n->nic->ncs[i]->link_down) { > + return false; > } > } > + return true; > } > > static void virtio_net_set_link_status(VLANClientState *nc) > { > - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > + VirtIONet *n = ((NICState *)(nc->opaque))->opaque; > uint16_t old_status = n->status; > > - if (nc->link_down) > + if (virtio_net_is_link_up(n)) { > n->status &= ~VIRTIO_NET_S_LINK_UP; > - else > + } else { > n->status |= VIRTIO_NET_S_LINK_UP; > + } > > - if (n->status != old_status) > + if (n->status != old_status) { > virtio_notify_config(&n->vdev); > + } > > virtio_net_set_status(&n->vdev, n->vdev.status); > } > @@ -202,13 +287,15 @@ static void virtio_net_reset(VirtIODevice *vdev) > > static int peer_has_vnet_hdr(VirtIONet *n) > { > - if (!n->nic->nc.peer) > + if (!n->nic->ncs[0]->peer) { > return 0; > + } > > - if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) > + if (n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { > return 0; > + } > > - n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); > + n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->ncs[0]->peer); > > return n->has_vnet_hdr; > } > @@ -218,7 +305,7 @@ static int peer_has_ufo(VirtIONet *n) > if (!peer_has_vnet_hdr(n)) > return 0; > > - n->has_ufo = tap_has_ufo(n->nic->nc.peer); > + n->has_ufo = tap_has_ufo(n->nic->ncs[0]->peer); > > return n->has_ufo; > } > @@ -228,9 +315,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) > VirtIONet *n = to_virtio_net(vdev); > > features |= (1 << VIRTIO_NET_F_MAC); > + features |= (1 << VIRTIO_NET_F_MULTIQUEUE); > > if (peer_has_vnet_hdr(n)) { > - tap_using_vnet_hdr(n->nic->nc.peer, 1); > + int i; > + for (i = 0; i < n->queues; i++) { > + tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); > + } > } else { > features &= ~(0x1 << VIRTIO_NET_F_CSUM); > features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); > @@ -248,14 +339,15 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) > features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); > } > > - if (!n->nic->nc.peer || > - n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > + if (!n->nic->ncs[0]->peer || > + n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { > return features; > } > - if (!tap_get_vhost_net(n->nic->nc.peer)) { > + if (!tap_get_vhost_net(n->nic->ncs[0]->peer)) { > return features; > } > - return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); > + return vhost_net_get_features(tap_get_vhost_net(n->nic->ncs[0]->peer), > + features); > } > > static uint32_t virtio_net_bad_features(VirtIODevice *vdev) > @@ -276,25 +368,38 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) > static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) > { > VirtIONet *n = to_virtio_net(vdev); > + int i, r; > > n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); > + n->multiqueue = !!(features & (1 << VIRTIO_NET_F_MULTIQUEUE)); > > - if (n->has_vnet_hdr) { > - tap_set_offload(n->nic->nc.peer, > - (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, > - (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, > - (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, > - (features >> VIRTIO_NET_F_GUEST_ECN) & 1, > - (features >> VIRTIO_NET_F_GUEST_UFO) & 1); > - } > - if (!n->nic->nc.peer || > - n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > - return; > - } > - if (!tap_get_vhost_net(n->nic->nc.peer)) { > - return; > + for (i = 0; i < n->queues; i++) { > + if (!n->multiqueue && i != 0) { > + r = peer_detach(n, i); > + assert(r == 0); > + } else { > + r = peer_attach(n, i); > + assert(r == 0); > + > + if (n->has_vnet_hdr) { > + tap_set_offload(n->nic->ncs[i]->peer, > + (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, > + (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, > + (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, > + (features >> VIRTIO_NET_F_GUEST_ECN) & 1, > + (features >> VIRTIO_NET_F_GUEST_UFO) & 1); > + } > + if (!n->nic->ncs[i]->peer || > + n->nic->ncs[i]->peer->info->type != NET_CLIENT_TYPE_TAP) { > + continue; > + } > + if (!tap_get_vhost_net(n->nic->ncs[i]->peer)) { > + continue; > + } > + vhost_net_ack_features(tap_get_vhost_net(n->nic->ncs[i]->peer), > + features); > + } > } > - vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); > } > > static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, > @@ -446,7 +551,7 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > > - qemu_flush_queued_packets(&n->nic->nc); > + qemu_flush_queued_packets(n->nic->ncs[vq_get_pair_index(n, vq)]); > > /* We now have RX buffers, signal to the IO thread to break out of the > * select to re-poll the tap file descriptor */ > @@ -455,36 +560,37 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) > > static int virtio_net_can_receive(VLANClientState *nc) > { > - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > + int queue_index = nc->queue_index; > + VirtIONet *n = ((NICState *)nc->opaque)->opaque; > + > if (!n->vdev.vm_running) { > return 0; > } > > - if (!virtio_queue_ready(n->rx_vq) || > + if (!virtio_queue_ready(n->vqs[queue_index].rx_vq) || > !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) > return 0; > > return 1; > } > > -static int virtio_net_has_buffers(VirtIONet *n, int bufsize) > +static int virtio_net_has_buffers(VirtIONet *n, int bufsize, VirtQueue *vq) > { > - if (virtio_queue_empty(n->rx_vq) || > - (n->mergeable_rx_bufs && > - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { > - virtio_queue_set_notification(n->rx_vq, 1); > + if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs && > + !virtqueue_avail_bytes(vq, bufsize, 0))) { > + virtio_queue_set_notification(vq, 1); > > /* To avoid a race condition where the guest has made some buffers > * available after the above check but before notification was > * enabled, check for available buffers again. > */ > - if (virtio_queue_empty(n->rx_vq) || > - (n->mergeable_rx_bufs && > - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) > + if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs && > + !virtqueue_avail_bytes(vq, bufsize, 0))) { > return 0; > + } > } > > - virtio_queue_set_notification(n->rx_vq, 0); > + virtio_queue_set_notification(vq, 0); > return 1; > } > > @@ -595,12 +701,15 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) > > static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size) > { > - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > + int queue_index = nc->queue_index; > + VirtIONet *n = ((NICState *)(nc->opaque))->opaque; > + VirtQueue *vq = n->vqs[queue_index].rx_vq; > struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; > size_t guest_hdr_len, offset, i, host_hdr_len; > > - if (!virtio_net_can_receive(&n->nic->nc)) > + if (!virtio_net_can_receive(n->nic->ncs[queue_index])) { > return -1; > + } > > /* hdr_len refers to the header we supply to the guest */ > guest_hdr_len = n->mergeable_rx_bufs ? > @@ -608,7 +717,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > > > host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; > - if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) > + if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len, vq)) > return 0; > > if (!receive_filter(n, buf, size)) > @@ -623,7 +732,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > > total = 0; > > - if (virtqueue_pop(n->rx_vq, &elem) == 0) { > + if (virtqueue_pop(vq, &elem) == 0) { > if (i == 0) > return -1; > error_report("virtio-net unexpected empty queue: " > @@ -675,47 +784,50 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > } > > /* signal other side */ > - virtqueue_fill(n->rx_vq, &elem, total, i++); > + virtqueue_fill(vq, &elem, total, i++); > } > > if (mhdr) { > stw_p(&mhdr->num_buffers, i); > } > > - virtqueue_flush(n->rx_vq, i); > - virtio_notify(&n->vdev, n->rx_vq); > + virtqueue_flush(vq, i); > + virtio_notify(&n->vdev, vq); > > return size; > } > > -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); > +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *tvq); > > static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) > { > - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > + VirtIONet *n = ((NICState *)nc->opaque)->opaque; > + VirtIONetQueue *netq = &n->vqs[nc->queue_index]; > > - virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); > - virtio_notify(&n->vdev, n->tx_vq); > + virtqueue_push(netq->tx_vq, &netq->async_tx.elem, netq->async_tx.len); > + virtio_notify(&n->vdev, netq->tx_vq); > > - n->async_tx.elem.out_num = n->async_tx.len = 0; > + netq->async_tx.elem.out_num = netq->async_tx.len; > > - virtio_queue_set_notification(n->tx_vq, 1); > - virtio_net_flush_tx(n, n->tx_vq); > + virtio_queue_set_notification(netq->tx_vq, 1); > + virtio_net_flush_tx(n, netq); > } > > /* TX */ > -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *netq) > { > VirtQueueElement elem; > int32_t num_packets = 0; > + VirtQueue *vq = netq->tx_vq; > + > if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { > return num_packets; > } > > assert(n->vdev.vm_running); > > - if (n->async_tx.elem.out_num) { > - virtio_queue_set_notification(n->tx_vq, 0); > + if (netq->async_tx.elem.out_num) { > + virtio_queue_set_notification(vq, 0); > return num_packets; > } > > @@ -747,12 +859,12 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > len += hdr_len; > } > > - ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, > - virtio_net_tx_complete); > + ret = qemu_sendv_packet_async(n->nic->ncs[vq_get_pair_index(n, vq)], > + out_sg, out_num, virtio_net_tx_complete); > if (ret == 0) { > - virtio_queue_set_notification(n->tx_vq, 0); > - n->async_tx.elem = elem; > - n->async_tx.len = len; > + virtio_queue_set_notification(vq, 0); > + netq->async_tx.elem = elem; > + netq->async_tx.len = len; > return -EBUSY; > } > > @@ -771,22 +883,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > + VirtIONetQueue *netq = &n->vqs[vq_get_pair_index(n, vq)]; > > /* This happens when device was stopped but VCPU wasn't. */ > if (!n->vdev.vm_running) { > - n->tx_waiting = 1; > + netq->tx_waiting = 1; > return; > } > > - if (n->tx_waiting) { > + if (netq->tx_waiting) { > virtio_queue_set_notification(vq, 1); > - qemu_del_timer(n->tx_timer); > - n->tx_waiting = 0; > - virtio_net_flush_tx(n, vq); > + qemu_del_timer(netq->tx_timer); > + netq->tx_waiting = 0; > + virtio_net_flush_tx(n, netq); > } else { > - qemu_mod_timer(n->tx_timer, > - qemu_get_clock_ns(vm_clock) + n->tx_timeout); > - n->tx_waiting = 1; > + qemu_mod_timer(netq->tx_timer, > + qemu_get_clock_ns(vm_clock) + netq->tx_timeout); > + netq->tx_waiting = 1; > virtio_queue_set_notification(vq, 0); > } > } > @@ -794,48 +907,53 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) > static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > + VirtIONetQueue *netq = &n->vqs[vq_get_pair_index(n, vq)]; > > - if (unlikely(n->tx_waiting)) { > + if (unlikely(netq->tx_waiting)) { > return; > } > - n->tx_waiting = 1; > + netq->tx_waiting = 1; > /* This happens when device was stopped but VCPU wasn't. */ > if (!n->vdev.vm_running) { > return; > } > virtio_queue_set_notification(vq, 0); > - qemu_bh_schedule(n->tx_bh); > + qemu_bh_schedule(netq->tx_bh); > } > > static void virtio_net_tx_timer(void *opaque) > { > - VirtIONet *n = opaque; > + VirtIONetQueue *netq = opaque; > + VirtIONet *n = netq->n; > + > assert(n->vdev.vm_running); > > - n->tx_waiting = 0; > + netq->tx_waiting = 0; > > /* Just in case the driver is not ready on more */ > if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) > return; > > - virtio_queue_set_notification(n->tx_vq, 1); > - virtio_net_flush_tx(n, n->tx_vq); > + virtio_queue_set_notification(netq->tx_vq, 1); > + virtio_net_flush_tx(n, netq); > } > > static void virtio_net_tx_bh(void *opaque) > { > - VirtIONet *n = opaque; > + VirtIONetQueue *netq = opaque; > + VirtQueue *vq = netq->tx_vq; > + VirtIONet *n = netq->n; > int32_t ret; > > assert(n->vdev.vm_running); > > - n->tx_waiting = 0; > + netq->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); > + ret = virtio_net_flush_tx(n, netq); > if (ret == -EBUSY) { > return; /* Notification re-enable handled by tx_complete */ > } > @@ -843,33 +961,39 @@ static void virtio_net_tx_bh(void *opaque) > /* 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; > + qemu_bh_schedule(netq->tx_bh); > + netq->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; > + virtio_queue_set_notification(vq, 1); > + if (virtio_net_flush_tx(n, netq) > 0) { > + virtio_queue_set_notification(vq, 0); > + qemu_bh_schedule(netq->tx_bh); > + netq->tx_waiting = 1; > } > } > > static void virtio_net_save(QEMUFile *f, void *opaque) > { > VirtIONet *n = opaque; > + int i; > > /* At this point, backend must be stopped, otherwise > * it might keep writing to memory. */ > - assert(!n->vhost_started); > + for (i = 0; i < n->queues; i++) { > + assert(!n->vqs[i].vhost_started); > + } > virtio_save(&n->vdev, f); > > qemu_put_buffer(f, n->mac, ETH_ALEN); > - qemu_put_be32(f, n->tx_waiting); > + qemu_put_be32(f, n->queues); > + for (i = 0; i < n->queues; i++) { > + qemu_put_be32(f, n->vqs[i].tx_waiting); > + } > qemu_put_be32(f, n->mergeable_rx_bufs); > qemu_put_be16(f, n->status); > qemu_put_byte(f, n->promisc); > @@ -902,7 +1026,10 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > } > > qemu_get_buffer(f, n->mac, ETH_ALEN); > - n->tx_waiting = qemu_get_be32(f); > + n->queues = qemu_get_be32(f); > + for (i = 0; i < n->queues; i++) { > + n->vqs[i].tx_waiting = qemu_get_be32(f); > + } > n->mergeable_rx_bufs = qemu_get_be32(f); > > if (version_id >= 3) > @@ -930,7 +1057,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > n->mac_table.in_use = 0; > } > } > - > + > if (version_id >= 6) > qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); > > @@ -941,13 +1068,16 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > } > > if (n->has_vnet_hdr) { > - tap_using_vnet_hdr(n->nic->nc.peer, 1); > - tap_set_offload(n->nic->nc.peer, > - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, > - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, > - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, > - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN) & 1, > - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO) & 1); > + for(i = 0; i < n->queues; i++) { > + tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); > + tap_set_offload(n->nic->ncs[i]->peer, > + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, > + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, > + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, > + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN) & 1, > + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO) & > + 1); > + } > } > } > > @@ -982,7 +1112,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > > static void virtio_net_cleanup(VLANClientState *nc) > { > - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > + VirtIONet *n = ((NICState *)nc->opaque)->opaque; > > n->nic = NULL; > } > @@ -1000,6 +1130,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > virtio_net_conf *net) > { > VirtIONet *n; > + int i; > > n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, > sizeof(struct virtio_net_config), > @@ -1012,7 +1143,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > n->vdev.bad_features = virtio_net_bad_features; > 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); > > if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { > error_report("virtio-net: " > @@ -1021,15 +1151,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > error_report("Defaulting to \"bh\""); > } > > - 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_ns(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)); > n->status = VIRTIO_NET_S_LINK_UP; > @@ -1038,7 +1159,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > > qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); > > - n->tx_waiting = 0; > n->tx_burst = net->txburst; > n->mergeable_rx_bufs = 0; > n->promisc = 1; /* for compatibility */ > @@ -1046,6 +1166,32 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); > > n->vlans = g_malloc0(MAX_VLAN >> 3); > + n->queues = conf->queues; > + > + /* Allocate per rx/tx vq's */ > + for (i = 0; i < n->queues; i++) { > + n->vqs[i].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); > + if (net->tx && !strcmp(net->tx, "timer")) { > + n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, > + virtio_net_handle_tx_timer); > + n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock, > + virtio_net_tx_timer, > + &n->vqs[i]); > + n->vqs[i].tx_timeout = net->txtimer; > + } else { > + n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, > + virtio_net_handle_tx_bh); > + n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[i]); > + } > + > + n->vqs[i].tx_waiting = 0; > + n->vqs[i].n = n; > + > + if (i == 0) { > + /* keep compatiable with spec and old guest */ > + n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); > + } > + } > > n->qdev = dev; > register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, > @@ -1059,24 +1205,33 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > void virtio_net_exit(VirtIODevice *vdev) > { > VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); > + int i; > > /* This will stop vhost backend if appropriate. */ > virtio_net_set_status(vdev, 0); > > - qemu_purge_queued_packets(&n->nic->nc); > + for (i = 0; i < n->queues; i++) { > + qemu_purge_queued_packets(n->nic->ncs[i]); > + } > > unregister_savevm(n->qdev, "virtio-net", n); > > g_free(n->mac_table.macs); > g_free(n->vlans); > > - if (n->tx_timer) { > - qemu_del_timer(n->tx_timer); > - qemu_free_timer(n->tx_timer); > - } else { > - qemu_bh_delete(n->tx_bh); > + for (i = 0; i < n->queues; i++) { > + VirtIONetQueue *netq = &n->vqs[i]; > + if (netq->tx_timer) { > + qemu_del_timer(netq->tx_timer); > + qemu_free_timer(netq->tx_timer); > + } else { > + qemu_bh_delete(netq->tx_bh); > + } > } > > - qemu_del_vlan_client(&n->nic->nc); > virtio_cleanup(&n->vdev); > + > + for (i = 0; i < n->queues; i++) { > + qemu_del_vlan_client(n->nic->ncs[i]); > + } > } > diff --git a/hw/virtio-net.h b/hw/virtio-net.h > index 36aa463..b35ba5d 100644 > --- a/hw/virtio-net.h > +++ b/hw/virtio-net.h > @@ -44,6 +44,7 @@ > #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ > #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ > #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ > +#define VIRTIO_NET_F_MULTIQUEUE 22 > > #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ > > @@ -72,6 +73,8 @@ struct virtio_net_config > uint8_t mac[ETH_ALEN]; > /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ > uint16_t status; > + > + uint16_t queues; > } QEMU_PACKED; > > /* This is the first element of the scatter-gather list. If you don't ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Qemu-devel] [RFC V2 PATCH 4/4] virtio-net: add multiqueue support 2012-07-01 9:43 ` [Qemu-devel] [RFC V2 PATCH 4/4] virtio-net: add multiqueue support Michael S. Tsirkin @ 2012-07-02 7:04 ` Jason Wang 2012-07-02 7:34 ` Michael S. Tsirkin 0 siblings, 1 reply; 3+ messages in thread From: Jason Wang @ 2012-07-02 7:04 UTC (permalink / raw) To: Michael S. Tsirkin Cc: krkumar2, habanero, aliguori, mashirle, kvm, rusty, qemu-devel, virtualization, tahm, jwhan, akong On 07/01/2012 05:43 PM, Michael S. Tsirkin wrote: > On Mon, Jun 25, 2012 at 06:04:49PM +0800, Jason Wang wrote: >> This patch let the virtio-net can transmit and recevie packets through multiuple >> VLANClientStates and abstract them as multiple virtqueues to guest. A new >> parameter 'queues' were introduced to specify the number of queue pairs. >> >> The main goal for vhost support is to let the multiqueue could be used without >> changes in vhost code. So each vhost_net structure were used to track a single >> VLANClientState and two virtqueues in the past. As multiple VLANClientState were >> stored in the NICState, we can infer the correspond VLANClientState from this >> and queue_index easily. >> >> Signed-off-by: Jason Wang<jasowang@redhat.com> > Can this patch be split up? > 1. extend vhost API to allow multiqueue and minimally tweak virtio > 2. add real multiqueue for virtio > > Hmm? Sure, do you think it's necessary to separate the vhost parts of multiqueue from virtio? >> --- >> hw/vhost.c | 58 ++++--- >> hw/vhost.h | 1 >> hw/vhost_net.c | 7 + >> hw/vhost_net.h | 2 >> hw/virtio-net.c | 461 +++++++++++++++++++++++++++++++++++++------------------ >> hw/virtio-net.h | 3 >> 6 files changed, 355 insertions(+), 177 deletions(-) >> >> diff --git a/hw/vhost.c b/hw/vhost.c >> index 43664e7..6318bb2 100644 >> --- a/hw/vhost.c >> +++ b/hw/vhost.c >> @@ -620,11 +620,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, >> { >> target_phys_addr_t s, l, a; >> int r; >> + int vhost_vq_index = (idx> 2 ? idx - 1 : idx) % dev->nvqs; >> struct vhost_vring_file file = { >> - .index = idx, >> + .index = vhost_vq_index >> }; >> struct vhost_vring_state state = { >> - .index = idx, >> + .index = vhost_vq_index >> }; >> struct VirtQueue *vvq = virtio_get_queue(vdev, idx); >> >> @@ -670,11 +671,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, >> goto fail_alloc_ring; >> } >> >> - r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); >> + r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); >> if (r< 0) { >> r = -errno; >> goto fail_alloc; >> } >> + >> file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); >> r = ioctl(dev->control, VHOST_SET_VRING_KICK,&file); >> if (r) { >> @@ -715,7 +717,7 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev, >> unsigned idx) >> { >> struct vhost_vring_state state = { >> - .index = idx, >> + .index = (idx> 2 ? idx - 1 : idx) % dev->nvqs, >> }; >> int r; >> r = ioctl(dev->control, VHOST_GET_VRING_BASE,&state); >> @@ -829,7 +831,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) >> } >> >> for (i = 0; i< hdev->nvqs; ++i) { >> - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); >> + r = vdev->binding->set_host_notifier(vdev->binding_opaque, >> + hdev->start_idx + i, >> + true); >> if (r< 0) { >> fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); >> goto fail_vq; >> @@ -839,7 +843,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) >> return 0; >> fail_vq: >> while (--i>= 0) { >> - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); >> + r = vdev->binding->set_host_notifier(vdev->binding_opaque, >> + hdev->start_idx + i, >> + false); >> if (r< 0) { >> fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); >> fflush(stderr); >> @@ -860,7 +866,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) >> int i, r; >> >> for (i = 0; i< hdev->nvqs; ++i) { >> - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); >> + r = vdev->binding->set_host_notifier(vdev->binding_opaque, >> + hdev->start_idx + i, >> + false); >> if (r< 0) { >> fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); >> fflush(stderr); >> @@ -874,15 +882,17 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) >> { >> int i, r; >> if (!vdev->binding->set_guest_notifiers) { >> - fprintf(stderr, "binding does not support guest notifiers\n"); >> + fprintf(stderr, "binding does not support guest notifier\n"); >> r = -ENOSYS; >> goto fail; >> } >> >> - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); >> - if (r< 0) { >> - fprintf(stderr, "Error binding guest notifier: %d\n", -r); >> - goto fail_notifiers; >> + if (hdev->start_idx == 0) { >> + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); >> + if (r< 0) { >> + fprintf(stderr, "Error binding guest notifier: %d\n", -r); >> + goto fail_notifiers; >> + } >> } >> >> r = vhost_dev_set_features(hdev, hdev->log_enabled); >> @@ -898,7 +908,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) >> r = vhost_virtqueue_init(hdev, >> vdev, >> hdev->vqs + i, >> - i); >> + hdev->start_idx + i); >> if (r< 0) { >> goto fail_vq; >> } >> @@ -925,11 +935,13 @@ fail_vq: >> vhost_virtqueue_cleanup(hdev, >> vdev, >> hdev->vqs + i, >> - i); >> + hdev->start_idx + i); >> } >> + i = hdev->nvqs; >> fail_mem: >> fail_features: >> - vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); >> + if (hdev->start_idx == 0) >> + vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); >> fail_notifiers: >> fail: >> return r; >> @@ -944,18 +956,22 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) >> vhost_virtqueue_cleanup(hdev, >> vdev, >> hdev->vqs + i, >> - i); >> + hdev->start_idx + i); >> } >> + >> for (i = 0; i< hdev->n_mem_sections; ++i) { >> vhost_sync_dirty_bitmap(hdev,&hdev->mem_sections[i], >> 0, (target_phys_addr_t)~0x0ull); >> } >> - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); >> - if (r< 0) { >> - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); >> - fflush(stderr); >> + >> + if (hdev->start_idx == 0) { >> + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); >> + if (r< 0) { >> + fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); >> + fflush(stderr); >> + } >> + assert (r>= 0); >> } >> - assert (r>= 0); >> >> hdev->started = false; >> g_free(hdev->log); >> diff --git a/hw/vhost.h b/hw/vhost.h >> index 80e64df..fa5357a 100644 >> --- a/hw/vhost.h >> +++ b/hw/vhost.h >> @@ -34,6 +34,7 @@ struct vhost_dev { >> MemoryRegionSection *mem_sections; >> struct vhost_virtqueue *vqs; >> int nvqs; >> + int start_idx; >> unsigned long long features; >> unsigned long long acked_features; >> unsigned long long backend_features; >> diff --git a/hw/vhost_net.c b/hw/vhost_net.c >> index f672e9d..73a72bb 100644 >> --- a/hw/vhost_net.c >> +++ b/hw/vhost_net.c >> @@ -138,13 +138,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) >> } >> >> int vhost_net_start(struct vhost_net *net, >> - VirtIODevice *dev) >> + VirtIODevice *dev, >> + int start_idx) >> { >> struct vhost_vring_file file = { }; >> int r; >> >> net->dev.nvqs = 2; >> net->dev.vqs = net->vqs; >> + net->dev.start_idx = start_idx; >> >> r = vhost_dev_enable_notifiers(&net->dev, dev); >> if (r< 0) { >> @@ -227,7 +229,8 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) >> } >> >> int vhost_net_start(struct vhost_net *net, >> - VirtIODevice *dev) >> + VirtIODevice *dev, >> + int start_idx) >> { >> return -ENOSYS; >> } >> diff --git a/hw/vhost_net.h b/hw/vhost_net.h >> index 91e40b1..79a4f09 100644 >> --- a/hw/vhost_net.h >> +++ b/hw/vhost_net.h >> @@ -9,7 +9,7 @@ typedef struct vhost_net VHostNetState; >> VHostNetState *vhost_net_init(VLANClientState *backend, int devfd, bool force); >> >> bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); >> -int vhost_net_start(VHostNetState *net, VirtIODevice *dev); >> +int vhost_net_start(VHostNetState *net, VirtIODevice *dev, int start_idx); >> void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); >> >> void vhost_net_cleanup(VHostNetState *net); >> diff --git a/hw/virtio-net.c b/hw/virtio-net.c >> index 3f190d4..d42c4cc 100644 >> --- a/hw/virtio-net.c >> +++ b/hw/virtio-net.c >> @@ -26,34 +26,43 @@ >> #define MAC_TABLE_ENTRIES 64 >> #define MAX_VLAN (1<< 12) /* Per 802.1Q definition */ >> >> -typedef struct VirtIONet >> +struct VirtIONet; >> + >> +typedef struct VirtIONetQueue >> { >> - VirtIODevice vdev; >> - uint8_t mac[ETH_ALEN]; >> - uint16_t status; >> VirtQueue *rx_vq; >> VirtQueue *tx_vq; >> - VirtQueue *ctrl_vq; >> - NICState *nic; >> QEMUTimer *tx_timer; >> QEMUBH *tx_bh; >> uint32_t tx_timeout; >> - int32_t tx_burst; >> int tx_waiting; >> - uint32_t has_vnet_hdr; >> - uint8_t has_ufo; >> struct { >> VirtQueueElement elem; >> ssize_t len; >> } async_tx; >> + struct VirtIONet *n; >> + uint8_t vhost_started; >> +} VirtIONetQueue; >> + >> +typedef struct VirtIONet >> +{ >> + VirtIODevice vdev; >> + uint8_t mac[ETH_ALEN]; >> + uint16_t status; >> + VirtIONetQueue vqs[MAX_QUEUE_NUM]; >> + VirtQueue *ctrl_vq; >> + NICState *nic; >> + int32_t tx_burst; >> + uint32_t has_vnet_hdr; >> + uint8_t has_ufo; >> int mergeable_rx_bufs; >> + int multiqueue; >> uint8_t promisc; >> uint8_t allmulti; >> uint8_t alluni; >> uint8_t nomulti; >> uint8_t nouni; >> uint8_t nobcast; >> - uint8_t vhost_started; >> struct { >> int in_use; >> int first_multi; >> @@ -63,6 +72,7 @@ typedef struct VirtIONet >> } mac_table; >> uint32_t *vlans; >> DeviceState *qdev; >> + uint32_t queues; >> } VirtIONet; >> >> /* TODO >> @@ -74,12 +84,25 @@ static VirtIONet *to_virtio_net(VirtIODevice *vdev) >> return (VirtIONet *)vdev; >> } >> >> +static int vq_get_pair_index(VirtIONet *n, VirtQueue *vq) >> +{ >> + int i; >> + for (i = 0; i< n->queues; i++) { >> + if (n->vqs[i].tx_vq == vq || n->vqs[i].rx_vq == vq) { >> + return i; >> + } >> + } >> + assert(1); >> + return -1; >> +} >> + >> static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) >> { >> VirtIONet *n = to_virtio_net(vdev); >> struct virtio_net_config netcfg; >> >> stw_p(&netcfg.status, n->status); >> + netcfg.queues = n->queues * 2; >> memcpy(netcfg.mac, n->mac, ETH_ALEN); >> memcpy(config,&netcfg, sizeof(netcfg)); >> } >> @@ -103,78 +126,140 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) >> (n->status& VIRTIO_NET_S_LINK_UP)&& n->vdev.vm_running; >> } >> >> -static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) >> +static void nc_vhost_status(VLANClientState *nc, VirtIONet *n, >> + uint8_t status) >> { >> - if (!n->nic->nc.peer) { >> + int queue_index = nc->queue_index; >> + VLANClientState *peer = nc->peer; >> + VirtIONetQueue *netq =&n->vqs[nc->queue_index]; >> + >> + if (!peer) { >> return; >> } >> - if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { >> + if (peer->info->type != NET_CLIENT_TYPE_TAP) { >> return; >> } >> >> - if (!tap_get_vhost_net(n->nic->nc.peer)) { >> + if (!tap_get_vhost_net(peer)) { >> return; >> } >> - if (!!n->vhost_started == virtio_net_started(n, status)&& >> - !n->nic->nc.peer->link_down) { >> + if (!!netq->vhost_started == virtio_net_started(n, status)&& >> + !peer->link_down) { >> return; >> } >> - if (!n->vhost_started) { >> - int r; >> - if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer),&n->vdev)) { >> + if (!netq->vhost_started) { >> + /* skip ctrl vq */ >> + int r, start_idx = queue_index == 0 ? 0 : queue_index * 2 + 1; >> + if (!vhost_net_query(tap_get_vhost_net(peer),&n->vdev)) { >> return; >> } >> - r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer),&n->vdev); >> + r = vhost_net_start(tap_get_vhost_net(peer),&n->vdev, start_idx); >> if (r< 0) { >> error_report("unable to start vhost net: %d: " >> "falling back on userspace virtio", -r); >> } else { >> - n->vhost_started = 1; >> + netq->vhost_started = 1; >> } >> } else { >> - vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer),&n->vdev); >> - n->vhost_started = 0; >> + vhost_net_stop(tap_get_vhost_net(peer),&n->vdev); >> + netq->vhost_started = 0; >> + } >> +} >> + >> +static int peer_attach(VirtIONet *n, int index) >> +{ >> + if (!n->nic->ncs[index]->peer) { >> + return -1; >> + } >> + >> + if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { >> + return -1; >> + } >> + >> + return tap_attach(n->nic->ncs[index]->peer); >> +} >> + >> +static int peer_detach(VirtIONet *n, int index) >> +{ >> + if (!n->nic->ncs[index]->peer) { >> + return -1; >> + } >> + >> + if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { >> + return -1; >> + } >> + >> + return tap_detach(n->nic->ncs[index]->peer); >> +} >> + >> +static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) >> +{ >> + int i; >> + for (i = 0; i< n->queues; i++) { >> + if (!n->multiqueue&& i != 0) >> + status = 0; >> + nc_vhost_status(n->nic->ncs[i], n, status); >> } >> } >> >> static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) >> { >> VirtIONet *n = to_virtio_net(vdev); >> + int i; >> >> virtio_net_vhost_status(n, status); >> >> - if (!n->tx_waiting) { >> - return; >> - } >> + for (i = 0; i< n->queues; i++) { >> + VirtIONetQueue *netq =&n->vqs[i]; >> + if (!netq->tx_waiting) { >> + continue; >> + } >> + >> + if (!n->multiqueue&& i != 0) >> + status = 0; >> >> - if (virtio_net_started(n, status)&& !n->vhost_started) { >> - if (n->tx_timer) { >> - qemu_mod_timer(n->tx_timer, >> - qemu_get_clock_ns(vm_clock) + n->tx_timeout); >> + if (virtio_net_started(n, status)&& !netq->vhost_started) { >> + if (netq->tx_timer) { >> + qemu_mod_timer(netq->tx_timer, >> + qemu_get_clock_ns(vm_clock) + netq->tx_timeout); >> + } else { >> + qemu_bh_schedule(netq->tx_bh); >> + } >> } else { >> - qemu_bh_schedule(n->tx_bh); >> + if (netq->tx_timer) { >> + qemu_del_timer(netq->tx_timer); >> + } else { >> + qemu_bh_cancel(netq->tx_bh); >> + } >> } >> - } else { >> - if (n->tx_timer) { >> - qemu_del_timer(n->tx_timer); >> - } else { >> - qemu_bh_cancel(n->tx_bh); >> + } >> +} >> + >> +static bool virtio_net_is_link_up(VirtIONet *n) >> +{ >> + int i; >> + for (i = 0; i< n->queues; i++) { >> + if (n->nic->ncs[i]->link_down) { >> + return false; >> } >> } >> + return true; >> } >> >> static void virtio_net_set_link_status(VLANClientState *nc) >> { >> - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; >> + VirtIONet *n = ((NICState *)(nc->opaque))->opaque; >> uint16_t old_status = n->status; >> >> - if (nc->link_down) >> + if (virtio_net_is_link_up(n)) { >> n->status&= ~VIRTIO_NET_S_LINK_UP; >> - else >> + } else { >> n->status |= VIRTIO_NET_S_LINK_UP; >> + } >> >> - if (n->status != old_status) >> + if (n->status != old_status) { >> virtio_notify_config(&n->vdev); >> + } >> >> virtio_net_set_status(&n->vdev, n->vdev.status); >> } >> @@ -202,13 +287,15 @@ static void virtio_net_reset(VirtIODevice *vdev) >> >> static int peer_has_vnet_hdr(VirtIONet *n) >> { >> - if (!n->nic->nc.peer) >> + if (!n->nic->ncs[0]->peer) { >> return 0; >> + } >> >> - if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) >> + if (n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { >> return 0; >> + } >> >> - n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); >> + n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->ncs[0]->peer); >> >> return n->has_vnet_hdr; >> } >> @@ -218,7 +305,7 @@ static int peer_has_ufo(VirtIONet *n) >> if (!peer_has_vnet_hdr(n)) >> return 0; >> >> - n->has_ufo = tap_has_ufo(n->nic->nc.peer); >> + n->has_ufo = tap_has_ufo(n->nic->ncs[0]->peer); >> >> return n->has_ufo; >> } >> @@ -228,9 +315,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) >> VirtIONet *n = to_virtio_net(vdev); >> >> features |= (1<< VIRTIO_NET_F_MAC); >> + features |= (1<< VIRTIO_NET_F_MULTIQUEUE); >> >> if (peer_has_vnet_hdr(n)) { >> - tap_using_vnet_hdr(n->nic->nc.peer, 1); >> + int i; >> + for (i = 0; i< n->queues; i++) { >> + tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); >> + } >> } else { >> features&= ~(0x1<< VIRTIO_NET_F_CSUM); >> features&= ~(0x1<< VIRTIO_NET_F_HOST_TSO4); >> @@ -248,14 +339,15 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) >> features&= ~(0x1<< VIRTIO_NET_F_HOST_UFO); >> } >> >> - if (!n->nic->nc.peer || >> - n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { >> + if (!n->nic->ncs[0]->peer || >> + n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { >> return features; >> } >> - if (!tap_get_vhost_net(n->nic->nc.peer)) { >> + if (!tap_get_vhost_net(n->nic->ncs[0]->peer)) { >> return features; >> } >> - return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); >> + return vhost_net_get_features(tap_get_vhost_net(n->nic->ncs[0]->peer), >> + features); >> } >> >> static uint32_t virtio_net_bad_features(VirtIODevice *vdev) >> @@ -276,25 +368,38 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) >> static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) >> { >> VirtIONet *n = to_virtio_net(vdev); >> + int i, r; >> >> n->mergeable_rx_bufs = !!(features& (1<< VIRTIO_NET_F_MRG_RXBUF)); >> + n->multiqueue = !!(features& (1<< VIRTIO_NET_F_MULTIQUEUE)); >> >> - if (n->has_vnet_hdr) { >> - tap_set_offload(n->nic->nc.peer, >> - (features>> VIRTIO_NET_F_GUEST_CSUM)& 1, >> - (features>> VIRTIO_NET_F_GUEST_TSO4)& 1, >> - (features>> VIRTIO_NET_F_GUEST_TSO6)& 1, >> - (features>> VIRTIO_NET_F_GUEST_ECN)& 1, >> - (features>> VIRTIO_NET_F_GUEST_UFO)& 1); >> - } >> - if (!n->nic->nc.peer || >> - n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { >> - return; >> - } >> - if (!tap_get_vhost_net(n->nic->nc.peer)) { >> - return; >> + for (i = 0; i< n->queues; i++) { >> + if (!n->multiqueue&& i != 0) { >> + r = peer_detach(n, i); >> + assert(r == 0); >> + } else { >> + r = peer_attach(n, i); >> + assert(r == 0); >> + >> + if (n->has_vnet_hdr) { >> + tap_set_offload(n->nic->ncs[i]->peer, >> + (features>> VIRTIO_NET_F_GUEST_CSUM)& 1, >> + (features>> VIRTIO_NET_F_GUEST_TSO4)& 1, >> + (features>> VIRTIO_NET_F_GUEST_TSO6)& 1, >> + (features>> VIRTIO_NET_F_GUEST_ECN)& 1, >> + (features>> VIRTIO_NET_F_GUEST_UFO)& 1); >> + } >> + if (!n->nic->ncs[i]->peer || >> + n->nic->ncs[i]->peer->info->type != NET_CLIENT_TYPE_TAP) { >> + continue; >> + } >> + if (!tap_get_vhost_net(n->nic->ncs[i]->peer)) { >> + continue; >> + } >> + vhost_net_ack_features(tap_get_vhost_net(n->nic->ncs[i]->peer), >> + features); >> + } >> } >> - vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); >> } >> >> static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, >> @@ -446,7 +551,7 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) >> { >> VirtIONet *n = to_virtio_net(vdev); >> >> - qemu_flush_queued_packets(&n->nic->nc); >> + qemu_flush_queued_packets(n->nic->ncs[vq_get_pair_index(n, vq)]); >> >> /* We now have RX buffers, signal to the IO thread to break out of the >> * select to re-poll the tap file descriptor */ >> @@ -455,36 +560,37 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) >> >> static int virtio_net_can_receive(VLANClientState *nc) >> { >> - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; >> + int queue_index = nc->queue_index; >> + VirtIONet *n = ((NICState *)nc->opaque)->opaque; >> + >> if (!n->vdev.vm_running) { >> return 0; >> } >> >> - if (!virtio_queue_ready(n->rx_vq) || >> + if (!virtio_queue_ready(n->vqs[queue_index].rx_vq) || >> !(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) >> return 0; >> >> return 1; >> } >> >> -static int virtio_net_has_buffers(VirtIONet *n, int bufsize) >> +static int virtio_net_has_buffers(VirtIONet *n, int bufsize, VirtQueue *vq) >> { >> - if (virtio_queue_empty(n->rx_vq) || >> - (n->mergeable_rx_bufs&& >> - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { >> - virtio_queue_set_notification(n->rx_vq, 1); >> + if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs&& >> + !virtqueue_avail_bytes(vq, bufsize, 0))) { >> + virtio_queue_set_notification(vq, 1); >> >> /* To avoid a race condition where the guest has made some buffers >> * available after the above check but before notification was >> * enabled, check for available buffers again. >> */ >> - if (virtio_queue_empty(n->rx_vq) || >> - (n->mergeable_rx_bufs&& >> - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) >> + if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs&& >> + !virtqueue_avail_bytes(vq, bufsize, 0))) { >> return 0; >> + } >> } >> >> - virtio_queue_set_notification(n->rx_vq, 0); >> + virtio_queue_set_notification(vq, 0); >> return 1; >> } >> >> @@ -595,12 +701,15 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) >> >> static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size) >> { >> - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; >> + int queue_index = nc->queue_index; >> + VirtIONet *n = ((NICState *)(nc->opaque))->opaque; >> + VirtQueue *vq = n->vqs[queue_index].rx_vq; >> struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; >> size_t guest_hdr_len, offset, i, host_hdr_len; >> >> - if (!virtio_net_can_receive(&n->nic->nc)) >> + if (!virtio_net_can_receive(n->nic->ncs[queue_index])) { >> return -1; >> + } >> >> /* hdr_len refers to the header we supply to the guest */ >> guest_hdr_len = n->mergeable_rx_bufs ? >> @@ -608,7 +717,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ >> >> >> host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; >> - if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) >> + if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len, vq)) >> return 0; >> >> if (!receive_filter(n, buf, size)) >> @@ -623,7 +732,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ >> >> total = 0; >> >> - if (virtqueue_pop(n->rx_vq,&elem) == 0) { >> + if (virtqueue_pop(vq,&elem) == 0) { >> if (i == 0) >> return -1; >> error_report("virtio-net unexpected empty queue: " >> @@ -675,47 +784,50 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ >> } >> >> /* signal other side */ >> - virtqueue_fill(n->rx_vq,&elem, total, i++); >> + virtqueue_fill(vq,&elem, total, i++); >> } >> >> if (mhdr) { >> stw_p(&mhdr->num_buffers, i); >> } >> >> - virtqueue_flush(n->rx_vq, i); >> - virtio_notify(&n->vdev, n->rx_vq); >> + virtqueue_flush(vq, i); >> + virtio_notify(&n->vdev, vq); >> >> return size; >> } >> >> -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); >> +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *tvq); >> >> static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) >> { >> - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; >> + VirtIONet *n = ((NICState *)nc->opaque)->opaque; >> + VirtIONetQueue *netq =&n->vqs[nc->queue_index]; >> >> - virtqueue_push(n->tx_vq,&n->async_tx.elem, n->async_tx.len); >> - virtio_notify(&n->vdev, n->tx_vq); >> + virtqueue_push(netq->tx_vq,&netq->async_tx.elem, netq->async_tx.len); >> + virtio_notify(&n->vdev, netq->tx_vq); >> >> - n->async_tx.elem.out_num = n->async_tx.len = 0; >> + netq->async_tx.elem.out_num = netq->async_tx.len; >> >> - virtio_queue_set_notification(n->tx_vq, 1); >> - virtio_net_flush_tx(n, n->tx_vq); >> + virtio_queue_set_notification(netq->tx_vq, 1); >> + virtio_net_flush_tx(n, netq); >> } >> >> /* TX */ >> -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) >> +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *netq) >> { >> VirtQueueElement elem; >> int32_t num_packets = 0; >> + VirtQueue *vq = netq->tx_vq; >> + >> if (!(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) { >> return num_packets; >> } >> >> assert(n->vdev.vm_running); >> >> - if (n->async_tx.elem.out_num) { >> - virtio_queue_set_notification(n->tx_vq, 0); >> + if (netq->async_tx.elem.out_num) { >> + virtio_queue_set_notification(vq, 0); >> return num_packets; >> } >> >> @@ -747,12 +859,12 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) >> len += hdr_len; >> } >> >> - ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, >> - virtio_net_tx_complete); >> + ret = qemu_sendv_packet_async(n->nic->ncs[vq_get_pair_index(n, vq)], >> + out_sg, out_num, virtio_net_tx_complete); >> if (ret == 0) { >> - virtio_queue_set_notification(n->tx_vq, 0); >> - n->async_tx.elem = elem; >> - n->async_tx.len = len; >> + virtio_queue_set_notification(vq, 0); >> + netq->async_tx.elem = elem; >> + netq->async_tx.len = len; >> return -EBUSY; >> } >> >> @@ -771,22 +883,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) >> static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) >> { >> VirtIONet *n = to_virtio_net(vdev); >> + VirtIONetQueue *netq =&n->vqs[vq_get_pair_index(n, vq)]; >> >> /* This happens when device was stopped but VCPU wasn't. */ >> if (!n->vdev.vm_running) { >> - n->tx_waiting = 1; >> + netq->tx_waiting = 1; >> return; >> } >> >> - if (n->tx_waiting) { >> + if (netq->tx_waiting) { >> virtio_queue_set_notification(vq, 1); >> - qemu_del_timer(n->tx_timer); >> - n->tx_waiting = 0; >> - virtio_net_flush_tx(n, vq); >> + qemu_del_timer(netq->tx_timer); >> + netq->tx_waiting = 0; >> + virtio_net_flush_tx(n, netq); >> } else { >> - qemu_mod_timer(n->tx_timer, >> - qemu_get_clock_ns(vm_clock) + n->tx_timeout); >> - n->tx_waiting = 1; >> + qemu_mod_timer(netq->tx_timer, >> + qemu_get_clock_ns(vm_clock) + netq->tx_timeout); >> + netq->tx_waiting = 1; >> virtio_queue_set_notification(vq, 0); >> } >> } >> @@ -794,48 +907,53 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) >> static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) >> { >> VirtIONet *n = to_virtio_net(vdev); >> + VirtIONetQueue *netq =&n->vqs[vq_get_pair_index(n, vq)]; >> >> - if (unlikely(n->tx_waiting)) { >> + if (unlikely(netq->tx_waiting)) { >> return; >> } >> - n->tx_waiting = 1; >> + netq->tx_waiting = 1; >> /* This happens when device was stopped but VCPU wasn't. */ >> if (!n->vdev.vm_running) { >> return; >> } >> virtio_queue_set_notification(vq, 0); >> - qemu_bh_schedule(n->tx_bh); >> + qemu_bh_schedule(netq->tx_bh); >> } >> >> static void virtio_net_tx_timer(void *opaque) >> { >> - VirtIONet *n = opaque; >> + VirtIONetQueue *netq = opaque; >> + VirtIONet *n = netq->n; >> + >> assert(n->vdev.vm_running); >> >> - n->tx_waiting = 0; >> + netq->tx_waiting = 0; >> >> /* Just in case the driver is not ready on more */ >> if (!(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) >> return; >> >> - virtio_queue_set_notification(n->tx_vq, 1); >> - virtio_net_flush_tx(n, n->tx_vq); >> + virtio_queue_set_notification(netq->tx_vq, 1); >> + virtio_net_flush_tx(n, netq); >> } >> >> static void virtio_net_tx_bh(void *opaque) >> { >> - VirtIONet *n = opaque; >> + VirtIONetQueue *netq = opaque; >> + VirtQueue *vq = netq->tx_vq; >> + VirtIONet *n = netq->n; >> int32_t ret; >> >> assert(n->vdev.vm_running); >> >> - n->tx_waiting = 0; >> + netq->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); >> + ret = virtio_net_flush_tx(n, netq); >> if (ret == -EBUSY) { >> return; /* Notification re-enable handled by tx_complete */ >> } >> @@ -843,33 +961,39 @@ static void virtio_net_tx_bh(void *opaque) >> /* 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; >> + qemu_bh_schedule(netq->tx_bh); >> + netq->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; >> + virtio_queue_set_notification(vq, 1); >> + if (virtio_net_flush_tx(n, netq)> 0) { >> + virtio_queue_set_notification(vq, 0); >> + qemu_bh_schedule(netq->tx_bh); >> + netq->tx_waiting = 1; >> } >> } >> >> static void virtio_net_save(QEMUFile *f, void *opaque) >> { >> VirtIONet *n = opaque; >> + int i; >> >> /* At this point, backend must be stopped, otherwise >> * it might keep writing to memory. */ >> - assert(!n->vhost_started); >> + for (i = 0; i< n->queues; i++) { >> + assert(!n->vqs[i].vhost_started); >> + } >> virtio_save(&n->vdev, f); >> >> qemu_put_buffer(f, n->mac, ETH_ALEN); >> - qemu_put_be32(f, n->tx_waiting); >> + qemu_put_be32(f, n->queues); >> + for (i = 0; i< n->queues; i++) { >> + qemu_put_be32(f, n->vqs[i].tx_waiting); >> + } >> qemu_put_be32(f, n->mergeable_rx_bufs); >> qemu_put_be16(f, n->status); >> qemu_put_byte(f, n->promisc); >> @@ -902,7 +1026,10 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) >> } >> >> qemu_get_buffer(f, n->mac, ETH_ALEN); >> - n->tx_waiting = qemu_get_be32(f); >> + n->queues = qemu_get_be32(f); >> + for (i = 0; i< n->queues; i++) { >> + n->vqs[i].tx_waiting = qemu_get_be32(f); >> + } >> n->mergeable_rx_bufs = qemu_get_be32(f); >> >> if (version_id>= 3) >> @@ -930,7 +1057,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) >> n->mac_table.in_use = 0; >> } >> } >> - >> + >> if (version_id>= 6) >> qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN>> 3); >> >> @@ -941,13 +1068,16 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) >> } >> >> if (n->has_vnet_hdr) { >> - tap_using_vnet_hdr(n->nic->nc.peer, 1); >> - tap_set_offload(n->nic->nc.peer, >> - (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_CSUM)& 1, >> - (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO4)& 1, >> - (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO6)& 1, >> - (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_ECN)& 1, >> - (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_UFO)& 1); >> + for(i = 0; i< n->queues; i++) { >> + tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); >> + tap_set_offload(n->nic->ncs[i]->peer, >> + (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_CSUM)& 1, >> + (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO4)& 1, >> + (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO6)& 1, >> + (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_ECN)& 1, >> + (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_UFO)& >> + 1); >> + } >> } >> } >> >> @@ -982,7 +1112,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) >> >> static void virtio_net_cleanup(VLANClientState *nc) >> { >> - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; >> + VirtIONet *n = ((NICState *)nc->opaque)->opaque; >> >> n->nic = NULL; >> } >> @@ -1000,6 +1130,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> virtio_net_conf *net) >> { >> VirtIONet *n; >> + int i; >> >> n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, >> sizeof(struct virtio_net_config), >> @@ -1012,7 +1143,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> n->vdev.bad_features = virtio_net_bad_features; >> 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); >> >> if (net->tx&& strcmp(net->tx, "timer")&& strcmp(net->tx, "bh")) { >> error_report("virtio-net: " >> @@ -1021,15 +1151,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> error_report("Defaulting to \"bh\""); >> } >> >> - 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_ns(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)); >> n->status = VIRTIO_NET_S_LINK_UP; >> @@ -1038,7 +1159,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> >> qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); >> >> - n->tx_waiting = 0; >> n->tx_burst = net->txburst; >> n->mergeable_rx_bufs = 0; >> n->promisc = 1; /* for compatibility */ >> @@ -1046,6 +1166,32 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); >> >> n->vlans = g_malloc0(MAX_VLAN>> 3); >> + n->queues = conf->queues; >> + >> + /* Allocate per rx/tx vq's */ >> + for (i = 0; i< n->queues; i++) { >> + n->vqs[i].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); >> + if (net->tx&& !strcmp(net->tx, "timer")) { >> + n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, >> + virtio_net_handle_tx_timer); >> + n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock, >> + virtio_net_tx_timer, >> +&n->vqs[i]); >> + n->vqs[i].tx_timeout = net->txtimer; >> + } else { >> + n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, >> + virtio_net_handle_tx_bh); >> + n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh,&n->vqs[i]); >> + } >> + >> + n->vqs[i].tx_waiting = 0; >> + n->vqs[i].n = n; >> + >> + if (i == 0) { >> + /* keep compatiable with spec and old guest */ >> + n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); >> + } >> + } >> >> n->qdev = dev; >> register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, >> @@ -1059,24 +1205,33 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, >> void virtio_net_exit(VirtIODevice *vdev) >> { >> VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); >> + int i; >> >> /* This will stop vhost backend if appropriate. */ >> virtio_net_set_status(vdev, 0); >> >> - qemu_purge_queued_packets(&n->nic->nc); >> + for (i = 0; i< n->queues; i++) { >> + qemu_purge_queued_packets(n->nic->ncs[i]); >> + } >> >> unregister_savevm(n->qdev, "virtio-net", n); >> >> g_free(n->mac_table.macs); >> g_free(n->vlans); >> >> - if (n->tx_timer) { >> - qemu_del_timer(n->tx_timer); >> - qemu_free_timer(n->tx_timer); >> - } else { >> - qemu_bh_delete(n->tx_bh); >> + for (i = 0; i< n->queues; i++) { >> + VirtIONetQueue *netq =&n->vqs[i]; >> + if (netq->tx_timer) { >> + qemu_del_timer(netq->tx_timer); >> + qemu_free_timer(netq->tx_timer); >> + } else { >> + qemu_bh_delete(netq->tx_bh); >> + } >> } >> >> - qemu_del_vlan_client(&n->nic->nc); >> virtio_cleanup(&n->vdev); >> + >> + for (i = 0; i< n->queues; i++) { >> + qemu_del_vlan_client(n->nic->ncs[i]); >> + } >> } >> diff --git a/hw/virtio-net.h b/hw/virtio-net.h >> index 36aa463..b35ba5d 100644 >> --- a/hw/virtio-net.h >> +++ b/hw/virtio-net.h >> @@ -44,6 +44,7 @@ >> #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ >> #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ >> #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ >> +#define VIRTIO_NET_F_MULTIQUEUE 22 >> >> #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ >> >> @@ -72,6 +73,8 @@ struct virtio_net_config >> uint8_t mac[ETH_ALEN]; >> /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ >> uint16_t status; >> + >> + uint16_t queues; >> } QEMU_PACKED; >> >> /* This is the first element of the scatter-gather list. If you don't > -- > To unsubscribe from this list: send the line "unsubscribe kvm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Qemu-devel] [RFC V2 PATCH 4/4] virtio-net: add multiqueue support 2012-07-02 7:04 ` Jason Wang @ 2012-07-02 7:34 ` Michael S. Tsirkin 0 siblings, 0 replies; 3+ messages in thread From: Michael S. Tsirkin @ 2012-07-02 7:34 UTC (permalink / raw) To: Jason Wang Cc: krkumar2, habanero, aliguori, mashirle, kvm, rusty, qemu-devel, virtualization, tahm, jwhan, akong On Mon, Jul 02, 2012 at 03:04:00PM +0800, Jason Wang wrote: > On 07/01/2012 05:43 PM, Michael S. Tsirkin wrote: > >On Mon, Jun 25, 2012 at 06:04:49PM +0800, Jason Wang wrote: > >>This patch let the virtio-net can transmit and recevie packets through multiuple > >>VLANClientStates and abstract them as multiple virtqueues to guest. A new > >>parameter 'queues' were introduced to specify the number of queue pairs. > >> > >>The main goal for vhost support is to let the multiqueue could be used without > >>changes in vhost code. So each vhost_net structure were used to track a single > >>VLANClientState and two virtqueues in the past. As multiple VLANClientState were > >>stored in the NICState, we can infer the correspond VLANClientState from this > >>and queue_index easily. > >> > >>Signed-off-by: Jason Wang<jasowang@redhat.com> > >Can this patch be split up? > >1. extend vhost API to allow multiqueue and minimally tweak virtio > >2. add real multiqueue for virtio > > > >Hmm? > > Sure, do you think it's necessary to separate the vhost parts of > multiqueue from virtio? Separating is always nice if it is not too hard. > >>--- > >> hw/vhost.c | 58 ++++--- > >> hw/vhost.h | 1 > >> hw/vhost_net.c | 7 + > >> hw/vhost_net.h | 2 > >> hw/virtio-net.c | 461 +++++++++++++++++++++++++++++++++++++------------------ > >> hw/virtio-net.h | 3 > >> 6 files changed, 355 insertions(+), 177 deletions(-) > >> > >>diff --git a/hw/vhost.c b/hw/vhost.c > >>index 43664e7..6318bb2 100644 > >>--- a/hw/vhost.c > >>+++ b/hw/vhost.c > >>@@ -620,11 +620,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, > >> { > >> target_phys_addr_t s, l, a; > >> int r; > >>+ int vhost_vq_index = (idx> 2 ? idx - 1 : idx) % dev->nvqs; > >> struct vhost_vring_file file = { > >>- .index = idx, > >>+ .index = vhost_vq_index > >> }; > >> struct vhost_vring_state state = { > >>- .index = idx, > >>+ .index = vhost_vq_index > >> }; > >> struct VirtQueue *vvq = virtio_get_queue(vdev, idx); > >> > >>@@ -670,11 +671,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, > >> goto fail_alloc_ring; > >> } > >> > >>- r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); > >>+ r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); > >> if (r< 0) { > >> r = -errno; > >> goto fail_alloc; > >> } > >>+ > >> file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); > >> r = ioctl(dev->control, VHOST_SET_VRING_KICK,&file); > >> if (r) { > >>@@ -715,7 +717,7 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev, > >> unsigned idx) > >> { > >> struct vhost_vring_state state = { > >>- .index = idx, > >>+ .index = (idx> 2 ? idx - 1 : idx) % dev->nvqs, > >> }; > >> int r; > >> r = ioctl(dev->control, VHOST_GET_VRING_BASE,&state); > >>@@ -829,7 +831,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > >> } > >> > >> for (i = 0; i< hdev->nvqs; ++i) { > >>- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); > >>+ r = vdev->binding->set_host_notifier(vdev->binding_opaque, > >>+ hdev->start_idx + i, > >>+ true); > >> if (r< 0) { > >> fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); > >> goto fail_vq; > >>@@ -839,7 +843,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > >> return 0; > >> fail_vq: > >> while (--i>= 0) { > >>- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); > >>+ r = vdev->binding->set_host_notifier(vdev->binding_opaque, > >>+ hdev->start_idx + i, > >>+ false); > >> if (r< 0) { > >> fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); > >> fflush(stderr); > >>@@ -860,7 +866,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) > >> int i, r; > >> > >> for (i = 0; i< hdev->nvqs; ++i) { > >>- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); > >>+ r = vdev->binding->set_host_notifier(vdev->binding_opaque, > >>+ hdev->start_idx + i, > >>+ false); > >> if (r< 0) { > >> fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); > >> fflush(stderr); > >>@@ -874,15 +882,17 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) > >> { > >> int i, r; > >> if (!vdev->binding->set_guest_notifiers) { > >>- fprintf(stderr, "binding does not support guest notifiers\n"); > >>+ fprintf(stderr, "binding does not support guest notifier\n"); > >> r = -ENOSYS; > >> goto fail; > >> } > >> > >>- r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); > >>- if (r< 0) { > >>- fprintf(stderr, "Error binding guest notifier: %d\n", -r); > >>- goto fail_notifiers; > >>+ if (hdev->start_idx == 0) { > >>+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); > >>+ if (r< 0) { > >>+ fprintf(stderr, "Error binding guest notifier: %d\n", -r); > >>+ goto fail_notifiers; > >>+ } > >> } > >> > >> r = vhost_dev_set_features(hdev, hdev->log_enabled); > >>@@ -898,7 +908,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) > >> r = vhost_virtqueue_init(hdev, > >> vdev, > >> hdev->vqs + i, > >>- i); > >>+ hdev->start_idx + i); > >> if (r< 0) { > >> goto fail_vq; > >> } > >>@@ -925,11 +935,13 @@ fail_vq: > >> vhost_virtqueue_cleanup(hdev, > >> vdev, > >> hdev->vqs + i, > >>- i); > >>+ hdev->start_idx + i); > >> } > >>+ i = hdev->nvqs; > >> fail_mem: > >> fail_features: > >>- vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > >>+ if (hdev->start_idx == 0) > >>+ vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > >> fail_notifiers: > >> fail: > >> return r; > >>@@ -944,18 +956,22 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) > >> vhost_virtqueue_cleanup(hdev, > >> vdev, > >> hdev->vqs + i, > >>- i); > >>+ hdev->start_idx + i); > >> } > >>+ > >> for (i = 0; i< hdev->n_mem_sections; ++i) { > >> vhost_sync_dirty_bitmap(hdev,&hdev->mem_sections[i], > >> 0, (target_phys_addr_t)~0x0ull); > >> } > >>- r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > >>- if (r< 0) { > >>- fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); > >>- fflush(stderr); > >>+ > >>+ if (hdev->start_idx == 0) { > >>+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); > >>+ if (r< 0) { > >>+ fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); > >>+ fflush(stderr); > >>+ } > >>+ assert (r>= 0); > >> } > >>- assert (r>= 0); > >> > >> hdev->started = false; > >> g_free(hdev->log); > >>diff --git a/hw/vhost.h b/hw/vhost.h > >>index 80e64df..fa5357a 100644 > >>--- a/hw/vhost.h > >>+++ b/hw/vhost.h > >>@@ -34,6 +34,7 @@ struct vhost_dev { > >> MemoryRegionSection *mem_sections; > >> struct vhost_virtqueue *vqs; > >> int nvqs; > >>+ int start_idx; > >> unsigned long long features; > >> unsigned long long acked_features; > >> unsigned long long backend_features; > >>diff --git a/hw/vhost_net.c b/hw/vhost_net.c > >>index f672e9d..73a72bb 100644 > >>--- a/hw/vhost_net.c > >>+++ b/hw/vhost_net.c > >>@@ -138,13 +138,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) > >> } > >> > >> int vhost_net_start(struct vhost_net *net, > >>- VirtIODevice *dev) > >>+ VirtIODevice *dev, > >>+ int start_idx) > >> { > >> struct vhost_vring_file file = { }; > >> int r; > >> > >> net->dev.nvqs = 2; > >> net->dev.vqs = net->vqs; > >>+ net->dev.start_idx = start_idx; > >> > >> r = vhost_dev_enable_notifiers(&net->dev, dev); > >> if (r< 0) { > >>@@ -227,7 +229,8 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) > >> } > >> > >> int vhost_net_start(struct vhost_net *net, > >>- VirtIODevice *dev) > >>+ VirtIODevice *dev, > >>+ int start_idx) > >> { > >> return -ENOSYS; > >> } > >>diff --git a/hw/vhost_net.h b/hw/vhost_net.h > >>index 91e40b1..79a4f09 100644 > >>--- a/hw/vhost_net.h > >>+++ b/hw/vhost_net.h > >>@@ -9,7 +9,7 @@ typedef struct vhost_net VHostNetState; > >> VHostNetState *vhost_net_init(VLANClientState *backend, int devfd, bool force); > >> > >> bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); > >>-int vhost_net_start(VHostNetState *net, VirtIODevice *dev); > >>+int vhost_net_start(VHostNetState *net, VirtIODevice *dev, int start_idx); > >> void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); > >> > >> void vhost_net_cleanup(VHostNetState *net); > >>diff --git a/hw/virtio-net.c b/hw/virtio-net.c > >>index 3f190d4..d42c4cc 100644 > >>--- a/hw/virtio-net.c > >>+++ b/hw/virtio-net.c > >>@@ -26,34 +26,43 @@ > >> #define MAC_TABLE_ENTRIES 64 > >> #define MAX_VLAN (1<< 12) /* Per 802.1Q definition */ > >> > >>-typedef struct VirtIONet > >>+struct VirtIONet; > >>+ > >>+typedef struct VirtIONetQueue > >> { > >>- VirtIODevice vdev; > >>- uint8_t mac[ETH_ALEN]; > >>- uint16_t status; > >> VirtQueue *rx_vq; > >> VirtQueue *tx_vq; > >>- VirtQueue *ctrl_vq; > >>- NICState *nic; > >> QEMUTimer *tx_timer; > >> QEMUBH *tx_bh; > >> uint32_t tx_timeout; > >>- int32_t tx_burst; > >> int tx_waiting; > >>- uint32_t has_vnet_hdr; > >>- uint8_t has_ufo; > >> struct { > >> VirtQueueElement elem; > >> ssize_t len; > >> } async_tx; > >>+ struct VirtIONet *n; > >>+ uint8_t vhost_started; > >>+} VirtIONetQueue; > >>+ > >>+typedef struct VirtIONet > >>+{ > >>+ VirtIODevice vdev; > >>+ uint8_t mac[ETH_ALEN]; > >>+ uint16_t status; > >>+ VirtIONetQueue vqs[MAX_QUEUE_NUM]; > >>+ VirtQueue *ctrl_vq; > >>+ NICState *nic; > >>+ int32_t tx_burst; > >>+ uint32_t has_vnet_hdr; > >>+ uint8_t has_ufo; > >> int mergeable_rx_bufs; > >>+ int multiqueue; > >> uint8_t promisc; > >> uint8_t allmulti; > >> uint8_t alluni; > >> uint8_t nomulti; > >> uint8_t nouni; > >> uint8_t nobcast; > >>- uint8_t vhost_started; > >> struct { > >> int in_use; > >> int first_multi; > >>@@ -63,6 +72,7 @@ typedef struct VirtIONet > >> } mac_table; > >> uint32_t *vlans; > >> DeviceState *qdev; > >>+ uint32_t queues; > >> } VirtIONet; > >> > >> /* TODO > >>@@ -74,12 +84,25 @@ static VirtIONet *to_virtio_net(VirtIODevice *vdev) > >> return (VirtIONet *)vdev; > >> } > >> > >>+static int vq_get_pair_index(VirtIONet *n, VirtQueue *vq) > >>+{ > >>+ int i; > >>+ for (i = 0; i< n->queues; i++) { > >>+ if (n->vqs[i].tx_vq == vq || n->vqs[i].rx_vq == vq) { > >>+ return i; > >>+ } > >>+ } > >>+ assert(1); > >>+ return -1; > >>+} > >>+ > >> static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >> struct virtio_net_config netcfg; > >> > >> stw_p(&netcfg.status, n->status); > >>+ netcfg.queues = n->queues * 2; > >> memcpy(netcfg.mac, n->mac, ETH_ALEN); > >> memcpy(config,&netcfg, sizeof(netcfg)); > >> } > >>@@ -103,78 +126,140 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) > >> (n->status& VIRTIO_NET_S_LINK_UP)&& n->vdev.vm_running; > >> } > >> > >>-static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > >>+static void nc_vhost_status(VLANClientState *nc, VirtIONet *n, > >>+ uint8_t status) > >> { > >>- if (!n->nic->nc.peer) { > >>+ int queue_index = nc->queue_index; > >>+ VLANClientState *peer = nc->peer; > >>+ VirtIONetQueue *netq =&n->vqs[nc->queue_index]; > >>+ > >>+ if (!peer) { > >> return; > >> } > >>- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > >>+ if (peer->info->type != NET_CLIENT_TYPE_TAP) { > >> return; > >> } > >> > >>- if (!tap_get_vhost_net(n->nic->nc.peer)) { > >>+ if (!tap_get_vhost_net(peer)) { > >> return; > >> } > >>- if (!!n->vhost_started == virtio_net_started(n, status)&& > >>- !n->nic->nc.peer->link_down) { > >>+ if (!!netq->vhost_started == virtio_net_started(n, status)&& > >>+ !peer->link_down) { > >> return; > >> } > >>- if (!n->vhost_started) { > >>- int r; > >>- if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer),&n->vdev)) { > >>+ if (!netq->vhost_started) { > >>+ /* skip ctrl vq */ > >>+ int r, start_idx = queue_index == 0 ? 0 : queue_index * 2 + 1; > >>+ if (!vhost_net_query(tap_get_vhost_net(peer),&n->vdev)) { > >> return; > >> } > >>- r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer),&n->vdev); > >>+ r = vhost_net_start(tap_get_vhost_net(peer),&n->vdev, start_idx); > >> if (r< 0) { > >> error_report("unable to start vhost net: %d: " > >> "falling back on userspace virtio", -r); > >> } else { > >>- n->vhost_started = 1; > >>+ netq->vhost_started = 1; > >> } > >> } else { > >>- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer),&n->vdev); > >>- n->vhost_started = 0; > >>+ vhost_net_stop(tap_get_vhost_net(peer),&n->vdev); > >>+ netq->vhost_started = 0; > >>+ } > >>+} > >>+ > >>+static int peer_attach(VirtIONet *n, int index) > >>+{ > >>+ if (!n->nic->ncs[index]->peer) { > >>+ return -1; > >>+ } > >>+ > >>+ if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { > >>+ return -1; > >>+ } > >>+ > >>+ return tap_attach(n->nic->ncs[index]->peer); > >>+} > >>+ > >>+static int peer_detach(VirtIONet *n, int index) > >>+{ > >>+ if (!n->nic->ncs[index]->peer) { > >>+ return -1; > >>+ } > >>+ > >>+ if (n->nic->ncs[index]->peer->info->type != NET_CLIENT_TYPE_TAP) { > >>+ return -1; > >>+ } > >>+ > >>+ return tap_detach(n->nic->ncs[index]->peer); > >>+} > >>+ > >>+static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > >>+{ > >>+ int i; > >>+ for (i = 0; i< n->queues; i++) { > >>+ if (!n->multiqueue&& i != 0) > >>+ status = 0; > >>+ nc_vhost_status(n->nic->ncs[i], n, status); > >> } > >> } > >> > >> static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >>+ int i; > >> > >> virtio_net_vhost_status(n, status); > >> > >>- if (!n->tx_waiting) { > >>- return; > >>- } > >>+ for (i = 0; i< n->queues; i++) { > >>+ VirtIONetQueue *netq =&n->vqs[i]; > >>+ if (!netq->tx_waiting) { > >>+ continue; > >>+ } > >>+ > >>+ if (!n->multiqueue&& i != 0) > >>+ status = 0; > >> > >>- if (virtio_net_started(n, status)&& !n->vhost_started) { > >>- if (n->tx_timer) { > >>- qemu_mod_timer(n->tx_timer, > >>- qemu_get_clock_ns(vm_clock) + n->tx_timeout); > >>+ if (virtio_net_started(n, status)&& !netq->vhost_started) { > >>+ if (netq->tx_timer) { > >>+ qemu_mod_timer(netq->tx_timer, > >>+ qemu_get_clock_ns(vm_clock) + netq->tx_timeout); > >>+ } else { > >>+ qemu_bh_schedule(netq->tx_bh); > >>+ } > >> } else { > >>- qemu_bh_schedule(n->tx_bh); > >>+ if (netq->tx_timer) { > >>+ qemu_del_timer(netq->tx_timer); > >>+ } else { > >>+ qemu_bh_cancel(netq->tx_bh); > >>+ } > >> } > >>- } else { > >>- if (n->tx_timer) { > >>- qemu_del_timer(n->tx_timer); > >>- } else { > >>- qemu_bh_cancel(n->tx_bh); > >>+ } > >>+} > >>+ > >>+static bool virtio_net_is_link_up(VirtIONet *n) > >>+{ > >>+ int i; > >>+ for (i = 0; i< n->queues; i++) { > >>+ if (n->nic->ncs[i]->link_down) { > >>+ return false; > >> } > >> } > >>+ return true; > >> } > >> > >> static void virtio_net_set_link_status(VLANClientState *nc) > >> { > >>- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > >>+ VirtIONet *n = ((NICState *)(nc->opaque))->opaque; > >> uint16_t old_status = n->status; > >> > >>- if (nc->link_down) > >>+ if (virtio_net_is_link_up(n)) { > >> n->status&= ~VIRTIO_NET_S_LINK_UP; > >>- else > >>+ } else { > >> n->status |= VIRTIO_NET_S_LINK_UP; > >>+ } > >> > >>- if (n->status != old_status) > >>+ if (n->status != old_status) { > >> virtio_notify_config(&n->vdev); > >>+ } > >> > >> virtio_net_set_status(&n->vdev, n->vdev.status); > >> } > >>@@ -202,13 +287,15 @@ static void virtio_net_reset(VirtIODevice *vdev) > >> > >> static int peer_has_vnet_hdr(VirtIONet *n) > >> { > >>- if (!n->nic->nc.peer) > >>+ if (!n->nic->ncs[0]->peer) { > >> return 0; > >>+ } > >> > >>- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) > >>+ if (n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { > >> return 0; > >>+ } > >> > >>- n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); > >>+ n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->ncs[0]->peer); > >> > >> return n->has_vnet_hdr; > >> } > >>@@ -218,7 +305,7 @@ static int peer_has_ufo(VirtIONet *n) > >> if (!peer_has_vnet_hdr(n)) > >> return 0; > >> > >>- n->has_ufo = tap_has_ufo(n->nic->nc.peer); > >>+ n->has_ufo = tap_has_ufo(n->nic->ncs[0]->peer); > >> > >> return n->has_ufo; > >> } > >>@@ -228,9 +315,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) > >> VirtIONet *n = to_virtio_net(vdev); > >> > >> features |= (1<< VIRTIO_NET_F_MAC); > >>+ features |= (1<< VIRTIO_NET_F_MULTIQUEUE); > >> > >> if (peer_has_vnet_hdr(n)) { > >>- tap_using_vnet_hdr(n->nic->nc.peer, 1); > >>+ int i; > >>+ for (i = 0; i< n->queues; i++) { > >>+ tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); > >>+ } > >> } else { > >> features&= ~(0x1<< VIRTIO_NET_F_CSUM); > >> features&= ~(0x1<< VIRTIO_NET_F_HOST_TSO4); > >>@@ -248,14 +339,15 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) > >> features&= ~(0x1<< VIRTIO_NET_F_HOST_UFO); > >> } > >> > >>- if (!n->nic->nc.peer || > >>- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > >>+ if (!n->nic->ncs[0]->peer || > >>+ n->nic->ncs[0]->peer->info->type != NET_CLIENT_TYPE_TAP) { > >> return features; > >> } > >>- if (!tap_get_vhost_net(n->nic->nc.peer)) { > >>+ if (!tap_get_vhost_net(n->nic->ncs[0]->peer)) { > >> return features; > >> } > >>- return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); > >>+ return vhost_net_get_features(tap_get_vhost_net(n->nic->ncs[0]->peer), > >>+ features); > >> } > >> > >> static uint32_t virtio_net_bad_features(VirtIODevice *vdev) > >>@@ -276,25 +368,38 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) > >> static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >>+ int i, r; > >> > >> n->mergeable_rx_bufs = !!(features& (1<< VIRTIO_NET_F_MRG_RXBUF)); > >>+ n->multiqueue = !!(features& (1<< VIRTIO_NET_F_MULTIQUEUE)); > >> > >>- if (n->has_vnet_hdr) { > >>- tap_set_offload(n->nic->nc.peer, > >>- (features>> VIRTIO_NET_F_GUEST_CSUM)& 1, > >>- (features>> VIRTIO_NET_F_GUEST_TSO4)& 1, > >>- (features>> VIRTIO_NET_F_GUEST_TSO6)& 1, > >>- (features>> VIRTIO_NET_F_GUEST_ECN)& 1, > >>- (features>> VIRTIO_NET_F_GUEST_UFO)& 1); > >>- } > >>- if (!n->nic->nc.peer || > >>- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { > >>- return; > >>- } > >>- if (!tap_get_vhost_net(n->nic->nc.peer)) { > >>- return; > >>+ for (i = 0; i< n->queues; i++) { > >>+ if (!n->multiqueue&& i != 0) { > >>+ r = peer_detach(n, i); > >>+ assert(r == 0); > >>+ } else { > >>+ r = peer_attach(n, i); > >>+ assert(r == 0); > >>+ > >>+ if (n->has_vnet_hdr) { > >>+ tap_set_offload(n->nic->ncs[i]->peer, > >>+ (features>> VIRTIO_NET_F_GUEST_CSUM)& 1, > >>+ (features>> VIRTIO_NET_F_GUEST_TSO4)& 1, > >>+ (features>> VIRTIO_NET_F_GUEST_TSO6)& 1, > >>+ (features>> VIRTIO_NET_F_GUEST_ECN)& 1, > >>+ (features>> VIRTIO_NET_F_GUEST_UFO)& 1); > >>+ } > >>+ if (!n->nic->ncs[i]->peer || > >>+ n->nic->ncs[i]->peer->info->type != NET_CLIENT_TYPE_TAP) { > >>+ continue; > >>+ } > >>+ if (!tap_get_vhost_net(n->nic->ncs[i]->peer)) { > >>+ continue; > >>+ } > >>+ vhost_net_ack_features(tap_get_vhost_net(n->nic->ncs[i]->peer), > >>+ features); > >>+ } > >> } > >>- vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); > >> } > >> > >> static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, > >>@@ -446,7 +551,7 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >> > >>- qemu_flush_queued_packets(&n->nic->nc); > >>+ qemu_flush_queued_packets(n->nic->ncs[vq_get_pair_index(n, vq)]); > >> > >> /* We now have RX buffers, signal to the IO thread to break out of the > >> * select to re-poll the tap file descriptor */ > >>@@ -455,36 +560,37 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) > >> > >> static int virtio_net_can_receive(VLANClientState *nc) > >> { > >>- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > >>+ int queue_index = nc->queue_index; > >>+ VirtIONet *n = ((NICState *)nc->opaque)->opaque; > >>+ > >> if (!n->vdev.vm_running) { > >> return 0; > >> } > >> > >>- if (!virtio_queue_ready(n->rx_vq) || > >>+ if (!virtio_queue_ready(n->vqs[queue_index].rx_vq) || > >> !(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) > >> return 0; > >> > >> return 1; > >> } > >> > >>-static int virtio_net_has_buffers(VirtIONet *n, int bufsize) > >>+static int virtio_net_has_buffers(VirtIONet *n, int bufsize, VirtQueue *vq) > >> { > >>- if (virtio_queue_empty(n->rx_vq) || > >>- (n->mergeable_rx_bufs&& > >>- !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { > >>- virtio_queue_set_notification(n->rx_vq, 1); > >>+ if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs&& > >>+ !virtqueue_avail_bytes(vq, bufsize, 0))) { > >>+ virtio_queue_set_notification(vq, 1); > >> > >> /* To avoid a race condition where the guest has made some buffers > >> * available after the above check but before notification was > >> * enabled, check for available buffers again. > >> */ > >>- if (virtio_queue_empty(n->rx_vq) || > >>- (n->mergeable_rx_bufs&& > >>- !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) > >>+ if (virtio_queue_empty(vq) || (n->mergeable_rx_bufs&& > >>+ !virtqueue_avail_bytes(vq, bufsize, 0))) { > >> return 0; > >>+ } > >> } > >> > >>- virtio_queue_set_notification(n->rx_vq, 0); > >>+ virtio_queue_set_notification(vq, 0); > >> return 1; > >> } > >> > >>@@ -595,12 +701,15 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) > >> > >> static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size) > >> { > >>- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > >>+ int queue_index = nc->queue_index; > >>+ VirtIONet *n = ((NICState *)(nc->opaque))->opaque; > >>+ VirtQueue *vq = n->vqs[queue_index].rx_vq; > >> struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; > >> size_t guest_hdr_len, offset, i, host_hdr_len; > >> > >>- if (!virtio_net_can_receive(&n->nic->nc)) > >>+ if (!virtio_net_can_receive(n->nic->ncs[queue_index])) { > >> return -1; > >>+ } > >> > >> /* hdr_len refers to the header we supply to the guest */ > >> guest_hdr_len = n->mergeable_rx_bufs ? > >>@@ -608,7 +717,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > >> > >> > >> host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; > >>- if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) > >>+ if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len, vq)) > >> return 0; > >> > >> if (!receive_filter(n, buf, size)) > >>@@ -623,7 +732,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > >> > >> total = 0; > >> > >>- if (virtqueue_pop(n->rx_vq,&elem) == 0) { > >>+ if (virtqueue_pop(vq,&elem) == 0) { > >> if (i == 0) > >> return -1; > >> error_report("virtio-net unexpected empty queue: " > >>@@ -675,47 +784,50 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ > >> } > >> > >> /* signal other side */ > >>- virtqueue_fill(n->rx_vq,&elem, total, i++); > >>+ virtqueue_fill(vq,&elem, total, i++); > >> } > >> > >> if (mhdr) { > >> stw_p(&mhdr->num_buffers, i); > >> } > >> > >>- virtqueue_flush(n->rx_vq, i); > >>- virtio_notify(&n->vdev, n->rx_vq); > >>+ virtqueue_flush(vq, i); > >>+ virtio_notify(&n->vdev, vq); > >> > >> return size; > >> } > >> > >>-static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); > >>+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *tvq); > >> > >> static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) > >> { > >>- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > >>+ VirtIONet *n = ((NICState *)nc->opaque)->opaque; > >>+ VirtIONetQueue *netq =&n->vqs[nc->queue_index]; > >> > >>- virtqueue_push(n->tx_vq,&n->async_tx.elem, n->async_tx.len); > >>- virtio_notify(&n->vdev, n->tx_vq); > >>+ virtqueue_push(netq->tx_vq,&netq->async_tx.elem, netq->async_tx.len); > >>+ virtio_notify(&n->vdev, netq->tx_vq); > >> > >>- n->async_tx.elem.out_num = n->async_tx.len = 0; > >>+ netq->async_tx.elem.out_num = netq->async_tx.len; > >> > >>- virtio_queue_set_notification(n->tx_vq, 1); > >>- virtio_net_flush_tx(n, n->tx_vq); > >>+ virtio_queue_set_notification(netq->tx_vq, 1); > >>+ virtio_net_flush_tx(n, netq); > >> } > >> > >> /* TX */ > >>-static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > >>+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtIONetQueue *netq) > >> { > >> VirtQueueElement elem; > >> int32_t num_packets = 0; > >>+ VirtQueue *vq = netq->tx_vq; > >>+ > >> if (!(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) { > >> return num_packets; > >> } > >> > >> assert(n->vdev.vm_running); > >> > >>- if (n->async_tx.elem.out_num) { > >>- virtio_queue_set_notification(n->tx_vq, 0); > >>+ if (netq->async_tx.elem.out_num) { > >>+ virtio_queue_set_notification(vq, 0); > >> return num_packets; > >> } > >> > >>@@ -747,12 +859,12 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > >> len += hdr_len; > >> } > >> > >>- ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, > >>- virtio_net_tx_complete); > >>+ ret = qemu_sendv_packet_async(n->nic->ncs[vq_get_pair_index(n, vq)], > >>+ out_sg, out_num, virtio_net_tx_complete); > >> if (ret == 0) { > >>- virtio_queue_set_notification(n->tx_vq, 0); > >>- n->async_tx.elem = elem; > >>- n->async_tx.len = len; > >>+ virtio_queue_set_notification(vq, 0); > >>+ netq->async_tx.elem = elem; > >>+ netq->async_tx.len = len; > >> return -EBUSY; > >> } > >> > >>@@ -771,22 +883,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) > >> static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >>+ VirtIONetQueue *netq =&n->vqs[vq_get_pair_index(n, vq)]; > >> > >> /* This happens when device was stopped but VCPU wasn't. */ > >> if (!n->vdev.vm_running) { > >>- n->tx_waiting = 1; > >>+ netq->tx_waiting = 1; > >> return; > >> } > >> > >>- if (n->tx_waiting) { > >>+ if (netq->tx_waiting) { > >> virtio_queue_set_notification(vq, 1); > >>- qemu_del_timer(n->tx_timer); > >>- n->tx_waiting = 0; > >>- virtio_net_flush_tx(n, vq); > >>+ qemu_del_timer(netq->tx_timer); > >>+ netq->tx_waiting = 0; > >>+ virtio_net_flush_tx(n, netq); > >> } else { > >>- qemu_mod_timer(n->tx_timer, > >>- qemu_get_clock_ns(vm_clock) + n->tx_timeout); > >>- n->tx_waiting = 1; > >>+ qemu_mod_timer(netq->tx_timer, > >>+ qemu_get_clock_ns(vm_clock) + netq->tx_timeout); > >>+ netq->tx_waiting = 1; > >> virtio_queue_set_notification(vq, 0); > >> } > >> } > >>@@ -794,48 +907,53 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) > >> static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) > >> { > >> VirtIONet *n = to_virtio_net(vdev); > >>+ VirtIONetQueue *netq =&n->vqs[vq_get_pair_index(n, vq)]; > >> > >>- if (unlikely(n->tx_waiting)) { > >>+ if (unlikely(netq->tx_waiting)) { > >> return; > >> } > >>- n->tx_waiting = 1; > >>+ netq->tx_waiting = 1; > >> /* This happens when device was stopped but VCPU wasn't. */ > >> if (!n->vdev.vm_running) { > >> return; > >> } > >> virtio_queue_set_notification(vq, 0); > >>- qemu_bh_schedule(n->tx_bh); > >>+ qemu_bh_schedule(netq->tx_bh); > >> } > >> > >> static void virtio_net_tx_timer(void *opaque) > >> { > >>- VirtIONet *n = opaque; > >>+ VirtIONetQueue *netq = opaque; > >>+ VirtIONet *n = netq->n; > >>+ > >> assert(n->vdev.vm_running); > >> > >>- n->tx_waiting = 0; > >>+ netq->tx_waiting = 0; > >> > >> /* Just in case the driver is not ready on more */ > >> if (!(n->vdev.status& VIRTIO_CONFIG_S_DRIVER_OK)) > >> return; > >> > >>- virtio_queue_set_notification(n->tx_vq, 1); > >>- virtio_net_flush_tx(n, n->tx_vq); > >>+ virtio_queue_set_notification(netq->tx_vq, 1); > >>+ virtio_net_flush_tx(n, netq); > >> } > >> > >> static void virtio_net_tx_bh(void *opaque) > >> { > >>- VirtIONet *n = opaque; > >>+ VirtIONetQueue *netq = opaque; > >>+ VirtQueue *vq = netq->tx_vq; > >>+ VirtIONet *n = netq->n; > >> int32_t ret; > >> > >> assert(n->vdev.vm_running); > >> > >>- n->tx_waiting = 0; > >>+ netq->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); > >>+ ret = virtio_net_flush_tx(n, netq); > >> if (ret == -EBUSY) { > >> return; /* Notification re-enable handled by tx_complete */ > >> } > >>@@ -843,33 +961,39 @@ static void virtio_net_tx_bh(void *opaque) > >> /* 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; > >>+ qemu_bh_schedule(netq->tx_bh); > >>+ netq->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; > >>+ virtio_queue_set_notification(vq, 1); > >>+ if (virtio_net_flush_tx(n, netq)> 0) { > >>+ virtio_queue_set_notification(vq, 0); > >>+ qemu_bh_schedule(netq->tx_bh); > >>+ netq->tx_waiting = 1; > >> } > >> } > >> > >> static void virtio_net_save(QEMUFile *f, void *opaque) > >> { > >> VirtIONet *n = opaque; > >>+ int i; > >> > >> /* At this point, backend must be stopped, otherwise > >> * it might keep writing to memory. */ > >>- assert(!n->vhost_started); > >>+ for (i = 0; i< n->queues; i++) { > >>+ assert(!n->vqs[i].vhost_started); > >>+ } > >> virtio_save(&n->vdev, f); > >> > >> qemu_put_buffer(f, n->mac, ETH_ALEN); > >>- qemu_put_be32(f, n->tx_waiting); > >>+ qemu_put_be32(f, n->queues); > >>+ for (i = 0; i< n->queues; i++) { > >>+ qemu_put_be32(f, n->vqs[i].tx_waiting); > >>+ } > >> qemu_put_be32(f, n->mergeable_rx_bufs); > >> qemu_put_be16(f, n->status); > >> qemu_put_byte(f, n->promisc); > >>@@ -902,7 +1026,10 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > >> } > >> > >> qemu_get_buffer(f, n->mac, ETH_ALEN); > >>- n->tx_waiting = qemu_get_be32(f); > >>+ n->queues = qemu_get_be32(f); > >>+ for (i = 0; i< n->queues; i++) { > >>+ n->vqs[i].tx_waiting = qemu_get_be32(f); > >>+ } > >> n->mergeable_rx_bufs = qemu_get_be32(f); > >> > >> if (version_id>= 3) > >>@@ -930,7 +1057,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > >> n->mac_table.in_use = 0; > >> } > >> } > >>- > >>+ > >> if (version_id>= 6) > >> qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN>> 3); > >> > >>@@ -941,13 +1068,16 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > >> } > >> > >> if (n->has_vnet_hdr) { > >>- tap_using_vnet_hdr(n->nic->nc.peer, 1); > >>- tap_set_offload(n->nic->nc.peer, > >>- (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_CSUM)& 1, > >>- (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO4)& 1, > >>- (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO6)& 1, > >>- (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_ECN)& 1, > >>- (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_UFO)& 1); > >>+ for(i = 0; i< n->queues; i++) { > >>+ tap_using_vnet_hdr(n->nic->ncs[i]->peer, 1); > >>+ tap_set_offload(n->nic->ncs[i]->peer, > >>+ (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_CSUM)& 1, > >>+ (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO4)& 1, > >>+ (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_TSO6)& 1, > >>+ (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_ECN)& 1, > >>+ (n->vdev.guest_features>> VIRTIO_NET_F_GUEST_UFO)& > >>+ 1); > >>+ } > >> } > >> } > >> > >>@@ -982,7 +1112,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > >> > >> static void virtio_net_cleanup(VLANClientState *nc) > >> { > >>- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; > >>+ VirtIONet *n = ((NICState *)nc->opaque)->opaque; > >> > >> n->nic = NULL; > >> } > >>@@ -1000,6 +1130,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> virtio_net_conf *net) > >> { > >> VirtIONet *n; > >>+ int i; > >> > >> n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, > >> sizeof(struct virtio_net_config), > >>@@ -1012,7 +1143,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> n->vdev.bad_features = virtio_net_bad_features; > >> 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); > >> > >> if (net->tx&& strcmp(net->tx, "timer")&& strcmp(net->tx, "bh")) { > >> error_report("virtio-net: " > >>@@ -1021,15 +1151,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> error_report("Defaulting to \"bh\""); > >> } > >> > >>- 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_ns(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)); > >> n->status = VIRTIO_NET_S_LINK_UP; > >>@@ -1038,7 +1159,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> > >> qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); > >> > >>- n->tx_waiting = 0; > >> n->tx_burst = net->txburst; > >> n->mergeable_rx_bufs = 0; > >> n->promisc = 1; /* for compatibility */ > >>@@ -1046,6 +1166,32 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); > >> > >> n->vlans = g_malloc0(MAX_VLAN>> 3); > >>+ n->queues = conf->queues; > >>+ > >>+ /* Allocate per rx/tx vq's */ > >>+ for (i = 0; i< n->queues; i++) { > >>+ n->vqs[i].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); > >>+ if (net->tx&& !strcmp(net->tx, "timer")) { > >>+ n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, > >>+ virtio_net_handle_tx_timer); > >>+ n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock, > >>+ virtio_net_tx_timer, > >>+&n->vqs[i]); > >>+ n->vqs[i].tx_timeout = net->txtimer; > >>+ } else { > >>+ n->vqs[i].tx_vq = virtio_add_queue(&n->vdev, 256, > >>+ virtio_net_handle_tx_bh); > >>+ n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh,&n->vqs[i]); > >>+ } > >>+ > >>+ n->vqs[i].tx_waiting = 0; > >>+ n->vqs[i].n = n; > >>+ > >>+ if (i == 0) { > >>+ /* keep compatiable with spec and old guest */ > >>+ n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); > >>+ } > >>+ } > >> > >> n->qdev = dev; > >> register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, > >>@@ -1059,24 +1205,33 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, > >> void virtio_net_exit(VirtIODevice *vdev) > >> { > >> VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); > >>+ int i; > >> > >> /* This will stop vhost backend if appropriate. */ > >> virtio_net_set_status(vdev, 0); > >> > >>- qemu_purge_queued_packets(&n->nic->nc); > >>+ for (i = 0; i< n->queues; i++) { > >>+ qemu_purge_queued_packets(n->nic->ncs[i]); > >>+ } > >> > >> unregister_savevm(n->qdev, "virtio-net", n); > >> > >> g_free(n->mac_table.macs); > >> g_free(n->vlans); > >> > >>- if (n->tx_timer) { > >>- qemu_del_timer(n->tx_timer); > >>- qemu_free_timer(n->tx_timer); > >>- } else { > >>- qemu_bh_delete(n->tx_bh); > >>+ for (i = 0; i< n->queues; i++) { > >>+ VirtIONetQueue *netq =&n->vqs[i]; > >>+ if (netq->tx_timer) { > >>+ qemu_del_timer(netq->tx_timer); > >>+ qemu_free_timer(netq->tx_timer); > >>+ } else { > >>+ qemu_bh_delete(netq->tx_bh); > >>+ } > >> } > >> > >>- qemu_del_vlan_client(&n->nic->nc); > >> virtio_cleanup(&n->vdev); > >>+ > >>+ for (i = 0; i< n->queues; i++) { > >>+ qemu_del_vlan_client(n->nic->ncs[i]); > >>+ } > >> } > >>diff --git a/hw/virtio-net.h b/hw/virtio-net.h > >>index 36aa463..b35ba5d 100644 > >>--- a/hw/virtio-net.h > >>+++ b/hw/virtio-net.h > >>@@ -44,6 +44,7 @@ > >> #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ > >> #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ > >> #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ > >>+#define VIRTIO_NET_F_MULTIQUEUE 22 > >> > >> #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ > >> > >>@@ -72,6 +73,8 @@ struct virtio_net_config > >> uint8_t mac[ETH_ALEN]; > >> /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ > >> uint16_t status; > >>+ > >>+ uint16_t queues; > >> } QEMU_PACKED; > >> > >> /* This is the first element of the scatter-gather list. If you don't > >-- > >To unsubscribe from this list: send the line "unsubscribe kvm" in > >the body of a message to majordomo@vger.kernel.org > >More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2012-07-02 7:35 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <20120625095059.8096.49429.stgit@amd-6168-8-1.englab.nay.redhat.com> [not found] ` <20120625100449.8096.65545.stgit@amd-6168-8-1.englab.nay.redhat.com> 2012-07-01 9:43 ` [Qemu-devel] [RFC V2 PATCH 4/4] virtio-net: add multiqueue support Michael S. Tsirkin 2012-07-02 7:04 ` Jason Wang 2012-07-02 7:34 ` Michael S. Tsirkin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).