diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 39 |
1 files changed, 21 insertions, 18 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c5a5165a5927..5ad4d2c4b83c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -42,13 +42,14 @@ static DEFINE_SPINLOCK(xfrm_policy_sk_bundle_lock); | |||
42 | static struct dst_entry *xfrm_policy_sk_bundles; | 42 | static struct dst_entry *xfrm_policy_sk_bundles; |
43 | static DEFINE_RWLOCK(xfrm_policy_lock); | 43 | static DEFINE_RWLOCK(xfrm_policy_lock); |
44 | 44 | ||
45 | static DEFINE_RWLOCK(xfrm_policy_afinfo_lock); | 45 | static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); |
46 | static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO]; | 46 | static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO] |
47 | __read_mostly; | ||
47 | 48 | ||
48 | static struct kmem_cache *xfrm_dst_cache __read_mostly; | 49 | static struct kmem_cache *xfrm_dst_cache __read_mostly; |
49 | 50 | ||
50 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); | 51 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); |
51 | static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); | 52 | static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); |
52 | static void xfrm_init_pmtu(struct dst_entry *dst); | 53 | static void xfrm_init_pmtu(struct dst_entry *dst); |
53 | static int stale_bundle(struct dst_entry *dst); | 54 | static int stale_bundle(struct dst_entry *dst); |
54 | static int xfrm_bundle_ok(struct xfrm_dst *xdst); | 55 | static int xfrm_bundle_ok(struct xfrm_dst *xdst); |
@@ -2418,7 +2419,7 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) | |||
2418 | return -EINVAL; | 2419 | return -EINVAL; |
2419 | if (unlikely(afinfo->family >= NPROTO)) | 2420 | if (unlikely(afinfo->family >= NPROTO)) |
2420 | return -EAFNOSUPPORT; | 2421 | return -EAFNOSUPPORT; |
2421 | write_lock_bh(&xfrm_policy_afinfo_lock); | 2422 | spin_lock_bh(&xfrm_policy_afinfo_lock); |
2422 | if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL)) | 2423 | if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL)) |
2423 | err = -ENOBUFS; | 2424 | err = -ENOBUFS; |
2424 | else { | 2425 | else { |
@@ -2439,9 +2440,9 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) | |||
2439 | dst_ops->neigh_lookup = xfrm_neigh_lookup; | 2440 | dst_ops->neigh_lookup = xfrm_neigh_lookup; |
2440 | if (likely(afinfo->garbage_collect == NULL)) | 2441 | if (likely(afinfo->garbage_collect == NULL)) |
2441 | afinfo->garbage_collect = xfrm_garbage_collect_deferred; | 2442 | afinfo->garbage_collect = xfrm_garbage_collect_deferred; |
2442 | xfrm_policy_afinfo[afinfo->family] = afinfo; | 2443 | rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], afinfo); |
2443 | } | 2444 | } |
2444 | write_unlock_bh(&xfrm_policy_afinfo_lock); | 2445 | spin_unlock_bh(&xfrm_policy_afinfo_lock); |
2445 | 2446 | ||
2446 | rtnl_lock(); | 2447 | rtnl_lock(); |
2447 | for_each_net(net) { | 2448 | for_each_net(net) { |
@@ -2474,13 +2475,14 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo) | |||
2474 | return -EINVAL; | 2475 | return -EINVAL; |
2475 | if (unlikely(afinfo->family >= NPROTO)) | 2476 | if (unlikely(afinfo->family >= NPROTO)) |
2476 | return -EAFNOSUPPORT; | 2477 | return -EAFNOSUPPORT; |
2477 | write_lock_bh(&xfrm_policy_afinfo_lock); | 2478 | spin_lock_bh(&xfrm_policy_afinfo_lock); |
2478 | if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) { | 2479 | if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) { |
2479 | if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo)) | 2480 | if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo)) |
2480 | err = -EINVAL; | 2481 | err = -EINVAL; |
2481 | else { | 2482 | else { |
2482 | struct dst_ops *dst_ops = afinfo->dst_ops; | 2483 | struct dst_ops *dst_ops = afinfo->dst_ops; |
2483 | xfrm_policy_afinfo[afinfo->family] = NULL; | 2484 | rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], |
2485 | NULL); | ||
2484 | dst_ops->kmem_cachep = NULL; | 2486 | dst_ops->kmem_cachep = NULL; |
2485 | dst_ops->check = NULL; | 2487 | dst_ops->check = NULL; |
2486 | dst_ops->negative_advice = NULL; | 2488 | dst_ops->negative_advice = NULL; |
@@ -2488,7 +2490,8 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo) | |||
2488 | afinfo->garbage_collect = NULL; | 2490 | afinfo->garbage_collect = NULL; |
2489 | } | 2491 | } |
2490 | } | 2492 | } |
2491 | write_unlock_bh(&xfrm_policy_afinfo_lock); | 2493 | spin_unlock_bh(&xfrm_policy_afinfo_lock); |
2494 | synchronize_rcu(); | ||
2492 | return err; | 2495 | return err; |
2493 | } | 2496 | } |
2494 | EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); | 2497 | EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); |
@@ -2497,16 +2500,16 @@ static void __net_init xfrm_dst_ops_init(struct net *net) | |||
2497 | { | 2500 | { |
2498 | struct xfrm_policy_afinfo *afinfo; | 2501 | struct xfrm_policy_afinfo *afinfo; |
2499 | 2502 | ||
2500 | read_lock_bh(&xfrm_policy_afinfo_lock); | 2503 | rcu_read_lock_bh(); |
2501 | afinfo = xfrm_policy_afinfo[AF_INET]; | 2504 | afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]); |
2502 | if (afinfo) | 2505 | if (afinfo) |
2503 | net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops; | 2506 | net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops; |
2504 | #if IS_ENABLED(CONFIG_IPV6) | 2507 | #if IS_ENABLED(CONFIG_IPV6) |
2505 | afinfo = xfrm_policy_afinfo[AF_INET6]; | 2508 | afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]); |
2506 | if (afinfo) | 2509 | if (afinfo) |
2507 | net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops; | 2510 | net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops; |
2508 | #endif | 2511 | #endif |
2509 | read_unlock_bh(&xfrm_policy_afinfo_lock); | 2512 | rcu_read_unlock_bh(); |
2510 | } | 2513 | } |
2511 | 2514 | ||
2512 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) | 2515 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) |
@@ -2514,16 +2517,16 @@ static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) | |||
2514 | struct xfrm_policy_afinfo *afinfo; | 2517 | struct xfrm_policy_afinfo *afinfo; |
2515 | if (unlikely(family >= NPROTO)) | 2518 | if (unlikely(family >= NPROTO)) |
2516 | return NULL; | 2519 | return NULL; |
2517 | read_lock(&xfrm_policy_afinfo_lock); | 2520 | rcu_read_lock(); |
2518 | afinfo = xfrm_policy_afinfo[family]; | 2521 | afinfo = rcu_dereference(xfrm_policy_afinfo[family]); |
2519 | if (unlikely(!afinfo)) | 2522 | if (unlikely(!afinfo)) |
2520 | read_unlock(&xfrm_policy_afinfo_lock); | 2523 | rcu_read_unlock(); |
2521 | return afinfo; | 2524 | return afinfo; |
2522 | } | 2525 | } |
2523 | 2526 | ||
2524 | static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) | 2527 | static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) |
2525 | { | 2528 | { |
2526 | read_unlock(&xfrm_policy_afinfo_lock); | 2529 | rcu_read_unlock(); |
2527 | } | 2530 | } |
2528 | 2531 | ||
2529 | static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) | 2532 | static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) |