On Fri, Dec 28, 2012 at 10:32 AM, Jason Wang <jasow...@redhat.com> wrote: > This patch implements both userspace and vhost support for multiple queue > virtio-net (VIRTIO_NET_F_MQ). This is done by introducing an array of > VirtIONetQueue to VirtIONet. > > Signed-off-by: Jason Wang <jasow...@redhat.com> > --- > hw/virtio-net.c | 318 > ++++++++++++++++++++++++++++++++++++++++++------------- > hw/virtio-net.h | 27 +++++- > 2 files changed, 271 insertions(+), 74 deletions(-) > > diff --git a/hw/virtio-net.c b/hw/virtio-net.c > index c6f0915..aaeef1b 100644 > --- a/hw/virtio-net.c > +++ b/hw/virtio-net.c > @@ -45,7 +45,7 @@ typedef struct VirtIONet > VirtIODevice vdev; > uint8_t mac[ETH_ALEN]; > uint16_t status; > - VirtIONetQueue vq; > + VirtIONetQueue vqs[MAX_QUEUE_NUM]; > VirtQueue *ctrl_vq; > NICState *nic; > uint32_t tx_timeout; > @@ -70,14 +70,23 @@ typedef struct VirtIONet > } mac_table; > uint32_t *vlans; > DeviceState *qdev; > + int multiqueue; > + uint16_t max_queues; > + uint16_t curr_queues; > } VirtIONet; > > -static VirtIONetQueue *virtio_net_get_queue(NetClientState *nc) > +static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) > { > VirtIONet *n = qemu_get_nic_opaque(nc); > > - return &n->vq; > + return &n->vqs[nc->queue_index]; > } > + > +static int vq2q(int queue_index) > +{ > + return queue_index / 2; > +} > + > /* TODO > * - we could suppress RX interrupt if we were so inclined. > */ > @@ -93,6 +102,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, > uint8_t *config) > struct virtio_net_config netcfg; > > stw_p(&netcfg.status, n->status); > + stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); > memcpy(netcfg.mac, n->mac, ETH_ALEN); > memcpy(config, &netcfg, sizeof(netcfg)); > } > @@ -116,31 +126,33 @@ 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 virtio_net_vhost_status(VirtIONet *n, int queue_index, > + uint8_t status) > { > - VirtIONetQueue *q = &n->vq; > + NetClientState *nc = qemu_get_subqueue(n->nic, queue_index); > + VirtIONetQueue *q = &n->vqs[queue_index]; > > - if (!qemu_get_queue(n->nic)->peer) { > + if (!nc->peer) { > return; > } > - if (qemu_get_queue(n->nic)->peer->info->type != > NET_CLIENT_OPTIONS_KIND_TAP) { > + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { > return; > } > > - if (!tap_get_vhost_net(qemu_get_queue(n->nic)->peer)) { > + if (!tap_get_vhost_net(nc->peer)) { > return; > } > - if (!!q->vhost_started == virtio_net_started(n, status) && > - !qemu_get_queue(n->nic)->peer->link_down) { > + if (!!q->vhost_started == > + (virtio_net_started(n, status) && !nc->peer->link_down)) { > return; > } > if (!q->vhost_started) { > int r; > - if > (!vhost_net_query(tap_get_vhost_net(qemu_get_queue(n->nic)->peer), &n->vdev)) > { > + if (!vhost_net_query(tap_get_vhost_net(nc->peer), &n->vdev)) { > return; > } > - r = vhost_net_start(tap_get_vhost_net(qemu_get_queue(n->nic)->peer), > - &n->vdev, 0); > + r = vhost_net_start(tap_get_vhost_net(nc->peer), &n->vdev, > + queue_index * 2); > if (r < 0) { > error_report("unable to start vhost net: %d: " > "falling back on userspace virtio", -r); > @@ -148,7 +160,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t > status) > q->vhost_started = 1; > } > } else { > - vhost_net_stop(tap_get_vhost_net(qemu_get_queue(n->nic)->peer), > &n->vdev); > + vhost_net_stop(tap_get_vhost_net(nc->peer), &n->vdev); > q->vhost_started = 0; > } > } > @@ -156,26 +168,35 @@ static void virtio_net_vhost_status(VirtIONet *n, > uint8_t status) > static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) > { > VirtIONet *n = to_virtio_net(vdev); > - VirtIONetQueue *q = &n->vq; > + int i; > > - virtio_net_vhost_status(n, status); > + for (i = 0; i < n->max_queues; i++) { > + VirtIONetQueue *q = &n->vqs[i]; > + uint8_t queue_status = status; > > - if (!q->tx_waiting) { > - return; > - } > + if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { > + queue_status = 0; > + } > > - if (virtio_net_started(n, status) && !q->vhost_started) { > - if (q->tx_timer) { > - qemu_mod_timer(q->tx_timer, > - qemu_get_clock_ns(vm_clock) + n->tx_timeout); > - } else { > - qemu_bh_schedule(q->tx_bh); > + virtio_net_vhost_status(n, i, queue_status); > + > + if (!q->tx_waiting) { > + continue; > } > - } else { > - if (q->tx_timer) { > - qemu_del_timer(q->tx_timer); > + > + if (virtio_net_started(n, status) && !q->vhost_started) { > + if (q->tx_timer) { > + qemu_mod_timer(q->tx_timer, > + qemu_get_clock_ns(vm_clock) + n->tx_timeout); > + } else { > + qemu_bh_schedule(q->tx_bh); > + } > } else { > - qemu_bh_cancel(q->tx_bh); > + if (q->tx_timer) { > + qemu_del_timer(q->tx_timer); > + } else { > + qemu_bh_cancel(q->tx_bh); > + } > } > } > } > @@ -207,6 +228,8 @@ static void virtio_net_reset(VirtIODevice *vdev) > n->nomulti = 0; > n->nouni = 0; > n->nobcast = 0; > + /* multiqueue is disalbed by default */ > + n->curr_queues = 1; > > /* Flush any MAC and VLAN filter table state */ > n->mac_table.in_use = 0; > @@ -245,18 +268,72 @@ static int peer_has_ufo(VirtIONet *n) > > static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) > { > + int i; > + NetClientState *nc; > + > n->mergeable_rx_bufs = mergeable_rx_bufs; > > n->guest_hdr_len = n->mergeable_rx_bufs ? > sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct > virtio_net_hdr); > > - if (peer_has_vnet_hdr(n) && > - tap_has_vnet_hdr_len(qemu_get_queue(n->nic)->peer, > n->guest_hdr_len)) { > - tap_set_vnet_hdr_len(qemu_get_queue(n->nic)->peer, n->guest_hdr_len); > - n->host_hdr_len = n->guest_hdr_len; > + for (i = 0; i < n->max_queues; i++) { > + nc = qemu_get_subqueue(n->nic, i); > + > + if (peer_has_vnet_hdr(n) && > + tap_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { > + tap_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); > + n->host_hdr_len = n->guest_hdr_len; > + } > } > } > > +static int peer_attach(VirtIONet *n, int index) > +{ > + NetClientState *nc = qemu_get_subqueue(n->nic, index); > + int ret; > + > + if (!nc->peer) { > + ret = -1; > + } else if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { > + ret = -1; > + } else { > + ret = tap_attach(nc->peer); > + } > + > + return ret; > +} > + > +static int peer_detach(VirtIONet *n, int index) > +{ > + NetClientState *nc = qemu_get_subqueue(n->nic, index); > + int ret; > + > + if (!nc->peer) { > + ret = -1; > + } else if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { > + ret = -1; > + } else { > + ret = tap_detach(nc->peer); > + } > + > + return ret; > +} > + > +static void virtio_net_set_queues(VirtIONet *n) > +{ > + int i; > + > + for (i = 0; i < n->max_queues; i++) { > + if (i < n->curr_queues) { > + assert(!peer_attach(n, i)); > + } else { > + assert(!peer_detach(n, i)); > + } > + } > +} > + > +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int > ctrl); > + > static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t > features) > { > VirtIONet *n = to_virtio_net(vdev); > @@ -308,25 +385,33 @@ 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; > + > + virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ)), > + !!(features & (1 << VIRTIO_NET_F_CTRL_VQ))); > > virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << > VIRTIO_NET_F_MRG_RXBUF))); > > if (n->has_vnet_hdr) { > - tap_set_offload(qemu_get_queue(n->nic)->peer, > + tap_set_offload(qemu_get_subqueue(n->nic, 0)->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 (!qemu_get_queue(n->nic)->peer || > - qemu_get_queue(n->nic)->peer->info->type != > NET_CLIENT_OPTIONS_KIND_TAP) { > - return; > - } > - if (!tap_get_vhost_net(qemu_get_queue(n->nic)->peer)) { > - return; > + > + for (i = 0; i < n->max_queues; i++) { > + NetClientState *nc = qemu_get_subqueue(n->nic, i); > + > + if (!nc->peer || nc->peer->info->type != > NET_CLIENT_OPTIONS_KIND_TAP) { > + continue; > + } > + if (!tap_get_vhost_net(nc->peer)) { > + continue; > + } > + vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); > } > - vhost_net_ack_features(tap_get_vhost_net(qemu_get_queue(n->nic)->peer), > features); > } > > static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, > @@ -436,6 +521,35 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, > uint8_t cmd, > return VIRTIO_NET_OK; > } > > +static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, > + VirtQueueElement *elem) > +{ > + struct virtio_net_ctrl_mq s; > + > + if (elem->out_num != 2 || > + elem->out_sg[1].iov_len != sizeof(struct virtio_net_ctrl_mq)) { > + error_report("virtio-net ctrl invalid steering command"); > + return VIRTIO_NET_ERR; > + } > + > + if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { > + return VIRTIO_NET_ERR; > + } > + > + memcpy(&s, elem->out_sg[1].iov_base, sizeof(struct virtio_net_ctrl_mq)); > + > + if (s.virtqueue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || > + s.virtqueue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || > + s.virtqueue_pairs > n->max_queues) { > + return VIRTIO_NET_ERR; > + } > + > + n->curr_queues = s.virtqueue_pairs; > + virtio_net_set_queues(n); > + virtio_net_set_status(&n->vdev, n->vdev.status); > + > + return VIRTIO_NET_OK; > +} > static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > @@ -464,6 +578,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, > VirtQueue *vq) > status = virtio_net_handle_mac(n, ctrl.cmd, &elem); > else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) > status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem); > + else if (ctrl.class == VIRTIO_NET_CTRL_MQ)
Please add braces. > + status = virtio_net_handle_mq(n, ctrl.cmd, &elem); > > stb_p(elem.in_sg[elem.in_num - 1].iov_base, status); > > @@ -477,19 +593,24 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, > VirtQueue *vq) > static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > + int queue_index = vq2q(virtio_get_queue_index(vq)); > > - qemu_flush_queued_packets(qemu_get_queue(n->nic)); > + qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); > } > > static int virtio_net_can_receive(NetClientState *nc) > { > VirtIONet *n = qemu_get_nic_opaque(nc); > - VirtIONetQueue *q = virtio_net_get_queue(nc); > + VirtIONetQueue *q = virtio_net_get_subqueue(nc); > > if (!n->vdev.vm_running) { > return 0; > } > > + if (nc->queue_index >= n->curr_queues) { > + return 0; > + } > + > if (!virtio_queue_ready(q->rx_vq) || > !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { > return 0; > @@ -620,14 +741,15 @@ static int receive_filter(VirtIONet *n, const uint8_t > *buf, int size) > static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, > size_t size) > { > VirtIONet *n = qemu_get_nic_opaque(nc); > - VirtIONetQueue *q = virtio_net_get_queue(nc); > + VirtIONetQueue *q = virtio_net_get_subqueue(nc); > struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; > struct virtio_net_hdr_mrg_rxbuf mhdr; > unsigned mhdr_cnt = 0; > size_t offset, i, guest_offset; > > - if (!virtio_net_can_receive(qemu_get_queue(n->nic))) > + if (!virtio_net_can_receive(nc)) { > return -1; > + } > > /* hdr_len refers to the header we supply to the guest */ > if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - > n->host_hdr_len)) { > @@ -720,7 +842,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q); > static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) > { > VirtIONet *n = qemu_get_nic_opaque(nc); > - VirtIONetQueue *q = virtio_net_get_queue(nc); > + VirtIONetQueue *q = virtio_net_get_subqueue(nc); > > virtqueue_push(q->tx_vq, &q->async_tx.elem, 0); > virtio_notify(&n->vdev, q->tx_vq); > @@ -737,6 +859,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) > VirtIONet *n = q->n; > VirtQueueElement elem; > int32_t num_packets = 0; > + int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); > if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { > return num_packets; > } > @@ -778,8 +901,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) > > len = n->guest_hdr_len; > > - ret = qemu_sendv_packet_async(qemu_get_queue(n->nic), out_sg, > out_num, > - virtio_net_tx_complete); > + ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), > + out_sg, out_num, > virtio_net_tx_complete); > if (ret == 0) { > virtio_queue_set_notification(q->tx_vq, 0); > q->async_tx.elem = elem; > @@ -802,7 +925,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) > static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) > { > VirtIONet *n = to_virtio_net(vdev); > - VirtIONetQueue *q = &n->vq; > + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; > > /* This happens when device was stopped but VCPU wasn't. */ > if (!n->vdev.vm_running) { > @@ -826,7 +949,7 @@ 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 *q = &n->vq; > + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; > > if (unlikely(q->tx_waiting)) { > return; > @@ -894,10 +1017,49 @@ static void virtio_net_tx_bh(void *opaque) > } > } > > +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl) > +{ > + VirtIODevice *vdev = &n->vdev; > + int i; > + > + n->multiqueue = multiqueue; > + > + if (!multiqueue) > + n->curr_queues = 1; Ditto. Didn't checkpatch.pl catch these or did you not check? > + > + for (i = 2; i <= n->max_queues * 2 + 1; i++) { > + virtio_del_queue(vdev, i); > + } > + > + for (i = 1; i < n->max_queues; i++) { > + n->vqs[i].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); > + if (n->vqs[i].tx_timer) { > + n->vqs[i].tx_vq = > + virtio_add_queue(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]); > + } else { > + n->vqs[i].tx_vq = > + virtio_add_queue(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 (ctrl) { > + n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); > + } > + > + virtio_net_set_queues(n); > +} > + > static void virtio_net_save(QEMUFile *f, void *opaque) > { > VirtIONet *n = opaque; > - VirtIONetQueue *q = &n->vq; > + VirtIONetQueue *q = &n->vqs[0]; > > /* At this point, backend must be stopped, otherwise > * it might keep writing to memory. */ > @@ -926,9 +1088,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque) > static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) > { > VirtIONet *n = opaque; > - VirtIONetQueue *q = &n->vq; > - int i; > - int ret; > + VirtIONetQueue *q = &n->vqs[0]; > + int ret, i; > > if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) > return -EINVAL; > @@ -1044,6 +1205,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), > @@ -1056,8 +1218,11 @@ 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->vq.rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); > - n->vq.n = n; > + n->vqs[0].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); > + n->max_queues = conf->queues; > + n->curr_queues = 1; > + n->vqs[0].n = n; > + n->tx_timeout = net->txtimer; > > if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { > error_report("virtio-net: " > @@ -1067,14 +1232,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev, > NICConf *conf, > } > > if (net->tx && !strcmp(net->tx, "timer")) { > - n->vq.tx_vq = virtio_add_queue(&n->vdev, 256, > - virtio_net_handle_tx_timer); > - n->vq.tx_timer = qemu_new_timer_ns(vm_clock, > - virtio_net_tx_timer, &n->vq); > - n->tx_timeout = net->txtimer; > + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, > + virtio_net_handle_tx_timer); > + n->vqs[0].tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, > + &n->vqs[0]); > } else { > - n->vq.tx_vq = virtio_add_queue(&n->vdev, 256, > virtio_net_handle_tx_bh); > - n->vq.tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vq); > + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, > + virtio_net_handle_tx_bh); > + n->vqs[0].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[0]); > } > n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); > qemu_macaddr_default_if_unset(&conf->macaddr); > @@ -1084,7 +1249,9 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf > *conf, > n->nic = qemu_new_nic(&net_virtio_info, conf, > object_get_typename(OBJECT(dev)), dev->id, n); > peer_test_vnet_hdr(n); > if (peer_has_vnet_hdr(n)) { > - tap_using_vnet_hdr(qemu_get_queue(n->nic)->peer, 1); > + for (i = 0; i < n->max_queues; i++) { > + tap_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, 1); > + } > n->host_hdr_len = sizeof(struct virtio_net_hdr); > } else { > n->host_hdr_len = 0; > @@ -1092,7 +1259,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf > *conf, > > qemu_format_nic_info_str(qemu_get_queue(n->nic), conf->macaddr.a); > > - n->vq.tx_waiting = 0; > + n->vqs[0].tx_waiting = 0; > n->tx_burst = net->txburst; > virtio_net_set_mrg_rx_bufs(n, 0); > n->promisc = 1; /* for compatibility */ > @@ -1113,23 +1280,28 @@ VirtIODevice *virtio_net_init(DeviceState *dev, > NICConf *conf, > void virtio_net_exit(VirtIODevice *vdev) > { > VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); > - VirtIONetQueue *q = &n->vq; > + int i; > > /* This will stop vhost backend if appropriate. */ > virtio_net_set_status(vdev, 0); > > - qemu_purge_queued_packets(qemu_get_queue(n->nic)); > - > unregister_savevm(n->qdev, "virtio-net", n); > > g_free(n->mac_table.macs); > g_free(n->vlans); > > - if (q->tx_timer) { > - qemu_del_timer(q->tx_timer); > - qemu_free_timer(q->tx_timer); > - } else { > - qemu_bh_delete(q->tx_bh); > + for (i = 0; i < n->max_queues; i++) { > + VirtIONetQueue *q = &n->vqs[i]; > + NetClientState *nc = qemu_get_subqueue(n->nic, i); > + > + qemu_purge_queued_packets(nc); > + > + if (q->tx_timer) { > + qemu_del_timer(q->tx_timer); > + qemu_free_timer(q->tx_timer); > + } else { > + qemu_bh_delete(q->tx_bh); > + } > } > > qemu_del_nic(n->nic); > diff --git a/hw/virtio-net.h b/hw/virtio-net.h > index 36aa463..bc5857a 100644 > --- a/hw/virtio-net.h > +++ b/hw/virtio-net.h > @@ -44,6 +44,8 @@ > #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_MQ 22 /* Device supports Receive Flow > + * Steering */ > > #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ > > @@ -72,6 +74,8 @@ struct virtio_net_config > uint8_t mac[ETH_ALEN]; > /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ > uint16_t status; > + /* Max virtqueue pairs supported by the device */ > + uint16_t max_virtqueue_pairs; > } QEMU_PACKED; > > /* This is the first element of the scatter-gather list. If you don't > @@ -168,6 +172,26 @@ struct virtio_net_ctrl_mac { > #define VIRTIO_NET_CTRL_VLAN_ADD 0 > #define VIRTIO_NET_CTRL_VLAN_DEL 1 > > +/* > + * Control Multiqueue > + * > + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET > + * enables multiqueue, specifying the number of the transmit and > + * receive queues that will be used. After the command is consumed and acked > by > + * the device, the device will not steer new packets on receive virtqueues > + * other than specified nor read from transmit virtqueues other than > specified. > + * Accordingly, driver should not transmit new packets on virtqueues other > than > + * specified. > + */ > +struct virtio_net_ctrl_mq { VirtIONetCtrlMQ and please don't forget the typedef. > + uint16_t virtqueue_pairs; > +}; > + > +#define VIRTIO_NET_CTRL_MQ 4 > + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 > + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 > + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 > + > #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ > DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ > DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ > @@ -186,5 +210,6 @@ struct virtio_net_ctrl_mac { > DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, > true), \ > DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, > true), \ > DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, > true), \ > - DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, > VIRTIO_NET_F_CTRL_RX_EXTRA, true) > + DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, > VIRTIO_NET_F_CTRL_RX_EXTRA, true), \ > + DEFINE_PROP_BIT("mq", _state, _field, VIRTIO_NET_F_MQ, true) > #endif > -- > 1.7.1 > >