@@ -106,6 +106,9 @@ struct virtnet_info {
106106 /* Has control virtqueue */
107107 bool has_cvq ;
108108
109+ /* Host can handle any s/g split between our header and packet data */
110+ bool any_header_sg ;
111+
109112 /* enable config space updates */
110113 bool config_enable ;
111114
@@ -669,12 +672,28 @@ static void free_old_xmit_skbs(struct send_queue *sq)
669672
670673static int xmit_skb (struct send_queue * sq , struct sk_buff * skb )
671674{
672- struct skb_vnet_hdr * hdr = skb_vnet_hdr ( skb ) ;
675+ struct skb_vnet_hdr * hdr ;
673676 const unsigned char * dest = ((struct ethhdr * )skb -> data )-> h_dest ;
674677 struct virtnet_info * vi = sq -> vq -> vdev -> priv ;
675678 unsigned num_sg ;
679+ unsigned hdr_len ;
680+ bool can_push ;
676681
677682 pr_debug ("%s: xmit %p %pM\n" , vi -> dev -> name , skb , dest );
683+ if (vi -> mergeable_rx_bufs )
684+ hdr_len = sizeof hdr -> mhdr ;
685+ else
686+ hdr_len = sizeof hdr -> hdr ;
687+
688+ can_push = vi -> any_header_sg &&
689+ !((unsigned long )skb -> data & (__alignof__(* hdr ) - 1 )) &&
690+ !skb_header_cloned (skb ) && skb_headroom (skb ) >= hdr_len ;
691+ /* Even if we can, don't push here yet as this would skew
692+ * csum_start offset below. */
693+ if (can_push )
694+ hdr = (struct skb_vnet_hdr * )(skb -> data - hdr_len );
695+ else
696+ hdr = skb_vnet_hdr (skb );
678697
679698 if (skb -> ip_summed == CHECKSUM_PARTIAL ) {
680699 hdr -> hdr .flags = VIRTIO_NET_HDR_F_NEEDS_CSUM ;
@@ -703,15 +722,18 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
703722 hdr -> hdr .gso_size = hdr -> hdr .hdr_len = 0 ;
704723 }
705724
706- hdr -> mhdr .num_buffers = 0 ;
707-
708- /* Encode metadata header at front. */
709725 if (vi -> mergeable_rx_bufs )
710- sg_set_buf (sq -> sg , & hdr -> mhdr , sizeof hdr -> mhdr );
711- else
712- sg_set_buf (sq -> sg , & hdr -> hdr , sizeof hdr -> hdr );
726+ hdr -> mhdr .num_buffers = 0 ;
713727
714- num_sg = skb_to_sgvec (skb , sq -> sg + 1 , 0 , skb -> len ) + 1 ;
728+ if (can_push ) {
729+ __skb_push (skb , hdr_len );
730+ num_sg = skb_to_sgvec (skb , sq -> sg , 0 , skb -> len );
731+ /* Pull header back to avoid skew in tx bytes calculations. */
732+ __skb_pull (skb , hdr_len );
733+ } else {
734+ sg_set_buf (sq -> sg , hdr , hdr_len );
735+ num_sg = skb_to_sgvec (skb , sq -> sg + 1 , 0 , skb -> len ) + 1 ;
736+ }
715737 return virtqueue_add_outbuf (sq -> vq , sq -> sg , num_sg , skb , GFP_ATOMIC );
716738}
717739
@@ -1552,6 +1574,9 @@ static int virtnet_probe(struct virtio_device *vdev)
15521574 if (virtio_has_feature (vdev , VIRTIO_NET_F_MRG_RXBUF ))
15531575 vi -> mergeable_rx_bufs = true;
15541576
1577+ if (virtio_has_feature (vdev , VIRTIO_F_ANY_LAYOUT ))
1578+ vi -> any_header_sg = true;
1579+
15551580 if (virtio_has_feature (vdev , VIRTIO_NET_F_CTRL_VQ ))
15561581 vi -> has_cvq = true;
15571582
@@ -1727,6 +1752,7 @@ static unsigned int features[] = {
17271752 VIRTIO_NET_F_CTRL_RX , VIRTIO_NET_F_CTRL_VLAN ,
17281753 VIRTIO_NET_F_GUEST_ANNOUNCE , VIRTIO_NET_F_MQ ,
17291754 VIRTIO_NET_F_CTRL_MAC_ADDR ,
1755+ VIRTIO_F_ANY_LAYOUT ,
17301756};
17311757
17321758static struct virtio_driver virtio_net_driver = {
0 commit comments