vhost: improve performance by supporting large buffer
[dpdk.git] / lib / librte_vhost / virtio_net.c
index 241d467..66f0c72 100644 (file)
@@ -177,7 +177,8 @@ do_data_copy_enqueue(struct virtio_net *dev, struct vhost_virtqueue *vq)
 
        for (i = 0; i < count; i++) {
                rte_memcpy(elem[i].dst, elem[i].src, elem[i].len);
-               vhost_log_cache_write(dev, vq, elem[i].log_addr, elem[i].len);
+               vhost_log_cache_write_iova(dev, vq, elem[i].log_addr,
+                                          elem[i].len);
                PRINT_PACKET(dev, (uintptr_t)elem[i].dst, elem[i].len, 0);
        }
 
@@ -631,7 +632,7 @@ copy_vnet_hdr_to_desc(struct virtio_net *dev, struct vhost_virtqueue *vq,
 
                PRINT_PACKET(dev, (uintptr_t)dst,
                                (uint32_t)len, 0);
-               vhost_log_cache_write(dev, vq,
+               vhost_log_cache_write_iova(dev, vq,
                                iova, len);
 
                remain -= len;
@@ -731,7 +732,7 @@ copy_mbuf_to_desc(struct virtio_net *dev, struct vhost_virtqueue *vq,
                        } else {
                                PRINT_PACKET(dev, (uintptr_t)hdr_addr,
                                                dev->vhost_hlen, 0);
-                               vhost_log_cache_write(dev, vq,
+                               vhost_log_cache_write_iova(dev, vq,
                                                buf_vec[0].buf_iova,
                                                dev->vhost_hlen);
                        }
@@ -746,8 +747,9 @@ copy_mbuf_to_desc(struct virtio_net *dev, struct vhost_virtqueue *vq,
                        rte_memcpy((void *)((uintptr_t)(buf_addr + buf_offset)),
                                rte_pktmbuf_mtod_offset(m, void *, mbuf_offset),
                                cpy_len);
-                       vhost_log_cache_write(dev, vq, buf_iova + buf_offset,
-                                       cpy_len);
+                       vhost_log_cache_write_iova(dev, vq,
+                                                  buf_iova + buf_offset,
+                                                  cpy_len);
                        PRINT_PACKET(dev, (uintptr_t)(buf_addr + buf_offset),
                                cpy_len, 0);
                } else {
@@ -1287,6 +1289,93 @@ again:
        return NULL;
 }
 
+static void
+virtio_dev_extbuf_free(void *addr __rte_unused, void *opaque)
+{
+       rte_free(opaque);
+}
+
+static int
+virtio_dev_extbuf_alloc(struct rte_mbuf *pkt, uint32_t size)
+{
+       struct rte_mbuf_ext_shared_info *shinfo = NULL;
+       uint32_t total_len = RTE_PKTMBUF_HEADROOM + size;
+       uint16_t buf_len;
+       rte_iova_t iova;
+       void *buf;
+
+       /* Try to use pkt buffer to store shinfo to reduce the amount of memory
+        * required, otherwise store shinfo in the new buffer.
+        */
+       if (rte_pktmbuf_tailroom(pkt) >= sizeof(*shinfo))
+               shinfo = rte_pktmbuf_mtod(pkt,
+                                         struct rte_mbuf_ext_shared_info *);
+       else {
+               total_len += sizeof(*shinfo) + sizeof(uintptr_t);
+               total_len = RTE_ALIGN_CEIL(total_len, sizeof(uintptr_t));
+       }
+
+       if (unlikely(total_len > UINT16_MAX))
+               return -ENOSPC;
+
+       buf_len = total_len;
+       buf = rte_malloc(NULL, buf_len, RTE_CACHE_LINE_SIZE);
+       if (unlikely(buf == NULL))
+               return -ENOMEM;
+
+       /* Initialize shinfo */
+       if (shinfo) {
+               shinfo->free_cb = virtio_dev_extbuf_free;
+               shinfo->fcb_opaque = buf;
+               rte_mbuf_ext_refcnt_set(shinfo, 1);
+       } else {
+               shinfo = rte_pktmbuf_ext_shinfo_init_helper(buf, &buf_len,
+                                             virtio_dev_extbuf_free, buf);
+               if (unlikely(shinfo == NULL)) {
+                       rte_free(buf);
+                       RTE_LOG(ERR, VHOST_DATA, "Failed to init shinfo\n");
+                       return -1;
+               }
+       }
+
+       iova = rte_malloc_virt2iova(buf);
+       rte_pktmbuf_attach_extbuf(pkt, buf, iova, buf_len, shinfo);
+       rte_pktmbuf_reset_headroom(pkt);
+
+       return 0;
+}
+
+/*
+ * Allocate a host supported pktmbuf.
+ */
+static __rte_always_inline struct rte_mbuf *
+virtio_dev_pktmbuf_alloc(struct virtio_net *dev, struct rte_mempool *mp,
+                        uint32_t data_len)
+{
+       struct rte_mbuf *pkt = rte_pktmbuf_alloc(mp);
+
+       if (unlikely(pkt == NULL))
+               return NULL;
+
+       if (rte_pktmbuf_tailroom(pkt) >= data_len)
+               return pkt;
+
+       /* attach an external buffer if supported */
+       if (dev->extbuf && !virtio_dev_extbuf_alloc(pkt, data_len))
+               return pkt;
+
+       /* check if chained buffers are allowed */
+       if (!dev->linearbuf)
+               return pkt;
+
+       /* Data doesn't fit into the buffer and the host supports
+        * only linear buffers
+        */
+       rte_pktmbuf_free(pkt);
+
+       return NULL;
+}
+
 static __rte_noinline uint16_t
 virtio_dev_tx_split(struct virtio_net *dev, struct vhost_virtqueue *vq,
        struct rte_mempool *mbuf_pool, struct rte_mbuf **pkts, uint16_t count)
@@ -1341,26 +1430,23 @@ virtio_dev_tx_split(struct virtio_net *dev, struct vhost_virtqueue *vq,
        for (i = 0; i < count; i++) {
                struct buf_vector buf_vec[BUF_VECTOR_MAX];
                uint16_t head_idx;
-               uint32_t dummy_len;
+               uint32_t buf_len;
                uint16_t nr_vec = 0;
                int err;
 
                if (unlikely(fill_vec_buf_split(dev, vq,
                                                vq->last_avail_idx + i,
                                                &nr_vec, buf_vec,
-                                               &head_idx, &dummy_len,
+                                               &head_idx, &buf_len,
                                                VHOST_ACCESS_RO) < 0))
                        break;
 
                if (likely(dev->dequeue_zero_copy == 0))
                        update_shadow_used_ring_split(vq, head_idx, 0);
 
-               pkts[i] = rte_pktmbuf_alloc(mbuf_pool);
-               if (unlikely(pkts[i] == NULL)) {
-                       RTE_LOG(ERR, VHOST_DATA,
-                               "Failed to allocate memory for mbuf.\n");
+               pkts[i] = virtio_dev_pktmbuf_alloc(dev, mbuf_pool, buf_len);
+               if (unlikely(pkts[i] == NULL))
                        break;
-               }
 
                err = copy_desc_to_mbuf(dev, vq, buf_vec, nr_vec, pkts[i],
                                mbuf_pool);
@@ -1449,14 +1535,14 @@ virtio_dev_tx_packed(struct virtio_net *dev, struct vhost_virtqueue *vq,
        for (i = 0; i < count; i++) {
                struct buf_vector buf_vec[BUF_VECTOR_MAX];
                uint16_t buf_id;
-               uint32_t dummy_len;
+               uint32_t buf_len;
                uint16_t desc_count, nr_vec = 0;
                int err;
 
                if (unlikely(fill_vec_buf_packed(dev, vq,
                                                vq->last_avail_idx, &desc_count,
                                                buf_vec, &nr_vec,
-                                               &buf_id, &dummy_len,
+                                               &buf_id, &buf_len,
                                                VHOST_ACCESS_RO) < 0))
                        break;
 
@@ -1464,12 +1550,9 @@ virtio_dev_tx_packed(struct virtio_net *dev, struct vhost_virtqueue *vq,
                        update_shadow_used_ring_packed(vq, buf_id, 0,
                                        desc_count);
 
-               pkts[i] = rte_pktmbuf_alloc(mbuf_pool);
-               if (unlikely(pkts[i] == NULL)) {
-                       RTE_LOG(ERR, VHOST_DATA,
-                               "Failed to allocate memory for mbuf.\n");
+               pkts[i] = virtio_dev_pktmbuf_alloc(dev, mbuf_pool, buf_len);
+               if (unlikely(pkts[i] == NULL))
                        break;
-               }
 
                err = copy_desc_to_mbuf(dev, vq, buf_vec, nr_vec, pkts[i],
                                mbuf_pool);