.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* SCTP kernel implementation |
---|
2 | 3 | * (C) Copyright IBM Corp. 2001, 2004 |
---|
3 | 4 | * Copyright (c) 1999-2000 Cisco, Inc. |
---|
.. | .. |
---|
7 | 8 | * This file is part of the SCTP kernel implementation |
---|
8 | 9 | * |
---|
9 | 10 | * This file contains sctp stream maniuplation primitives and helpers. |
---|
10 | | - * |
---|
11 | | - * This SCTP implementation is free software; |
---|
12 | | - * you can redistribute it and/or modify it under the terms of |
---|
13 | | - * the GNU General Public License as published by |
---|
14 | | - * the Free Software Foundation; either version 2, or (at your option) |
---|
15 | | - * any later version. |
---|
16 | | - * |
---|
17 | | - * This SCTP implementation is distributed in the hope that it |
---|
18 | | - * will be useful, but WITHOUT ANY WARRANTY; without even the implied |
---|
19 | | - * ************************ |
---|
20 | | - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
---|
21 | | - * See the GNU General Public License for more details. |
---|
22 | | - * |
---|
23 | | - * You should have received a copy of the GNU General Public License |
---|
24 | | - * along with GNU CC; see the file COPYING. If not, see |
---|
25 | | - * <http://www.gnu.org/licenses/>. |
---|
26 | 11 | * |
---|
27 | 12 | * Please send any bug reports or fixes you make to the |
---|
28 | 13 | * email address(es): |
---|
.. | .. |
---|
36 | 21 | #include <net/sctp/sctp.h> |
---|
37 | 22 | #include <net/sctp/sm.h> |
---|
38 | 23 | #include <net/sctp/stream_sched.h> |
---|
39 | | - |
---|
40 | | -static struct flex_array *fa_alloc(size_t elem_size, size_t elem_count, |
---|
41 | | - gfp_t gfp) |
---|
42 | | -{ |
---|
43 | | - struct flex_array *result; |
---|
44 | | - int err; |
---|
45 | | - |
---|
46 | | - result = flex_array_alloc(elem_size, elem_count, gfp); |
---|
47 | | - if (result) { |
---|
48 | | - err = flex_array_prealloc(result, 0, elem_count, gfp); |
---|
49 | | - if (err) { |
---|
50 | | - flex_array_free(result); |
---|
51 | | - result = NULL; |
---|
52 | | - } |
---|
53 | | - } |
---|
54 | | - |
---|
55 | | - return result; |
---|
56 | | -} |
---|
57 | | - |
---|
58 | | -static void fa_free(struct flex_array *fa) |
---|
59 | | -{ |
---|
60 | | - if (fa) |
---|
61 | | - flex_array_free(fa); |
---|
62 | | -} |
---|
63 | | - |
---|
64 | | -static void fa_copy(struct flex_array *fa, struct flex_array *from, |
---|
65 | | - size_t index, size_t count) |
---|
66 | | -{ |
---|
67 | | - void *elem; |
---|
68 | | - |
---|
69 | | - while (count--) { |
---|
70 | | - elem = flex_array_get(from, index); |
---|
71 | | - flex_array_put(fa, index, elem, 0); |
---|
72 | | - index++; |
---|
73 | | - } |
---|
74 | | -} |
---|
75 | | - |
---|
76 | | -static void fa_zero(struct flex_array *fa, size_t index, size_t count) |
---|
77 | | -{ |
---|
78 | | - void *elem; |
---|
79 | | - |
---|
80 | | - while (count--) { |
---|
81 | | - elem = flex_array_get(fa, index); |
---|
82 | | - memset(elem, 0, fa->element_size); |
---|
83 | | - index++; |
---|
84 | | - } |
---|
85 | | -} |
---|
86 | | - |
---|
87 | | -static size_t fa_index(struct flex_array *fa, void *elem, size_t count) |
---|
88 | | -{ |
---|
89 | | - size_t index = 0; |
---|
90 | | - |
---|
91 | | - while (count--) { |
---|
92 | | - if (elem == flex_array_get(fa, index)) |
---|
93 | | - break; |
---|
94 | | - index++; |
---|
95 | | - } |
---|
96 | | - |
---|
97 | | - return index; |
---|
98 | | -} |
---|
99 | 24 | |
---|
100 | 25 | static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt) |
---|
101 | 26 | { |
---|
.. | .. |
---|
127 | 52 | } |
---|
128 | 53 | } |
---|
129 | 54 | |
---|
| 55 | +static void sctp_stream_free_ext(struct sctp_stream *stream, __u16 sid) |
---|
| 56 | +{ |
---|
| 57 | + struct sctp_sched_ops *sched; |
---|
| 58 | + |
---|
| 59 | + if (!SCTP_SO(stream, sid)->ext) |
---|
| 60 | + return; |
---|
| 61 | + |
---|
| 62 | + sched = sctp_sched_ops_from_stream(stream); |
---|
| 63 | + sched->free_sid(stream, sid); |
---|
| 64 | + kfree(SCTP_SO(stream, sid)->ext); |
---|
| 65 | + SCTP_SO(stream, sid)->ext = NULL; |
---|
| 66 | +} |
---|
| 67 | + |
---|
130 | 68 | /* Migrates chunks from stream queues to new stream queues if needed, |
---|
131 | 69 | * but not across associations. Also, removes those chunks to streams |
---|
132 | 70 | * higher than the new max. |
---|
.. | .. |
---|
145 | 83 | * sctp_stream_update will swap ->out pointers. |
---|
146 | 84 | */ |
---|
147 | 85 | for (i = 0; i < outcnt; i++) { |
---|
148 | | - kfree(SCTP_SO(new, i)->ext); |
---|
| 86 | + sctp_stream_free_ext(new, i); |
---|
149 | 87 | SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext; |
---|
150 | 88 | SCTP_SO(stream, i)->ext = NULL; |
---|
151 | 89 | } |
---|
152 | 90 | } |
---|
153 | 91 | |
---|
154 | | - for (i = outcnt; i < stream->outcnt; i++) { |
---|
155 | | - kfree(SCTP_SO(stream, i)->ext); |
---|
156 | | - SCTP_SO(stream, i)->ext = NULL; |
---|
157 | | - } |
---|
| 92 | + for (i = outcnt; i < stream->outcnt; i++) |
---|
| 93 | + sctp_stream_free_ext(stream, i); |
---|
158 | 94 | } |
---|
159 | 95 | |
---|
160 | 96 | static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, |
---|
161 | 97 | gfp_t gfp) |
---|
162 | 98 | { |
---|
163 | | - struct flex_array *out; |
---|
164 | | - size_t elem_size = sizeof(struct sctp_stream_out); |
---|
| 99 | + int ret; |
---|
165 | 100 | |
---|
166 | | - out = fa_alloc(elem_size, outcnt, gfp); |
---|
167 | | - if (!out) |
---|
168 | | - return -ENOMEM; |
---|
| 101 | + if (outcnt <= stream->outcnt) |
---|
| 102 | + goto out; |
---|
169 | 103 | |
---|
170 | | - if (stream->out) { |
---|
171 | | - fa_copy(out, stream->out, 0, min(outcnt, stream->outcnt)); |
---|
172 | | - if (stream->out_curr) { |
---|
173 | | - size_t index = fa_index(stream->out, stream->out_curr, |
---|
174 | | - stream->outcnt); |
---|
| 104 | + ret = genradix_prealloc(&stream->out, outcnt, gfp); |
---|
| 105 | + if (ret) |
---|
| 106 | + return ret; |
---|
175 | 107 | |
---|
176 | | - BUG_ON(index == stream->outcnt); |
---|
177 | | - stream->out_curr = flex_array_get(out, index); |
---|
178 | | - } |
---|
179 | | - fa_free(stream->out); |
---|
180 | | - } |
---|
181 | | - |
---|
182 | | - if (outcnt > stream->outcnt) |
---|
183 | | - fa_zero(out, stream->outcnt, (outcnt - stream->outcnt)); |
---|
184 | | - |
---|
185 | | - stream->out = out; |
---|
186 | | - |
---|
| 108 | +out: |
---|
| 109 | + stream->outcnt = outcnt; |
---|
187 | 110 | return 0; |
---|
188 | 111 | } |
---|
189 | 112 | |
---|
190 | 113 | static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, |
---|
191 | 114 | gfp_t gfp) |
---|
192 | 115 | { |
---|
193 | | - struct flex_array *in; |
---|
194 | | - size_t elem_size = sizeof(struct sctp_stream_in); |
---|
| 116 | + int ret; |
---|
195 | 117 | |
---|
196 | | - in = fa_alloc(elem_size, incnt, gfp); |
---|
197 | | - if (!in) |
---|
198 | | - return -ENOMEM; |
---|
| 118 | + if (incnt <= stream->incnt) |
---|
| 119 | + goto out; |
---|
199 | 120 | |
---|
200 | | - if (stream->in) { |
---|
201 | | - fa_copy(in, stream->in, 0, min(incnt, stream->incnt)); |
---|
202 | | - fa_free(stream->in); |
---|
203 | | - } |
---|
| 121 | + ret = genradix_prealloc(&stream->in, incnt, gfp); |
---|
| 122 | + if (ret) |
---|
| 123 | + return ret; |
---|
204 | 124 | |
---|
205 | | - if (incnt > stream->incnt) |
---|
206 | | - fa_zero(in, stream->incnt, (incnt - stream->incnt)); |
---|
207 | | - |
---|
208 | | - stream->in = in; |
---|
209 | | - |
---|
| 125 | +out: |
---|
| 126 | + stream->incnt = incnt; |
---|
210 | 127 | return 0; |
---|
211 | 128 | } |
---|
212 | 129 | |
---|
.. | .. |
---|
222 | 139 | * a new one with new outcnt to save memory if needed. |
---|
223 | 140 | */ |
---|
224 | 141 | if (outcnt == stream->outcnt) |
---|
225 | | - goto in; |
---|
| 142 | + goto handle_in; |
---|
226 | 143 | |
---|
227 | 144 | /* Filter out chunks queued on streams that won't exist anymore */ |
---|
228 | 145 | sched->unsched_all(stream); |
---|
.. | .. |
---|
231 | 148 | |
---|
232 | 149 | ret = sctp_stream_alloc_out(stream, outcnt, gfp); |
---|
233 | 150 | if (ret) |
---|
234 | | - goto out; |
---|
| 151 | + return ret; |
---|
235 | 152 | |
---|
236 | | - stream->outcnt = outcnt; |
---|
237 | 153 | for (i = 0; i < stream->outcnt; i++) |
---|
238 | 154 | SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; |
---|
239 | 155 | |
---|
240 | | -in: |
---|
| 156 | +handle_in: |
---|
241 | 157 | sctp_stream_interleave_init(stream); |
---|
242 | 158 | if (!incnt) |
---|
243 | | - goto out; |
---|
| 159 | + return 0; |
---|
244 | 160 | |
---|
245 | | - ret = sctp_stream_alloc_in(stream, incnt, gfp); |
---|
246 | | - if (ret) { |
---|
247 | | - sched->free(stream); |
---|
248 | | - fa_free(stream->out); |
---|
249 | | - stream->out = NULL; |
---|
250 | | - stream->outcnt = 0; |
---|
251 | | - goto out; |
---|
252 | | - } |
---|
253 | | - |
---|
254 | | - stream->incnt = incnt; |
---|
255 | | - |
---|
256 | | -out: |
---|
257 | | - return ret; |
---|
| 161 | + return sctp_stream_alloc_in(stream, incnt, gfp); |
---|
258 | 162 | } |
---|
259 | 163 | |
---|
260 | 164 | int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) |
---|
.. | .. |
---|
281 | 185 | struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); |
---|
282 | 186 | int i; |
---|
283 | 187 | |
---|
284 | | - sched->free(stream); |
---|
| 188 | + sched->unsched_all(stream); |
---|
285 | 189 | for (i = 0; i < stream->outcnt; i++) |
---|
286 | | - kfree(SCTP_SO(stream, i)->ext); |
---|
287 | | - fa_free(stream->out); |
---|
288 | | - fa_free(stream->in); |
---|
| 190 | + sctp_stream_free_ext(stream, i); |
---|
| 191 | + genradix_free(&stream->out); |
---|
| 192 | + genradix_free(&stream->in); |
---|
289 | 193 | } |
---|
290 | 194 | |
---|
291 | 195 | void sctp_stream_clear(struct sctp_stream *stream) |
---|
.. | .. |
---|
316 | 220 | |
---|
317 | 221 | sched->sched_all(stream); |
---|
318 | 222 | |
---|
319 | | - new->out = NULL; |
---|
320 | | - new->in = NULL; |
---|
| 223 | + new->out.tree.root = NULL; |
---|
| 224 | + new->in.tree.root = NULL; |
---|
321 | 225 | new->outcnt = 0; |
---|
322 | 226 | new->incnt = 0; |
---|
323 | 227 | } |
---|
.. | .. |
---|
325 | 229 | static int sctp_send_reconf(struct sctp_association *asoc, |
---|
326 | 230 | struct sctp_chunk *chunk) |
---|
327 | 231 | { |
---|
328 | | - struct net *net = sock_net(asoc->base.sk); |
---|
329 | 232 | int retval = 0; |
---|
330 | 233 | |
---|
331 | | - retval = sctp_primitive_RECONF(net, asoc, chunk); |
---|
| 234 | + retval = sctp_primitive_RECONF(asoc->base.net, asoc, chunk); |
---|
332 | 235 | if (retval) |
---|
333 | 236 | sctp_chunk_free(chunk); |
---|
334 | 237 | |
---|
.. | .. |
---|
569 | 472 | asoc->strreset_chunk = NULL; |
---|
570 | 473 | goto out; |
---|
571 | 474 | } |
---|
572 | | - |
---|
573 | | - stream->outcnt = outcnt; |
---|
574 | 475 | |
---|
575 | 476 | asoc->strreset_outstanding = !!out + !!in; |
---|
576 | 477 | |
---|