Skip to content

Commit 90017ac

Browse files
marceloleitnerdavem330
authored andcommitted
sctp: Add GSO support
SCTP 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. v3: - consider sctphdr size in skb_gso_transport_seglen() - rebased due to 5c7cdf3 ("gso: Remove arbitrary checks for unsupported GSO") Signed-off-by: Marcelo Ricardo Leitner <[email protected]> Tested-by: Xin Long <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3acb50c commit 90017ac

File tree

14 files changed

+429
-126
lines changed

14 files changed

+429
-126
lines changed

include/linux/netdev_features.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ enum {
5353
* headers in software.
5454
*/
5555
NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */
56+
NETIF_F_GSO_SCTP_BIT, /* ... SCTP fragmentation */
5657
/**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */
57-
NETIF_F_GSO_TUNNEL_REMCSUM_BIT,
58+
NETIF_F_GSO_SCTP_BIT,
5859

5960
NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */
6061
NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */
@@ -128,6 +129,7 @@ enum {
128129
#define NETIF_F_TSO_MANGLEID __NETIF_F(TSO_MANGLEID)
129130
#define NETIF_F_GSO_PARTIAL __NETIF_F(GSO_PARTIAL)
130131
#define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM)
132+
#define NETIF_F_GSO_SCTP __NETIF_F(GSO_SCTP)
131133
#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
132134
#define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX)
133135
#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
@@ -166,7 +168,8 @@ enum {
166168
NETIF_F_FSO)
167169

168170
/* List of features with software fallbacks. */
169-
#define NETIF_F_GSO_SOFTWARE (NETIF_F_ALL_TSO | NETIF_F_UFO)
171+
#define NETIF_F_GSO_SOFTWARE (NETIF_F_ALL_TSO | NETIF_F_UFO | \
172+
NETIF_F_GSO_SCTP)
170173

171174
/*
172175
* If one device supports one of these features, then enable them

include/linux/netdevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4012,6 +4012,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
40124012
BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
40134013
BUILD_BUG_ON(SKB_GSO_PARTIAL != (NETIF_F_GSO_PARTIAL >> NETIF_F_GSO_SHIFT));
40144014
BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT));
4015+
BUILD_BUG_ON(SKB_GSO_SCTP != (NETIF_F_GSO_SCTP >> NETIF_F_GSO_SHIFT));
40154016

40164017
return (features & feature) == feature;
40174018
}

include/linux/skbuff.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ enum {
487487
SKB_GSO_PARTIAL = 1 << 13,
488488

489489
SKB_GSO_TUNNEL_REMCSUM = 1 << 14,
490+
491+
SKB_GSO_SCTP = 1 << 15,
490492
};
491493

492494
#if BITS_PER_LONG > 32

include/net/sctp/sctp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ void sctp_assocs_proc_exit(struct net *net);
186186
int sctp_remaddr_proc_init(struct net *net);
187187
void sctp_remaddr_proc_exit(struct net *net);
188188

189+
/*
190+
* sctp/offload.c
191+
*/
192+
int sctp_offload_init(void);
189193

190194
/*
191195
* Module global variables

include/net/sctp/structs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ struct sctp_chunk {
566566
/* This points to the sk_buff containing the actual data. */
567567
struct sk_buff *skb;
568568

569+
/* In case of GSO packets, this will store the head one */
570+
struct sk_buff *head_skb;
571+
569572
/* These are the SCTP headers by reverse order in a packet.
570573
* Note that some of these may happen more than once. In that
571574
* case, we point at the "current" one, whatever that means
@@ -696,6 +699,8 @@ struct sctp_packet {
696699
size_t overhead;
697700
/* This is the total size of all chunks INCLUDING padding. */
698701
size_t size;
702+
/* This is the maximum size this packet may have */
703+
size_t max_size;
699704

700705
/* The packet is destined for this transport address.
701706
* The function we finally use to pass down to the next lower

net/core/ethtool.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
8989
[NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
9090
[NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation",
9191
[NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial",
92+
[NETIF_F_GSO_SCTP_BIT] = "tx-sctp-segmentation",
9293

9394
[NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
9495
[NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",

net/core/skbuff.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <linux/slab.h>
5050
#include <linux/tcp.h>
5151
#include <linux/udp.h>
52+
#include <linux/sctp.h>
5253
#include <linux/netdevice.h>
5354
#ifdef CONFIG_NET_CLS_ACT
5455
#include <net/pkt_sched.h>
@@ -4383,6 +4384,8 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
43834384
thlen += inner_tcp_hdrlen(skb);
43844385
} else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
43854386
thlen = tcp_hdrlen(skb);
4387+
} else if (unlikely(shinfo->gso_type & SKB_GSO_SCTP)) {
4388+
thlen = sizeof(struct sctphdr);
43864389
}
43874390
/* UFO sets gso_size to the size of the fragmentation
43884391
* payload, i.e. the size of the L4 (UDP) header is already

net/sctp/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
1111
transport.o chunk.o sm_make_chunk.o ulpevent.o \
1212
inqueue.o outqueue.o ulpqueue.o \
1313
tsnmap.o bind_addr.o socket.o primitive.o \
14-
output.o input.o debug.o ssnmap.o auth.o
14+
output.o input.o debug.o ssnmap.o auth.o \
15+
offload.o
1516

1617
sctp_probe-y := probe.o
1718

net/sctp/input.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ int sctp_rcv(struct sk_buff *skb)
139139
skb->csum_valid = 0; /* Previous value not applicable */
140140
if (skb_csum_unnecessary(skb))
141141
__skb_decr_checksum_unnecessary(skb);
142-
else if (!sctp_checksum_disable && sctp_rcv_checksum(net, skb) < 0)
142+
else if (!sctp_checksum_disable &&
143+
!(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP) &&
144+
sctp_rcv_checksum(net, skb) < 0)
143145
goto discard_it;
144146
skb->csum_valid = 1;
145147

@@ -1175,6 +1177,14 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net,
11751177
{
11761178
sctp_chunkhdr_t *ch;
11771179

1180+
/* We do not allow GSO frames here as we need to linearize and
1181+
* then cannot guarantee frame boundaries. This shouldn't be an
1182+
* issue as packets hitting this are mostly INIT or INIT-ACK and
1183+
* those cannot be on GSO-style anyway.
1184+
*/
1185+
if ((skb_shinfo(skb)->gso_type & SKB_GSO_SCTP) == SKB_GSO_SCTP)
1186+
return NULL;
1187+
11781188
if (skb_linearize(skb))
11791189
return NULL;
11801190

net/sctp/inqueue.c

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
138138
if (chunk->singleton ||
139139
chunk->end_of_packet ||
140140
chunk->pdiscard) {
141+
if (chunk->head_skb == chunk->skb) {
142+
chunk->skb = skb_shinfo(chunk->skb)->frag_list;
143+
goto new_skb;
144+
}
145+
if (chunk->skb->next) {
146+
chunk->skb = chunk->skb->next;
147+
goto new_skb;
148+
}
149+
150+
if (chunk->head_skb)
151+
chunk->skb = chunk->head_skb;
141152
sctp_chunk_free(chunk);
142153
chunk = queue->in_progress = NULL;
143154
} else {
@@ -155,15 +166,15 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
155166

156167
next_chunk:
157168
/* Is the queue empty? */
158-
if (list_empty(&queue->in_chunk_list))
169+
entry = sctp_list_dequeue(&queue->in_chunk_list);
170+
if (!entry)
159171
return NULL;
160172

161-
entry = queue->in_chunk_list.next;
162173
chunk = list_entry(entry, struct sctp_chunk, list);
163-
list_del_init(entry);
164174

165175
/* Linearize if it's not GSO */
166-
if (skb_is_nonlinear(chunk->skb)) {
176+
if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) != SKB_GSO_SCTP &&
177+
skb_is_nonlinear(chunk->skb)) {
167178
if (skb_linearize(chunk->skb)) {
168179
__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
169180
sctp_chunk_free(chunk);
@@ -174,15 +185,39 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
174185
chunk->sctp_hdr = sctp_hdr(chunk->skb);
175186
}
176187

188+
if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) == SKB_GSO_SCTP) {
189+
/* GSO-marked skbs but without frags, handle
190+
* them normally
191+
*/
192+
if (skb_shinfo(chunk->skb)->frag_list)
193+
chunk->head_skb = chunk->skb;
194+
195+
/* skbs with "cover letter" */
196+
if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len)
197+
chunk->skb = skb_shinfo(chunk->skb)->frag_list;
198+
199+
if (WARN_ON(!chunk->skb)) {
200+
__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
201+
sctp_chunk_free(chunk);
202+
goto next_chunk;
203+
}
204+
}
205+
206+
if (chunk->asoc)
207+
sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb);
208+
177209
queue->in_progress = chunk;
178210

211+
new_skb:
179212
/* This is the first chunk in the packet. */
180-
chunk->singleton = 1;
181213
ch = (sctp_chunkhdr_t *) chunk->skb->data;
214+
chunk->singleton = 1;
182215
chunk->data_accepted = 0;
183-
184-
if (chunk->asoc)
185-
sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb);
216+
chunk->pdiscard = 0;
217+
chunk->auth = 0;
218+
chunk->has_asconf = 0;
219+
chunk->end_of_packet = 0;
220+
chunk->ecn_ce_done = 0;
186221
}
187222

188223
chunk->chunk_hdr = ch;

net/sctp/offload.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* sctp_offload - GRO/GSO Offloading for SCTP
3+
*
4+
* Copyright (C) 2015, Marcelo Ricardo Leitner <[email protected]>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*/
16+
17+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18+
19+
#include <linux/kernel.h>
20+
#include <linux/kprobes.h>
21+
#include <linux/socket.h>
22+
#include <linux/sctp.h>
23+
#include <linux/proc_fs.h>
24+
#include <linux/vmalloc.h>
25+
#include <linux/module.h>
26+
#include <linux/kfifo.h>
27+
#include <linux/time.h>
28+
#include <net/net_namespace.h>
29+
30+
#include <linux/skbuff.h>
31+
#include <net/sctp/sctp.h>
32+
#include <net/sctp/checksum.h>
33+
#include <net/protocol.h>
34+
35+
static __le32 sctp_gso_make_checksum(struct sk_buff *skb)
36+
{
37+
skb->ip_summed = CHECKSUM_NONE;
38+
return sctp_compute_cksum(skb, skb_transport_offset(skb));
39+
}
40+
41+
static struct sk_buff *sctp_gso_segment(struct sk_buff *skb,
42+
netdev_features_t features)
43+
{
44+
struct sk_buff *segs = ERR_PTR(-EINVAL);
45+
struct sctphdr *sh;
46+
47+
sh = sctp_hdr(skb);
48+
if (!pskb_may_pull(skb, sizeof(*sh)))
49+
goto out;
50+
51+
__skb_pull(skb, sizeof(*sh));
52+
53+
if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
54+
/* Packet is from an untrusted source, reset gso_segs. */
55+
struct skb_shared_info *pinfo = skb_shinfo(skb);
56+
struct sk_buff *frag_iter;
57+
58+
pinfo->gso_segs = 0;
59+
if (skb->len != skb->data_len) {
60+
/* Means we have chunks in here too */
61+
pinfo->gso_segs++;
62+
}
63+
64+
skb_walk_frags(skb, frag_iter)
65+
pinfo->gso_segs++;
66+
67+
segs = NULL;
68+
goto out;
69+
}
70+
71+
segs = skb_segment(skb, features | NETIF_F_HW_CSUM);
72+
if (IS_ERR(segs))
73+
goto out;
74+
75+
/* All that is left is update SCTP CRC if necessary */
76+
if (!(features & NETIF_F_SCTP_CRC)) {
77+
for (skb = segs; skb; skb = skb->next) {
78+
if (skb->ip_summed == CHECKSUM_PARTIAL) {
79+
sh = sctp_hdr(skb);
80+
sh->checksum = sctp_gso_make_checksum(skb);
81+
}
82+
}
83+
}
84+
85+
out:
86+
return segs;
87+
}
88+
89+
static const struct net_offload sctp_offload = {
90+
.callbacks = {
91+
.gso_segment = sctp_gso_segment,
92+
},
93+
};
94+
95+
int __init sctp_offload_init(void)
96+
{
97+
return inet_add_offload(&sctp_offload, IPPROTO_SCTP);
98+
}

0 commit comments

Comments
 (0)