diff options
author | Alexey Dobriyan <adobriyan@gmail.com> | 2010-01-25 05:37:54 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-28 09:31:05 -0500 |
commit | a1664773907a2b69e2a3019598dcbeffa6bc724b (patch) | |
tree | 77070892675e29de901587a4f55bbc620b20e211 /net/ipv6/xfrm6_tunnel.c | |
parent | e924960dacdf85d118a98c7262edf2f99c3015cf (diff) |
netns xfrm: xfrm6_tunnel in netns
I'm not sure about rcu stuff near kmem cache destruction:
* checks for non-empty hashes look bogus, they're done _before_
rcu_berrier()
* unregistering netns ops is done before kmem_cache destoy
(as it should), and unregistering involves rcu barriers by itself
So it looks nothing should be done.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/xfrm6_tunnel.c')
-rw-r--r-- | net/ipv6/xfrm6_tunnel.c | 140 |
1 files changed, 83 insertions, 57 deletions
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 23fb1002124c..d6f9aeec69f7 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c | |||
@@ -30,6 +30,25 @@ | |||
30 | #include <linux/ipv6.h> | 30 | #include <linux/ipv6.h> |
31 | #include <linux/icmpv6.h> | 31 | #include <linux/icmpv6.h> |
32 | #include <linux/mutex.h> | 32 | #include <linux/mutex.h> |
33 | #include <net/netns/generic.h> | ||
34 | |||
35 | #define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256 | ||
36 | #define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256 | ||
37 | |||
38 | #define XFRM6_TUNNEL_SPI_MIN 1 | ||
39 | #define XFRM6_TUNNEL_SPI_MAX 0xffffffff | ||
40 | |||
41 | struct xfrm6_tunnel_net { | ||
42 | struct hlist_head spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE]; | ||
43 | struct hlist_head spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE]; | ||
44 | u32 spi; | ||
45 | }; | ||
46 | |||
47 | static int xfrm6_tunnel_net_id __read_mostly; | ||
48 | static inline struct xfrm6_tunnel_net *xfrm6_tunnel_pernet(struct net *net) | ||
49 | { | ||
50 | return net_generic(net, xfrm6_tunnel_net_id); | ||
51 | } | ||
33 | 52 | ||
34 | /* | 53 | /* |
35 | * xfrm_tunnel_spi things are for allocating unique id ("spi") | 54 | * xfrm_tunnel_spi things are for allocating unique id ("spi") |
@@ -46,19 +65,8 @@ struct xfrm6_tunnel_spi { | |||
46 | 65 | ||
47 | static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock); | 66 | static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock); |
48 | 67 | ||
49 | static u32 xfrm6_tunnel_spi; | ||
50 | |||
51 | #define XFRM6_TUNNEL_SPI_MIN 1 | ||
52 | #define XFRM6_TUNNEL_SPI_MAX 0xffffffff | ||
53 | |||
54 | static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; | 68 | static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; |
55 | 69 | ||
56 | #define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256 | ||
57 | #define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256 | ||
58 | |||
59 | static struct hlist_head xfrm6_tunnel_spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE]; | ||
60 | static struct hlist_head xfrm6_tunnel_spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE]; | ||
61 | |||
62 | static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) | 70 | static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) |
63 | { | 71 | { |
64 | unsigned h; | 72 | unsigned h; |
@@ -77,49 +85,30 @@ static inline unsigned xfrm6_tunnel_spi_hash_byspi(u32 spi) | |||
77 | } | 85 | } |
78 | 86 | ||
79 | 87 | ||
80 | static int xfrm6_tunnel_spi_init(void) | 88 | static int __init xfrm6_tunnel_spi_init(void) |
81 | { | 89 | { |
82 | int i; | ||
83 | |||
84 | xfrm6_tunnel_spi = 0; | ||
85 | xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi", | 90 | xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi", |
86 | sizeof(struct xfrm6_tunnel_spi), | 91 | sizeof(struct xfrm6_tunnel_spi), |
87 | 0, SLAB_HWCACHE_ALIGN, | 92 | 0, SLAB_HWCACHE_ALIGN, |
88 | NULL); | 93 | NULL); |
89 | if (!xfrm6_tunnel_spi_kmem) | 94 | if (!xfrm6_tunnel_spi_kmem) |
90 | return -ENOMEM; | 95 | return -ENOMEM; |
91 | |||
92 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) | ||
93 | INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byaddr[i]); | ||
94 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) | ||
95 | INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byspi[i]); | ||
96 | return 0; | 96 | return 0; |
97 | } | 97 | } |
98 | 98 | ||
99 | static void xfrm6_tunnel_spi_fini(void) | 99 | static void xfrm6_tunnel_spi_fini(void) |
100 | { | 100 | { |
101 | int i; | ||
102 | |||
103 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) { | ||
104 | if (!hlist_empty(&xfrm6_tunnel_spi_byaddr[i])) | ||
105 | return; | ||
106 | } | ||
107 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) { | ||
108 | if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i])) | ||
109 | return; | ||
110 | } | ||
111 | rcu_barrier(); | ||
112 | kmem_cache_destroy(xfrm6_tunnel_spi_kmem); | 101 | kmem_cache_destroy(xfrm6_tunnel_spi_kmem); |
113 | xfrm6_tunnel_spi_kmem = NULL; | ||
114 | } | 102 | } |
115 | 103 | ||
116 | static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | 104 | static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr) |
117 | { | 105 | { |
106 | struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); | ||
118 | struct xfrm6_tunnel_spi *x6spi; | 107 | struct xfrm6_tunnel_spi *x6spi; |
119 | struct hlist_node *pos; | 108 | struct hlist_node *pos; |
120 | 109 | ||
121 | hlist_for_each_entry_rcu(x6spi, pos, | 110 | hlist_for_each_entry_rcu(x6spi, pos, |
122 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], | 111 | &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], |
123 | list_byaddr) { | 112 | list_byaddr) { |
124 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) | 113 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) |
125 | return x6spi; | 114 | return x6spi; |
@@ -128,13 +117,13 @@ static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | |||
128 | return NULL; | 117 | return NULL; |
129 | } | 118 | } |
130 | 119 | ||
131 | __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | 120 | __be32 xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr) |
132 | { | 121 | { |
133 | struct xfrm6_tunnel_spi *x6spi; | 122 | struct xfrm6_tunnel_spi *x6spi; |
134 | u32 spi; | 123 | u32 spi; |
135 | 124 | ||
136 | rcu_read_lock_bh(); | 125 | rcu_read_lock_bh(); |
137 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); | 126 | x6spi = __xfrm6_tunnel_spi_lookup(net, saddr); |
138 | spi = x6spi ? x6spi->spi : 0; | 127 | spi = x6spi ? x6spi->spi : 0; |
139 | rcu_read_unlock_bh(); | 128 | rcu_read_unlock_bh(); |
140 | return htonl(spi); | 129 | return htonl(spi); |
@@ -142,14 +131,15 @@ __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | |||
142 | 131 | ||
143 | EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup); | 132 | EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup); |
144 | 133 | ||
145 | static int __xfrm6_tunnel_spi_check(u32 spi) | 134 | static int __xfrm6_tunnel_spi_check(struct net *net, u32 spi) |
146 | { | 135 | { |
136 | struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); | ||
147 | struct xfrm6_tunnel_spi *x6spi; | 137 | struct xfrm6_tunnel_spi *x6spi; |
148 | int index = xfrm6_tunnel_spi_hash_byspi(spi); | 138 | int index = xfrm6_tunnel_spi_hash_byspi(spi); |
149 | struct hlist_node *pos; | 139 | struct hlist_node *pos; |
150 | 140 | ||
151 | hlist_for_each_entry(x6spi, pos, | 141 | hlist_for_each_entry(x6spi, pos, |
152 | &xfrm6_tunnel_spi_byspi[index], | 142 | &xfrm6_tn->spi_byspi[index], |
153 | list_byspi) { | 143 | list_byspi) { |
154 | if (x6spi->spi == spi) | 144 | if (x6spi->spi == spi) |
155 | return -1; | 145 | return -1; |
@@ -157,32 +147,33 @@ static int __xfrm6_tunnel_spi_check(u32 spi) | |||
157 | return index; | 147 | return index; |
158 | } | 148 | } |
159 | 149 | ||
160 | static u32 __xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) | 150 | static u32 __xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr) |
161 | { | 151 | { |
152 | struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); | ||
162 | u32 spi; | 153 | u32 spi; |
163 | struct xfrm6_tunnel_spi *x6spi; | 154 | struct xfrm6_tunnel_spi *x6spi; |
164 | int index; | 155 | int index; |
165 | 156 | ||
166 | if (xfrm6_tunnel_spi < XFRM6_TUNNEL_SPI_MIN || | 157 | if (xfrm6_tn->spi < XFRM6_TUNNEL_SPI_MIN || |
167 | xfrm6_tunnel_spi >= XFRM6_TUNNEL_SPI_MAX) | 158 | xfrm6_tn->spi >= XFRM6_TUNNEL_SPI_MAX) |
168 | xfrm6_tunnel_spi = XFRM6_TUNNEL_SPI_MIN; | 159 | xfrm6_tn->spi = XFRM6_TUNNEL_SPI_MIN; |
169 | else | 160 | else |
170 | xfrm6_tunnel_spi++; | 161 | xfrm6_tn->spi++; |
171 | 162 | ||
172 | for (spi = xfrm6_tunnel_spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) { | 163 | for (spi = xfrm6_tn->spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) { |
173 | index = __xfrm6_tunnel_spi_check(spi); | 164 | index = __xfrm6_tunnel_spi_check(net, spi); |
174 | if (index >= 0) | 165 | if (index >= 0) |
175 | goto alloc_spi; | 166 | goto alloc_spi; |
176 | } | 167 | } |
177 | for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tunnel_spi; spi++) { | 168 | for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tn->spi; spi++) { |
178 | index = __xfrm6_tunnel_spi_check(spi); | 169 | index = __xfrm6_tunnel_spi_check(net, spi); |
179 | if (index >= 0) | 170 | if (index >= 0) |
180 | goto alloc_spi; | 171 | goto alloc_spi; |
181 | } | 172 | } |
182 | spi = 0; | 173 | spi = 0; |
183 | goto out; | 174 | goto out; |
184 | alloc_spi: | 175 | alloc_spi: |
185 | xfrm6_tunnel_spi = spi; | 176 | xfrm6_tn->spi = spi; |
186 | x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC); | 177 | x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC); |
187 | if (!x6spi) | 178 | if (!x6spi) |
188 | goto out; | 179 | goto out; |
@@ -192,26 +183,26 @@ alloc_spi: | |||
192 | x6spi->spi = spi; | 183 | x6spi->spi = spi; |
193 | atomic_set(&x6spi->refcnt, 1); | 184 | atomic_set(&x6spi->refcnt, 1); |
194 | 185 | ||
195 | hlist_add_head_rcu(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]); | 186 | hlist_add_head_rcu(&x6spi->list_byspi, &xfrm6_tn->spi_byspi[index]); |
196 | 187 | ||
197 | index = xfrm6_tunnel_spi_hash_byaddr(saddr); | 188 | index = xfrm6_tunnel_spi_hash_byaddr(saddr); |
198 | hlist_add_head_rcu(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]); | 189 | hlist_add_head_rcu(&x6spi->list_byaddr, &xfrm6_tn->spi_byaddr[index]); |
199 | out: | 190 | out: |
200 | return spi; | 191 | return spi; |
201 | } | 192 | } |
202 | 193 | ||
203 | __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) | 194 | __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr) |
204 | { | 195 | { |
205 | struct xfrm6_tunnel_spi *x6spi; | 196 | struct xfrm6_tunnel_spi *x6spi; |
206 | u32 spi; | 197 | u32 spi; |
207 | 198 | ||
208 | spin_lock_bh(&xfrm6_tunnel_spi_lock); | 199 | spin_lock_bh(&xfrm6_tunnel_spi_lock); |
209 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); | 200 | x6spi = __xfrm6_tunnel_spi_lookup(net, saddr); |
210 | if (x6spi) { | 201 | if (x6spi) { |
211 | atomic_inc(&x6spi->refcnt); | 202 | atomic_inc(&x6spi->refcnt); |
212 | spi = x6spi->spi; | 203 | spi = x6spi->spi; |
213 | } else | 204 | } else |
214 | spi = __xfrm6_tunnel_alloc_spi(saddr); | 205 | spi = __xfrm6_tunnel_alloc_spi(net, saddr); |
215 | spin_unlock_bh(&xfrm6_tunnel_spi_lock); | 206 | spin_unlock_bh(&xfrm6_tunnel_spi_lock); |
216 | 207 | ||
217 | return htonl(spi); | 208 | return htonl(spi); |
@@ -225,15 +216,16 @@ static void x6spi_destroy_rcu(struct rcu_head *head) | |||
225 | container_of(head, struct xfrm6_tunnel_spi, rcu_head)); | 216 | container_of(head, struct xfrm6_tunnel_spi, rcu_head)); |
226 | } | 217 | } |
227 | 218 | ||
228 | void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) | 219 | void xfrm6_tunnel_free_spi(struct net *net, xfrm_address_t *saddr) |
229 | { | 220 | { |
221 | struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); | ||
230 | struct xfrm6_tunnel_spi *x6spi; | 222 | struct xfrm6_tunnel_spi *x6spi; |
231 | struct hlist_node *pos, *n; | 223 | struct hlist_node *pos, *n; |
232 | 224 | ||
233 | spin_lock_bh(&xfrm6_tunnel_spi_lock); | 225 | spin_lock_bh(&xfrm6_tunnel_spi_lock); |
234 | 226 | ||
235 | hlist_for_each_entry_safe(x6spi, pos, n, | 227 | hlist_for_each_entry_safe(x6spi, pos, n, |
236 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], | 228 | &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], |
237 | list_byaddr) | 229 | list_byaddr) |
238 | { | 230 | { |
239 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { | 231 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { |
@@ -263,10 +255,11 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) | |||
263 | 255 | ||
264 | static int xfrm6_tunnel_rcv(struct sk_buff *skb) | 256 | static int xfrm6_tunnel_rcv(struct sk_buff *skb) |
265 | { | 257 | { |
258 | struct net *net = dev_net(skb->dev); | ||
266 | struct ipv6hdr *iph = ipv6_hdr(skb); | 259 | struct ipv6hdr *iph = ipv6_hdr(skb); |
267 | __be32 spi; | 260 | __be32 spi; |
268 | 261 | ||
269 | spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr); | 262 | spi = xfrm6_tunnel_spi_lookup(net, (xfrm_address_t *)&iph->saddr); |
270 | return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi) > 0 ? : 0; | 263 | return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi) > 0 ? : 0; |
271 | } | 264 | } |
272 | 265 | ||
@@ -326,7 +319,9 @@ static int xfrm6_tunnel_init_state(struct xfrm_state *x) | |||
326 | 319 | ||
327 | static void xfrm6_tunnel_destroy(struct xfrm_state *x) | 320 | static void xfrm6_tunnel_destroy(struct xfrm_state *x) |
328 | { | 321 | { |
329 | xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr); | 322 | struct net *net = xs_net(x); |
323 | |||
324 | xfrm6_tunnel_free_spi(net, (xfrm_address_t *)&x->props.saddr); | ||
330 | } | 325 | } |
331 | 326 | ||
332 | static const struct xfrm_type xfrm6_tunnel_type = { | 327 | static const struct xfrm_type xfrm6_tunnel_type = { |
@@ -351,6 +346,31 @@ static struct xfrm6_tunnel xfrm46_tunnel_handler = { | |||
351 | .priority = 2, | 346 | .priority = 2, |
352 | }; | 347 | }; |
353 | 348 | ||
349 | static int __net_init xfrm6_tunnel_net_init(struct net *net) | ||
350 | { | ||
351 | struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); | ||
352 | unsigned int i; | ||
353 | |||
354 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) | ||
355 | INIT_HLIST_HEAD(&xfrm6_tn->spi_byaddr[i]); | ||
356 | for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) | ||
357 | INIT_HLIST_HEAD(&xfrm6_tn->spi_byspi[i]); | ||
358 | xfrm6_tn->spi = 0; | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static void __net_exit xfrm6_tunnel_net_exit(struct net *net) | ||
364 | { | ||
365 | } | ||
366 | |||
367 | static struct pernet_operations xfrm6_tunnel_net_ops = { | ||
368 | .init = xfrm6_tunnel_net_init, | ||
369 | .exit = xfrm6_tunnel_net_exit, | ||
370 | .id = &xfrm6_tunnel_net_id, | ||
371 | .size = sizeof(struct xfrm6_tunnel_net), | ||
372 | }; | ||
373 | |||
354 | static int __init xfrm6_tunnel_init(void) | 374 | static int __init xfrm6_tunnel_init(void) |
355 | { | 375 | { |
356 | int rv; | 376 | int rv; |
@@ -367,8 +387,13 @@ static int __init xfrm6_tunnel_init(void) | |||
367 | rv = xfrm6_tunnel_spi_init(); | 387 | rv = xfrm6_tunnel_spi_init(); |
368 | if (rv < 0) | 388 | if (rv < 0) |
369 | goto dereg46; | 389 | goto dereg46; |
390 | rv = register_pernet_subsys(&xfrm6_tunnel_net_ops); | ||
391 | if (rv < 0) | ||
392 | goto deregspi; | ||
370 | return 0; | 393 | return 0; |
371 | 394 | ||
395 | deregspi: | ||
396 | xfrm6_tunnel_spi_fini(); | ||
372 | dereg46: | 397 | dereg46: |
373 | xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); | 398 | xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); |
374 | dereg6: | 399 | dereg6: |
@@ -381,6 +406,7 @@ err: | |||
381 | 406 | ||
382 | static void __exit xfrm6_tunnel_fini(void) | 407 | static void __exit xfrm6_tunnel_fini(void) |
383 | { | 408 | { |
409 | unregister_pernet_subsys(&xfrm6_tunnel_net_ops); | ||
384 | xfrm6_tunnel_spi_fini(); | 410 | xfrm6_tunnel_spi_fini(); |
385 | xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); | 411 | xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); |
386 | xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); | 412 | xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); |