diff options
Diffstat (limited to 'net/sctp/inqueue.c')
-rw-r--r-- | net/sctp/inqueue.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 9d87bba0ff1d..5ba08ceda3ab 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c | |||
@@ -130,7 +130,8 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) | |||
130 | * at this time. | 130 | * at this time. |
131 | */ | 131 | */ |
132 | 132 | ||
133 | if ((chunk = queue->in_progress)) { | 133 | chunk = queue->in_progress; |
134 | if (chunk) { | ||
134 | /* There is a packet that we have been working on. | 135 | /* There is a packet that we have been working on. |
135 | * Any post processing work to do before we move on? | 136 | * Any post processing work to do before we move on? |
136 | */ | 137 | */ |
@@ -152,15 +153,29 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) | |||
152 | if (!chunk) { | 153 | if (!chunk) { |
153 | struct list_head *entry; | 154 | struct list_head *entry; |
154 | 155 | ||
156 | next_chunk: | ||
155 | /* Is the queue empty? */ | 157 | /* Is the queue empty? */ |
156 | if (list_empty(&queue->in_chunk_list)) | 158 | if (list_empty(&queue->in_chunk_list)) |
157 | return NULL; | 159 | return NULL; |
158 | 160 | ||
159 | entry = queue->in_chunk_list.next; | 161 | entry = queue->in_chunk_list.next; |
160 | chunk = queue->in_progress = | 162 | chunk = list_entry(entry, struct sctp_chunk, list); |
161 | list_entry(entry, struct sctp_chunk, list); | ||
162 | list_del_init(entry); | 163 | list_del_init(entry); |
163 | 164 | ||
165 | /* Linearize if it's not GSO */ | ||
166 | if (skb_is_nonlinear(chunk->skb)) { | ||
167 | if (skb_linearize(chunk->skb)) { | ||
168 | __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS); | ||
169 | sctp_chunk_free(chunk); | ||
170 | goto next_chunk; | ||
171 | } | ||
172 | |||
173 | /* Update sctp_hdr as it probably changed */ | ||
174 | chunk->sctp_hdr = sctp_hdr(chunk->skb); | ||
175 | } | ||
176 | |||
177 | queue->in_progress = chunk; | ||
178 | |||
164 | /* This is the first chunk in the packet. */ | 179 | /* This is the first chunk in the packet. */ |
165 | chunk->singleton = 1; | 180 | chunk->singleton = 1; |
166 | ch = (sctp_chunkhdr_t *) chunk->skb->data; | 181 | ch = (sctp_chunkhdr_t *) chunk->skb->data; |
@@ -172,14 +187,6 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) | |||
172 | 187 | ||
173 | chunk->chunk_hdr = ch; | 188 | chunk->chunk_hdr = ch; |
174 | chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); | 189 | chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); |
175 | /* In the unlikely case of an IP reassembly, the skb could be | ||
176 | * non-linear. If so, update chunk_end so that it doesn't go past | ||
177 | * the skb->tail. | ||
178 | */ | ||
179 | if (unlikely(skb_is_nonlinear(chunk->skb))) { | ||
180 | if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) | ||
181 | chunk->chunk_end = skb_tail_pointer(chunk->skb); | ||
182 | } | ||
183 | skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); | 190 | skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); |
184 | chunk->subh.v = NULL; /* Subheader is no longer valid. */ | 191 | chunk->subh.v = NULL; /* Subheader is no longer valid. */ |
185 | 192 | ||