summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Klassert <steffen.klassert@secunet.com>2012-03-06 16:20:26 -0500
committerDavid S. Miller <davem@davemloft.net>2012-03-08 03:30:24 -0500
commit5faa5df1fa2024bd750089ff21dcc4191798263d (patch)
treea47a76a4bd9a3b61dcc2d2a34149e6ffecdf8cd8
parentdcf353b17007841f91e1735e33790508c84c9ad0 (diff)
inetpeer: Invalidate the inetpeer tree along with the routing cache
We initialize the routing metrics with the values cached on the inetpeer in rt_init_metrics(). So if we have the metrics cached on the inetpeer, we ignore the user configured fib_metrics. To fix this issue, we replace the old tree with a fresh initialized inet_peer_base. The old tree is removed later with a delayed work queue. Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/inetpeer.h3
-rw-r--r--net/ipv4/inetpeer.c80
-rw-r--r--net/ipv4/route.c1
3 files changed, 83 insertions, 1 deletions
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
index 06b795dd5906..ff04a33acf00 100644
--- a/include/net/inetpeer.h
+++ b/include/net/inetpeer.h
@@ -41,6 +41,7 @@ struct inet_peer {
41 u32 pmtu_orig; 41 u32 pmtu_orig;
42 u32 pmtu_learned; 42 u32 pmtu_learned;
43 struct inetpeer_addr_base redirect_learned; 43 struct inetpeer_addr_base redirect_learned;
44 struct list_head gc_list;
44 /* 45 /*
45 * Once inet_peer is queued for deletion (refcnt == -1), following fields 46 * Once inet_peer is queued for deletion (refcnt == -1), following fields
46 * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp 47 * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp
@@ -96,6 +97,8 @@ static inline struct inet_peer *inet_getpeer_v6(const struct in6_addr *v6daddr,
96extern void inet_putpeer(struct inet_peer *p); 97extern void inet_putpeer(struct inet_peer *p);
97extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); 98extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
98 99
100extern void inetpeer_invalidate_tree(int family);
101
99/* 102/*
100 * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, 103 * temporary check to make sure we dont access rid, ip_id_count, tcp_ts,
101 * tcp_ts_stamp if no refcount is taken on inet_peer 104 * tcp_ts_stamp if no refcount is taken on inet_peer
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index bf4a9c4808e1..deea2e96b7f2 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -17,6 +17,7 @@
17#include <linux/kernel.h> 17#include <linux/kernel.h>
18#include <linux/mm.h> 18#include <linux/mm.h>
19#include <linux/net.h> 19#include <linux/net.h>
20#include <linux/workqueue.h>
20#include <net/ip.h> 21#include <net/ip.h>
21#include <net/inetpeer.h> 22#include <net/inetpeer.h>
22#include <net/secure_seq.h> 23#include <net/secure_seq.h>
@@ -66,6 +67,11 @@
66 67
67static struct kmem_cache *peer_cachep __read_mostly; 68static struct kmem_cache *peer_cachep __read_mostly;
68 69
70static LIST_HEAD(gc_list);
71static const int gc_delay = 60 * HZ;
72static struct delayed_work gc_work;
73static DEFINE_SPINLOCK(gc_lock);
74
69#define node_height(x) x->avl_height 75#define node_height(x) x->avl_height
70 76
71#define peer_avl_empty ((struct inet_peer *)&peer_fake_node) 77#define peer_avl_empty ((struct inet_peer *)&peer_fake_node)
@@ -102,6 +108,50 @@ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries m
102int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ 108int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */
103int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ 109int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */
104 110
111static void inetpeer_gc_worker(struct work_struct *work)
112{
113 struct inet_peer *p, *n;
114 LIST_HEAD(list);
115
116 spin_lock_bh(&gc_lock);
117 list_replace_init(&gc_list, &list);
118 spin_unlock_bh(&gc_lock);
119
120 if (list_empty(&list))
121 return;
122
123 list_for_each_entry_safe(p, n, &list, gc_list) {
124
125 if(need_resched())
126 cond_resched();
127
128 if (p->avl_left != peer_avl_empty) {
129 list_add_tail(&p->avl_left->gc_list, &list);
130 p->avl_left = peer_avl_empty;
131 }
132
133 if (p->avl_right != peer_avl_empty) {
134 list_add_tail(&p->avl_right->gc_list, &list);
135 p->avl_right = peer_avl_empty;
136 }
137
138 n = list_entry(p->gc_list.next, struct inet_peer, gc_list);
139
140 if (!atomic_read(&p->refcnt)) {
141 list_del(&p->gc_list);
142 kmem_cache_free(peer_cachep, p);
143 }
144 }
145
146 if (list_empty(&list))
147 return;
148
149 spin_lock_bh(&gc_lock);
150 list_splice(&list, &gc_list);
151 spin_unlock_bh(&gc_lock);
152
153 schedule_delayed_work(&gc_work, gc_delay);
154}
105 155
106/* Called from ip_output.c:ip_init */ 156/* Called from ip_output.c:ip_init */
107void __init inet_initpeers(void) 157void __init inet_initpeers(void)
@@ -126,6 +176,7 @@ void __init inet_initpeers(void)
126 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 176 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
127 NULL); 177 NULL);
128 178
179 INIT_DELAYED_WORK_DEFERRABLE(&gc_work, inetpeer_gc_worker);
129} 180}
130 181
131static int addr_compare(const struct inetpeer_addr *a, 182static int addr_compare(const struct inetpeer_addr *a,
@@ -449,7 +500,7 @@ relookup:
449 p->pmtu_orig = 0; 500 p->pmtu_orig = 0;
450 p->redirect_genid = 0; 501 p->redirect_genid = 0;
451 memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); 502 memset(&p->redirect_learned, 0, sizeof(p->redirect_learned));
452 503 INIT_LIST_HEAD(&p->gc_list);
453 504
454 /* Link the node. */ 505 /* Link the node. */
455 link_to_pool(p, base); 506 link_to_pool(p, base);
@@ -509,3 +560,30 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout)
509 return rc; 560 return rc;
510} 561}
511EXPORT_SYMBOL(inet_peer_xrlim_allow); 562EXPORT_SYMBOL(inet_peer_xrlim_allow);
563
564void inetpeer_invalidate_tree(int family)
565{
566 struct inet_peer *old, *new, *prev;
567 struct inet_peer_base *base = family_to_base(family);
568
569 write_seqlock_bh(&base->lock);
570
571 old = base->root;
572 if (old == peer_avl_empty_rcu)
573 goto out;
574
575 new = peer_avl_empty_rcu;
576
577 prev = cmpxchg(&base->root, old, new);
578 if (prev == old) {
579 base->total = 0;
580 spin_lock(&gc_lock);
581 list_add_tail(&prev->gc_list, &gc_list);
582 spin_unlock(&gc_lock);
583 schedule_delayed_work(&gc_work, gc_delay);
584 }
585
586out:
587 write_sequnlock_bh(&base->lock);
588}
589EXPORT_SYMBOL(inetpeer_invalidate_tree);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index bcacf54e5418..23ce0c1287ab 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -938,6 +938,7 @@ static void rt_cache_invalidate(struct net *net)
938 get_random_bytes(&shuffle, sizeof(shuffle)); 938 get_random_bytes(&shuffle, sizeof(shuffle));
939 atomic_add(shuffle + 1U, &net->ipv4.rt_genid); 939 atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
940 redirect_genid++; 940 redirect_genid++;
941 inetpeer_invalidate_tree(AF_INET);
941} 942}
942 943
943/* 944/*