Skip to content

Commit 3acb50c

Browse files
marceloleitnerdavem330
authored andcommitted
sctp: delay as much as possible skb_linearize
This 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. Signed-off-by: Marcelo Ricardo Leitner <[email protected]> Tested-by: Xin Long <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ae7ef81 commit 3acb50c

File tree

2 files changed

+43
-31
lines changed

2 files changed

+43
-31
lines changed

net/sctp/input.c

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ int sctp_rcv(struct sk_buff *skb)
112112
struct sctp_ep_common *rcvr;
113113
struct sctp_transport *transport = NULL;
114114
struct sctp_chunk *chunk;
115-
struct sctphdr *sh;
116115
union sctp_addr src;
117116
union sctp_addr dest;
118117
int family;
@@ -124,15 +123,18 @@ int sctp_rcv(struct sk_buff *skb)
124123

125124
__SCTP_INC_STATS(net, SCTP_MIB_INSCTPPACKS);
126125

127-
if (skb_linearize(skb))
126+
/* If packet is too small to contain a single chunk, let's not
127+
* waste time on it anymore.
128+
*/
129+
if (skb->len < sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) +
130+
skb_transport_offset(skb))
128131
goto discard_it;
129132

130-
sh = sctp_hdr(skb);
133+
if (!pskb_may_pull(skb, sizeof(struct sctphdr)))
134+
goto discard_it;
131135

132-
/* Pull up the IP and SCTP headers. */
136+
/* Pull up the IP header. */
133137
__skb_pull(skb, skb_transport_offset(skb));
134-
if (skb->len < sizeof(struct sctphdr))
135-
goto discard_it;
136138

137139
skb->csum_valid = 0; /* Previous value not applicable */
138140
if (skb_csum_unnecessary(skb))
@@ -141,11 +143,7 @@ int sctp_rcv(struct sk_buff *skb)
141143
goto discard_it;
142144
skb->csum_valid = 1;
143145

144-
skb_pull(skb, sizeof(struct sctphdr));
145-
146-
/* Make sure we at least have chunk headers worth of data left. */
147-
if (skb->len < sizeof(struct sctp_chunkhdr))
148-
goto discard_it;
146+
__skb_pull(skb, sizeof(struct sctphdr));
149147

150148
family = ipver2af(ip_hdr(skb)->version);
151149
af = sctp_get_af_specific(family);
@@ -230,7 +228,7 @@ int sctp_rcv(struct sk_buff *skb)
230228
chunk->rcvr = rcvr;
231229

232230
/* Remember the SCTP header. */
233-
chunk->sctp_hdr = sh;
231+
chunk->sctp_hdr = sctp_hdr(skb);
234232

235233
/* Set the source and destination addresses of the incoming chunk. */
236234
sctp_init_addrs(chunk, &src, &dest);
@@ -660,19 +658,23 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
660658
*/
661659
static int sctp_rcv_ootb(struct sk_buff *skb)
662660
{
663-
sctp_chunkhdr_t *ch;
664-
__u8 *ch_end;
665-
666-
ch = (sctp_chunkhdr_t *) skb->data;
661+
sctp_chunkhdr_t *ch, _ch;
662+
int ch_end, offset = 0;
667663

668664
/* Scan through all the chunks in the packet. */
669665
do {
666+
/* Make sure we have at least the header there */
667+
if (offset + sizeof(sctp_chunkhdr_t) > skb->len)
668+
break;
669+
670+
ch = skb_header_pointer(skb, offset, sizeof(*ch), &_ch);
671+
670672
/* Break out if chunk length is less then minimal. */
671673
if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t))
672674
break;
673675

674-
ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
675-
if (ch_end > skb_tail_pointer(skb))
676+
ch_end = offset + WORD_ROUND(ntohs(ch->length));
677+
if (ch_end > skb->len)
676678
break;
677679

678680
/* 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)
697699
if (SCTP_CID_INIT == ch->type && (void *)ch != skb->data)
698700
goto discard;
699701

700-
ch = (sctp_chunkhdr_t *) ch_end;
701-
} while (ch_end < skb_tail_pointer(skb));
702+
offset = ch_end;
703+
} while (ch_end < skb->len);
702704

703705
return 0;
704706

@@ -1173,6 +1175,9 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net,
11731175
{
11741176
sctp_chunkhdr_t *ch;
11751177

1178+
if (skb_linearize(skb))
1179+
return NULL;
1180+
11761181
ch = (sctp_chunkhdr_t *) skb->data;
11771182

11781183
/* The code below will attempt to walk the chunk and extract

net/sctp/inqueue.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
130130
* at this time.
131131
*/
132132

133-
if ((chunk = queue->in_progress)) {
133+
chunk = queue->in_progress;
134+
if (chunk) {
134135
/* There is a packet that we have been working on.
135136
* Any post processing work to do before we move on?
136137
*/
@@ -152,15 +153,29 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
152153
if (!chunk) {
153154
struct list_head *entry;
154155

156+
next_chunk:
155157
/* Is the queue empty? */
156158
if (list_empty(&queue->in_chunk_list))
157159
return NULL;
158160

159161
entry = queue->in_chunk_list.next;
160-
chunk = queue->in_progress =
161-
list_entry(entry, struct sctp_chunk, list);
162+
chunk = list_entry(entry, struct sctp_chunk, list);
162163
list_del_init(entry);
163164

165+
/* Linearize if it's not GSO */
166+
if (skb_is_nonlinear(chunk->skb)) {
167+
if (skb_linearize(chunk->skb)) {
168+
__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
169+
sctp_chunk_free(chunk);
170+
goto next_chunk;
171+
}
172+
173+
/* Update sctp_hdr as it probably changed */
174+
chunk->sctp_hdr = sctp_hdr(chunk->skb);
175+
}
176+
177+
queue->in_progress = chunk;
178+
164179
/* This is the first chunk in the packet. */
165180
chunk->singleton = 1;
166181
ch = (sctp_chunkhdr_t *) chunk->skb->data;
@@ -172,14 +187,6 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
172187

173188
chunk->chunk_hdr = ch;
174189
chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
175-
/* In the unlikely case of an IP reassembly, the skb could be
176-
* non-linear. If so, update chunk_end so that it doesn't go past
177-
* the skb->tail.
178-
*/
179-
if (unlikely(skb_is_nonlinear(chunk->skb))) {
180-
if (chunk->chunk_end > skb_tail_pointer(chunk->skb))
181-
chunk->chunk_end = skb_tail_pointer(chunk->skb);
182-
}
183190
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
184191
chunk->subh.v = NULL; /* Subheader is no longer valid. */
185192

0 commit comments

Comments
 (0)