]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv6/reassembly.c
fragment: add fast path for in-order fragments
[net-next-2.6.git] / net / ipv6 / reassembly.c
index 6d4292ff585463178885153cdd2fe6e044b962a9..545c4141b755ee91277c6bd2912de4f42813bb05 100644 (file)
@@ -150,11 +150,8 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a)
 EXPORT_SYMBOL(ip6_frag_match);
 
 /* Memory Tracking Functions. */
-static inline void frag_kfree_skb(struct netns_frags *nf,
-               struct sk_buff *skb, int *work)
+static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb)
 {
-       if (work)
-               *work -= skb->truesize;
        atomic_sub(skb->truesize, &nf->mem);
        kfree_skb(skb);
 }
@@ -336,6 +333,11 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
         * in the chain of fragments so far.  We must know where to put
         * this fragment, right?
         */
+       prev = fq->q.fragments_tail;
+       if (!prev || FRAG6_CB(prev)->offset < offset) {
+               next = NULL;
+               goto found;
+       }
        prev = NULL;
        for(next = fq->q.fragments; next != NULL; next = next->next) {
                if (FRAG6_CB(next)->offset >= offset)
@@ -343,6 +345,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
                prev = next;
        }
 
+found:
        /* We found where to put this one.  Check for overlap with
         * preceding fragment, and, if needed, align things so that
         * any overlaps are eliminated.
@@ -392,7 +395,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
                                fq->q.fragments = next;
 
                        fq->q.meat -= free_it->len;
-                       frag_kfree_skb(fq->q.net, free_it, NULL);
+                       frag_kfree_skb(fq->q.net, free_it);
                }
        }
 
@@ -400,6 +403,8 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
 
        /* Insert this fragment in the chain of fragments. */
        skb->next = next;
+       if (!next)
+               fq->q.fragments_tail = skb;
        if (prev)
                prev->next = skb;
        else
@@ -466,6 +471,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
                        goto out_oom;
 
                fp->next = head->next;
+               if (!fp->next)
+                       fq->q.fragments_tail = fp;
                prev->next = fp;
 
                skb_morph(head, fq->q.fragments);
@@ -524,7 +531,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        skb_shinfo(head)->frag_list = head->next;
        skb_reset_transport_header(head);
        skb_push(head, head->data - skb_network_header(head));
-       atomic_sub(head->truesize, &fq->q.net->mem);
 
        for (fp=head->next; fp; fp = fp->next) {
                head->data_len += fp->len;
@@ -534,8 +540,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
                else if (head->ip_summed == CHECKSUM_COMPLETE)
                        head->csum = csum_add(head->csum, fp->csum);
                head->truesize += fp->truesize;
-               atomic_sub(fp->truesize, &fq->q.net->mem);
        }
+       atomic_sub(head->truesize, &fq->q.net->mem);
 
        head->next = NULL;
        head->dev = dev;
@@ -553,6 +559,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
        rcu_read_unlock();
        fq->q.fragments = NULL;
+       fq->q.fragments_tail = NULL;
        return 1;
 
 out_oversize: