diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
| -rw-r--r-- | net/ipv6/reassembly.c | 56 |
1 files changed, 29 insertions, 27 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index bb66e1789b..33e5193bf9 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -22,6 +22,7 @@ * * Horst von Brand Add missing #include <linux/string.h> * Alexey Kuznetsov SMP races, threading, cleanup. + * Patrick McHardy LRU queue of frag heads for evictor. */ #include <linux/config.h> #include <linux/errno.h> @@ -31,6 +32,7 @@ #include <linux/sockios.h> #include <linux/jiffies.h> #include <linux/net.h> +#include <linux/list.h> #include <linux/netdevice.h> #include <linux/in6.h> #include <linux/ipv6.h> @@ -67,6 +69,7 @@ struct ip6frag_skb_cb struct frag_queue { struct frag_queue *next; + struct list_head lru_list; /* lru list member */ __u32 id; /* fragment id */ struct in6_addr saddr; @@ -95,6 +98,7 @@ struct frag_queue static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ]; static rwlock_t ip6_frag_lock = RW_LOCK_UNLOCKED; +static LIST_HEAD(ip6_frag_lru_list); int ip6_frag_nqueues = 0; static __inline__ void __fq_unlink(struct frag_queue *fq) @@ -102,6 +106,7 @@ static __inline__ void __fq_unlink(struct frag_queue *fq) if(fq->next) fq->next->pprev = fq->pprev; *fq->pprev = fq->next; + list_del(&fq->lru_list); ip6_frag_nqueues--; } @@ -193,38 +198,30 @@ static __inline__ void fq_kill(struct frag_queue *fq) static void ip6_evictor(void) { - int i, progress; + struct frag_queue *fq; + struct list_head *tmp; - do { + for(;;) { if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh) return; - progress = 0; - for (i = 0; i < IP6Q_HASHSZ; i++) { - struct frag_queue *fq; - if (ip6_frag_hash[i] == NULL) - continue; - - read_lock(&ip6_frag_lock); - if ((fq = ip6_frag_hash[i]) != NULL) { - /* find the oldest queue for this hash bucket */ - while (fq->next) - fq = fq->next; - atomic_inc(&fq->refcnt); - read_unlock(&ip6_frag_lock); - - spin_lock(&fq->lock); - if (!(fq->last_in&COMPLETE)) - fq_kill(fq); - spin_unlock(&fq->lock); - - fq_put(fq); - IP6_INC_STATS_BH(Ip6ReasmFails); - progress = 1; - continue; - } + read_lock(&ip6_frag_lock); + if (list_empty(&ip6_frag_lru_list)) { read_unlock(&ip6_frag_lock); + return; } - } while (progress); + tmp = ip6_frag_lru_list.next; + fq = list_entry(tmp, struct frag_queue, lru_list); + atomic_inc(&fq->refcnt); + read_unlock(&ip6_frag_lock); + + spin_lock(&fq->lock); + if (!(fq->last_in&COMPLETE)) + fq_kill(fq); + spin_unlock(&fq->lock); + + fq_put(fq); + IP6_INC_STATS_BH(Ip6ReasmFails); + } } static void ip6_frag_expire(unsigned long data) @@ -294,6 +291,8 @@ static struct frag_queue *ip6_frag_intern(unsigned int hash, fq->next->pprev = &fq->next; ip6_frag_hash[hash] = fq; fq->pprev = &ip6_frag_hash[hash]; + INIT_LIST_HEAD(&fq->lru_list); + list_add_tail(&fq->lru_list, &ip6_frag_lru_list); ip6_frag_nqueues++; write_unlock(&ip6_frag_lock); return fq; @@ -501,6 +500,9 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, fq->nhoffset = nhoff; fq->last_in |= FIRST_IN; } + write_lock(&ip6_frag_lock); + list_move_tail(&fq->lru_list, &ip6_frag_lru_list); + write_unlock(&ip6_frag_lock); return; err: |
