+struct virtio_ctrl_mq_status_hdr
+{
+ struct virtio_net_ctrl_hdr ctrl;
+ struct virtio_net_ctrl_mq num_mqs;
+ virtio_net_ctrl_ack status;
+};
+
+static int
+virtio_pci_enable_multiqueue (vlib_main_t * vm, virtio_if_t * vif,
+ u16 num_queues)
+{
+ virtio_main_t *vim = &virtio_main;
+ virtio_vring_t *vring = vif->cxq_vring;
+ u32 buffer_index;
+ vlib_buffer_t *b;
+ u16 used, next, avail;
+ u16 sz = vring->size;
+ u16 mask = sz - 1;
+ struct virtio_ctrl_mq_status_hdr mq_hdr, result;
+ virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
+
+ mq_hdr.ctrl.class = VIRTIO_NET_CTRL_MQ;
+ mq_hdr.ctrl.cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
+ mq_hdr.status = VIRTIO_NET_ERR;
+ mq_hdr.num_mqs.virtqueue_pairs = num_queues;
+
+ used = vring->desc_in_use;
+ next = vring->desc_next;
+ avail = vring->avail->idx;
+ struct vring_desc *d = &vring->desc[next];
+
+ if (vlib_buffer_alloc (vm, &buffer_index, 1))
+ b = vlib_get_buffer (vm, buffer_index);
+ else
+ return VIRTIO_NET_ERR;
+ /*
+ * current_data may not be initialized with 0 and may contain
+ * previous offset.
+ */
+ b->current_data = 0;
+ clib_memcpy (vlib_buffer_get_current (b), &mq_hdr,
+ sizeof (struct virtio_ctrl_mq_status_hdr));
+ d->flags = VRING_DESC_F_NEXT;
+ d->addr = vlib_buffer_get_current_pa (vm, b);
+ d->len = sizeof (struct virtio_net_ctrl_hdr);
+ vring->avail->ring[avail & mask] = next;
+ avail++;
+ next = (next + 1) & mask;
+ d->next = next;
+ used++;
+
+ d = &vring->desc[next];
+ d->flags = VRING_DESC_F_NEXT;
+ d->addr = vlib_buffer_get_current_pa (vm, b) +
+ STRUCT_OFFSET_OF (struct virtio_ctrl_mq_status_hdr, num_mqs);
+ d->len = sizeof (struct virtio_net_ctrl_mq);
+ next = (next + 1) & mask;
+ d->next = next;
+ used++;
+
+ d = &vring->desc[next];
+ d->flags = VRING_DESC_F_WRITE;
+ d->addr = vlib_buffer_get_current_pa (vm, b) +
+ STRUCT_OFFSET_OF (struct virtio_ctrl_mq_status_hdr, status);
+ d->len = sizeof (mq_hdr.status);
+ next = (next + 1) & mask;
+ used++;
+
+ CLIB_MEMORY_STORE_BARRIER ();
+ vring->avail->idx = avail;
+ vring->desc_next = next;
+ vring->desc_in_use = used;
+
+ if ((vring->used->flags & VIRTIO_RING_FLAG_MASK_INT) == 0)
+ {
+ virtio_kick (vm, vring, vif);
+ }
+
+ clib_memset (&result, 0, sizeof (result));
+ u16 last = vring->last_used_idx, n_left = 0;
+ n_left = vring->used->idx - last;
+
+ while (n_left)
+ {
+ struct vring_used_elem *e = &vring->used->ring[last & mask];
+ u16 slot = e->id;
+
+ d = &vring->desc[slot];
+ while (d->flags & VRING_DESC_F_NEXT)
+ {
+ used--;
+ slot = d->next;
+ d = &vring->desc[slot];
+ }
+ used--;
+ last++;
+ n_left--;
+ }
+ vring->desc_in_use = used;
+ vring->last_used_idx = last;
+
+ CLIB_MEMORY_BARRIER ();
+ clib_memcpy (&result, vlib_buffer_get_current (b),
+ sizeof (struct virtio_ctrl_mq_status_hdr));
+
+ virtio_log_debug (vim, vif, "multi-queue enable status on Ctrl queue : %u",
+ result.status);
+ status = result.status;
+ vlib_buffer_free (vm, &buffer_index, 1);
+ return status;
+}
+