diff options
Diffstat (limited to 'net/ipv6/xfrm6_tunnel.c')
-rw-r--r-- | net/ipv6/xfrm6_tunnel.c | 47 |
1 files changed, 28 insertions, 19 deletions
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 81a95c00e503..438831d33593 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c | |||
@@ -23,7 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/xfrm.h> | 25 | #include <linux/xfrm.h> |
26 | #include <linux/list.h> | 26 | #include <linux/rculist.h> |
27 | #include <net/ip.h> | 27 | #include <net/ip.h> |
28 | #include <net/xfrm.h> | 28 | #include <net/xfrm.h> |
29 | #include <net/ipv6.h> | 29 | #include <net/ipv6.h> |
@@ -36,14 +36,15 @@ | |||
36 | * per xfrm_address_t. | 36 | * per xfrm_address_t. |
37 | */ | 37 | */ |
38 | struct xfrm6_tunnel_spi { | 38 | struct xfrm6_tunnel_spi { |
39 | struct hlist_node list_byaddr; | 39 | struct hlist_node list_byaddr; |
40 | struct hlist_node list_byspi; | 40 | struct hlist_node list_byspi; |
41 | xfrm_address_t addr; | 41 | xfrm_address_t addr; |
42 | u32 spi; | 42 | u32 spi; |
43 | atomic_t refcnt; | 43 | atomic_t refcnt; |
44 | struct rcu_head rcu_head; | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | static DEFINE_RWLOCK(xfrm6_tunnel_spi_lock); | 47 | static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock); |
47 | 48 | ||
48 | static u32 xfrm6_tunnel_spi; | 49 | static u32 xfrm6_tunnel_spi; |
49 | 50 | ||
@@ -107,6 +108,7 @@ static void xfrm6_tunnel_spi_fini(void) | |||
107 | if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i])) | 108 | if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i])) |
108 | return; | 109 | return; |
109 | } | 110 | } |
111 | rcu_barrier(); | ||
110 | kmem_cache_destroy(xfrm6_tunnel_spi_kmem); | 112 | kmem_cache_destroy(xfrm6_tunnel_spi_kmem); |
111 | xfrm6_tunnel_spi_kmem = NULL; | 113 | xfrm6_tunnel_spi_kmem = NULL; |
112 | } | 114 | } |
@@ -116,7 +118,7 @@ static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | |||
116 | struct xfrm6_tunnel_spi *x6spi; | 118 | struct xfrm6_tunnel_spi *x6spi; |
117 | struct hlist_node *pos; | 119 | struct hlist_node *pos; |
118 | 120 | ||
119 | hlist_for_each_entry(x6spi, pos, | 121 | hlist_for_each_entry_rcu(x6spi, pos, |
120 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], | 122 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], |
121 | list_byaddr) { | 123 | list_byaddr) { |
122 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) | 124 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) |
@@ -131,10 +133,10 @@ __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) | |||
131 | struct xfrm6_tunnel_spi *x6spi; | 133 | struct xfrm6_tunnel_spi *x6spi; |
132 | u32 spi; | 134 | u32 spi; |
133 | 135 | ||
134 | read_lock_bh(&xfrm6_tunnel_spi_lock); | 136 | rcu_read_lock_bh(); |
135 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); | 137 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); |
136 | spi = x6spi ? x6spi->spi : 0; | 138 | spi = x6spi ? x6spi->spi : 0; |
137 | read_unlock_bh(&xfrm6_tunnel_spi_lock); | 139 | rcu_read_unlock_bh(); |
138 | return htonl(spi); | 140 | return htonl(spi); |
139 | } | 141 | } |
140 | 142 | ||
@@ -185,14 +187,15 @@ alloc_spi: | |||
185 | if (!x6spi) | 187 | if (!x6spi) |
186 | goto out; | 188 | goto out; |
187 | 189 | ||
190 | INIT_RCU_HEAD(&x6spi->rcu_head); | ||
188 | memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr)); | 191 | memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr)); |
189 | x6spi->spi = spi; | 192 | x6spi->spi = spi; |
190 | atomic_set(&x6spi->refcnt, 1); | 193 | atomic_set(&x6spi->refcnt, 1); |
191 | 194 | ||
192 | hlist_add_head(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]); | 195 | hlist_add_head_rcu(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]); |
193 | 196 | ||
194 | index = xfrm6_tunnel_spi_hash_byaddr(saddr); | 197 | index = xfrm6_tunnel_spi_hash_byaddr(saddr); |
195 | hlist_add_head(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]); | 198 | hlist_add_head_rcu(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]); |
196 | out: | 199 | out: |
197 | return spi; | 200 | return spi; |
198 | } | 201 | } |
@@ -202,26 +205,32 @@ __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) | |||
202 | struct xfrm6_tunnel_spi *x6spi; | 205 | struct xfrm6_tunnel_spi *x6spi; |
203 | u32 spi; | 206 | u32 spi; |
204 | 207 | ||
205 | write_lock_bh(&xfrm6_tunnel_spi_lock); | 208 | spin_lock_bh(&xfrm6_tunnel_spi_lock); |
206 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); | 209 | x6spi = __xfrm6_tunnel_spi_lookup(saddr); |
207 | if (x6spi) { | 210 | if (x6spi) { |
208 | atomic_inc(&x6spi->refcnt); | 211 | atomic_inc(&x6spi->refcnt); |
209 | spi = x6spi->spi; | 212 | spi = x6spi->spi; |
210 | } else | 213 | } else |
211 | spi = __xfrm6_tunnel_alloc_spi(saddr); | 214 | spi = __xfrm6_tunnel_alloc_spi(saddr); |
212 | write_unlock_bh(&xfrm6_tunnel_spi_lock); | 215 | spin_unlock_bh(&xfrm6_tunnel_spi_lock); |
213 | 216 | ||
214 | return htonl(spi); | 217 | return htonl(spi); |
215 | } | 218 | } |
216 | 219 | ||
217 | EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi); | 220 | EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi); |
218 | 221 | ||
222 | static void x6spi_destroy_rcu(struct rcu_head *head) | ||
223 | { | ||
224 | kmem_cache_free(xfrm6_tunnel_spi_kmem, | ||
225 | container_of(head, struct xfrm6_tunnel_spi, rcu_head)); | ||
226 | } | ||
227 | |||
219 | void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) | 228 | void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) |
220 | { | 229 | { |
221 | struct xfrm6_tunnel_spi *x6spi; | 230 | struct xfrm6_tunnel_spi *x6spi; |
222 | struct hlist_node *pos, *n; | 231 | struct hlist_node *pos, *n; |
223 | 232 | ||
224 | write_lock_bh(&xfrm6_tunnel_spi_lock); | 233 | spin_lock_bh(&xfrm6_tunnel_spi_lock); |
225 | 234 | ||
226 | hlist_for_each_entry_safe(x6spi, pos, n, | 235 | hlist_for_each_entry_safe(x6spi, pos, n, |
227 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], | 236 | &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], |
@@ -229,14 +238,14 @@ void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) | |||
229 | { | 238 | { |
230 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { | 239 | if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { |
231 | if (atomic_dec_and_test(&x6spi->refcnt)) { | 240 | if (atomic_dec_and_test(&x6spi->refcnt)) { |
232 | hlist_del(&x6spi->list_byaddr); | 241 | hlist_del_rcu(&x6spi->list_byaddr); |
233 | hlist_del(&x6spi->list_byspi); | 242 | hlist_del_rcu(&x6spi->list_byspi); |
234 | kmem_cache_free(xfrm6_tunnel_spi_kmem, x6spi); | 243 | call_rcu(&x6spi->rcu_head, x6spi_destroy_rcu); |
235 | break; | 244 | break; |
236 | } | 245 | } |
237 | } | 246 | } |
238 | } | 247 | } |
239 | write_unlock_bh(&xfrm6_tunnel_spi_lock); | 248 | spin_unlock_bh(&xfrm6_tunnel_spi_lock); |
240 | } | 249 | } |
241 | 250 | ||
242 | EXPORT_SYMBOL(xfrm6_tunnel_free_spi); | 251 | EXPORT_SYMBOL(xfrm6_tunnel_free_spi); |