aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/inqueue.c
diff options
context:
space:
mode:
authorMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>2016-06-02 14:05:43 -0400
committerDavid S. Miller <davem@davemloft.net>2016-06-03 19:37:21 -0400
commit90017accff61ae89283ad9a51f9ac46ca01633fb (patch)
treec62d8801baf03dfd048848e6b6ea325d94c481a6 /net/sctp/inqueue.c
parent3acb50c18d8d6650f10919464ade4dcdaf41d62f (diff)
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 5c7cdf339af5 ("gso: Remove arbitrary checks for unsupported GSO") Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Tested-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp/inqueue.c')
-rw-r--r--net/sctp/inqueue.c51
1 files changed, 43 insertions, 8 deletions
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 5ba08ceda3ab..edabbbdfca54 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -138,6 +138,17 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
138 if (chunk->singleton || 138 if (chunk->singleton ||
139 chunk->end_of_packet || 139 chunk->end_of_packet ||
140 chunk->pdiscard) { 140 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;
141 sctp_chunk_free(chunk); 152 sctp_chunk_free(chunk);
142 chunk = queue->in_progress = NULL; 153 chunk = queue->in_progress = NULL;
143 } else { 154 } else {
@@ -155,15 +166,15 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
155 166
156next_chunk: 167next_chunk:
157 /* Is the queue empty? */ 168 /* Is the queue empty? */
158 if (list_empty(&queue->in_chunk_list)) 169 entry = sctp_list_dequeue(&queue->in_chunk_list);
170 if (!entry)
159 return NULL; 171 return NULL;
160 172
161 entry = queue->in_chunk_list.next;
162 chunk = list_entry(entry, struct sctp_chunk, list); 173 chunk = list_entry(entry, struct sctp_chunk, list);
163 list_del_init(entry);
164 174
165 /* Linearize if it's not GSO */ 175 /* 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)) {
167 if (skb_linearize(chunk->skb)) { 178 if (skb_linearize(chunk->skb)) {
168 __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS); 179 __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
169 sctp_chunk_free(chunk); 180 sctp_chunk_free(chunk);
@@ -174,15 +185,39 @@ next_chunk:
174 chunk->sctp_hdr = sctp_hdr(chunk->skb); 185 chunk->sctp_hdr = sctp_hdr(chunk->skb);
175 } 186 }
176 187
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
177 queue->in_progress = chunk; 209 queue->in_progress = chunk;
178 210
211new_skb:
179 /* This is the first chunk in the packet. */ 212 /* This is the first chunk in the packet. */
180 chunk->singleton = 1;
181 ch = (sctp_chunkhdr_t *) chunk->skb->data; 213 ch = (sctp_chunkhdr_t *) chunk->skb->data;
214 chunk->singleton = 1;
182 chunk->data_accepted = 0; 215 chunk->data_accepted = 0;
183 216 chunk->pdiscard = 0;
184 if (chunk->asoc) 217 chunk->auth = 0;
185 sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb); 218 chunk->has_asconf = 0;
219 chunk->end_of_packet = 0;
220 chunk->ecn_ce_done = 0;
186 } 221 }
187 222
188 chunk->chunk_hdr = ch; 223 chunk->chunk_hdr = ch;