diff options
-rw-r--r-- | include/net/ipip.h | 1 | ||||
-rw-r--r-- | net/ipv6/sit.c | 73 |
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 | ||
243 | static 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 | |||
243 | static struct ip_tunnel_prl_entry * | 250 | static 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 | } |
305 | out: | 312 | out: |
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++; | ||
352 | update: | ||
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); | ||
355 | out: | 364 | out: |
356 | write_unlock(&ipip6_lock); | 365 | spin_unlock(&ipip6_prl_lock); |
357 | return err; | 366 | return err; |
358 | } | 367 | } |
359 | 368 | ||
369 | static void prl_entry_destroy_rcu(struct rcu_head *head) | ||
370 | { | ||
371 | kfree(container_of(head, struct ip_tunnel_prl_entry, rcu_head)); | ||
372 | } | ||
373 | |||
374 | static 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 | |||
360 | static int | 386 | static int |
361 | ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | 387 | ipip6_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 | } |
387 | out: | 413 | out: |
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 | ||
1197 | static int __init sit_init(void) | 1224 | static int __init sit_init(void) |