summaryrefslogtreecommitdiff
path: root/net/ipv4/ip_fragment.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-02-19 01:06:44 -0800
committerLinus Torvalds <torvalds@home.transmeta.com>2003-02-19 01:06:44 -0800
commitb76a852bfb51ffe25505bab3dfd87ddbc821c2aa (patch)
tree25e4f88a1d60f92c7f00f19e835a23d1502c5c82 /net/ipv4/ip_fragment.c
parent612cbdcf44289e81a4327d8f67fcc41e07ad33c3 (diff)
parent6ab007bf3fd8db711771db1e93880213a63ecd38 (diff)
Merge bk://are.twiddle.net/axp-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
Diffstat (limited to 'net/ipv4/ip_fragment.c')
-rw-r--r--net/ipv4/ip_fragment.c58
1 files changed, 30 insertions, 28 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 1a0d3cf287..4799d1e074 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -19,6 +19,7 @@
* Bill Hawes : Frag accounting and evictor fixes.
* John McDonald : 0 length frag bug.
* Alexey Kuznetsov: SMP races, threading, cleanup.
+ * Patrick McHardy : LRU queue of frag heads for evictor.
*/
#include <linux/config.h>
@@ -26,6 +27,7 @@
#include <linux/mm.h>
#include <linux/jiffies.h>
#include <linux/skbuff.h>
+#include <linux/list.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
@@ -67,6 +69,7 @@ struct ipfrag_skb_cb
/* Describe an entry in the "incomplete datagrams" queue. */
struct ipq {
struct ipq *next; /* linked list pointers */
+ struct list_head lru_list; /* lru list member */
u32 saddr;
u32 daddr;
u16 id;
@@ -94,6 +97,7 @@ struct ipq {
/* Per-bucket lock is easy to add now. */
static struct ipq *ipq_hash[IPQ_HASHSZ];
static rwlock_t ipfrag_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(ipq_lru_list);
int ip_frag_nqueues = 0;
static __inline__ void __ipq_unlink(struct ipq *qp)
@@ -101,6 +105,7 @@ static __inline__ void __ipq_unlink(struct ipq *qp)
if(qp->next)
qp->next->pprev = qp->pprev;
*qp->pprev = qp->next;
+ list_del(&qp->lru_list);
ip_frag_nqueues--;
}
@@ -202,39 +207,30 @@ static void ipq_kill(struct ipq *ipq)
*/
static void ip_evictor(void)
{
- int i, progress;
+ struct ipq *qp;
+ struct list_head *tmp;
- do {
+ for(;;) {
if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh)
return;
- progress = 0;
- /* FIXME: Make LRU queue of frag heads. -DaveM */
- for (i = 0; i < IPQ_HASHSZ; i++) {
- struct ipq *qp;
- if (ipq_hash[i] == NULL)
- continue;
-
- read_lock(&ipfrag_lock);
- if ((qp = ipq_hash[i]) != NULL) {
- /* find the oldest queue for this hash bucket */
- while (qp->next)
- qp = qp->next;
- atomic_inc(&qp->refcnt);
- read_unlock(&ipfrag_lock);
-
- spin_lock(&qp->lock);
- if (!(qp->last_in&COMPLETE))
- ipq_kill(qp);
- spin_unlock(&qp->lock);
-
- ipq_put(qp);
- IP_INC_STATS_BH(IpReasmFails);
- progress = 1;
- continue;
- }
+ read_lock(&ipfrag_lock);
+ if (list_empty(&ipq_lru_list)) {
read_unlock(&ipfrag_lock);
+ return;
}
- } while (progress);
+ tmp = ipq_lru_list.next;
+ qp = list_entry(tmp, struct ipq, lru_list);
+ atomic_inc(&qp->refcnt);
+ read_unlock(&ipfrag_lock);
+
+ spin_lock(&qp->lock);
+ if (!(qp->last_in&COMPLETE))
+ ipq_kill(qp);
+ spin_unlock(&qp->lock);
+
+ ipq_put(qp);
+ IP_INC_STATS_BH(IpReasmFails);
+ }
}
/*
@@ -302,6 +298,8 @@ static struct ipq *ip_frag_intern(unsigned int hash, struct ipq *qp_in)
qp->next->pprev = &qp->next;
ipq_hash[hash] = qp;
qp->pprev = &ipq_hash[hash];
+ INIT_LIST_HEAD(&qp->lru_list);
+ list_add_tail(&qp->lru_list, &ipq_lru_list);
ip_frag_nqueues++;
write_unlock(&ipfrag_lock);
return qp;
@@ -496,6 +494,10 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
if (offset == 0)
qp->last_in |= FIRST_IN;
+ write_lock(&ipfrag_lock);
+ list_move_tail(&qp->lru_list, &ipq_lru_list);
+ write_unlock(&ipfrag_lock);
+
return;
err: