diff options
author | Steffen Klassert <steffen.klassert@secunet.com> | 2012-03-06 16:20:26 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-08 03:30:24 -0500 |
commit | 5faa5df1fa2024bd750089ff21dcc4191798263d (patch) | |
tree | a47a76a4bd9a3b61dcc2d2a34149e6ffecdf8cd8 | |
parent | dcf353b17007841f91e1735e33790508c84c9ad0 (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.h | 3 | ||||
-rw-r--r-- | net/ipv4/inetpeer.c | 80 | ||||
-rw-r--r-- | net/ipv4/route.c | 1 |
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, | |||
96 | extern void inet_putpeer(struct inet_peer *p); | 97 | extern void inet_putpeer(struct inet_peer *p); |
97 | extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); | 98 | extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); |
98 | 99 | ||
100 | extern 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 | ||
67 | static struct kmem_cache *peer_cachep __read_mostly; | 68 | static struct kmem_cache *peer_cachep __read_mostly; |
68 | 69 | ||
70 | static LIST_HEAD(gc_list); | ||
71 | static const int gc_delay = 60 * HZ; | ||
72 | static struct delayed_work gc_work; | ||
73 | static 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 | |||
102 | int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ | 108 | int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ |
103 | int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ | 109 | int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ |
104 | 110 | ||
111 | static 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 */ |
107 | void __init inet_initpeers(void) | 157 | void __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 | ||
131 | static int addr_compare(const struct inetpeer_addr *a, | 182 | static 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 | } |
511 | EXPORT_SYMBOL(inet_peer_xrlim_allow); | 562 | EXPORT_SYMBOL(inet_peer_xrlim_allow); |
563 | |||
564 | void 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 | |||
586 | out: | ||
587 | write_sequnlock_bh(&base->lock); | ||
588 | } | ||
589 | EXPORT_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 | /* |