diff options
author | Florian Westphal <fw@strlen.de> | 2016-08-11 09:17:54 -0400 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2016-08-12 02:07:11 -0400 |
commit | 30846090a746edfdb230deadd638cfa96f7b8c91 (patch) | |
tree | 3f6aeebcccfc90ad302ea2ce6c567f55d1e52131 /net/xfrm | |
parent | e1e551bc56302b80ff930c966f9985095fb1b70d (diff) |
xfrm: policy: add sequence count to sync with hash resize
Once xfrm_policy_lookup_bytype doesn't grab xfrm_policy_lock anymore its
possible for a hash resize to occur in parallel.
Use sequence counter to block lookup in case a resize is in
progress and to also re-lookup in case hash table was altered
in the mean time (might cause use to not find the best-match).
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 4a8d90a88c83..576d90321068 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -49,6 +49,7 @@ static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO] | |||
49 | __read_mostly; | 49 | __read_mostly; |
50 | 50 | ||
51 | static struct kmem_cache *xfrm_dst_cache __read_mostly; | 51 | static struct kmem_cache *xfrm_dst_cache __read_mostly; |
52 | static __read_mostly seqcount_t xfrm_policy_hash_generation; | ||
52 | 53 | ||
53 | static void xfrm_init_pmtu(struct dst_entry *dst); | 54 | static void xfrm_init_pmtu(struct dst_entry *dst); |
54 | static int stale_bundle(struct dst_entry *dst); | 55 | static int stale_bundle(struct dst_entry *dst); |
@@ -479,6 +480,10 @@ static void xfrm_bydst_resize(struct net *net, int dir) | |||
479 | return; | 480 | return; |
480 | 481 | ||
481 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | 482 | write_lock_bh(&net->xfrm.xfrm_policy_lock); |
483 | write_seqcount_begin(&xfrm_policy_hash_generation); | ||
484 | |||
485 | odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, | ||
486 | lockdep_is_held(&net->xfrm.xfrm_policy_lock)); | ||
482 | 487 | ||
483 | odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, | 488 | odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, |
484 | lockdep_is_held(&net->xfrm.xfrm_policy_lock)); | 489 | lockdep_is_held(&net->xfrm.xfrm_policy_lock)); |
@@ -489,6 +494,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) | |||
489 | rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst); | 494 | rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst); |
490 | net->xfrm.policy_bydst[dir].hmask = nhashmask; | 495 | net->xfrm.policy_bydst[dir].hmask = nhashmask; |
491 | 496 | ||
497 | write_seqcount_end(&xfrm_policy_hash_generation); | ||
492 | write_unlock_bh(&net->xfrm.xfrm_policy_lock); | 498 | write_unlock_bh(&net->xfrm.xfrm_policy_lock); |
493 | 499 | ||
494 | synchronize_rcu(); | 500 | synchronize_rcu(); |
@@ -1104,7 +1110,8 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, | |||
1104 | struct xfrm_policy *pol, *ret; | 1110 | struct xfrm_policy *pol, *ret; |
1105 | const xfrm_address_t *daddr, *saddr; | 1111 | const xfrm_address_t *daddr, *saddr; |
1106 | struct hlist_head *chain; | 1112 | struct hlist_head *chain; |
1107 | u32 priority = ~0U; | 1113 | unsigned int sequence; |
1114 | u32 priority; | ||
1108 | 1115 | ||
1109 | daddr = xfrm_flowi_daddr(fl, family); | 1116 | daddr = xfrm_flowi_daddr(fl, family); |
1110 | saddr = xfrm_flowi_saddr(fl, family); | 1117 | saddr = xfrm_flowi_saddr(fl, family); |
@@ -1112,7 +1119,13 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, | |||
1112 | return NULL; | 1119 | return NULL; |
1113 | 1120 | ||
1114 | read_lock_bh(&net->xfrm.xfrm_policy_lock); | 1121 | read_lock_bh(&net->xfrm.xfrm_policy_lock); |
1115 | chain = policy_hash_direct(net, daddr, saddr, family, dir); | 1122 | retry: |
1123 | do { | ||
1124 | sequence = read_seqcount_begin(&xfrm_policy_hash_generation); | ||
1125 | chain = policy_hash_direct(net, daddr, saddr, family, dir); | ||
1126 | } while (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)); | ||
1127 | |||
1128 | priority = ~0U; | ||
1116 | ret = NULL; | 1129 | ret = NULL; |
1117 | hlist_for_each_entry_rcu(pol, chain, bydst) { | 1130 | hlist_for_each_entry_rcu(pol, chain, bydst) { |
1118 | err = xfrm_policy_match(pol, fl, type, family, dir); | 1131 | err = xfrm_policy_match(pol, fl, type, family, dir); |
@@ -1148,6 +1161,9 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, | |||
1148 | } | 1161 | } |
1149 | } | 1162 | } |
1150 | 1163 | ||
1164 | if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)) | ||
1165 | goto retry; | ||
1166 | |||
1151 | xfrm_pol_hold(ret); | 1167 | xfrm_pol_hold(ret); |
1152 | fail: | 1168 | fail: |
1153 | read_unlock_bh(&net->xfrm.xfrm_policy_lock); | 1169 | read_unlock_bh(&net->xfrm.xfrm_policy_lock); |
@@ -3090,6 +3106,7 @@ static struct pernet_operations __net_initdata xfrm_net_ops = { | |||
3090 | void __init xfrm_init(void) | 3106 | void __init xfrm_init(void) |
3091 | { | 3107 | { |
3092 | register_pernet_subsys(&xfrm_net_ops); | 3108 | register_pernet_subsys(&xfrm_net_ops); |
3109 | seqcount_init(&xfrm_policy_hash_generation); | ||
3093 | xfrm_input_init(); | 3110 | xfrm_input_init(); |
3094 | } | 3111 | } |
3095 | 3112 | ||