aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/ipip.h1
-rw-r--r--net/ipv6/sit.c73
2 files changed, 51 insertions, 23 deletions
diff --git a/include/net/ipip.h b/include/net/ipip.h
index 86f1c8bd040c..b3db2fd6e61c 100644
--- a/include/net/ipip.h
+++ b/include/net/ipip.h
@@ -45,6 +45,7 @@ struct ip_tunnel_prl_entry
45 struct ip_tunnel_prl_entry *next; 45 struct ip_tunnel_prl_entry *next;
46 __be32 addr; 46 __be32 addr;
47 u16 flags; 47 u16 flags;
48 struct rcu_head rcu_head;
48}; 49};
49 50
50#define IPTUNNEL_XMIT() do { \ 51#define IPTUNNEL_XMIT() do { \
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 510d31f3cb96..8cdcc2ad048c 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -240,15 +240,22 @@ failed:
240 return NULL; 240 return NULL;
241} 241}
242 242
243static DEFINE_SPINLOCK(ipip6_prl_lock);
244
245#define for_each_prl_rcu(start) \
246 for (prl = rcu_dereference(start); \
247 prl; \
248 prl = rcu_dereference(prl->next))
249
243static struct ip_tunnel_prl_entry * 250static struct ip_tunnel_prl_entry *
244__ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr) 251__ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
245{ 252{
246 struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL; 253 struct ip_tunnel_prl_entry *prl;
247 254
248 for (p = t->prl; p; p = p->next) 255 for_each_prl_rcu(t->prl)
249 if (p->addr == addr) 256 if (prl->addr == addr)
250 break; 257 break;
251 return p; 258 return prl;
252 259
253} 260}
254 261
@@ -273,7 +280,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
273 kcalloc(cmax, sizeof(*kp), GFP_KERNEL) : 280 kcalloc(cmax, sizeof(*kp), GFP_KERNEL) :
274 NULL; 281 NULL;
275 282
276 read_lock(&ipip6_lock); 283 rcu_read_lock();
277 284
278 ca = t->prl_count < cmax ? t->prl_count : cmax; 285 ca = t->prl_count < cmax ? t->prl_count : cmax;
279 286
@@ -291,7 +298,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
291 } 298 }
292 299
293 c = 0; 300 c = 0;
294 for (prl = t->prl; prl; prl = prl->next) { 301 for_each_prl_rcu(t->prl) {
295 if (c >= cmax) 302 if (c >= cmax)
296 break; 303 break;
297 if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr) 304 if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr)
@@ -303,7 +310,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
303 break; 310 break;
304 } 311 }
305out: 312out:
306 read_unlock(&ipip6_lock); 313 rcu_read_unlock();
307 314
308 len = sizeof(*kp) * c; 315 len = sizeof(*kp) * c;
309 ret = 0; 316 ret = 0;
@@ -324,12 +331,14 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
324 if (a->addr == htonl(INADDR_ANY)) 331 if (a->addr == htonl(INADDR_ANY))
325 return -EINVAL; 332 return -EINVAL;
326 333
327 write_lock(&ipip6_lock); 334 spin_lock(&ipip6_prl_lock);
328 335
329 for (p = t->prl; p; p = p->next) { 336 for (p = t->prl; p; p = p->next) {
330 if (p->addr == a->addr) { 337 if (p->addr == a->addr) {
331 if (chg) 338 if (chg) {
332 goto update; 339 p->flags = a->flags;
340 goto out;
341 }
333 err = -EEXIST; 342 err = -EEXIST;
334 goto out; 343 goto out;
335 } 344 }
@@ -346,46 +355,63 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
346 goto out; 355 goto out;
347 } 356 }
348 357
358 INIT_RCU_HEAD(&p->rcu_head);
349 p->next = t->prl; 359 p->next = t->prl;
350 t->prl = p;
351 t->prl_count++;
352update:
353 p->addr = a->addr; 360 p->addr = a->addr;
354 p->flags = a->flags; 361 p->flags = a->flags;
362 t->prl_count++;
363 rcu_assign_pointer(t->prl, p);
355out: 364out:
356 write_unlock(&ipip6_lock); 365 spin_unlock(&ipip6_prl_lock);
357 return err; 366 return err;
358} 367}
359 368
369static void prl_entry_destroy_rcu(struct rcu_head *head)
370{
371 kfree(container_of(head, struct ip_tunnel_prl_entry, rcu_head));
372}
373
374static void prl_list_destroy_rcu(struct rcu_head *head)
375{
376 struct ip_tunnel_prl_entry *p, *n;
377
378 p = container_of(head, struct ip_tunnel_prl_entry, rcu_head);
379 do {
380 n = p->next;
381 kfree(p);
382 p = n;
383 } while (p);
384}
385
360static int 386static int
361ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) 387ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
362{ 388{
363 struct ip_tunnel_prl_entry *x, **p; 389 struct ip_tunnel_prl_entry *x, **p;
364 int err = 0; 390 int err = 0;
365 391
366 write_lock(&ipip6_lock); 392 spin_lock(&ipip6_prl_lock);
367 393
368 if (a && a->addr != htonl(INADDR_ANY)) { 394 if (a && a->addr != htonl(INADDR_ANY)) {
369 for (p = &t->prl; *p; p = &(*p)->next) { 395 for (p = &t->prl; *p; p = &(*p)->next) {
370 if ((*p)->addr == a->addr) { 396 if ((*p)->addr == a->addr) {
371 x = *p; 397 x = *p;
372 *p = x->next; 398 *p = x->next;
373 kfree(x); 399 call_rcu(&x->rcu_head, prl_entry_destroy_rcu);
374 t->prl_count--; 400 t->prl_count--;
375 goto out; 401 goto out;
376 } 402 }
377 } 403 }
378 err = -ENXIO; 404 err = -ENXIO;
379 } else { 405 } else {
380 while (t->prl) { 406 if (t->prl) {
407 t->prl_count = 0;
381 x = t->prl; 408 x = t->prl;
382 t->prl = t->prl->next; 409 call_rcu(&x->rcu_head, prl_list_destroy_rcu);
383 kfree(x); 410 t->prl = NULL;
384 t->prl_count--;
385 } 411 }
386 } 412 }
387out: 413out:
388 write_unlock(&ipip6_lock); 414 spin_unlock(&ipip6_prl_lock);
389 return err; 415 return err;
390} 416}
391 417
@@ -395,7 +421,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
395 struct ip_tunnel_prl_entry *p; 421 struct ip_tunnel_prl_entry *p;
396 int ok = 1; 422 int ok = 1;
397 423
398 read_lock(&ipip6_lock); 424 rcu_read_lock();
399 p = __ipip6_tunnel_locate_prl(t, iph->saddr); 425 p = __ipip6_tunnel_locate_prl(t, iph->saddr);
400 if (p) { 426 if (p) {
401 if (p->flags & PRL_DEFAULT) 427 if (p->flags & PRL_DEFAULT)
@@ -411,7 +437,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
411 else 437 else
412 ok = 0; 438 ok = 0;
413 } 439 }
414 read_unlock(&ipip6_lock); 440 rcu_read_unlock();
415 return ok; 441 return ok;
416} 442}
417 443
@@ -1192,6 +1218,7 @@ static void __exit sit_cleanup(void)
1192 xfrm4_tunnel_deregister(&sit_handler, AF_INET6); 1218 xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
1193 1219
1194 unregister_pernet_gen_device(sit_net_id, &sit_net_ops); 1220 unregister_pernet_gen_device(sit_net_id, &sit_net_ops);
1221 rcu_barrier(); /* Wait for completion of call_rcu()'s */
1195} 1222}
1196 1223
1197static int __init sit_init(void) 1224static int __init sit_init(void)