IPv4/IPv6: UDP Large Send Offload feature
Subsystems (4)
PATCH 1/1 IPv4/IPv6: UDP Large Send Offload feature
Hi,
Attached below is a kernel patch to provide UDP LSO(Large Send Offload) feature.
This is the UDP counterpart of TSO. Basically, an oversized packet (less than or equal to 65535) is handed to the NIC. The adapter then produces IP fragments in conformance with RFC791(IPv4) or RFC2460(IPv6).
Very much like TCP TSO, UDP LSO provides significant reduction in CPU utilization, especially with 1500 MTU frames. For 10GbE traffic, these extra CPU cycles translate into significant throughput increase on most server platforms.
Reasonable amount of testing has been done on the patch using Neterion's 10G Xframe-II adapter to ensure code stability.
Please review the patch.
Also, below is a "how-to" on changes required in network drivers to use the UDP LSO interface.
UDP Large Send Offload (ULSO) Interface:
----------------------------------------
ULSO is a feature wherein the Linux kernel network stack will offload the IP fragmentation functionality of large UDP datagram to hardware. This will reduce the overhead of stack in fragmenting the large UDP datagram to MTU sized packets.
1) Drivers indicate their capability of ULSO using dev->features |= NETIF_F_UDP_LSO | NETIF_F_HW_CSUM
NETIF_F_HW_CSUM is required for ULSO over ipv6.
2) ULSO packet will be submitted for transmission using driver xmit routine. ULSO packet will have a non zero value for
"skb_shinfo(skb)->ulso_size"
skb_shinfo(skb)->ulso_size will indicate the length of data in each IP fragment going out of the adapter after IP fragmentation by hardware.
skb->data will contain MAC/IP/UDP header and skb_shinfo(skb)->frags[] contains the data payload. The skb->ip_summed will be set to CHECKSUM_HW indicating that hardware has to perform checksum calculation. Hardware should compute the UDP checksum of complete UDP datagram and also the IP header checksum of each fragmented IP packet.
For IPV6 the ULSO provides the fragment identification id in skb_shinfo(skb)->ip6_id. The adapter should use this ID for generating IPv6 fragments.
---
include/linux/netdevice.h +1 -0
@@ -414,6 +414,7 @@#define NETIF_F_VLAN_CHALLENGED 1024/* Device cannot handle VLAN packets */ #define NETIF_F_TSO 2048/* Can offload TCP/IP segmentation */ #define NETIF_F_LLTX 4096/* LockLess TX */ +#define NETIF_F_UDP_LSO 8192/* Can offload UDP Large Send*/ /* Called after device is detached from network. */ void (*uninit)(struct net_device *dev);
include/linux/skbuff.h +2 -0
@@ -135,6 +135,8 @@atomic_t dataref;unsigned int nr_frags;unsigned short tso_size; +unsigned short ulso_size; +unsigned int ip6_id;unsigned short tso_segs;struct sk_buff *frag_list;skb_frag_t frags[MAX_SKB_FRAGS ];
include/net/sock.h +6 -0
@@ -1296,5 +1296,11 @@return -ENODEV ; }#endif +struct sk_buff *sock_append_data (struct sock *sk, +int getfrag (void *from,char *to,int offset,int len, +int odd,struct sk_buff *skb), +void *from,int length,int transhdrlen, +int hh_len,int fragheaderlen, +unsigned int flags,int *err);#endif /* _SOCK_H */
net/core/skbuff.c +2 -0
@@ -159,6 +159,8 @@skb_shinfo (skb)->tso_size =0 ;skb_shinfo (skb)->tso_segs =0 ;skb_shinfo (skb)->frag_list =NULL ; +skb_shinfo (skb)->ulso_size =0 ; +skb_shinfo (skb)->ip6_id =0 ; out:return skb; nodata:
net/core/sock.c +101 -0
107 lines not on this page · raw message
net/ipv4/ip_output.c +25 -1
@@ -291,7 +291,8 @@ {IP_INC_STATS (IPSTATS_MIB_OUTREQUESTS ); -if (skb->len >dst_mtu (skb->dst) && !skb_shinfo (skb)->tso_size) +if (skb->len >dst_mtu (skb->dst) && + !(skb_shinfo (skb)->ulso_size ||skb_shinfo (skb)->tso_size))return ip_fragment (skb, ip_finish_output);else return ip_finish_output (skb);
@@ -789,6 +790,29 @@ inet->cork.length += length; + sk->sk_route_caps |= rt->u.dst.dev->features; +if (((length > mtu) && (sk->sk_protocol ==IPPROTO_UDP )) && + (sk->sk_route_caps &NETIF_F_UDP_LSO )) { +/* There is support for UDP large send offload by network +* device, so create one single skb packet containing complete +* udp datagram +*/ + skb =sock_append_data (sk, getfrag, from, + (length - transhdrlen), transhdrlen, + hh_len, fragheaderlen, flags, &err); +if (skb !=NULL ) { +/* specify the length of each IP datagram fragment*/ +skb_shinfo (skb)->ulso_size = (mtu - fragheaderlen); +return 0 ; + }else if (err == -EOPNOTSUPP ) { +/* There is not enough support do UPD LSO, +* so follow normal path +*/ + err =0 ; + }else + goto error; + } +/* So, what's going on in the loop below? * * We use calculated fragment length to generate chained skb,
net/ipv6/ip6_output.c +31 -1
@@ -147,7 +147,8 @@int ip6_output (struct sk_buff *skb) { -if (skb->len >dst_mtu (skb->dst) ||dst_allfrag (skb->dst)) +if ((skb->len >dst_mtu (skb->dst) && !skb_shinfo (skb)->ulso_size) || +dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else return ip6_output2 (skb);
@@ -898,6 +899,35 @@*/ inet->cork.length += length; + sk->sk_route_caps |= rt->u.dst.dev->features; +if (((length > mtu) && (sk->sk_protocol ==IPPROTO_UDP )) && + ((sk->sk_route_caps & (NETIF_F_UDP_LSO |NETIF_F_HW_CSUM )) == + (NETIF_F_UDP_LSO |NETIF_F_HW_CSUM ))) { + +/* There is support for UDP large send offload by network +* device, so create one single skb packet containing complete +* udp datagram +*/ + skb =sock_append_data (sk, getfrag, from, + (length - transhdrlen), transhdrlen, + hh_len, fragheaderlen, flags, &err); +if (skb !=NULL ) { +struct frag_hdr fhdr; + +/* specify the length of each IP datagram fragment*/ +skb_shinfo (skb)->ulso_size = (mtu - fragheaderlen - +sizeof (struct frag_hdr )); +ipv6_select_ident (skb, &fhdr); +skb_shinfo (skb)->ip6_id = fhdr.identification; +return 0 ; + }else if (err == -EOPNOTSUPP ){ +/* There is not enough support for UDP LSO, +* so follow normal path +*/ + err =0 ; + }else + goto error; + }if ((skb =skb_peek_tail (&sk->sk_write_queue)) ==NULL ) goto alloc_new_skb;
David S. Miller
maintainerFrom: [email protected]
Date: Thu, 26 May 2005 16:20:06 -0700 (PDT)
Quoted 2 lines
Attached below is a kernel patch to provide UDP LSO(Large Send Offload) feature.
Interesting patch, thanks a lot. Some quick review:
1) I think you can use skb_shinfo(skb)->tso_size, and UDP packet with this non-zero will never be sent unless the driver indicates the capability.
2) I think NETIF_F_USO is a nicer name and consistent with the existing NETIF_F_TSO macro name. Please change it.
3) Make NETIF_F_USO require both NETIF_F_SG and checksumming capability. Check this at device registry, and ethtool operation time, so that you need not verify it during packet send.
For #3, it should be a simple change to net/core/dev.c and net/core/ethtool.c, for example see this test we have in net/core/dev.c:register_netdevice()
/* TSO requires that SG is present as well. */
if ((dev->features & NETIF_F_TSO) &&
!(dev->features & NETIF_F_SG)) {
printk("%s: Dropping NETIF_F_TSO since no SG feature.\n",
dev->name);
dev->features &= ~NETIF_F_TSO;
}Just make the same exact check for NETIF_F_USO.
Similarly, you'll need to add the necessary ethtool machinery (missing from your patch, but really needed) then do something similar to net/core/ethtool.c:ethtool_set_tso() for ethtool setting of NETIF_F_USO. Probably you'll name this function ethtool_set_uso() :-)
David S. Miller
maintainersock_append_data() seems like a lot of wasted work.
We already pass around the fragmented SKB as a list chained by skb_shinfo(skb)->fraglist, just pass this thing to the device and in this way you'll avoid all of that work sock_append_data() does entirely.
Or is there a reason you did not implement it this way?
This is one of the uses the skb_shinfo(skb)->fraglist was intended for.
IN FACT, this fragmentation offload you are implementing here is what the feature bit NETIF_F_FRAGLIST was meant to indicate.
Hi David,
Thanks for the quick feedback.
At that time when we considered using skb_shinfo(skb)->fraglist, it contained fragments of MTU size. So, for a 60k udp datagram and 1500 MTU we will have 60k/1500 = 45 fragments which is more than MAX_SKB_FRAGS(18).
However we will relook at fraglist for the possibility of increasing frag size to >MTU.
Thanks,
Ravi
-----Original Message-----
From: David S. Miller [mailto:[email protected]]
Sent: Thursday, May 26, 2005 4:42 PM
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH 2.6.12-rc4] IPv4/IPv6: UDP Large Send Offload feature
sock_append_data() seems like a lot of wasted work.
We already pass around the fragmented SKB as a list chained by skb_shinfo(skb)->fraglist, just pass this thing to the device and in this way you'll avoid all of that work sock_append_data() does entirely.
Or is there a reason you did not implement it this way?
This is one of the uses the skb_shinfo(skb)->fraglist was intended for.
IN FACT, this fragmentation offload you are implementing here is what the feature bit NETIF_F_FRAGLIST was meant to indicate.
David S. Miller
maintainerFrom: "Ravinandan Arakali" <[email protected]>
Date: Fri, 27 May 2005 09:32:00 -0700
Quoted 8 lines
Thanks for the quick feedback.
At that time when we considered using skb_shinfo(skb)->fraglist, it contained fragments of MTU size. So, for a 60k udp datagram and 1500 MTU we will have 60k/1500 = 45 fragments which is more thanMAX_SKB_FRAGS(18).However we will relook at fraglist for the possibility of increasing frag size to >MTU.
MAX_SKB_FRAGS controls the limit of skb_shinfo(skb)->frags[] entries, not how many SKBs may be chained via skb_shinfo(skb)->fraglist, there is no limit on the latter.
Note that there is much coalescing that can be performed on the SKB list data areas, particularly if UDP sendfile() is being used.
But such coalescing is messy to be performing inside of the drivers. It may end up being the case that your approach ends up being a better one for these reasons.
David,
Since there seems to be pros and cons for both the approaches, we are planning
to submit two separate patches(one for each approach). These patches also include the ethtool changes. In terms of performance, we did not observe any diff between the two approaches although the first approach(using SG) minimizes
coalescing in driver.
Also, some changes will be required in the ethtool user-level utility. I'm not sure if this is the right forum to submit patches for the ethtool utility as well..
Thanks,
Ravi
-----Original Message-----
From: David S. Miller [mailto:[email protected]]
Sent: Friday, May 27, 2005 12:02 PM
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH 2.6.12-rc4] IPv4/IPv6: UDP Large Send Offload feature
From: "Ravinandan Arakali" <[email protected]>
Date: Fri, 27 May 2005 09:32:00 -0700
Quoted 8 lines
Thanks for the quick feedback.
At that time when we considered using skb_shinfo(skb)->fraglist, it contained fragments of MTU size. So, for a 60k udp datagram and 1500 MTU we will have 60k/1500 = 45 fragments which is more thanMAX_SKB_FRAGS(18).However we will relook at fraglist for the possibility of increasing frag size to >MTU.
MAX_SKB_FRAGS controls the limit of skb_shinfo(skb)->frags[] entries, not how many SKBs may be chained via skb_shinfo(skb)->fraglist, there is no limit on the latter.
Note that there is much coalescing that can be performed on the SKB list data areas, particularly if UDP sendfile() is being used.
But such coalescing is messy to be performing inside of the drivers. It may end up being the case that your approach ends up being a better one for these reasons.
David S. Miller
maintainerFrom: "Ravinandan Arakali" <[email protected]>
Date: Thu, 2 Jun 2005 16:18:55 -0700
Quoted 7 lines
Since there seems to be pros and cons for both the approaches, we are planning
to submit two separate patches(one for each approach). These patches also include the ethtool changes. In terms of performance, we did not observe any diff between the two approaches although the first approach(using SG) minimizes
coalescing in driver.
Ok. I think minimizing driver specific work is probably going to make the SG approach more desirable, but we'll see.
Quoted 3 lines
Also, some changes will be required in the ethtool user-level utility. I'm not sure if this is the right forum to submit patches for the ethtool utility as well..
Making sure [email protected] gets the patch is usually the way to go wrt. ethtool submissions.
Quoted 12 lines · On Thu, 26 May 2005 16:20:06 -0700 (PDT) [email protected] wrote:
Hi,
Attached below is a kernel patch to provide UDP LSO(Large Send Offload) feature.
This is the UDP counterpart of TSO. Basically, an oversized packet (less than or equal to 65535) is handed to the NIC. The adapter then produces IP fragments in conformance with RFC791(IPv4) or RFC2460(IPv6).Very much like TCP TSO, UDP LSO provides significant reduction in CPU utilization, especially with 1500 MTU frames. For 10GbE traffic, these extra CPU cycles translate into significant throughput increase on most server platforms.
Quoted 7 lines
Reasonable amount of testing has been done on the patch using Neterion's 10G Xframe-II adapter to ensure code stability.
Please review the patch.
Also, below is a "how-to" on changes required in network drivers to use the UDP LSO interface.
The only downside is that it might encourage continued use of NFS over UDP. Perhaps we need a big fat warning in NFS that says:
*** Your using NFS over UDP, your data integrity is no longer guaranteed; *** you have been warned.
David S. Miller
maintainerFrom: Stephen Hemminger <[email protected]>
Date: Fri, 27 May 2005 08:57:13 -0700
Quoted 5 lines
The only downside is that it might encourage continued use of NFS over UDP. Perhaps we need a big fat warning in NFS that says:
*** Your using NFS over UDP, your data integrity is no longer guaranteed; *** you have been warned.
I agree, I think the NFS client should print out something like this when it mounts a volume over UDP for the first time.