aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2016-08-11 09:17:54 -0400
committerSteffen Klassert <steffen.klassert@secunet.com>2016-08-12 02:07:11 -0400
commit30846090a746edfdb230deadd638cfa96f7b8c91 (patch)
tree3f6aeebcccfc90ad302ea2ce6c567f55d1e52131 /net/xfrm
parente1e551bc56302b80ff930c966f9985095fb1b70d (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.c21
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
51static struct kmem_cache *xfrm_dst_cache __read_mostly; 51static struct kmem_cache *xfrm_dst_cache __read_mostly;
52static __read_mostly seqcount_t xfrm_policy_hash_generation;
52 53
53static void xfrm_init_pmtu(struct dst_entry *dst); 54static void xfrm_init_pmtu(struct dst_entry *dst);
54static int stale_bundle(struct dst_entry *dst); 55static 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);
1152fail: 1168fail:
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 = {
3090void __init xfrm_init(void) 3106void __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