Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 623859ae authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-sched-race-fix'



Cong Wang says:

====================
net_sched: close the race between call_rcu() and cleanup_net()

This patchset tries to fix the race between call_rcu() and
cleanup_net() again. Without holding the netns refcnt the
tc_action_net_exit() in netns workqueue could be called before
filter destroy works in tc filter workqueue. This patchset
moves the netns refcnt from tc actions to tcf_exts, without
breaking per-netns tc actions.

Patch 1 reverts the previous fix, patch 2 introduces two new
API's to help to address the bug and the rest patches switch
to the new API's. Please see each patch for details.

I was not able to reproduce this bug, but now after adding
some delay in filter destroy work I manage to trigger the
crash. After this patchset, the crash is not reproducible
any more and the debugging printk's show the order is expected
too.
====================

Fixes: ddf97ccd ("net_sched: add network namespace support for tc actions")
Reported-by: default avatarLucas Bates <lucasb@mojatatu.com>
Cc: Lucas Bates <lucasb@mojatatu.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8f562462 35c55fc1
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@
struct tcf_idrinfo {
	spinlock_t	lock;
	struct idr	action_idr;
	struct net	*net;
};

struct tc_action_ops;
@@ -106,7 +105,7 @@ struct tc_action_net {

static inline
int tc_action_net_init(struct tc_action_net *tn,
		       const struct tc_action_ops *ops, struct net *net)
		       const struct tc_action_ops *ops)
{
	int err = 0;

@@ -114,7 +113,6 @@ int tc_action_net_init(struct tc_action_net *tn,
	if (!tn->idrinfo)
		return -ENOMEM;
	tn->ops = ops;
	tn->idrinfo->net = net;
	spin_lock_init(&tn->idrinfo->lock);
	idr_init(&tn->idrinfo->action_idr);
	return err;
+24 −0
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ struct tcf_exts {
	__u32	type; /* for backward compat(TCA_OLD_COMPAT) */
	int nr_actions;
	struct tc_action **actions;
	struct net *net;
#endif
	/* Map to export classifier specific extension TLV types to the
	 * generic extensions API. Unsupported extensions must be set to 0.
@@ -107,6 +108,7 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police)
#ifdef CONFIG_NET_CLS_ACT
	exts->type = 0;
	exts->nr_actions = 0;
	exts->net = NULL;
	exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
				GFP_KERNEL);
	if (!exts->actions)
@@ -117,6 +119,28 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police)
	return 0;
}

/* Return false if the netns is being destroyed in cleanup_net(). Callers
 * need to do cleanup synchronously in this case, otherwise may race with
 * tc_action_net_exit(). Return true for other cases.
 */
static inline bool tcf_exts_get_net(struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
	exts->net = maybe_get_net(exts->net);
	return exts->net != NULL;
#else
	return true;
#endif
}

static inline void tcf_exts_put_net(struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
	if (exts->net)
		put_net(exts->net);
#endif
}

static inline void tcf_exts_to_list(const struct tcf_exts *exts,
				    struct list_head *actions)
{
+0 −2
Original line number Diff line number Diff line
@@ -78,7 +78,6 @@ static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
	spin_lock_bh(&idrinfo->lock);
	idr_remove_ext(&idrinfo->action_idr, p->tcfa_index);
	spin_unlock_bh(&idrinfo->lock);
	put_net(idrinfo->net);
	gen_kill_estimator(&p->tcfa_rate_est);
	free_tcf(p);
}
@@ -337,7 +336,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
	p->idrinfo = idrinfo;
	p->ops = ops;
	INIT_LIST_HEAD(&p->list);
	get_net(idrinfo->net);
	*a = p;
	return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -398,7 +398,7 @@ static __net_init int bpf_init_net(struct net *net)
{
	struct tc_action_net *tn = net_generic(net, bpf_net_id);

	return tc_action_net_init(tn, &act_bpf_ops, net);
	return tc_action_net_init(tn, &act_bpf_ops);
}

static void __net_exit bpf_exit_net(struct net *net)
+1 −1
Original line number Diff line number Diff line
@@ -206,7 +206,7 @@ static __net_init int connmark_init_net(struct net *net)
{
	struct tc_action_net *tn = net_generic(net, connmark_net_id);

	return tc_action_net_init(tn, &act_connmark_ops, net);
	return tc_action_net_init(tn, &act_connmark_ops);
}

static void __net_exit connmark_exit_net(struct net *net)
Loading