mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-11 00:08:50 +00:00
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>
This commit is contained in:
parent
56f047305d
commit
d188ba86dd
@ -388,7 +388,7 @@ struct sock {
|
|||||||
struct socket_wq *sk_wq_raw;
|
struct socket_wq *sk_wq_raw;
|
||||||
};
|
};
|
||||||
#ifdef CONFIG_XFRM
|
#ifdef CONFIG_XFRM
|
||||||
struct xfrm_policy *sk_policy[2];
|
struct xfrm_policy __rcu *sk_policy[2];
|
||||||
#endif
|
#endif
|
||||||
struct dst_entry *sk_rx_dst;
|
struct dst_entry *sk_rx_dst;
|
||||||
struct dst_entry __rcu *sk_dst_cache;
|
struct dst_entry __rcu *sk_dst_cache;
|
||||||
|
@ -1142,12 +1142,14 @@ static inline int xfrm6_route_forward(struct sk_buff *skb)
|
|||||||
return xfrm_route_forward(skb, AF_INET6);
|
return xfrm_route_forward(skb, AF_INET6);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __xfrm_sk_clone_policy(struct sock *sk);
|
int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk);
|
||||||
|
|
||||||
static inline int xfrm_sk_clone_policy(struct sock *sk)
|
static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
|
||||||
{
|
{
|
||||||
if (unlikely(sk->sk_policy[0] || sk->sk_policy[1]))
|
sk->sk_policy[0] = NULL;
|
||||||
return __xfrm_sk_clone_policy(sk);
|
sk->sk_policy[1] = NULL;
|
||||||
|
if (unlikely(osk->sk_policy[0] || osk->sk_policy[1]))
|
||||||
|
return __xfrm_sk_clone_policy(sk, osk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1155,12 +1157,16 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir);
|
|||||||
|
|
||||||
static inline void xfrm_sk_free_policy(struct sock *sk)
|
static inline void xfrm_sk_free_policy(struct sock *sk)
|
||||||
{
|
{
|
||||||
if (unlikely(sk->sk_policy[0] != NULL)) {
|
struct xfrm_policy *pol;
|
||||||
xfrm_policy_delete(sk->sk_policy[0], XFRM_POLICY_MAX);
|
|
||||||
|
pol = rcu_dereference_protected(sk->sk_policy[0], 1);
|
||||||
|
if (unlikely(pol != NULL)) {
|
||||||
|
xfrm_policy_delete(pol, XFRM_POLICY_MAX);
|
||||||
sk->sk_policy[0] = NULL;
|
sk->sk_policy[0] = NULL;
|
||||||
}
|
}
|
||||||
if (unlikely(sk->sk_policy[1] != NULL)) {
|
pol = rcu_dereference_protected(sk->sk_policy[1], 1);
|
||||||
xfrm_policy_delete(sk->sk_policy[1], XFRM_POLICY_MAX+1);
|
if (unlikely(pol != NULL)) {
|
||||||
|
xfrm_policy_delete(pol, XFRM_POLICY_MAX+1);
|
||||||
sk->sk_policy[1] = NULL;
|
sk->sk_policy[1] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1170,7 +1176,7 @@ void xfrm_garbage_collect(struct net *net);
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
static inline void xfrm_sk_free_policy(struct sock *sk) {}
|
static inline void xfrm_sk_free_policy(struct sock *sk) {}
|
||||||
static inline int xfrm_sk_clone_policy(struct sock *sk) { return 0; }
|
static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) { return 0; }
|
||||||
static inline int xfrm6_route_forward(struct sk_buff *skb) { return 1; }
|
static inline int xfrm6_route_forward(struct sk_buff *skb) { return 1; }
|
||||||
static inline int xfrm4_route_forward(struct sk_buff *skb) { return 1; }
|
static inline int xfrm4_route_forward(struct sk_buff *skb) { return 1; }
|
||||||
static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
|
static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
|
||||||
|
@ -1550,7 +1550,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
|
|||||||
*/
|
*/
|
||||||
is_charged = sk_filter_charge(newsk, filter);
|
is_charged = sk_filter_charge(newsk, filter);
|
||||||
|
|
||||||
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) {
|
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
|
||||||
/* It is still raw copy of parent, so invalidate
|
/* It is still raw copy of parent, so invalidate
|
||||||
* destructor and make plain sk_free() */
|
* destructor and make plain sk_free() */
|
||||||
newsk->sk_destruct = NULL;
|
newsk->sk_destruct = NULL;
|
||||||
|
@ -1221,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|||||||
struct xfrm_policy *pol;
|
struct xfrm_policy *pol;
|
||||||
struct net *net = sock_net(sk);
|
struct net *net = sock_net(sk);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
read_lock_bh(&net->xfrm.xfrm_policy_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,
|
bool match = xfrm_selector_match(&pol->selector, fl,
|
||||||
sk->sk_family);
|
sk->sk_family);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@ -1246,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
|||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
read_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
read_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
return pol;
|
return pol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1314,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
write_lock_bh(&net->xfrm.xfrm_policy_lock);
|
write_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
old_pol = sk->sk_policy[dir];
|
old_pol = rcu_dereference_protected(sk->sk_policy[dir],
|
||||||
sk->sk_policy[dir] = pol;
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock));
|
||||||
if (pol) {
|
if (pol) {
|
||||||
pol->curlft.add_time = get_seconds();
|
pol->curlft.add_time = get_seconds();
|
||||||
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
|
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
|
||||||
xfrm_sk_policy_link(pol, dir);
|
xfrm_sk_policy_link(pol, dir);
|
||||||
}
|
}
|
||||||
|
rcu_assign_pointer(sk->sk_policy[dir], pol);
|
||||||
if (old_pol) {
|
if (old_pol) {
|
||||||
if (pol)
|
if (pol)
|
||||||
xfrm_policy_requeue(old_pol, 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;
|
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],
|
const struct xfrm_policy *p;
|
||||||
*p1 = sk->sk_policy[1];
|
struct xfrm_policy *np;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
sk->sk_policy[0] = sk->sk_policy[1] = NULL;
|
rcu_read_lock();
|
||||||
if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL)
|
for (i = 0; i < 2; i++) {
|
||||||
return -ENOMEM;
|
p = rcu_dereference(osk->sk_policy[i]);
|
||||||
if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL)
|
if (p) {
|
||||||
return -ENOMEM;
|
np = clone_policy(p, i);
|
||||||
return 0;
|
if (unlikely(!np)) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rcu_assign_pointer(sk->sk_policy[i], np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
Loading…
x
Reference in New Issue
Block a user