| .. | .. |
|---|
| 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 | |
|---|