@@ -1043,7 +1043,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
10431043 unsigned int metasize = 0 ;
10441044 unsigned int tailroom = headroom ? sizeof (struct skb_shared_info ) : 0 ;
10451045 unsigned int room = SKB_DATA_ALIGN (headroom + tailroom );
1046- unsigned int frame_sz ;
1046+ unsigned int frame_sz , xdp_room ;
10471047 int err ;
10481048
10491049 head_skb = NULL ;
@@ -1064,11 +1064,14 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
10641064 rcu_read_lock ();
10651065 xdp_prog = rcu_dereference (rq -> xdp_prog );
10661066 if (xdp_prog ) {
1067+ unsigned int xdp_frags_truesz = 0 ;
1068+ struct skb_shared_info * shinfo ;
10671069 struct xdp_frame * xdpf ;
10681070 struct page * xdp_page ;
10691071 struct xdp_buff xdp ;
10701072 void * data ;
10711073 u32 act ;
1074+ int i ;
10721075
10731076 /* Transient failure which in theory could occur if
10741077 * in-flight packets from before XDP was enabled reach
@@ -1084,14 +1087,16 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
10841087 */
10851088 frame_sz = truesize ;
10861089
1087- /* This happens when rx buffer size is underestimated
1088- * or headroom is not enough because of the buffer
1089- * was refilled before XDP is set. This should only
1090- * happen for the first several packets, so we don't
1091- * care much about its performance.
1090+ /* This happens when headroom is not enough because
1091+ * of the buffer was prefilled before XDP is set.
1092+ * This should only happen for the first several packets.
1093+ * In fact, vq reset can be used here to help us clean up
1094+ * the prefilled buffers, but many existing devices do not
1095+ * support it, and we don't want to bother users who are
1096+ * using xdp normally.
10921097 */
1093- if (unlikely ( num_buf > 1 ||
1094- headroom < virtnet_get_headroom (vi ))) {
1098+ if (! xdp_prog -> aux -> xdp_has_frags &&
1099+ ( num_buf > 1 || headroom < virtnet_get_headroom (vi ))) {
10951100 /* linearize data for XDP */
10961101 xdp_page = xdp_linearize_page (rq , & num_buf ,
10971102 page , offset ,
@@ -1102,17 +1107,29 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
11021107 if (!xdp_page )
11031108 goto err_xdp ;
11041109 offset = VIRTIO_XDP_HEADROOM ;
1110+ } else if (unlikely (headroom < virtnet_get_headroom (vi ))) {
1111+ xdp_room = SKB_DATA_ALIGN (VIRTIO_XDP_HEADROOM +
1112+ sizeof (struct skb_shared_info ));
1113+ if (len + xdp_room > PAGE_SIZE )
1114+ goto err_xdp ;
1115+
1116+ xdp_page = alloc_page (GFP_ATOMIC );
1117+ if (!xdp_page )
1118+ goto err_xdp ;
1119+
1120+ memcpy (page_address (xdp_page ) + VIRTIO_XDP_HEADROOM ,
1121+ page_address (page ) + offset , len );
1122+ frame_sz = PAGE_SIZE ;
1123+ offset = VIRTIO_XDP_HEADROOM ;
11051124 } else {
11061125 xdp_page = page ;
11071126 }
11081127
1109- /* Allow consuming headroom but reserve enough space to push
1110- * the descriptor on if we get an XDP_TX return code.
1111- */
11121128 data = page_address (xdp_page ) + offset ;
1113- xdp_init_buff (& xdp , frame_sz - vi -> hdr_len , & rq -> xdp_rxq );
1114- xdp_prepare_buff (& xdp , data - VIRTIO_XDP_HEADROOM + vi -> hdr_len ,
1115- VIRTIO_XDP_HEADROOM , len - vi -> hdr_len , true);
1129+ err = virtnet_build_xdp_buff_mrg (dev , vi , rq , & xdp , data , len , frame_sz ,
1130+ & num_buf , & xdp_frags_truesz , stats );
1131+ if (unlikely (err ))
1132+ goto err_xdp_frags ;
11161133
11171134 act = bpf_prog_run_xdp (xdp_prog , & xdp );
11181135 stats -> xdp_packets ++ ;
@@ -1208,6 +1225,19 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
12081225 __free_pages (xdp_page , 0 );
12091226 goto err_xdp ;
12101227 }
1228+ err_xdp_frags :
1229+ if (unlikely (xdp_page != page ))
1230+ __free_pages (xdp_page , 0 );
1231+
1232+ if (xdp_buff_has_frags (& xdp )) {
1233+ shinfo = xdp_get_shared_info_from_buff (& xdp );
1234+ for (i = 0 ; i < shinfo -> nr_frags ; i ++ ) {
1235+ xdp_page = skb_frag_page (& shinfo -> frags [i ]);
1236+ put_page (xdp_page );
1237+ }
1238+ }
1239+
1240+ goto err_xdp ;
12111241 }
12121242 rcu_read_unlock ();
12131243
0 commit comments