aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/xfrm6_tunnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/xfrm6_tunnel.c')
-rw-r--r--net/ipv6/xfrm6_tunnel.c47
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 */
38struct xfrm6_tunnel_spi { 38struct 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
46static DEFINE_RWLOCK(xfrm6_tunnel_spi_lock); 47static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock);
47 48
48static u32 xfrm6_tunnel_spi; 49static 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]);
196out: 199out:
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
217EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi); 220EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi);
218 221
222static 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
219void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) 228void 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
242EXPORT_SYMBOL(xfrm6_tunnel_free_spi); 251EXPORT_SYMBOL(xfrm6_tunnel_free_spi);