diff options
Diffstat (limited to 'net/sctp/ulpqueue.c')
-rw-r--r-- | net/sctp/ulpqueue.c | 103 |
1 files changed, 74 insertions, 29 deletions
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index b29e3e4b72c9..ac80c34f6c2c 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c | |||
@@ -138,18 +138,42 @@ int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk, | |||
138 | /* Clear the partial delivery mode for this socket. Note: This | 138 | /* Clear the partial delivery mode for this socket. Note: This |
139 | * assumes that no association is currently in partial delivery mode. | 139 | * assumes that no association is currently in partial delivery mode. |
140 | */ | 140 | */ |
141 | int sctp_clear_pd(struct sock *sk) | 141 | int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc) |
142 | { | 142 | { |
143 | struct sctp_sock *sp = sctp_sk(sk); | 143 | struct sctp_sock *sp = sctp_sk(sk); |
144 | 144 | ||
145 | sp->pd_mode = 0; | 145 | if (atomic_dec_and_test(&sp->pd_mode)) { |
146 | if (!skb_queue_empty(&sp->pd_lobby)) { | 146 | /* This means there are no other associations in PD, so |
147 | struct list_head *list; | 147 | * we can go ahead and clear out the lobby in one shot |
148 | sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue); | 148 | */ |
149 | list = (struct list_head *)&sctp_sk(sk)->pd_lobby; | 149 | if (!skb_queue_empty(&sp->pd_lobby)) { |
150 | INIT_LIST_HEAD(list); | 150 | struct list_head *list; |
151 | return 1; | 151 | sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue); |
152 | list = (struct list_head *)&sctp_sk(sk)->pd_lobby; | ||
153 | INIT_LIST_HEAD(list); | ||
154 | return 1; | ||
155 | } | ||
156 | } else { | ||
157 | /* There are other associations in PD, so we only need to | ||
158 | * pull stuff out of the lobby that belongs to the | ||
159 | * associations that is exiting PD (all of its notifications | ||
160 | * are posted here). | ||
161 | */ | ||
162 | if (!skb_queue_empty(&sp->pd_lobby) && asoc) { | ||
163 | struct sk_buff *skb, *tmp; | ||
164 | struct sctp_ulpevent *event; | ||
165 | |||
166 | sctp_skb_for_each(skb, &sp->pd_lobby, tmp) { | ||
167 | event = sctp_skb2event(skb); | ||
168 | if (event->asoc == asoc) { | ||
169 | __skb_unlink(skb, &sp->pd_lobby); | ||
170 | __skb_queue_tail(&sk->sk_receive_queue, | ||
171 | skb); | ||
172 | } | ||
173 | } | ||
174 | } | ||
152 | } | 175 | } |
176 | |||
153 | return 0; | 177 | return 0; |
154 | } | 178 | } |
155 | 179 | ||
@@ -157,7 +181,7 @@ int sctp_clear_pd(struct sock *sk) | |||
157 | static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) | 181 | static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) |
158 | { | 182 | { |
159 | ulpq->pd_mode = 0; | 183 | ulpq->pd_mode = 0; |
160 | return sctp_clear_pd(ulpq->asoc->base.sk); | 184 | return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc); |
161 | } | 185 | } |
162 | 186 | ||
163 | /* If the SKB of 'event' is on a list, it is the first such member | 187 | /* If the SKB of 'event' is on a list, it is the first such member |
@@ -187,25 +211,35 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) | |||
187 | * the association the cause of the partial delivery. | 211 | * the association the cause of the partial delivery. |
188 | */ | 212 | */ |
189 | 213 | ||
190 | if (!sctp_sk(sk)->pd_mode) { | 214 | if (atomic_read(&sctp_sk(sk)->pd_mode) == 0) { |
191 | queue = &sk->sk_receive_queue; | 215 | queue = &sk->sk_receive_queue; |
192 | } else if (ulpq->pd_mode) { | 216 | } else { |
193 | /* If the association is in partial delivery, we | 217 | if (ulpq->pd_mode) { |
194 | * need to finish delivering the partially processed | 218 | /* If the association is in partial delivery, we |
195 | * packet before passing any other data. This is | 219 | * need to finish delivering the partially processed |
196 | * because we don't truly support stream interleaving. | 220 | * packet before passing any other data. This is |
197 | */ | 221 | * because we don't truly support stream interleaving. |
198 | if ((event->msg_flags & MSG_NOTIFICATION) || | 222 | */ |
199 | (SCTP_DATA_NOT_FRAG == | 223 | if ((event->msg_flags & MSG_NOTIFICATION) || |
200 | (event->msg_flags & SCTP_DATA_FRAG_MASK))) | 224 | (SCTP_DATA_NOT_FRAG == |
201 | queue = &sctp_sk(sk)->pd_lobby; | 225 | (event->msg_flags & SCTP_DATA_FRAG_MASK))) |
202 | else { | 226 | queue = &sctp_sk(sk)->pd_lobby; |
203 | clear_pd = event->msg_flags & MSG_EOR; | 227 | else { |
204 | queue = &sk->sk_receive_queue; | 228 | clear_pd = event->msg_flags & MSG_EOR; |
229 | queue = &sk->sk_receive_queue; | ||
230 | } | ||
231 | } else { | ||
232 | /* | ||
233 | * If fragment interleave is enabled, we | ||
234 | * can queue this to the recieve queue instead | ||
235 | * of the lobby. | ||
236 | */ | ||
237 | if (sctp_sk(sk)->frag_interleave) | ||
238 | queue = &sk->sk_receive_queue; | ||
239 | else | ||
240 | queue = &sctp_sk(sk)->pd_lobby; | ||
205 | } | 241 | } |
206 | } else | 242 | } |
207 | queue = &sctp_sk(sk)->pd_lobby; | ||
208 | |||
209 | 243 | ||
210 | /* If we are harvesting multiple skbs they will be | 244 | /* If we are harvesting multiple skbs they will be |
211 | * collected on a list. | 245 | * collected on a list. |
@@ -826,18 +860,29 @@ void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq, | |||
826 | { | 860 | { |
827 | struct sctp_ulpevent *event; | 861 | struct sctp_ulpevent *event; |
828 | struct sctp_association *asoc; | 862 | struct sctp_association *asoc; |
863 | struct sctp_sock *sp; | ||
829 | 864 | ||
830 | asoc = ulpq->asoc; | 865 | asoc = ulpq->asoc; |
866 | sp = sctp_sk(asoc->base.sk); | ||
831 | 867 | ||
832 | /* Are we already in partial delivery mode? */ | 868 | /* If the association is already in Partial Delivery mode |
833 | if (!sctp_sk(asoc->base.sk)->pd_mode) { | 869 | * we have noting to do. |
870 | */ | ||
871 | if (ulpq->pd_mode) | ||
872 | return; | ||
834 | 873 | ||
874 | /* If the user enabled fragment interleave socket option, | ||
875 | * multiple associations can enter partial delivery. | ||
876 | * Otherwise, we can only enter partial delivery if the | ||
877 | * socket is not in partial deliver mode. | ||
878 | */ | ||
879 | if (sp->frag_interleave || atomic_read(&sp->pd_mode) == 0) { | ||
835 | /* Is partial delivery possible? */ | 880 | /* Is partial delivery possible? */ |
836 | event = sctp_ulpq_retrieve_first(ulpq); | 881 | event = sctp_ulpq_retrieve_first(ulpq); |
837 | /* Send event to the ULP. */ | 882 | /* Send event to the ULP. */ |
838 | if (event) { | 883 | if (event) { |
839 | sctp_ulpq_tail_event(ulpq, event); | 884 | sctp_ulpq_tail_event(ulpq, event); |
840 | sctp_sk(asoc->base.sk)->pd_mode = 1; | 885 | atomic_inc(&sp->pd_mode); |
841 | ulpq->pd_mode = 1; | 886 | ulpq->pd_mode = 1; |
842 | return; | 887 | return; |
843 | } | 888 | } |