> -----Original Message----- > From: Stephen Hemminger [mailto:stephen at networkplumber.org] > Sent: Saturday, September 5, 2015 4:58 AM > To: Xie, Huawei; Ouyang, Changchun > Cc: dev at dpdk.org; Stephen Hemminger > Subject: [PATCH 3/4] virtio: use indirect ring elements > > The virtio ring in QEMU/KVM is usually limited to 256 entries and the normal > way that virtio driver was queuing mbufs required nsegs + 1 ring elements. > By using the indirect ring element feature if available, each packet will take > only one ring slot even for multi-segment packets. > > Signed-off-by: Stephen Hemminger <stephen at networkplumber.org> > --- > drivers/net/virtio/virtio_ethdev.c | 11 +++++--- > drivers/net/virtio/virtio_ethdev.h | 3 ++- > drivers/net/virtio/virtio_rxtx.c | 51 ++++++++++++++++++++++++++++++- > ------- > drivers/net/virtio/virtqueue.h | 8 ++++++ > 4 files changed, 57 insertions(+), 16 deletions(-) > > diff --git a/drivers/net/virtio/virtio_ethdev.c > b/drivers/net/virtio/virtio_ethdev.c > index 465d3cd..bcfb87b 100644 > --- a/drivers/net/virtio/virtio_ethdev.c > +++ b/drivers/net/virtio/virtio_ethdev.c > @@ -359,12 +359,15 @@ int virtio_dev_queue_setup(struct rte_eth_dev > *dev, > if (queue_type == VTNET_TQ) {
Do we also need implement indirect ring elements for RX path? > /* > * For each xmit packet, allocate a virtio_net_hdr > + * and indirect ring elements > */ > snprintf(vq_name, sizeof(vq_name), > "port%d_tvq%d_hdrzone", > - dev->data->port_id, queue_idx); > - vq->virtio_net_hdr_mz = > rte_memzone_reserve_aligned(vq_name, > - vq_size * hw->vtnet_hdr_size, > - socket_id, 0, RTE_CACHE_LINE_SIZE); > + dev->data->port_id, queue_idx); > + > + vq->virtio_net_hdr_mz = > + rte_memzone_reserve_aligned(vq_name, > + vq_size * sizeof(struct > virtio_tx_region), > + socket_id, 0, > RTE_CACHE_LINE_SIZE); > if (vq->virtio_net_hdr_mz == NULL) { > if (rte_errno == EEXIST) > vq->virtio_net_hdr_mz = > diff --git a/drivers/net/virtio/virtio_ethdev.h > b/drivers/net/virtio/virtio_ethdev.h > index 9026d42..07a9265 100644 > --- a/drivers/net/virtio/virtio_ethdev.h > +++ b/drivers/net/virtio/virtio_ethdev.h > @@ -64,7 +64,8 @@ > 1u << VIRTIO_NET_F_CTRL_VQ | \ > 1u << VIRTIO_NET_F_CTRL_RX | \ > 1u << VIRTIO_NET_F_CTRL_VLAN | \ > - 1u << VIRTIO_NET_F_MRG_RXBUF) > + 1u << VIRTIO_NET_F_MRG_RXBUF | \ > + 1u << VIRTIO_RING_F_INDIRECT_DESC) > > /* > * CQ function prototype > diff --git a/drivers/net/virtio/virtio_rxtx.c > b/drivers/net/virtio/virtio_rxtx.c > index dbe6665..8979695 100644 > --- a/drivers/net/virtio/virtio_rxtx.c > +++ b/drivers/net/virtio/virtio_rxtx.c > @@ -199,14 +199,15 @@ virtqueue_enqueue_recv_refill(struct virtqueue > *vq, struct rte_mbuf *cookie) } > > static int > -virtqueue_enqueue_xmit(struct virtqueue *txvq, struct rte_mbuf *cookie) > +virtqueue_enqueue_xmit(struct virtqueue *txvq, struct rte_mbuf *cookie, > + int use_indirect) > { > struct vq_desc_extra *dxp; > struct vring_desc *start_dp; > uint16_t seg_num = cookie->nb_segs; > - uint16_t needed = 1 + seg_num; > + uint16_t needed = use_indirect ? 1 : 1 + seg_num; Do we need check if seg_num > VIRTIO_MAX_TX_INDIRECT? That mean one slot is not enough for the whole big packet even it is indirect ring. > uint16_t head_idx, idx; > - uint16_t head_size = txvq->hw->vtnet_hdr_size; > + unsigned long offs; > > if (unlikely(txvq->vq_free_cnt == 0)) > return -ENOSPC; > @@ -220,11 +221,26 @@ virtqueue_enqueue_xmit(struct virtqueue *txvq, > struct rte_mbuf *cookie) > dxp = &txvq->vq_descx[idx]; > dxp->cookie = (void *)cookie; > dxp->ndescs = needed; > - > start_dp = txvq->vq_ring.desc; > - start_dp[idx].addr = > - txvq->virtio_net_hdr_mem + idx * head_size; > - start_dp[idx].len = (uint32_t)head_size; > + > + if (use_indirect) { > + offs = offsetof(struct virtio_tx_region, tx_indir) > + + idx * sizeof(struct virtio_tx_region); > + > + start_dp[idx].addr = txvq->virtio_net_hdr_mem + offs; > + start_dp[idx].len = sizeof(struct vring_desc); > + start_dp[idx].flags = VRING_DESC_F_INDIRECT; > + > + start_dp = (struct vring_desc *) > + ((char *)txvq->virtio_net_hdr_mz->addr + offs); > + idx = 0; > + } > + > + offs = offsetof(struct virtio_tx_region, tx_hdr) > + + idx * sizeof(struct virtio_tx_region); > + > + start_dp[idx].addr = txvq->virtio_net_hdr_mem + offs; > + start_dp[idx].len = txvq->hw->vtnet_hdr_size; > start_dp[idx].flags = VRING_DESC_F_NEXT; > > for (; ((seg_num > 0) && (cookie != NULL)); seg_num--) { @@ -236,7 > +252,12 @@ virtqueue_enqueue_xmit(struct virtqueue *txvq, struct > rte_mbuf *cookie) > } > > start_dp[idx].flags &= ~VRING_DESC_F_NEXT; > - idx = start_dp[idx].next; > + > + if (use_indirect) > + idx = txvq->vq_ring.desc[head_idx].next; > + else > + idx = start_dp[idx].next; > + > txvq->vq_desc_head_idx = idx; > if (txvq->vq_desc_head_idx == VQ_RING_DESC_CHAIN_END) > txvq->vq_desc_tail_idx = idx; > @@ -760,7 +781,15 @@ virtio_xmit_pkts(void *tx_queue, struct rte_mbuf > **tx_pkts, uint16_t nb_pkts) > > for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) { > struct rte_mbuf *txm = tx_pkts[nb_tx]; > - int need = txm->nb_segs - txvq->vq_free_cnt + 1; > + int use_indirect, slots, need; > + > + use_indirect = vtpci_with_feature(txvq->hw, > + > VIRTIO_RING_F_INDIRECT_DESC) > + && (txm->nb_segs < VIRTIO_MAX_TX_INDIRECT); > + > + /* How many ring entries are needed to this Tx? */ > + slots = use_indirect ? 1 : 1 + txm->nb_segs; > + need = slots - txvq->vq_free_cnt; > > /* Positive value indicates it need free vring descriptors */ > if (need > 0) { > @@ -769,7 +798,7 @@ virtio_xmit_pkts(void *tx_queue, struct rte_mbuf > **tx_pkts, uint16_t nb_pkts) > need = RTE_MIN(need, (int)nb_used); > > virtio_xmit_cleanup(txvq, need); > - need = txm->nb_segs - txvq->vq_free_cnt + 1; > + need = slots - txvq->vq_free_cnt; > if (unlikely(need > 0)) { > PMD_TX_LOG(ERR, > "No free tx descriptors to > transmit"); > @@ -787,7 +816,7 @@ virtio_xmit_pkts(void *tx_queue, struct rte_mbuf > **tx_pkts, uint16_t nb_pkts) > } > > /* Enqueue Packet buffers */ > - error = virtqueue_enqueue_xmit(txvq, txm); > + error = virtqueue_enqueue_xmit(txvq, txm, use_indirect); > if (unlikely(error)) { > if (error == ENOSPC) > PMD_TX_LOG(ERR, "virtqueue_enqueue > Free count = 0"); diff --git a/drivers/net/virtio/virtqueue.h > b/drivers/net/virtio/virtqueue.h index 7789411..a9410b4 100644 > --- a/drivers/net/virtio/virtqueue.h > +++ b/drivers/net/virtio/virtqueue.h > @@ -237,6 +237,14 @@ struct virtio_net_hdr_mrg_rxbuf { > uint16_t num_buffers; /**< Number of merged rx buffers */ }; > > +/* Region reserved to allow for transmit header and indirect ring */ > +#define VIRTIO_MAX_TX_INDIRECT 8 struct virtio_tx_region { > + struct virtio_net_hdr_mrg_rxbuf tx_hdr; > + struct vring_desc tx_indir[VIRTIO_MAX_TX_INDIRECT] > + __attribute__((__aligned__(16))); }; > + > /** > * Tell the backend not to interrupt us. > */ > -- > 2.1.4