diff options
author | Florian Westphal <fw@strlen.de> | 2016-08-09 06:16:05 -0400 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2016-08-10 05:23:23 -0400 |
commit | 02efdff7e209859c2755ebe93b3bd0e3d40123ab (patch) | |
tree | d7008910ee8bc52787e18eda78733c31ea44ba39 /net/xfrm | |
parent | ae3fb6d32140e5c5b491892105ca89066171d217 (diff) |
xfrm: state: use atomic_inc_not_zero to increment refcount
Once xfrm_state_lookup_byaddr no longer acquires the state lock another
cpu might be freeing the state entry at the same time.
To detect this we use atomic_inc_not_zero, we then signal -EAGAIN to
caller in case our result was stale.
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_state.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 904ab4d4ac05..84c1db6254d5 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
@@ -37,6 +37,11 @@ | |||
37 | 37 | ||
38 | static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; | 38 | static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; |
39 | 39 | ||
40 | static inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x) | ||
41 | { | ||
42 | return atomic_inc_not_zero(&x->refcnt); | ||
43 | } | ||
44 | |||
40 | static inline unsigned int xfrm_dst_hash(struct net *net, | 45 | static inline unsigned int xfrm_dst_hash(struct net *net, |
41 | const xfrm_address_t *daddr, | 46 | const xfrm_address_t *daddr, |
42 | const xfrm_address_t *saddr, | 47 | const xfrm_address_t *saddr, |
@@ -668,7 +673,8 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, | |||
668 | 673 | ||
669 | if ((mark & x->mark.m) != x->mark.v) | 674 | if ((mark & x->mark.m) != x->mark.v) |
670 | continue; | 675 | continue; |
671 | xfrm_state_hold(x); | 676 | if (!xfrm_state_hold_rcu(x)) |
677 | continue; | ||
672 | return x; | 678 | return x; |
673 | } | 679 | } |
674 | 680 | ||
@@ -692,7 +698,8 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark, | |||
692 | 698 | ||
693 | if ((mark & x->mark.m) != x->mark.v) | 699 | if ((mark & x->mark.m) != x->mark.v) |
694 | continue; | 700 | continue; |
695 | xfrm_state_hold(x); | 701 | if (!xfrm_state_hold_rcu(x)) |
702 | continue; | ||
696 | return x; | 703 | return x; |
697 | } | 704 | } |
698 | 705 | ||
@@ -871,10 +878,14 @@ found: | |||
871 | } | 878 | } |
872 | } | 879 | } |
873 | out: | 880 | out: |
874 | if (x) | 881 | if (x) { |
875 | xfrm_state_hold(x); | 882 | if (!xfrm_state_hold_rcu(x)) { |
876 | else | 883 | *err = -EAGAIN; |
884 | x = NULL; | ||
885 | } | ||
886 | } else { | ||
877 | *err = acquire_in_progress ? -EAGAIN : error; | 887 | *err = acquire_in_progress ? -EAGAIN : error; |
888 | } | ||
878 | spin_unlock_bh(&net->xfrm.xfrm_state_lock); | 889 | spin_unlock_bh(&net->xfrm.xfrm_state_lock); |
879 | if (to_put) | 890 | if (to_put) |
880 | xfrm_state_put(to_put); | 891 | xfrm_state_put(to_put); |