fragment: add fast path for in-order fragments
authorChangli Gao <xiaosuo@gmail.com>
Tue, 29 Jun 2010 04:39:37 +0000 (04:39 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 30 Jun 2010 20:44:29 +0000 (13:44 -0700)
add fast path for in-order fragments

As the fragments are sent in order in most of OSes, such as Windows, Darwin and
FreeBSD, it is likely the new fragments are at the end of the inet_frag_queue.
In the fast path, we check if the skb at the end of the inet_frag_queue is the
prev we expect.

Signed-off-by: Changli Gao <xiaosuo@gmail.com>
----
 include/net/inet_frag.h |    1 +
 net/ipv4/ip_fragment.c  |   12 ++++++++++++
 net/ipv6/reassembly.c   |   11 +++++++++++
 3 files changed, 24 insertions(+)
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/inet_frag.h
net/ipv4/ip_fragment.c
net/ipv6/reassembly.c

index 39f2dc9439086b83e45b6495e712ebfe91daa2a4..16ff29a7bb30cfbf5e1581c1388e321e9d9f2a1b 100644 (file)
@@ -20,6 +20,7 @@ struct inet_frag_queue {
        atomic_t                refcnt;
        struct timer_list       timer;      /* when will this queue expire? */
        struct sk_buff          *fragments; /* list of received fragments */
+       struct sk_buff          *fragments_tail;
        ktime_t                 stamp;
        int                     len;        /* total length of orig datagram */
        int                     meat;
index 858d34648eee9ce90e7e7068ea8dfcea779cd0c8..dd0dbf0c6b7f7d0f5c00bae77599436274dc7452 100644 (file)
@@ -314,6 +314,7 @@ static int ip_frag_reinit(struct ipq *qp)
        qp->q.len = 0;
        qp->q.meat = 0;
        qp->q.fragments = NULL;
+       qp->q.fragments_tail = NULL;
        qp->iif = 0;
 
        return 0;
@@ -386,6 +387,11 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
         * in the chain of fragments so far.  We must know where to put
         * this fragment, right?
         */
+       prev = qp->q.fragments_tail;
+       if (!prev || FRAG_CB(prev)->offset < offset) {
+               next = NULL;
+               goto found;
+       }
        prev = NULL;
        for (next = qp->q.fragments; next != NULL; next = next->next) {
                if (FRAG_CB(next)->offset >= offset)
@@ -393,6 +399,7 @@ static int ip_frag_queue(struct ipq *qp, 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.
@@ -451,6 +458,8 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
 
        /* Insert this fragment in the chain of fragments. */
        skb->next = next;
+       if (!next)
+               qp->q.fragments_tail = skb;
        if (prev)
                prev->next = skb;
        else
@@ -504,6 +513,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
                        goto out_nomem;
 
                fp->next = head->next;
+               if (!fp->next)
+                       qp->q.fragments_tail = fp;
                prev->next = fp;
 
                skb_morph(head, qp->q.fragments);
@@ -574,6 +585,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
        iph->tot_len = htons(len);
        IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
        qp->q.fragments = NULL;
+       qp->q.fragments_tail = NULL;
        return 0;
 
 out_nomem:
index 0b97230a32517b599e26ab2bed4a405ec0f67f99..545c4141b755ee91277c6bd2912de4f42813bb05 100644 (file)
@@ -333,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)
@@ -340,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.
@@ -397,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
@@ -463,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);
@@ -549,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: