mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00
Merge branch 'ipsec: fix splat due to ipcomp fallback tunnel'
Sabrina Dubroca says:
====================
IPcomp tunnel states have an associated fallback tunnel, a keep a
reference on the corresponding xfrm_state, to allow deleting that
extra state when it's not needed anymore. These states cause issues
during netns deletion.
Commit f75a2804da
("xfrm: destroy xfrm_state synchronously on net
exit path") tried to address these problems but doesn't fully solve
them, and slowed down netns deletion by adding one synchronize_rcu per
deleted state.
The first patch solves the problem by moving the fallback state
deletion earlier (when we delete the user state, rather than at
destruction), then we can revert the previous fix.
====================
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
commit
28712d6ed3
|
@ -441,7 +441,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
|
|||
int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
|
||||
|
||||
void xfrm_flush_gc(void);
|
||||
void xfrm_state_delete_tunnel(struct xfrm_state *x);
|
||||
|
||||
struct xfrm_type {
|
||||
struct module *owner;
|
||||
|
@ -916,7 +915,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
|
|||
xfrm_pol_put(pols[i]);
|
||||
}
|
||||
|
||||
void __xfrm_state_destroy(struct xfrm_state *, bool);
|
||||
void __xfrm_state_destroy(struct xfrm_state *);
|
||||
|
||||
static inline void __xfrm_state_put(struct xfrm_state *x)
|
||||
{
|
||||
|
@ -926,13 +925,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
|
|||
static inline void xfrm_state_put(struct xfrm_state *x)
|
||||
{
|
||||
if (refcount_dec_and_test(&x->refcnt))
|
||||
__xfrm_state_destroy(x, false);
|
||||
}
|
||||
|
||||
static inline void xfrm_state_put_sync(struct xfrm_state *x)
|
||||
{
|
||||
if (refcount_dec_and_test(&x->refcnt))
|
||||
__xfrm_state_destroy(x, true);
|
||||
__xfrm_state_destroy(x);
|
||||
}
|
||||
|
||||
static inline void xfrm_state_hold(struct xfrm_state *x)
|
||||
|
@ -1770,7 +1763,7 @@ struct xfrmk_spdinfo {
|
|||
|
||||
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num);
|
||||
int xfrm_state_delete(struct xfrm_state *x);
|
||||
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
|
||||
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
|
||||
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
|
||||
int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
|
||||
bool task_valid);
|
||||
|
|
|
@ -54,6 +54,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
|
|||
}
|
||||
|
||||
/* We always hold one tunnel user reference to indicate a tunnel */
|
||||
static struct lock_class_key xfrm_state_lock_key;
|
||||
static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
|
||||
{
|
||||
struct net *net = xs_net(x);
|
||||
|
@ -62,6 +63,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
|
|||
t = xfrm_state_alloc(net);
|
||||
if (!t)
|
||||
goto out;
|
||||
lockdep_set_class(&t->lock, &xfrm_state_lock_key);
|
||||
|
||||
t->id.proto = IPPROTO_IPIP;
|
||||
t->id.spi = x->props.saddr.a4;
|
||||
|
|
|
@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct lock_class_key xfrm_state_lock_key;
|
||||
static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
|
||||
{
|
||||
struct net *net = xs_net(x);
|
||||
|
@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
|
|||
t = xfrm_state_alloc(net);
|
||||
if (!t)
|
||||
goto out;
|
||||
lockdep_set_class(&t->lock, &xfrm_state_lock_key);
|
||||
|
||||
t->id.proto = IPPROTO_IPV6;
|
||||
t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
|
||||
|
|
|
@ -334,8 +334,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
|
|||
struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
|
||||
unsigned int i;
|
||||
|
||||
xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
|
||||
xfrm_flush_gc();
|
||||
xfrm_state_flush(net, 0, false, true);
|
||||
|
||||
for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
|
||||
WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
|
||||
|
|
|
@ -1766,7 +1766,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
|
|||
if (proto == 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = xfrm_state_flush(net, proto, true, false);
|
||||
err = xfrm_state_flush(net, proto, true);
|
||||
err2 = unicast_flush_resp(sk, hdr);
|
||||
if (err || err2) {
|
||||
if (err == -ESRCH) /* empty table - go quietly */
|
||||
|
|
|
@ -313,7 +313,6 @@ void ipcomp_destroy(struct xfrm_state *x)
|
|||
struct ipcomp_data *ipcd = x->data;
|
||||
if (!ipcd)
|
||||
return;
|
||||
xfrm_state_delete_tunnel(x);
|
||||
ipcomp_free_data(ipcd);
|
||||
kfree(ipcd);
|
||||
}
|
||||
|
|
|
@ -592,7 +592,7 @@ void xfrm_state_free(struct xfrm_state *x)
|
|||
}
|
||||
EXPORT_SYMBOL(xfrm_state_free);
|
||||
|
||||
static void ___xfrm_state_destroy(struct xfrm_state *x)
|
||||
static void xfrm_state_gc_destroy(struct xfrm_state *x)
|
||||
{
|
||||
if (x->mode_cbs && x->mode_cbs->destroy_state)
|
||||
x->mode_cbs->destroy_state(x);
|
||||
|
@ -631,7 +631,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
|
|||
synchronize_rcu();
|
||||
|
||||
hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
|
||||
___xfrm_state_destroy(x);
|
||||
xfrm_state_gc_destroy(x);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
|
||||
|
@ -795,22 +795,18 @@ void xfrm_dev_state_free(struct xfrm_state *x)
|
|||
}
|
||||
#endif
|
||||
|
||||
void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
|
||||
void __xfrm_state_destroy(struct xfrm_state *x)
|
||||
{
|
||||
WARN_ON(x->km.state != XFRM_STATE_DEAD);
|
||||
|
||||
if (sync) {
|
||||
synchronize_rcu();
|
||||
___xfrm_state_destroy(x);
|
||||
} else {
|
||||
spin_lock_bh(&xfrm_state_gc_lock);
|
||||
hlist_add_head(&x->gclist, &xfrm_state_gc_list);
|
||||
spin_unlock_bh(&xfrm_state_gc_lock);
|
||||
schedule_work(&xfrm_state_gc_work);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(__xfrm_state_destroy);
|
||||
|
||||
static void xfrm_state_delete_tunnel(struct xfrm_state *x);
|
||||
int __xfrm_state_delete(struct xfrm_state *x)
|
||||
{
|
||||
struct net *net = xs_net(x);
|
||||
|
@ -838,6 +834,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
|
|||
|
||||
xfrm_dev_state_delete(x);
|
||||
|
||||
xfrm_state_delete_tunnel(x);
|
||||
|
||||
/* All xfrm_state objects are created by xfrm_state_alloc.
|
||||
* The xfrm_state_alloc call gives a reference, and that
|
||||
* is what we are dropping here.
|
||||
|
@ -919,7 +917,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
|
|||
}
|
||||
#endif
|
||||
|
||||
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
|
||||
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
|
||||
{
|
||||
int i, err = 0, cnt = 0;
|
||||
|
||||
|
@ -941,9 +939,6 @@ restart:
|
|||
err = xfrm_state_delete(x);
|
||||
xfrm_audit_state_delete(x, err ? 0 : 1,
|
||||
task_valid);
|
||||
if (sync)
|
||||
xfrm_state_put_sync(x);
|
||||
else
|
||||
xfrm_state_put(x);
|
||||
if (!err)
|
||||
cnt++;
|
||||
|
@ -3068,20 +3063,17 @@ void xfrm_flush_gc(void)
|
|||
}
|
||||
EXPORT_SYMBOL(xfrm_flush_gc);
|
||||
|
||||
/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
|
||||
void xfrm_state_delete_tunnel(struct xfrm_state *x)
|
||||
static void xfrm_state_delete_tunnel(struct xfrm_state *x)
|
||||
{
|
||||
if (x->tunnel) {
|
||||
struct xfrm_state *t = x->tunnel;
|
||||
|
||||
if (atomic_read(&t->tunnel_users) == 2)
|
||||
if (atomic_dec_return(&t->tunnel_users) == 1)
|
||||
xfrm_state_delete(t);
|
||||
atomic_dec(&t->tunnel_users);
|
||||
xfrm_state_put_sync(t);
|
||||
xfrm_state_put(t);
|
||||
x->tunnel = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_state_delete_tunnel);
|
||||
|
||||
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
|
||||
{
|
||||
|
@ -3286,8 +3278,8 @@ void xfrm_state_fini(struct net *net)
|
|||
unsigned int sz;
|
||||
|
||||
flush_work(&net->xfrm.state_hash_work);
|
||||
xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
|
||||
flush_work(&xfrm_state_gc_work);
|
||||
xfrm_state_flush(net, 0, false, true);
|
||||
|
||||
WARN_ON(!list_empty(&net->xfrm.state_all));
|
||||
|
||||
|
|
|
@ -2635,7 +2635,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
struct xfrm_usersa_flush *p = nlmsg_data(nlh);
|
||||
int err;
|
||||
|
||||
err = xfrm_state_flush(net, p->proto, true, false);
|
||||
err = xfrm_state_flush(net, p->proto, true);
|
||||
if (err) {
|
||||
if (err == -ESRCH) /* empty table */
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue
Block a user