virtio-net: build skb from multi-buffer xdp
authorHeng Qi <hengqi@linux.alibaba.com>
Sat, 14 Jan 2023 08:22:27 +0000 (16:22 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Jan 2023 11:15:48 +0000 (11:15 +0000)
This converts the xdp_buff directly to a skb, including
multi-buffer and single buffer xdp. We'll isolate the
construction of skb based on xdp from page_to_skb().

Signed-off-by: Heng Qi <hengqi@linux.alibaba.com>
Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/virtio_net.c

index 6d3cf8887dd7f8030b945fc0521b55baf546eb0d..5d3aad2ef8d00d069eab68086c284423d8fed756 100644 (file)
@@ -959,6 +959,55 @@ err:
        return NULL;
 }
 
+/* Why not use xdp_build_skb_from_frame() ?
+ * XDP core assumes that xdp frags are PAGE_SIZE in length, while in
+ * virtio-net there are 2 points that do not match its requirements:
+ *  1. The size of the prefilled buffer is not fixed before xdp is set.
+ *  2. xdp_build_skb_from_frame() does more checks that we don't need,
+ *     like eth_type_trans() (which virtio-net does in receive_buf()).
+ */
+static struct sk_buff *build_skb_from_xdp_buff(struct net_device *dev,
+                                              struct virtnet_info *vi,
+                                              struct xdp_buff *xdp,
+                                              unsigned int xdp_frags_truesz)
+{
+       struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
+       unsigned int headroom, data_len;
+       struct sk_buff *skb;
+       int metasize;
+       u8 nr_frags;
+
+       if (unlikely(xdp->data_end > xdp_data_hard_end(xdp))) {
+               pr_debug("Error building skb as missing reserved tailroom for xdp");
+               return NULL;
+       }
+
+       if (unlikely(xdp_buff_has_frags(xdp)))
+               nr_frags = sinfo->nr_frags;
+
+       skb = build_skb(xdp->data_hard_start, xdp->frame_sz);
+       if (unlikely(!skb))
+               return NULL;
+
+       headroom = xdp->data - xdp->data_hard_start;
+       data_len = xdp->data_end - xdp->data;
+       skb_reserve(skb, headroom);
+       __skb_put(skb, data_len);
+
+       metasize = xdp->data - xdp->data_meta;
+       metasize = metasize > 0 ? metasize : 0;
+       if (metasize)
+               skb_metadata_set(skb, metasize);
+
+       if (unlikely(xdp_buff_has_frags(xdp)))
+               xdp_update_skb_shared_info(skb, nr_frags,
+                                          sinfo->xdp_frags_size,
+                                          xdp_frags_truesz,
+                                          xdp_buff_is_frag_pfmemalloc(xdp));
+
+       return skb;
+}
+
 /* TODO: build xdp in big mode */
 static int virtnet_build_xdp_buff_mrg(struct net_device *dev,
                                      struct virtnet_info *vi,