sctp: Add GSO support
[cascardo/linux.git] / net / sctp / inqueue.c
index 5ba08ce..edabbbd 100644 (file)
@@ -138,6 +138,17 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
                if (chunk->singleton ||
                    chunk->end_of_packet ||
                    chunk->pdiscard) {
+                       if (chunk->head_skb == chunk->skb) {
+                               chunk->skb = skb_shinfo(chunk->skb)->frag_list;
+                               goto new_skb;
+                       }
+                       if (chunk->skb->next) {
+                               chunk->skb = chunk->skb->next;
+                               goto new_skb;
+                       }
+
+                       if (chunk->head_skb)
+                               chunk->skb = chunk->head_skb;
                        sctp_chunk_free(chunk);
                        chunk = queue->in_progress = NULL;
                } else {
@@ -155,15 +166,15 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
 
 next_chunk:
                /* Is the queue empty?  */
-               if (list_empty(&queue->in_chunk_list))
+               entry = sctp_list_dequeue(&queue->in_chunk_list);
+               if (!entry)
                        return NULL;
 
-               entry = queue->in_chunk_list.next;
                chunk = list_entry(entry, struct sctp_chunk, list);
-               list_del_init(entry);
 
                /* Linearize if it's not GSO */
-               if (skb_is_nonlinear(chunk->skb)) {
+               if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) != SKB_GSO_SCTP &&
+                   skb_is_nonlinear(chunk->skb)) {
                        if (skb_linearize(chunk->skb)) {
                                __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
                                sctp_chunk_free(chunk);
@@ -174,15 +185,39 @@ next_chunk:
                        chunk->sctp_hdr = sctp_hdr(chunk->skb);
                }
 
+               if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) == SKB_GSO_SCTP) {
+                       /* GSO-marked skbs but without frags, handle
+                        * them normally
+                        */
+                       if (skb_shinfo(chunk->skb)->frag_list)
+                               chunk->head_skb = chunk->skb;
+
+                       /* skbs with "cover letter" */
+                       if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len)
+                               chunk->skb = skb_shinfo(chunk->skb)->frag_list;
+
+                       if (WARN_ON(!chunk->skb)) {
+                               __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
+                               sctp_chunk_free(chunk);
+                               goto next_chunk;
+                       }
+               }
+
+               if (chunk->asoc)
+                       sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb);
+
                queue->in_progress = chunk;
 
+new_skb:
                /* This is the first chunk in the packet.  */
-               chunk->singleton = 1;
                ch = (sctp_chunkhdr_t *) chunk->skb->data;
+               chunk->singleton = 1;
                chunk->data_accepted = 0;
-
-               if (chunk->asoc)
-                       sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb);
+               chunk->pdiscard = 0;
+               chunk->auth = 0;
+               chunk->has_asconf = 0;
+               chunk->end_of_packet = 0;
+               chunk->ecn_ce_done = 0;
        }
 
        chunk->chunk_hdr = ch;