sctp: Add GSO support
Subsystems (4)
Marcelo Ricardo Leitner
maintainerv2 cover sctp: Add GSO support
This patchset adds sctp GSO support.
Performance tests indicates that increases throughput by 10% if using bigger chunk sizes, specially if bigger than MTU. For small chunks, it doesn't help much if not using heavy firewall rules.
For small chunks it will probably be of more use once we get something like MSG_MORE as David Laight had suggested.
v1->v2:
Added support for receiving GSO frames on SCTP stack, as requested by Dave Miller.
Marcelo Ricardo Leitner (7):
loopback: make use of NETIF_F_GSO_SOFTWARE
skbuff: export skb_gro_receive
sk_buff: allow segmenting based on frag sizes
skbuff: introduce skb_gso_validate_mtu
sctp: delay as much as possible skb_linearize
sctp: Add GSO support
sctp: improve debug message to also log curr pkt and new chunk size
drivers/net/loopback.c | 5 +- include/linux/netdev_features.h | 7 +- include/linux/netdevice.h | 1 + include/linux/skbuff.h | 8 + include/net/sctp/sctp.h | 4 + include/net/sctp/structs.h | 5 + net/core/ethtool.c | 1 + net/core/skbuff.c | 42 ++++- net/ipv4/af_inet.c | 1 + net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/mpls/af_mpls.c | 2 +- net/sctp/Makefile | 3 +- net/sctp/input.c | 57 ++++--- net/sctp/inqueue.c | 78 +++++++-- net/sctp/offload.c | 106 ++++++++++++ net/sctp/output.c | 366 +++++++++++++++++++++++++++------------- net/sctp/protocol.c | 3 + net/sctp/socket.c | 2 + 20 files changed, 530 insertions(+), 167 deletions(-) create mode 100644 net/sctp/offload.c
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 1/7 loopback: make use of NETIF_F_GSO_SOFTWARE
NETIF_F_GSO_SOFTWARE was defined to list all GSO software types, so lets make use of it in loopback code. Note that veth/vxlan/others already uses it.
Within this patch series, this patch causes lo to pick up SCTP GSO feature automatically (as it's added to NETIF_F_GSO_SOFTWARE) and thus avoiding segmentation if possible.
--- drivers/net/loopback.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/net/loopback.c +2 -3
@@ -169,10 +169,9 @@ static void loopback_setup(struct net_device *dev) dev->flags =IFF_LOOPBACK ; dev->priv_flags |=IFF_LIVE_ADDR_CHANGE |IFF_NO_QUEUE ;netif_keep_dst (dev); - dev->hw_features =NETIF_F_ALL_TSO |NETIF_F_UFO ; + dev->hw_features =NETIF_F_GSO_SOFTWARE ; dev->features =NETIF_F_SG |NETIF_F_FRAGLIST - |NETIF_F_ALL_TSO - |NETIF_F_UFO + |NETIF_F_GSO_SOFTWARE |NETIF_F_HW_CSUM |NETIF_F_RXCSUM |NETIF_F_SCTP_CRC
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 2/7 skbuff: export skb_gro_receive
In reply to Marcelo Ricardo Leitnersctp GSO requires it and sctp can be compiled as a module, so we need to export this function.
--- net/core/skbuff.c | 1 + 1 file changed, 1 insertion(+)
net/core/skbuff.c +1 -0
@@ -3438,6 +3438,7 @@ done:NAPI_GRO_CB (skb)->same_flow =1 ;return 0 ; } +EXPORT_SYMBOL_GPL (skb_gro_receive);void __init skb_init (void ) {
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 3/7 sk_buff: allow segmenting based on frag sizes
In reply to Marcelo Ricardo LeitnerThis patch allows segmenting a skb based on its frags sizes instead of based on a fixed value.
--- include/linux/skbuff.h | 5 +++++ net/core/skbuff.c | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-)
include/linux/skbuff.h +5 -0
@@ -301,6 +301,11 @@ struct sk_buff;#endif extern int sysctl_max_skb_frags; +/* Set skb_shinfo(skb)->gso_size to this in case you want skb_segment to +* segment using its current segmentation instead. +*/ +#define GSO_BY_FRAGS 0xFFFF +typedef struct skb_frag_struct skb_frag_t ;struct skb_frag_struct {
net/core/skbuff.c +7 -3
@@ -3116,9 +3116,13 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,int hsize;int size; - len = head_skb->len - offset; -if (len > mss) - len = mss; +if (unlikely (mss =GSO_BY_FRAGS )) { + len = list_skb->len; + }else { + len = head_skb->len - offset; +if (len > mss) + len = mss; + } hsize =skb_headlen (head_skb) - offset;if (hsize <0 )
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 4/7 skbuff: introduce skb_gso_validate_mtu
In reply to Marcelo Ricardo Leitnerskb_gso_network_seglen is not enough for checking fragment sizes if skb is using GSO_BY_FRAGS as we have to check frag per frag.
This patch introduces skb_gso_validate_mtu, based on the former, which will wrap the use case inside it as all calls to skb_gso_network_seglen were to validate if it fits on a given TMU, and improve the check.
--- include/linux/skbuff.h | 1 + net/core/skbuff.c | 31 +++++++++++++++++++++++++++++++ net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/mpls/af_mpls.c | 2 +- 6 files changed, 36 insertions(+), 4 deletions(-)
include/linux/skbuff.h +1 -0
@@ -2992,6 +2992,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);int skb_shift (struct sk_buff *tgt,struct sk_buff *skb,int shiftlen);void skb_scrub_packet (struct sk_buff *skb,bool xnet);unsigned int skb_gso_transport_seglen (const struct sk_buff *skb); +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu);struct sk_buff *skb_segment (struct sk_buff *skb,netdev_features_t features);struct sk_buff *skb_vlan_untag (struct sk_buff *skb);int skb_ensure_writable (struct sk_buff *skb,int write_len);
net/core/skbuff.c +31 -0
@@ -4392,6 +4392,37 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) }EXPORT_SYMBOL_GPL (skb_gso_transport_seglen); +/** +* skb_gso_validate_mtu - Return in case such skb fits a given MTU +* +* @skb: GSO skb +* +* skb_gso_validate_mtu validates if a given skb will fit a wanted MTU +* once split. +*/ +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu) +{ +const struct skb_shared_info *shinfo =skb_shinfo (skb); +const struct sk_buff *iter; +unsigned int hlen; + + hlen =skb_gso_network_seglen (skb); + +if (shinfo->gso_size !=GSO_BY_FRAGS ) +return hlen <= mtu; + +/* Undo this so we can re-use header sizes */ + hlen -=GSO_BY_FRAGS ; + + skb_walk_frags(skb, iter) { +if (hlen +skb_headlen (iter) > mtu) +return false; + } + +return true; +} +EXPORT_SYMBOL_GPL (skb_gso_validate_mtu); +static struct sk_buff *skb_reorder_vlan_header (struct sk_buff *skb) {if (skb_cow (skb,skb_headroom (skb)) <0 ) {
net/ipv4/ip_forward.c +1 -1
@@ -54,7 +54,7 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)if (skb->ignore_df)return false; -if (skb_is_gso (skb) &&skb_gso_network_seglen (skb) <= mtu) +if (skb_is_gso (skb) &&skb_gso_validate_mtu (skb, mtu))return false;return true;
net/ipv4/ip_output.c +1 -1
@@ -225,7 +225,7 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk,/* common case: locally created skb or seglen is <= mtu */ if (((IPCB (skb)->flags &IPSKB_FORWARDED ) =0 ) || -skb_gso_network_seglen (skb) <= mtu) +skb_gso_validate_mtu (skb, mtu))return ip_finish_output2 (net, sk, skb);/* Slowpath - GSO segment length is exceeding the dst MTU.
net/ipv6/ip6_output.c +1 -1
@@ -368,7 +368,7 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)if (skb->ignore_df)return false; -if (skb_is_gso (skb) &&skb_gso_network_seglen (skb) <= mtu) +if (skb_is_gso (skb) &&skb_gso_validate_mtu (skb, mtu))return false;return true;
net/mpls/af_mpls.c +1 -1
@@ -91,7 +91,7 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)if (skb->len <= mtu)return false; -if (skb_is_gso (skb) &&skb_gso_network_seglen (skb) <= mtu) +if (skb_is_gso (skb) &&skb_gso_validate_mtu (skb, mtu))return false;return true;
Signature · 1 line
2.5.5
Re: [PATCH v2 4/7] skbuff: introduce skb_gso_validate_mtu
Quoted 49 lines · Marcelo Ricardo Leitner
skb_gso_network_seglenis not enough for checking fragment sizes if skb is usingGSO_BY_FRAGSas we have to check frag per frag.This patch introduces
skb_gso_validate_mtu, based on the former, which will wrap the use case inside it as all calls toskb_gso_network_seglenwere to validate if it fits on a given TMU, and improve the check.Signed-off-by Marcelo Ricardo Leitner <[email protected]>Tested-by Xin Long <[email protected]>--- include/linux/skbuff.h | 1 + net/core/skbuff.c | 31 +++++++++++++++++++++++++++++++ net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/mpls/af_mpls.c | 2 +- 6 files changed, 36 insertions(+), 4 deletions(-)include/linux/skbuff.h +1
-0@@ -2992,6 +2992,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);int skb_shift (struct sk_buff *tgt,struct sk_buff *skb,int shiftlen);void skb_scrub_packet (struct sk_buff *skb,bool xnet);unsigned int skb_gso_transport_seglen (const struct sk_buff *skb); +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu);struct sk_buff *skb_segment (struct sk_buff *skb,netdev_features_t features);struct sk_buff *skb_vlan_untag (struct sk_buff *skb);int skb_ensure_writable (struct sk_buff *skb,int write_len);net/core/skbuff.c +11
-0@@ -4392,6 +4392,37 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) }EXPORT_SYMBOL_GPL (skb_gso_transport_seglen); +/** +* skb_gso_validate_mtu - Return in case such skb fits a given MTU +* +* @skb: GSO skb +* +* skb_gso_validate_mtu validates if a given skb will fit a wanted MTU +* once split. +*/ +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu) +{ +const struct skb_shared_info *shinfo =skb_shinfo (skb);
net/core/skbuff.c unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) · line 4415
Quoted 10 lines
net/core/skbuff.c +10
-0+const struct sk_buff *iter; +unsigned int hlen; + + hlen =skb_gso_network_seglen (skb); + +if (shinfo->gso_size !=GSO_BY_FRAGS ) +return hlen <= mtu; + +/* Undo this so we can re-use header sizes */ + hlen -=GSO_BY_FRAGS ;
Isn't this just "hlen = 0"? If so you could probably just remove this line and the references to hlen below and instead just loop through verifying skb_headlen() instead of adding a value that should be 0.
Quoted 12 lines
net/core/skbuff.c +9
-0+ skb_walk_frags(skb, iter) { +if (hlen +skb_headlen (iter) > mtu) +return false; + } + +return true; +} +EXPORT_SYMBOL_GPL (skb_gso_validate_mtu); +static struct sk_buff *skb_reorder_vlan_header (struct sk_buff *skb) {if (skb_cow (skb,skb_headroom (skb)) <0 ) {
Marcelo Ricardo Leitner
maintainerRe: [PATCH v2 4/7] skbuff: introduce skb_gso_validate_mtu
Quoted 57 lines · Alexander Duyck
Quoted 55 lines · Marcelo Ricardo Leitner
skb_gso_network_seglenis not enough for checking fragment sizes if skb is usingGSO_BY_FRAGSas we have to check frag per frag.This patch introduces
skb_gso_validate_mtu, based on the former, which will wrap the use case inside it as all calls toskb_gso_network_seglenwere to validate if it fits on a given TMU, and improve the check.Signed-off-by Marcelo Ricardo Leitner <[email protected]>Tested-by Xin Long <[email protected]>--- include/linux/skbuff.h | 1 + net/core/skbuff.c | 31 +++++++++++++++++++++++++++++++ net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/mpls/af_mpls.c | 2 +- 6 files changed, 36 insertions(+), 4 deletions(-)include/linux/skbuff.h +1
-0@@ -2992,6 +2992,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);int skb_shift (struct sk_buff *tgt,struct sk_buff *skb,int shiftlen);void skb_scrub_packet (struct sk_buff *skb,bool xnet);unsigned int skb_gso_transport_seglen (const struct sk_buff *skb); +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu);struct sk_buff *skb_segment (struct sk_buff *skb,netdev_features_t features);struct sk_buff *skb_vlan_untag (struct sk_buff *skb);int skb_ensure_writable (struct sk_buff *skb,int write_len);net/core/skbuff.c +17
-0@@ -4392,6 +4392,37 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) }EXPORT_SYMBOL_GPL (skb_gso_transport_seglen); +/** +* skb_gso_validate_mtu - Return in case such skb fits a given MTU +* +* @skb: GSO skb +* +* skb_gso_validate_mtu validates if a given skb will fit a wanted MTU +* once split. +*/ +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu) +{ +const struct skb_shared_info *shinfo =skb_shinfo (skb); +const struct sk_buff *iter; +unsigned int hlen; + + hlen =skb_gso_network_seglen (skb); + +if (shinfo->gso_size !=GSO_BY_FRAGS )
Quoted 8 lines
net/core/skbuff.c unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) · line 4415
Quoted 4 lines
net/core/skbuff.c +4
-0+return hlen <= mtu; + +/* Undo this so we can re-use header sizes */ + hlen -=GSO_BY_FRAGS ;Isn't this just "hlen = 0"? If so you could probably just remove this line and the references to hlen below and instead just loop through verifying
skb_headlen()instead of adding a value that should be 0.
By when this func is called the frags lack any headers, this is how I a ccount them. So I expect it to be different than 0 in most of the cases as it will contain the value of network header size, and it should have contained the size of sctp header too. Now reviewing it, I should have added a new check on skb_gso_transport_seglen() for sctp gso I think. As in:
net/core/skbuff.c +2 -0
@@ -4383,6 +4383,8 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) thlen +=inner_tcp_hdrlen (skb); }else if (likely (shinfo->gso_type & (SKB_GSO_TCPV4 |SKB_GSO_TCPV6 ))) { thlen =tcp_hdrlen (skb); + }else if (unlikely (shinfo->gso_type &SKB_GSO_SCTP )) { + thlen =sizeof (struct sctphdr ); }/* UFO sets gso_size to the size of the fragmentation * payload, i.e. the size of the L4 (UDP) header is already
This chunk would be on 6th patch. (v3 will be needed due to this)
I can ignore that and recalculate it but this way (with -GSO_BY_FRAGS) seemed cleaner as it reuses all that.
Quoted 13 lines
Quoted 12 lines
net/core/skbuff.c +9
-0+ skb_walk_frags(skb, iter) { +if (hlen +skb_headlen (iter) > mtu) +return false; + } + +return true; +} +EXPORT_SYMBOL_GPL (skb_gso_validate_mtu); +static struct sk_buff *skb_reorder_vlan_header (struct sk_buff *skb) {if (skb_cow (skb,skb_headroom (skb)) <0 ) {
Re: [PATCH v2 4/7] skbuff: introduce skb_gso_validate_mtu
Quoted 58 lines · Marcelo Ricardo Leitner
Quoted 57 lines · Alexander Duyck
Quoted 55 lines · Marcelo Ricardo Leitner
skb_gso_network_seglenis not enough for checking fragment sizes if skb is usingGSO_BY_FRAGSas we have to check frag per frag.This patch introduces
skb_gso_validate_mtu, based on the former, which will wrap the use case inside it as all calls toskb_gso_network_seglenwere to validate if it fits on a given TMU, and improve the check.Signed-off-by Marcelo Ricardo Leitner <[email protected]>Tested-by Xin Long <[email protected]>--- include/linux/skbuff.h | 1 + net/core/skbuff.c | 31 +++++++++++++++++++++++++++++++ net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/mpls/af_mpls.c | 2 +- 6 files changed, 36 insertions(+), 4 deletions(-)include/linux/skbuff.h +1
-0@@ -2992,6 +2992,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);int skb_shift (struct sk_buff *tgt,struct sk_buff *skb,int shiftlen);void skb_scrub_packet (struct sk_buff *skb,bool xnet);unsigned int skb_gso_transport_seglen (const struct sk_buff *skb); +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu);struct sk_buff *skb_segment (struct sk_buff *skb,netdev_features_t features);struct sk_buff *skb_vlan_untag (struct sk_buff *skb);int skb_ensure_writable (struct sk_buff *skb,int write_len);net/core/skbuff.c +17
-0@@ -4392,6 +4392,37 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) }EXPORT_SYMBOL_GPL (skb_gso_transport_seglen); +/** +* skb_gso_validate_mtu - Return in case such skb fits a given MTU +* +* @skb: GSO skb +* +* skb_gso_validate_mtu validates if a given skb will fit a wanted MTU +* once split. +*/ +bool skb_gso_validate_mtu (const struct sk_buff *skb,unsigned int mtu) +{ +const struct skb_shared_info *shinfo =skb_shinfo (skb); +const struct sk_buff *iter; +unsigned int hlen; + + hlen =skb_gso_network_seglen (skb); + +if (shinfo->gso_size !=GSO_BY_FRAGS )
Quoted 8 lines
Quoted 8 lines
net/core/skbuff.c unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) · line 4415
Quoted 4 lines
net/core/skbuff.c +4
-0+return hlen <= mtu; + +/* Undo this so we can re-use header sizes */ + hlen -=GSO_BY_FRAGS ;Isn't this just "hlen = 0"? If so you could probably just remove this line and the references to hlen below and instead just loop through verifying
skb_headlen()instead of adding a value that should be 0.
I see what you are saying. It also doesn't help I misread this as hlen != GSO_BY_FRAGS, not shinfo->gso_size.
Quoted 21 lines
By when this func is called the frags lack any headers, this is how I a ccount them. So I expect it to be different than 0 in most of the cases as it will contain the value of network header size, and it should have contained the size of sctp header too. Now reviewing it, I should have added a new check on
skb_gso_transport_seglen()for sctp gso I think. As in:net/core/skbuff.c +2
-0@@ -4383,6 +4383,8 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) thlen +=inner_tcp_hdrlen (skb); }else if (likely (shinfo->gso_type & (SKB_GSO_TCPV4 |SKB_GSO_TCPV6 ))) { thlen =tcp_hdrlen (skb); + }else if (unlikely (shinfo->gso_type &SKB_GSO_SCTP )) { + thlen =sizeof (struct sctphdr ); }/* UFO sets gso_size to the size of the fragmentation * payload, i.e. the size of the L4 (UDP) header is already
Quoted 4 lines
This chunk would be on 6th patch. (v3 will be needed due to this)
I can ignore that and recalculate it but this way (with -GSO_BY_FRAGS) seemed cleaner as it reuses all that.
Okay that works.
- Alex
Marcelo Ricardo Leitner
maintainerPATCH v2 5/7 sctp: delay as much as possible skb_linearize
In reply to Marcelo Ricardo LeitnerThis patch is a preparation for the GSO one. In order to successfully handle GSO packets on rx path we must not call skb_linearize, otherwise it defeats any gain GSO may have had.
This patch thus delays as much as possible the call to skb_linearize, leaving it to sctp_inq_pop() moment. For that the sanity checks performed now know how to deal with fragments.
One positive side-effect of this is that if the socket is backlogged it will have the chance of doing it on backlog processing instead of during softirq.
With this move, it's evident that a check for non-linearity in sctp_inq_pop was ineffective and is now removed. Note that a similar check is performed a bit below this one.
--- net/sctp/input.c | 45 +++++++++++++++++++++++++-------------------- net/sctp/inqueue.c | 29 ++++++++++++++++++----------- 2 files changed, 43 insertions(+), 31 deletions(-)
net/sctp/input.c +25 -20
@@ -112,7 +112,6 @@ int sctp_rcv(struct sk_buff *skb)struct sctp_ep_common *rcvr;struct sctp_transport *transport =NULL ;struct sctp_chunk *chunk; -struct sctphdr *sh;union sctp_addr src;union sctp_addr dest;int family;
@@ -124,15 +123,18 @@ int sctp_rcv(struct sk_buff *skb)__SCTP_INC_STATS (net,SCTP_MIB_INSCTPPACKS ); -if (skb_linearize (skb)) +/* If packet is too small to contain a single chunk, let's not +* waste time on it anymore. +*/ +if (skb->len <sizeof (struct sctphdr ) +sizeof (struct sctp_chunkhdr ) + +skb_transport_offset (skb)) goto discard_it; - sh =sctp_hdr (skb); +if (!pskb_may_pull (skb,sizeof (struct sctphdr ))) + goto discard_it; -/* Pull up the IP and SCTP headers. */ +/* Pull up the IP header. */ __skb_pull (skb,skb_transport_offset (skb)); -if (skb->len <sizeof (struct sctphdr )) - goto discard_it; skb->csum_valid =0 ;/* Previous value not applicable */ if (skb_csum_unnecessary (skb))
@@ -141,11 +143,7 @@ int sctp_rcv(struct sk_buff *skb) goto discard_it; skb->csum_valid =1 ; -skb_pull (skb,sizeof (struct sctphdr )); - -/* Make sure we at least have chunk headers worth of data left. */ -if (skb->len <sizeof (struct sctp_chunkhdr )) - goto discard_it; +__skb_pull (skb,sizeof (struct sctphdr )); family =ipver2af (ip_hdr (skb)->version); af =sctp_get_af_specific (family);
@@ -230,7 +228,7 @@ int sctp_rcv(struct sk_buff *skb) chunk->rcvr = rcvr;/* Remember the SCTP header. */ - chunk->sctp_hdr = sh; + chunk->sctp_hdr =sctp_hdr (skb);/* Set the source and destination addresses of the incoming chunk. */ sctp_init_addrs (chunk, &src, &dest);
@@ -660,19 +658,23 @@ out_unlock:*/ static int sctp_rcv_ootb (struct sk_buff *skb) { -sctp_chunkhdr_t *ch; -__u8 *ch_end; - - ch = (sctp_chunkhdr_t *) skb->data; +sctp_chunkhdr_t *ch, _ch; +int ch_end, offset =0 ;/* Scan through all the chunks in the packet. */ do { + /* Make sure we have at least the header there */ + if (offset + sizeof(sctp_chunkhdr_t) > skb->len) + break; + + ch = skb_header_pointer(skb, offset, sizeof(*ch), &_ch); + /* Break out if chunk length is less then minimal. */ if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t)) break; - ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); - if (ch_end > skb_tail_pointer(skb)) + ch_end = offset + WORD_ROUND(ntohs(ch->length)); + if (ch_end > skb->len) break; /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
@@ -697,8 +699,8 @@ static int sctp_rcv_ootb(struct sk_buff *skb)if (SCTP_CID_INIT = ch->type && (void *)ch != skb->data) goto discard; - ch = (sctp_chunkhdr_t *) ch_end; - }while (ch_end <skb_tail_pointer (skb)); + offset = ch_end; + }while (ch_end < skb->len);return 0 ;
@@ -1173,6 +1175,9 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net, {sctp_chunkhdr_t *ch; +if (skb_linearize (skb)) +return NULL ; + ch = (sctp_chunkhdr_t *) skb->data;/* The code below will attempt to walk the chunk and extract
net/sctp/inqueue.c +18 -10
@@ -130,7 +130,8 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)* at this time. */ -if ((chunk = queue->in_progress)) { + chunk = queue->in_progress; +if (chunk) {/* There is a packet that we have been working on. * Any post processing work to do before we move on? */
@@ -152,15 +153,29 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)if (!chunk) {struct list_head *entry; +next_chunk:/* Is the queue empty? */ if (list_empty (&queue->in_chunk_list))return NULL ; entry = queue->in_chunk_list.next; - chunk = queue->in_progress - list_entry(entry, struct sctp_chunk, list); + chunk = list_entry(entry, struct sctp_chunk, list);list_del_init (entry); +/* Linearize if it's not GSO */ +if (skb_is_nonlinear (chunk->skb)) { +if (skb_linearize (chunk->skb)) { +__SCTP_INC_STATS (dev_net (chunk->skb->dev),SCTP_MIB_IN_PKT_DISCARDS ); +sctp_chunk_free (chunk); + goto next_chunk; + } + +/* Update sctp_hdr as it probably changed */ + chunk->sctp_hdr =sctp_hdr (chunk->skb); + } + + queue->in_progress = chunk; +/* This is the first chunk in the packet. */ chunk->singleton =1 ; ch = (sctp_chunkhdr_t *) chunk->skb->data;
@@ -172,14 +187,6 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) chunk->chunk_hdr = ch; chunk->chunk_end = ((__u8 *)ch) +WORD_ROUND (ntohs (ch->length)); -/* In the unlikely case of an IP reassembly, the skb could be -* non-linear. If so, update chunk_end so that it doesn't go past -* the skb->tail. -*/ -if (unlikely (skb_is_nonlinear (chunk->skb))) { -if (chunk->chunk_end >skb_tail_pointer (chunk->skb)) - chunk->chunk_end =skb_tail_pointer (chunk->skb); - }skb_pull (chunk->skb,sizeof (sctp_chunkhdr_t)); chunk->subh.v =NULL ;/* Subheader is no longer valid. */
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 6/7 sctp: Add GSO support
In reply to Marcelo Ricardo LeitnerSCTP has this pecualiarity that its packets cannot be just segmented to (P)MTU. Its chunks must be contained in IP segments, padding respected. So we can't just generate a big skb, set gso_size to the fragmentation point and deliver it to IP layer.
This patch takes a different approach. SCTP will now build a skb as it would be if it was received using GRO. That is, there will be a cover skb with protocol headers and children ones containing the actual segments, already segmented to a way that respects SCTP RFCs.
With that, we can tell skb_segment() to just split based on frag_list, trusting its sizes are already in accordance.
This way SCTP can benefit from GSO and instead of passing several packets through the stack, it can pass a single large packet.
v2:
- Added support for receiving GSO frames, as requested by Dave Miller.
- Clear skb->cb if packet is GSO (otherwise it's not used by SCTP)
- Added heuristics similar to what we have in TCP for not generating single GSO packets that fills cwnd.
--- include/linux/netdev_features.h | 7 +- include/linux/netdevice.h | 1 + include/linux/skbuff.h | 2 + include/net/sctp/sctp.h | 4 + include/net/sctp/structs.h | 5 + net/core/ethtool.c | 1 + net/ipv4/af_inet.c | 1 + net/sctp/Makefile | 3 +- net/sctp/input.c | 12 +- net/sctp/inqueue.c | 51 +++++- net/sctp/offload.c | 106 ++++++++++++ net/sctp/output.c | 363 +++++++++++++++++++++++++++------------- net/sctp/protocol.c | 3 + net/sctp/socket.c | 2 + 14 files changed, 435 insertions(+), 126 deletions(-) create mode 100644 net/sctp/offload.c
include/linux/netdev_features.h +5 -2
@@ -53,8 +53,9 @@ enum {* headers in software. */ NETIF_F_GSO_TUNNEL_REMCSUM_BIT ,/* ... TUNNEL with TSO & REMCSUM */ +NETIF_F_GSO_SCTP_BIT ,/* ... SCTP fragmentation */ /**/ NETIF_F_GSO_LAST =/* last bit, see GSO_MASK */ -NETIF_F_GSO_TUNNEL_REMCSUM_BIT , +NETIF_F_GSO_SCTP_BIT ,NETIF_F_FCOE_CRC_BIT ,/* FCoE CRC32 */ NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */
@@ -128,6 +129,7 @@ enum {#define NETIF_F_TSO_MANGLEID __NETIF_F(TSO_MANGLEID)#define NETIF_F_GSO_PARTIAL __NETIF_F(GSO_PARTIAL)#define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM) +#define NETIF_F_GSO_SCTP __NETIF_F(GSO_SCTP)#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)#define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX)#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
@@ -166,7 +168,8 @@ enum { NETIF_F_FSO)/* List of features with software fallbacks. */ -#define NETIF_F_GSO_SOFTWARE (NETIF_F_ALL_TSO | NETIF_F_UFO) +#define NETIF_F_GSO_SOFTWARE (NETIF_F_ALL_TSO | NETIF_F_UFO | \ + NETIF_F_GSO_SCTP)/* * If one device supports one of these features, then enable them
include/linux/netdevice.h +1 -0
@@ -4012,6 +4012,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)BUILD_BUG_ON (SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >>NETIF_F_GSO_SHIFT ));BUILD_BUG_ON (SKB_GSO_PARTIAL != (NETIF_F_GSO_PARTIAL >>NETIF_F_GSO_SHIFT ));BUILD_BUG_ON (SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >>NETIF_F_GSO_SHIFT )); +BUILD_BUG_ON (SKB_GSO_SCTP != (NETIF_F_GSO_SCTP >>NETIF_F_GSO_SHIFT ));return (features & feature) = feature; }
include/linux/skbuff.h +2 -0
@@ -487,6 +487,8 @@ enum {SKB_GSO_PARTIAL =1 <<13 ,SKB_GSO_TUNNEL_REMCSUM =1 <<14 , + + SKB_GSO_SCTP = 1 << 15, };#if BITS_PER_LONG >32
include/net/sctp/sctp.h +4 -0
@@ -186,6 +186,10 @@ void sctp_assocs_proc_exit(struct net *net);int sctp_remaddr_proc_init (struct net *net);void sctp_remaddr_proc_exit (struct net *net); +/* +* sctp/offload.c +*/ +int sctp_offload_init (void );/* * Module global variables
include/net/sctp/structs.h +5 -0
@@ -566,6 +566,9 @@ struct sctp_chunk {/* This points to the sk_buff containing the actual data. */ struct sk_buff *skb; +/* In case of GSO packets, this will store the head one */ +struct sk_buff *head_skb; +/* These are the SCTP headers by reverse order in a packet. * Note that some of these may happen more than once. In that * case, we point at the "current" one, whatever that means
@@ -696,6 +699,8 @@ struct sctp_packet {size_t overhead;/* This is the total size of all chunks INCLUDING padding. */ size_t size; +/* This is the maximum size this packet may have */ +size_t max_size;/* The packet is destined for this transport address. * The function we finally use to pass down to the next lower
net/core/ethtool.c +1 -0
@@ -89,6 +89,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", [NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation", [NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial", + [NETIF_F_GSO_SCTP_BIT] = "tx-sctp-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
net/ipv4/af_inet.c +1 -0
@@ -1220,6 +1220,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,SKB_GSO_TCP_FIXEDID |SKB_GSO_TUNNEL_REMCSUM |SKB_GSO_PARTIAL | +SKB_GSO_SCTP | 0))) goto out;
net/sctp/Makefile +2 -1
@@ -11,7 +11,8 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ transport.o chunk.o sm_make_chunk.o ulpevent.o \ inqueue.o outqueue.o ulpqueue.o \ tsnmap.o bind_addr.o socket.o primitive.o \ - output.o input.o debug.o ssnmap.o auth.o + output.o input.o debug.o ssnmap.o auth.o \ + offload.o sctp_probe-y := probe.o
net/sctp/input.c +11 -1
@@ -139,7 +139,9 @@ int sctp_rcv(struct sk_buff *skb) skb->csum_valid =0 ;/* Previous value not applicable */ if (skb_csum_unnecessary (skb))__skb_decr_checksum_unnecessary (skb); -else if (!sctp_checksum_disable &&sctp_rcv_checksum (net, skb) <0 ) +else if (!sctp_checksum_disable && + !(skb_shinfo (skb)->gso_type &SKB_GSO_SCTP ) && +sctp_rcv_checksum (net, skb) <0 ) goto discard_it; skb->csum_valid =1 ;
@@ -1175,6 +1177,14 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net, {sctp_chunkhdr_t *ch; +/* We do not allow GSO frames here as we need to linearize and +* then cannot guarantee frame boundaries. This shouldn't be an +* issue as packets hitting this are mostly INIT or INIT-ACK and +* those cannot be on GSO-style anyway. +*/ +if ((skb_shinfo (skb)->gso_type &SKB_GSO_SCTP ) =SKB_GSO_SCTP ) +return NULL ; +if (skb_linearize (skb))return NULL ;
net/sctp/inqueue.c +43 -8
79 lines not on this page · raw message
net/sctp/offload.c new file +106 -0
106 lines not on this page · raw message
net/sctp/output.c +248 -114
482 lines not on this page · raw message
net/sctp/protocol.c +3 -0
@@ -1516,6 +1516,9 @@ static __init int sctp_init(void)if (status) goto err_v6_add_protocol; +if (sctp_offload_init () <0 ) +pr_crit ("%s: Cannot add SCTP protocol offload\n" , __func__); + out:return status; err_v6_add_protocol:
net/sctp/socket.c +2 -0
@@ -4003,6 +4003,8 @@ static int sctp_init_sock(struct sock *sk)return -ESOCKTNOSUPPORT ; } + sk->sk_gso_type =SKB_GSO_SCTP ; +/* Initialize default send parameters. These parameters can be * modified with the SCTP_DEFAULT_SEND_PARAM socket option. */
Signature · 1 line
2.5.5
Marcelo Ricardo Leitner
maintainerPATCH v2 7/7 sctp: improve debug message to also log curr pkt and new chunk size
In reply to Marcelo Ricardo LeitnerThis is useful for debugging packet sizes.
--- net/sctp/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sctp/output.c +2 -1
@@ -182,7 +182,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,sctp_xmit_t retval;int error =0 ; -pr_debug ("%s: packet:%p chunk:%p\n" , __func__, packet, chunk); +pr_debug ("%s: packet:%p size:%lu chunk:%p size:%d\n" , __func__, + packet, packet->size, chunk, chunk->skb ? chunk->skb->len :-1 );switch ((retval = (sctp_packet_append_chunk (packet, chunk)))) {case SCTP_XMIT_PMTU_FULL :
Signature · 1 line
2.5.5