diff options
Diffstat (limited to 'net/sctp/stream.c')
-rw-r--r-- | net/sctp/stream.c | 88 |
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 | */ | ||
44 | static 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 | ||
38 | static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, | 91 | static 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, | |||
87 | int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, | 140 | int 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 | |||
108 | in: | 169 | in: |
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; | 182 | free: |
183 | sched->free(stream); | ||
184 | kfree(stream->out); | ||
185 | stream->out = NULL; | ||
186 | out: | ||
187 | return ret; | ||
122 | } | 188 | } |
123 | 189 | ||
124 | int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) | 190 | int 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 | ||
136 | void sctp_stream_free(struct sctp_stream *stream) | 202 | void 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 | ||
157 | void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) | 225 | void 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 | } |