netdev
[Top] [All Lists]

dst cache overflow 2.2.x; x>=16

To: <netdev@xxxxxxxxxxx>
Subject: dst cache overflow 2.2.x; x>=16
From: "Milam, Chad" <Chad_Milam@xxxxxxxxxx>
Date: Sun, 14 Apr 2002 13:51:16 -0400
Sender: owner-netdev@xxxxxxxxxxx
Thread-index: AcHj3PXkyfkHrNSZQ+eVUFgtMtGwOg==
Thread-topic: dst cache overflow 2.2.x; x>=16
First, I apologise for the cross post.  I sent this to the lkml,
and several people said I should post it here as well.....

I have been looking into a problem on a couple of linux routers that 
I have.  All of them are used for routing between the private network 
and the Internet. Some run Check Point VPN-1 v4.1 SP5, some do not.  
The problem is that after about 22 hours, they all have "dst cache 
overflow", which is quite easily traced back to net/ipv4/route.c 
rt_garbage_collect().  rt_garbage_collect appears to be called or 
initiated from two places 1) the dst_ops structure 2) rt_intern_hash.

Based on my testing here is what I have come up with.... dst_ops.entries 
is not being set appropriately (or at least not what I would expect it 
to be). I determined this by changing dst_ops->gc to point to a new 
function (rt_display_tot) for debugging, as well as had rt_intern_hash() 
call this function instead of rt_garbage_collect().  

The sole purpose of this function was to loop through all hash chains and 
count up the entries that should be valid, do a printk("%d %d", count, 
dst_ops.entries), then return(rt_garbage_collect()).  The result was, that 
when rt_garbage_collect() returned 1 (dst cache overflow), the number of 
entries reported by dst_ops.entries was far different than the number 
reported by my loop/counter.  

Upon further investigation in to rt_free(), __dst_free(), and dst_destroy(), 
I found that only dst_destroy() decrements dst_ops.entries.

Furthermore, when dst_ops->gc returns 1, dst_alloc() will not create an 
entry, appropriately so, the box is at a stand still.  

So... For the interim, I have create a small patch that purges the dst cache 
table, and resets dst_ops.entries to 0 anytime rt_garbage_collect() returns 1.  
The result... The box stays up, and hums along quite happily.

I would appreciate any comments with regards to this matter.  I have also 
included a copy of the patch that I created to work around the issue.

Thanks,
Chad

diff -urNp linux-2.2.16cwm/net/ipv4/route.c linux-2.2.16cwm/net/ipv4/route.c
-n- linux-2.2.16/net/ipv4/route.c       Tue Jan  4 13:12:26 2000
+++ linux-2.2.16cwm/net/ipv4/route.c    Tue Apr  9 15:14:12 2002
@@ -96,7 +96,7 @@
 
 #define IP_MAX_MTU     0xFFF0
 
-#define RT_GC_TIMEOUT (300*HZ)
+#define RT_GC_TIMEOUT (120*HZ)
 
 int ip_rt_min_delay = 2*HZ;
 int ip_rt_max_delay = 10*HZ;
@@ -134,7 +134,8 @@ static struct dst_entry * ipv4_dst_rerou
 static struct dst_entry * ipv4_negative_advice(struct dst_entry *);
 static void              ipv4_link_failure(struct sk_buff *skb);
 static int rt_garbage_collect(void);
-
+static int rt_delete_now(void);
+static int rt_garbage_ctl(void);
 
 struct dst_ops ipv4_dst_ops =
 {
@@ -142,7 +143,7 @@ struct dst_ops ipv4_dst_ops =
        __constant_htons(ETH_P_IP),
        RT_HASH_DIVISOR,
 
-       rt_garbage_collect,
+       rt_garbage_ctl,
        ipv4_dst_check,
        ipv4_dst_reroute,
        NULL,
@@ -508,8 +509,7 @@ static int rt_garbage_collect(void)
 
        if (atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
                return 0;
-       if (net_ratelimit())
-               printk("dst cache overflow\n");
+
        return 1;
 
 work_done:
@@ -570,7 +570,7 @@ restart:
                                int saved_int = ip_rt_gc_min_interval;
                                ip_rt_gc_elasticity = 1;
                                ip_rt_gc_min_interval = 0;
-                               rt_garbage_collect();
+                               rt_garbage_ctl();
                                ip_rt_gc_min_interval = saved_int;
                                ip_rt_gc_elasticity = saved_elasticity;
                                goto restart;
@@ -2045,4 +2045,44 @@ __initfunc(void ip_rt_init(void))
        ent->read_proc = ip_rt_acct_read;
 #endif
 #endif
+}
+
+static int rt_delete_now(void){
+       struct rtable *rth, **rthp;
+       int i,ent1,ent2,c;
+
+       i=0;
+       ent1=0;
+       ent2=0;
+       c=0;
+
+       ent1=atomic_read(&ipv4_dst_ops.entries);
+       start_bh_atomic();
+       while(i<RT_HASH_DIVISOR){
+               rthp=&rt_hash_table[i];
+               while((rth=*rthp)!=NULL){
+                       *rthp=rth->u.rt_next;
+                       rth->u.rt_next=NULL;
+                       c+=1;
+                       rt_free(rth);
+               }
+               i++;
+       }
+
+       atomic_set(&ipv4_dst_ops.entries,0);
+       end_bh_atomic();
+       ent2=atomic_read(&ipv4_dst_ops.entries);
+
+       if(net_ratelimit()){
+               printk("dst cache overflow\n");
+               printk("rt_delete_now(); s:%d e:%d t:%d\n",ent1,ent2,c);
+       }
+
+       return 0;
+}
+
+static int rt_garbage_ctl(void){
+       if(rt_garbage_collect())
+               rt_delete_now();
+       return 0;
 }


<Prev in Thread] Current Thread [Next in Thread>