|
@@ -1221,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|
|
struct xfrm_policy *pol;
|
|
|
struct net *net = sock_net(sk);
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
read_lock_bh(&net->xfrm.xfrm_policy_lock);
|
|
|
- if ((pol = sk->sk_policy[dir]) != NULL) {
|
|
|
+ pol = rcu_dereference(sk->sk_policy[dir]);
|
|
|
+ if (pol != NULL) {
|
|
|
bool match = xfrm_selector_match(&pol->selector, fl,
|
|
|
sk->sk_family);
|
|
|
int err = 0;
|
|
@@ -1246,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|
|
}
|
|
|
out:
|
|
|
read_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
|
|
+ rcu_read_unlock();
|
|
|
return pol;
|
|
|
}
|
|
|
|
|
@@ -1314,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
|
|
|
#endif
|
|
|
|
|
|
write_lock_bh(&net->xfrm.xfrm_policy_lock);
|
|
|
- old_pol = sk->sk_policy[dir];
|
|
|
- sk->sk_policy[dir] = pol;
|
|
|
+ old_pol = rcu_dereference_protected(sk->sk_policy[dir],
|
|
|
+ lockdep_is_held(&net->xfrm.xfrm_policy_lock));
|
|
|
if (pol) {
|
|
|
pol->curlft.add_time = get_seconds();
|
|
|
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
|
|
|
xfrm_sk_policy_link(pol, dir);
|
|
|
}
|
|
|
+ rcu_assign_pointer(sk->sk_policy[dir], pol);
|
|
|
if (old_pol) {
|
|
|
if (pol)
|
|
|
xfrm_policy_requeue(old_pol, pol);
|
|
@@ -1368,17 +1372,26 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
|
|
|
return newp;
|
|
|
}
|
|
|
|
|
|
-int __xfrm_sk_clone_policy(struct sock *sk)
|
|
|
+int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
|
|
|
{
|
|
|
- struct xfrm_policy *p0 = sk->sk_policy[0],
|
|
|
- *p1 = sk->sk_policy[1];
|
|
|
+ const struct xfrm_policy *p;
|
|
|
+ struct xfrm_policy *np;
|
|
|
+ int i, ret = 0;
|
|
|
|
|
|
- sk->sk_policy[0] = sk->sk_policy[1] = NULL;
|
|
|
- if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- return 0;
|
|
|
+ rcu_read_lock();
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
+ p = rcu_dereference(osk->sk_policy[i]);
|
|
|
+ if (p) {
|
|
|
+ np = clone_policy(p, i);
|
|
|
+ if (unlikely(!np)) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_assign_pointer(sk->sk_policy[i], np);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int
|