diff options
author | Eric Dumazet <edumazet@google.com> | 2015-12-08 10:22:02 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-12-11 19:22:06 -0500 |
commit | d188ba86dd07a72ebebfa22fe9cb0b0572e57740 (patch) | |
tree | 3633c759d096b6fccc50e5641b1dcf8633a1dbf5 /net/xfrm | |
parent | 56f047305dd4b6b61771ac4f523718e4111052a8 (diff) |
xfrm: add rcu protection to sk->sk_policy[]
XFRM can deal with SYNACK messages, sent while listener socket
is not locked. We add proper rcu protection to __xfrm_sk_clone_policy()
and xfrm_sk_policy_lookup()
This might serve as the first step to remove xfrm.xfrm_policy_lock
use in fast path.
Fixes: fa76ce7328b2 ("inet: get rid of central tcp/dccp listener timer")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 37 |
1 files changed, 25 insertions, 12 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index f57a5712cedd..948fa5560de5 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -1221,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, | |||
1221 | struct xfrm_policy *pol; | 1221 | struct xfrm_policy *pol; |
1222 | struct net *net = sock_net(sk); | 1222 | struct net *net = sock_net(sk); |
1223 | 1223 | ||
1224 | rcu_read_lock(); | ||
1224 | read_lock_bh(&net->xfrm.xfrm_policy_lock); | 1225 | read_lock_bh(&net->xfrm.xfrm_policy_lock); |
1225 | if ((pol = sk->sk_policy[dir]) != NULL) { | 1226 | pol = rcu_dereference(sk->sk_policy[dir]); |
1227 | if (pol != NULL) { | ||
1226 | bool match = xfrm_selector_match(&pol->selector, fl, | 1228 | bool match = xfrm_selector_match(&pol->selector, fl, |
1227 | sk->sk_family); | 1229 | sk->sk_family); |
1228 | int err = 0; | 1230 | int err = 0; |
@@ -1246,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, | |||
1246 | } | 1248 | } |
1247 | out: | 1249 | out: |
1248 | read_unlock_bh(&net->xfrm.xfrm_policy_lock); | 1250 | read_unlock_bh(&net->xfrm.xfrm_policy_lock); |
1251 | rcu_read_unlock(); | ||
1249 | return pol; | 1252 | return pol; |
1250 | } | 1253 | } |
1251 | 1254 | ||
@@ -1314,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) | |||
1314 | #endif | 1317 | #endif |
1315 | 1318 | ||
1316 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | 1319 | write_lock_bh(&net->xfrm.xfrm_policy_lock); |
1317 | old_pol = sk->sk_policy[dir]; | 1320 | old_pol = rcu_dereference_protected(sk->sk_policy[dir], |
1318 | sk->sk_policy[dir] = pol; | 1321 | lockdep_is_held(&net->xfrm.xfrm_policy_lock)); |
1319 | if (pol) { | 1322 | if (pol) { |
1320 | pol->curlft.add_time = get_seconds(); | 1323 | pol->curlft.add_time = get_seconds(); |
1321 | pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0); | 1324 | pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0); |
1322 | xfrm_sk_policy_link(pol, dir); | 1325 | xfrm_sk_policy_link(pol, dir); |
1323 | } | 1326 | } |
1327 | rcu_assign_pointer(sk->sk_policy[dir], pol); | ||
1324 | if (old_pol) { | 1328 | if (old_pol) { |
1325 | if (pol) | 1329 | if (pol) |
1326 | xfrm_policy_requeue(old_pol, pol); | 1330 | xfrm_policy_requeue(old_pol, pol); |
@@ -1368,17 +1372,26 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) | |||
1368 | return newp; | 1372 | return newp; |
1369 | } | 1373 | } |
1370 | 1374 | ||
1371 | int __xfrm_sk_clone_policy(struct sock *sk) | 1375 | int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) |
1372 | { | 1376 | { |
1373 | struct xfrm_policy *p0 = sk->sk_policy[0], | 1377 | const struct xfrm_policy *p; |
1374 | *p1 = sk->sk_policy[1]; | 1378 | struct xfrm_policy *np; |
1379 | int i, ret = 0; | ||
1375 | 1380 | ||
1376 | sk->sk_policy[0] = sk->sk_policy[1] = NULL; | 1381 | rcu_read_lock(); |
1377 | if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL) | 1382 | for (i = 0; i < 2; i++) { |
1378 | return -ENOMEM; | 1383 | p = rcu_dereference(osk->sk_policy[i]); |
1379 | if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL) | 1384 | if (p) { |
1380 | return -ENOMEM; | 1385 | np = clone_policy(p, i); |
1381 | return 0; | 1386 | if (unlikely(!np)) { |
1387 | ret = -ENOMEM; | ||
1388 | break; | ||
1389 | } | ||
1390 | rcu_assign_pointer(sk->sk_policy[i], np); | ||
1391 | } | ||
1392 | } | ||
1393 | rcu_read_unlock(); | ||
1394 | return ret; | ||
1382 | } | 1395 | } |
1383 | 1396 | ||
1384 | static int | 1397 | static int |