summaryrefslogtreecommitdiffstats
path: root/net/sctp/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp/stream.c')
-rw-r--r--net/sctp/stream.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index 055ca25bbc91..5ea33a2c453b 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -32,8 +32,61 @@
32 * Xin Long <lucien.xin@gmail.com> 32 * Xin Long <lucien.xin@gmail.com>
33 */ 33 */
34 34
35#include <linux/list.h>
35#include <net/sctp/sctp.h> 36#include <net/sctp/sctp.h>
36#include <net/sctp/sm.h> 37#include <net/sctp/sm.h>
38#include <net/sctp/stream_sched.h>
39
40/* Migrates chunks from stream queues to new stream queues if needed,
41 * but not across associations. Also, removes those chunks to streams
42 * higher than the new max.
43 */
44static void sctp_stream_outq_migrate(struct sctp_stream *stream,
45 struct sctp_stream *new, __u16 outcnt)
46{
47 struct sctp_association *asoc;
48 struct sctp_chunk *ch, *temp;
49 struct sctp_outq *outq;
50 int i;
51
52 asoc = container_of(stream, struct sctp_association, stream);
53 outq = &asoc->outqueue;
54
55 list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) {
56 __u16 sid = sctp_chunk_stream_no(ch);
57
58 if (sid < outcnt)
59 continue;
60
61 sctp_sched_dequeue_common(outq, ch);
62 /* No need to call dequeue_done here because
63 * the chunks are not scheduled by now.
64 */
65
66 /* Mark as failed send. */
67 sctp_chunk_fail(ch, SCTP_ERROR_INV_STRM);
68 if (asoc->peer.prsctp_capable &&
69 SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))
70 asoc->sent_cnt_removable--;
71
72 sctp_chunk_free(ch);
73 }
74
75 if (new) {
76 /* Here we actually move the old ext stuff into the new
77 * buffer, because we want to keep it. Then
78 * sctp_stream_update will swap ->out pointers.
79 */
80 for (i = 0; i < outcnt; i++) {
81 kfree(new->out[i].ext);
82 new->out[i].ext = stream->out[i].ext;
83 stream->out[i].ext = NULL;
84 }
85 }
86
87 for (i = outcnt; i < stream->outcnt; i++)
88 kfree(stream->out[i].ext);
89}
37 90
38static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 91static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
39 gfp_t gfp) 92 gfp_t gfp)
@@ -87,7 +140,8 @@ static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
87int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 140int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
88 gfp_t gfp) 141 gfp_t gfp)
89{ 142{
90 int i; 143 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
144 int i, ret = 0;
91 145
92 gfp |= __GFP_NOWARN; 146 gfp |= __GFP_NOWARN;
93 147
@@ -97,6 +151,11 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
97 if (outcnt == stream->outcnt) 151 if (outcnt == stream->outcnt)
98 goto in; 152 goto in;
99 153
154 /* Filter out chunks queued on streams that won't exist anymore */
155 sched->unsched_all(stream);
156 sctp_stream_outq_migrate(stream, NULL, outcnt);
157 sched->sched_all(stream);
158
100 i = sctp_stream_alloc_out(stream, outcnt, gfp); 159 i = sctp_stream_alloc_out(stream, outcnt, gfp);
101 if (i) 160 if (i)
102 return i; 161 return i;
@@ -105,20 +164,27 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
105 for (i = 0; i < stream->outcnt; i++) 164 for (i = 0; i < stream->outcnt; i++)
106 stream->out[i].state = SCTP_STREAM_OPEN; 165 stream->out[i].state = SCTP_STREAM_OPEN;
107 166
167 sched->init(stream);
168
108in: 169in:
109 if (!incnt) 170 if (!incnt)
110 return 0; 171 goto out;
111 172
112 i = sctp_stream_alloc_in(stream, incnt, gfp); 173 i = sctp_stream_alloc_in(stream, incnt, gfp);
113 if (i) { 174 if (i) {
114 kfree(stream->out); 175 ret = -ENOMEM;
115 stream->out = NULL; 176 goto free;
116 return -ENOMEM;
117 } 177 }
118 178
119 stream->incnt = incnt; 179 stream->incnt = incnt;
180 goto out;
120 181
121 return 0; 182free:
183 sched->free(stream);
184 kfree(stream->out);
185 stream->out = NULL;
186out:
187 return ret;
122} 188}
123 189
124int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 190int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
@@ -130,13 +196,15 @@ int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
130 return -ENOMEM; 196 return -ENOMEM;
131 stream->out[sid].ext = soute; 197 stream->out[sid].ext = soute;
132 198
133 return 0; 199 return sctp_sched_init_sid(stream, sid, GFP_KERNEL);
134} 200}
135 201
136void sctp_stream_free(struct sctp_stream *stream) 202void sctp_stream_free(struct sctp_stream *stream)
137{ 203{
204 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
138 int i; 205 int i;
139 206
207 sched->free(stream);
140 for (i = 0; i < stream->outcnt; i++) 208 for (i = 0; i < stream->outcnt; i++)
141 kfree(stream->out[i].ext); 209 kfree(stream->out[i].ext);
142 kfree(stream->out); 210 kfree(stream->out);
@@ -156,6 +224,10 @@ void sctp_stream_clear(struct sctp_stream *stream)
156 224
157void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 225void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
158{ 226{
227 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
228
229 sched->unsched_all(stream);
230 sctp_stream_outq_migrate(stream, new, new->outcnt);
159 sctp_stream_free(stream); 231 sctp_stream_free(stream);
160 232
161 stream->out = new->out; 233 stream->out = new->out;
@@ -163,6 +235,8 @@ void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
163 stream->outcnt = new->outcnt; 235 stream->outcnt = new->outcnt;
164 stream->incnt = new->incnt; 236 stream->incnt = new->incnt;
165 237
238 sched->sched_all(stream);
239
166 new->out = NULL; 240 new->out = NULL;
167 new->in = NULL; 241 new->in = NULL;
168} 242}