summaryrefslogtreecommitdiff
path: root/net/ipv6/reassembly.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r--net/ipv6/reassembly.c56
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: