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

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
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
struct tcf_idrinfo { struct tcf_idrinfo {
spinlock_t lock; spinlock_t lock;
struct idr action_idr; struct idr action_idr;
struct net *net;
}; };
struct tc_action_ops; struct tc_action_ops;
...@@ -106,7 +105,7 @@ struct tc_action_net { ...@@ -106,7 +105,7 @@ struct tc_action_net {
static inline static inline
int tc_action_net_init(struct tc_action_net *tn, 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; int err = 0;
...@@ -114,7 +113,6 @@ int tc_action_net_init(struct tc_action_net *tn, ...@@ -114,7 +113,6 @@ int tc_action_net_init(struct tc_action_net *tn,
if (!tn->idrinfo) if (!tn->idrinfo)
return -ENOMEM; return -ENOMEM;
tn->ops = ops; tn->ops = ops;
tn->idrinfo->net = net;
spin_lock_init(&tn->idrinfo->lock); spin_lock_init(&tn->idrinfo->lock);
idr_init(&tn->idrinfo->action_idr); idr_init(&tn->idrinfo->action_idr);
return err; return err;
......
...@@ -94,6 +94,7 @@ struct tcf_exts { ...@@ -94,6 +94,7 @@ struct tcf_exts {
__u32 type; /* for backward compat(TCA_OLD_COMPAT) */ __u32 type; /* for backward compat(TCA_OLD_COMPAT) */
int nr_actions; int nr_actions;
struct tc_action **actions; struct tc_action **actions;
struct net *net;
#endif #endif
/* Map to export classifier specific extension TLV types to the /* Map to export classifier specific extension TLV types to the
* generic extensions API. Unsupported extensions must be set to 0. * 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) ...@@ -107,6 +108,7 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police)
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
exts->type = 0; exts->type = 0;
exts->nr_actions = 0; exts->nr_actions = 0;
exts->net = NULL;
exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
GFP_KERNEL); GFP_KERNEL);
if (!exts->actions) if (!exts->actions)
...@@ -117,6 +119,28 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) ...@@ -117,6 +119,28 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police)
return 0; 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, static inline void tcf_exts_to_list(const struct tcf_exts *exts,
struct list_head *actions) struct list_head *actions)
{ {
......
...@@ -78,7 +78,6 @@ static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p) ...@@ -78,7 +78,6 @@ static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
spin_lock_bh(&idrinfo->lock); spin_lock_bh(&idrinfo->lock);
idr_remove_ext(&idrinfo->action_idr, p->tcfa_index); idr_remove_ext(&idrinfo->action_idr, p->tcfa_index);
spin_unlock_bh(&idrinfo->lock); spin_unlock_bh(&idrinfo->lock);
put_net(idrinfo->net);
gen_kill_estimator(&p->tcfa_rate_est); gen_kill_estimator(&p->tcfa_rate_est);
free_tcf(p); free_tcf(p);
} }
...@@ -337,7 +336,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est, ...@@ -337,7 +336,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
p->idrinfo = idrinfo; p->idrinfo = idrinfo;
p->ops = ops; p->ops = ops;
INIT_LIST_HEAD(&p->list); INIT_LIST_HEAD(&p->list);
get_net(idrinfo->net);
*a = p; *a = p;
return 0; return 0;
} }
......
...@@ -398,7 +398,7 @@ static __net_init int bpf_init_net(struct net *net) ...@@ -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); 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) static void __net_exit bpf_exit_net(struct net *net)
......
...@@ -206,7 +206,7 @@ static __net_init int connmark_init_net(struct net *net) ...@@ -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); 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) static void __net_exit connmark_exit_net(struct net *net)
......
...@@ -626,7 +626,7 @@ static __net_init int csum_init_net(struct net *net) ...@@ -626,7 +626,7 @@ static __net_init int csum_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, csum_net_id); struct tc_action_net *tn = net_generic(net, csum_net_id);
return tc_action_net_init(tn, &act_csum_ops, net); return tc_action_net_init(tn, &act_csum_ops);
} }
static void __net_exit csum_exit_net(struct net *net) static void __net_exit csum_exit_net(struct net *net)
......
...@@ -232,7 +232,7 @@ static __net_init int gact_init_net(struct net *net) ...@@ -232,7 +232,7 @@ static __net_init int gact_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, gact_net_id); struct tc_action_net *tn = net_generic(net, gact_net_id);
return tc_action_net_init(tn, &act_gact_ops, net); return tc_action_net_init(tn, &act_gact_ops);
} }
static void __net_exit gact_exit_net(struct net *net) static void __net_exit gact_exit_net(struct net *net)
......
...@@ -818,7 +818,7 @@ static __net_init int ife_init_net(struct net *net) ...@@ -818,7 +818,7 @@ static __net_init int ife_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, ife_net_id); struct tc_action_net *tn = net_generic(net, ife_net_id);
return tc_action_net_init(tn, &act_ife_ops, net); return tc_action_net_init(tn, &act_ife_ops);
} }
static void __net_exit ife_exit_net(struct net *net) static void __net_exit ife_exit_net(struct net *net)
......
...@@ -334,7 +334,7 @@ static __net_init int ipt_init_net(struct net *net) ...@@ -334,7 +334,7 @@ static __net_init int ipt_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, ipt_net_id); struct tc_action_net *tn = net_generic(net, ipt_net_id);
return tc_action_net_init(tn, &act_ipt_ops, net); return tc_action_net_init(tn, &act_ipt_ops);
} }
static void __net_exit ipt_exit_net(struct net *net) static void __net_exit ipt_exit_net(struct net *net)
...@@ -384,7 +384,7 @@ static __net_init int xt_init_net(struct net *net) ...@@ -384,7 +384,7 @@ static __net_init int xt_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, xt_net_id); struct tc_action_net *tn = net_generic(net, xt_net_id);
return tc_action_net_init(tn, &act_xt_ops, net); return tc_action_net_init(tn, &act_xt_ops);
} }
static void __net_exit xt_exit_net(struct net *net) static void __net_exit xt_exit_net(struct net *net)
......
...@@ -343,7 +343,7 @@ static __net_init int mirred_init_net(struct net *net) ...@@ -343,7 +343,7 @@ static __net_init int mirred_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, mirred_net_id); struct tc_action_net *tn = net_generic(net, mirred_net_id);
return tc_action_net_init(tn, &act_mirred_ops, net); return tc_action_net_init(tn, &act_mirred_ops);
} }
static void __net_exit mirred_exit_net(struct net *net) static void __net_exit mirred_exit_net(struct net *net)
......
...@@ -307,7 +307,7 @@ static __net_init int nat_init_net(struct net *net) ...@@ -307,7 +307,7 @@ static __net_init int nat_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, nat_net_id); struct tc_action_net *tn = net_generic(net, nat_net_id);
return tc_action_net_init(tn, &act_nat_ops, net); return tc_action_net_init(tn, &act_nat_ops);
} }
static void __net_exit nat_exit_net(struct net *net) static void __net_exit nat_exit_net(struct net *net)
......
...@@ -450,7 +450,7 @@ static __net_init int pedit_init_net(struct net *net) ...@@ -450,7 +450,7 @@ static __net_init int pedit_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, pedit_net_id); struct tc_action_net *tn = net_generic(net, pedit_net_id);
return tc_action_net_init(tn, &act_pedit_ops, net); return tc_action_net_init(tn, &act_pedit_ops);
} }
static void __net_exit pedit_exit_net(struct net *net) static void __net_exit pedit_exit_net(struct net *net)
......
...@@ -331,7 +331,7 @@ static __net_init int police_init_net(struct net *net) ...@@ -331,7 +331,7 @@ static __net_init int police_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, police_net_id); struct tc_action_net *tn = net_generic(net, police_net_id);
return tc_action_net_init(tn, &act_police_ops, net); return tc_action_net_init(tn, &act_police_ops);
} }
static void __net_exit police_exit_net(struct net *net) static void __net_exit police_exit_net(struct net *net)
......
...@@ -240,7 +240,7 @@ static __net_init int sample_init_net(struct net *net) ...@@ -240,7 +240,7 @@ static __net_init int sample_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, sample_net_id); struct tc_action_net *tn = net_generic(net, sample_net_id);
return tc_action_net_init(tn, &act_sample_ops, net); return tc_action_net_init(tn, &act_sample_ops);
} }
static void __net_exit sample_exit_net(struct net *net) static void __net_exit sample_exit_net(struct net *net)
......
...@@ -201,7 +201,7 @@ static __net_init int simp_init_net(struct net *net) ...@@ -201,7 +201,7 @@ static __net_init int simp_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, simp_net_id); struct tc_action_net *tn = net_generic(net, simp_net_id);
return tc_action_net_init(tn, &act_simp_ops, net); return tc_action_net_init(tn, &act_simp_ops);
} }
static void __net_exit simp_exit_net(struct net *net) static void __net_exit simp_exit_net(struct net *net)
......
...@@ -238,7 +238,7 @@ static __net_init int skbedit_init_net(struct net *net) ...@@ -238,7 +238,7 @@ static __net_init int skbedit_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, skbedit_net_id); struct tc_action_net *tn = net_generic(net, skbedit_net_id);
return tc_action_net_init(tn, &act_skbedit_ops, net); return tc_action_net_init(tn, &act_skbedit_ops);
} }
static void __net_exit skbedit_exit_net(struct net *net) static void __net_exit skbedit_exit_net(struct net *net)
......
...@@ -263,7 +263,7 @@ static __net_init int skbmod_init_net(struct net *net) ...@@ -263,7 +263,7 @@ static __net_init int skbmod_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, skbmod_net_id); struct tc_action_net *tn = net_generic(net, skbmod_net_id);
return tc_action_net_init(tn, &act_skbmod_ops, net); return tc_action_net_init(tn, &act_skbmod_ops);
} }
static void __net_exit skbmod_exit_net(struct net *net) static void __net_exit skbmod_exit_net(struct net *net)
......
...@@ -322,7 +322,7 @@ static __net_init int tunnel_key_init_net(struct net *net) ...@@ -322,7 +322,7 @@ static __net_init int tunnel_key_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
return tc_action_net_init(tn, &act_tunnel_key_ops, net); return tc_action_net_init(tn, &act_tunnel_key_ops);
} }
static void __net_exit tunnel_key_exit_net(struct net *net) static void __net_exit tunnel_key_exit_net(struct net *net)
......
...@@ -269,7 +269,7 @@ static __net_init int vlan_init_net(struct net *net) ...@@ -269,7 +269,7 @@ static __net_init int vlan_init_net(struct net *net)
{ {
struct tc_action_net *tn = net_generic(net, vlan_net_id); struct tc_action_net *tn = net_generic(net, vlan_net_id);
return tc_action_net_init(tn, &act_vlan_ops, net); return tc_action_net_init(tn, &act_vlan_ops);
} }
static void __net_exit vlan_exit_net(struct net *net) static void __net_exit vlan_exit_net(struct net *net)
......
...@@ -927,6 +927,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, ...@@ -927,6 +927,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
exts->actions[i++] = act; exts->actions[i++] = act;
exts->nr_actions = i; exts->nr_actions = i;
} }
exts->net = net;
} }
#else #else
if ((exts->action && tb[exts->action]) || if ((exts->action && tb[exts->action]) ||
......
...@@ -85,16 +85,21 @@ static int basic_init(struct tcf_proto *tp) ...@@ -85,16 +85,21 @@ static int basic_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __basic_delete_filter(struct basic_filter *f)
{
tcf_exts_destroy(&f->exts);
tcf_em_tree_destroy(&f->ematches);
tcf_exts_put_net(&f->exts);
kfree(f);
}
static void basic_delete_filter_work(struct work_struct *work) static void basic_delete_filter_work(struct work_struct *work)
{ {
struct basic_filter *f = container_of(work, struct basic_filter, work); struct basic_filter *f = container_of(work, struct basic_filter, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->exts); __basic_delete_filter(f);
tcf_em_tree_destroy(&f->ematches);
rtnl_unlock(); rtnl_unlock();
kfree(f);
} }
static void basic_delete_filter(struct rcu_head *head) static void basic_delete_filter(struct rcu_head *head)
...@@ -113,7 +118,10 @@ static void basic_destroy(struct tcf_proto *tp) ...@@ -113,7 +118,10 @@ static void basic_destroy(struct tcf_proto *tp)
list_for_each_entry_safe(f, n, &head->flist, link) { list_for_each_entry_safe(f, n, &head->flist, link) {
list_del_rcu(&f->link); list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, basic_delete_filter); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, basic_delete_filter);
else
__basic_delete_filter(f);
} }
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
} }
...@@ -125,6 +133,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last) ...@@ -125,6 +133,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last)
list_del_rcu(&f->link); list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, basic_delete_filter); call_rcu(&f->rcu, basic_delete_filter);
*last = list_empty(&head->flist); *last = list_empty(&head->flist);
return 0; return 0;
...@@ -219,6 +228,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, ...@@ -219,6 +228,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (fold) { if (fold) {
list_replace_rcu(&fold->link, &fnew->link); list_replace_rcu(&fold->link, &fnew->link);
tcf_unbind_filter(tp, &fold->res); tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
call_rcu(&fold->rcu, basic_delete_filter); call_rcu(&fold->rcu, basic_delete_filter);
} else { } else {
list_add_rcu(&fnew->link, &head->flist); list_add_rcu(&fnew->link, &head->flist);
......
...@@ -249,6 +249,7 @@ static int cls_bpf_init(struct tcf_proto *tp) ...@@ -249,6 +249,7 @@ static int cls_bpf_init(struct tcf_proto *tp)
static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog)
{ {
tcf_exts_destroy(&prog->exts); tcf_exts_destroy(&prog->exts);
tcf_exts_put_net(&prog->exts);
if (cls_bpf_is_ebpf(prog)) if (cls_bpf_is_ebpf(prog))
bpf_prog_put(prog->filter); bpf_prog_put(prog->filter);
...@@ -282,7 +283,10 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) ...@@ -282,7 +283,10 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog)
cls_bpf_stop_offload(tp, prog); cls_bpf_stop_offload(tp, prog);
list_del_rcu(&prog->link); list_del_rcu(&prog->link);
tcf_unbind_filter(tp, &prog->res); tcf_unbind_filter(tp, &prog->res);
call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); if (tcf_exts_get_net(&prog->exts))
call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu);
else
__cls_bpf_delete_prog(prog);
} }
static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last) static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last)
...@@ -516,6 +520,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -516,6 +520,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
if (oldprog) { if (oldprog) {
list_replace_rcu(&oldprog->link, &prog->link); list_replace_rcu(&oldprog->link, &prog->link);
tcf_unbind_filter(tp, &oldprog->res); tcf_unbind_filter(tp, &oldprog->res);
tcf_exts_get_net(&oldprog->exts);
call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu); call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu);
} else { } else {
list_add_rcu(&prog->link, &head->plist); list_add_rcu(&prog->link, &head->plist);
......
...@@ -60,15 +60,21 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { ...@@ -60,15 +60,21 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
[TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED },
}; };
static void __cls_cgroup_destroy(struct cls_cgroup_head *head)
{
tcf_exts_destroy(&head->exts);
tcf_em_tree_destroy(&head->ematches);
tcf_exts_put_net(&head->exts);
kfree(head);
}
static void cls_cgroup_destroy_work(struct work_struct *work) static void cls_cgroup_destroy_work(struct work_struct *work)
{ {
struct cls_cgroup_head *head = container_of(work, struct cls_cgroup_head *head = container_of(work,
struct cls_cgroup_head, struct cls_cgroup_head,
work); work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&head->exts); __cls_cgroup_destroy(head);
tcf_em_tree_destroy(&head->ematches);
kfree(head);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -124,8 +130,10 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, ...@@ -124,8 +130,10 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
goto errout; goto errout;
rcu_assign_pointer(tp->root, new); rcu_assign_pointer(tp->root, new);
if (head) if (head) {
tcf_exts_get_net(&head->exts);
call_rcu(&head->rcu, cls_cgroup_destroy_rcu); call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
}
return 0; return 0;
errout: errout:
tcf_exts_destroy(&new->exts); tcf_exts_destroy(&new->exts);
...@@ -138,8 +146,12 @@ static void cls_cgroup_destroy(struct tcf_proto *tp) ...@@ -138,8 +146,12 @@ static void cls_cgroup_destroy(struct tcf_proto *tp)
struct cls_cgroup_head *head = rtnl_dereference(tp->root); struct cls_cgroup_head *head = rtnl_dereference(tp->root);
/* Head can still be NULL due to cls_cgroup_init(). */ /* Head can still be NULL due to cls_cgroup_init(). */
if (head) if (head) {
call_rcu(&head->rcu, cls_cgroup_destroy_rcu); if (tcf_exts_get_net(&head->exts))
call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
else
__cls_cgroup_destroy(head);
}
} }
static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last) static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last)
......
...@@ -372,15 +372,21 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { ...@@ -372,15 +372,21 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
[TCA_FLOW_PERTURB] = { .type = NLA_U32 }, [TCA_FLOW_PERTURB] = { .type = NLA_U32 },
}; };
static void flow_destroy_filter_work(struct work_struct *work) static void __flow_destroy_filter(struct flow_filter *f)
{ {
struct flow_filter *f = container_of(work, struct flow_filter, work);
rtnl_lock();
del_timer_sync(&f->perturb_timer); del_timer_sync(&f->perturb_timer);
tcf_exts_destroy(&f->exts); tcf_exts_destroy(&f->exts);
tcf_em_tree_destroy(&f->ematches); tcf_em_tree_destroy(&f->ematches);
tcf_exts_put_net(&f->exts);
kfree(f); kfree(f);
}
static void flow_destroy_filter_work(struct work_struct *work)
{
struct flow_filter *f = container_of(work, struct flow_filter, work);
rtnl_lock();
__flow_destroy_filter(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -552,8 +558,10 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, ...@@ -552,8 +558,10 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
*arg = fnew; *arg = fnew;
if (fold) if (fold) {
tcf_exts_get_net(&fold->exts);
call_rcu(&fold->rcu, flow_destroy_filter); call_rcu(&fold->rcu, flow_destroy_filter);
}
return 0; return 0;
err2: err2:
...@@ -570,6 +578,7 @@ static int flow_delete(struct tcf_proto *tp, void *arg, bool *last) ...@@ -570,6 +578,7 @@ static int flow_delete(struct tcf_proto *tp, void *arg, bool *last)
struct flow_filter *f = arg; struct flow_filter *f = arg;
list_del_rcu(&f->list); list_del_rcu(&f->list);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, flow_destroy_filter); call_rcu(&f->rcu, flow_destroy_filter);
*last = list_empty(&head->filters); *last = list_empty(&head->filters);
return 0; return 0;
...@@ -594,7 +603,10 @@ static void flow_destroy(struct tcf_proto *tp) ...@@ -594,7 +603,10 @@ static void flow_destroy(struct tcf_proto *tp)
list_for_each_entry_safe(f, next, &head->filters, list) { list_for_each_entry_safe(f, next, &head->filters, list) {
list_del_rcu(&f->list); list_del_rcu(&f->list);
call_rcu(&f->rcu, flow_destroy_filter); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, flow_destroy_filter);
else
__flow_destroy_filter(f);
} }
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
} }
......
...@@ -218,13 +218,19 @@ static int fl_init(struct tcf_proto *tp) ...@@ -218,13 +218,19 @@ static int fl_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __fl_destroy_filter(struct cls_fl_filter *f)
{
tcf_exts_destroy(&f->exts);
tcf_exts_put_net(&f->exts);
kfree(f);
}
static void fl_destroy_filter_work(struct work_struct *work) static void fl_destroy_filter_work(struct work_struct *work)
{ {
struct cls_fl_filter *f = container_of(work, struct cls_fl_filter, work); struct cls_fl_filter *f = container_of(work, struct cls_fl_filter, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->exts); __fl_destroy_filter(f);
kfree(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -318,7 +324,10 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) ...@@ -318,7 +324,10 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f)
if (!tc_skip_hw(f->flags)) if (!tc_skip_hw(f->flags))
fl_hw_destroy_filter(tp, f); fl_hw_destroy_filter(tp, f);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, fl_destroy_filter); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, fl_destroy_filter);
else
__fl_destroy_filter(f);
} }
static void fl_destroy_sleepable(struct work_struct *work) static void fl_destroy_sleepable(struct work_struct *work)
...@@ -988,6 +997,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, ...@@ -988,6 +997,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
idr_replace_ext(&head->handle_idr, fnew, fnew->handle); idr_replace_ext(&head->handle_idr, fnew, fnew->handle);
list_replace_rcu(&fold->list, &fnew->list); list_replace_rcu(&fold->list, &fnew->list);
tcf_unbind_filter(tp, &fold->res); tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
call_rcu(&fold->rcu, fl_destroy_filter); call_rcu(&fold->rcu, fl_destroy_filter);
} else { } else {
list_add_tail_rcu(&fnew->list, &head->filters); list_add_tail_rcu(&fnew->list, &head->filters);
......
...@@ -122,13 +122,19 @@ static int fw_init(struct tcf_proto *tp) ...@@ -122,13 +122,19 @@ static int fw_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __fw_delete_filter(struct fw_filter *f)
{
tcf_exts_destroy(&f->exts);
tcf_exts_put_net(&f->exts);
kfree(f);
}
static void fw_delete_filter_work(struct work_struct *work) static void fw_delete_filter_work(struct work_struct *work)
{ {
struct fw_filter *f = container_of(work, struct fw_filter, work); struct fw_filter *f = container_of(work, struct fw_filter, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->exts); __fw_delete_filter(f);
kfree(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -154,7 +160,10 @@ static void fw_destroy(struct tcf_proto *tp) ...@@ -154,7 +160,10 @@ static void fw_destroy(struct tcf_proto *tp)
RCU_INIT_POINTER(head->ht[h], RCU_INIT_POINTER(head->ht[h],
rtnl_dereference(f->next)); rtnl_dereference(f->next));
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, fw_delete_filter); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, fw_delete_filter);
else
__fw_delete_filter(f);
} }
} }
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
...@@ -179,6 +188,7 @@ static int fw_delete(struct tcf_proto *tp, void *arg, bool *last) ...@@ -179,6 +188,7 @@ static int fw_delete(struct tcf_proto *tp, void *arg, bool *last)
if (pfp == f) { if (pfp == f) {
RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, fw_delete_filter); call_rcu(&f->rcu, fw_delete_filter);
ret = 0; ret = 0;
break; break;
...@@ -299,6 +309,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, ...@@ -299,6 +309,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next));
rcu_assign_pointer(*fp, fnew); rcu_assign_pointer(*fp, fnew);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, fw_delete_filter); call_rcu(&f->rcu, fw_delete_filter);
*arg = fnew; *arg = fnew;
......
...@@ -44,13 +44,19 @@ static int mall_init(struct tcf_proto *tp) ...@@ -44,13 +44,19 @@ static int mall_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __mall_destroy(struct cls_mall_head *head)
{
tcf_exts_destroy(&head->exts);
tcf_exts_put_net(&head->exts);
kfree(head);
}
static void mall_destroy_work(struct work_struct *work) static void mall_destroy_work(struct work_struct *work)
{ {
struct cls_mall_head *head = container_of(work, struct cls_mall_head, struct cls_mall_head *head = container_of(work, struct cls_mall_head,
work); work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&head->exts); __mall_destroy(head);
kfree(head);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -109,7 +115,10 @@ static void mall_destroy(struct tcf_proto *tp) ...@@ -109,7 +115,10 @@ static void mall_destroy(struct tcf_proto *tp)
if (tc_should_offload(dev, head->flags)) if (tc_should_offload(dev, head->flags))
mall_destroy_hw_filter(tp, head, (unsigned long) head); mall_destroy_hw_filter(tp, head, (unsigned long) head);
call_rcu(&head->rcu, mall_destroy_rcu); if (tcf_exts_get_net(&head->exts))
call_rcu(&head->rcu, mall_destroy_rcu);
else
__mall_destroy(head);
} }
static void *mall_get(struct tcf_proto *tp, u32 handle) static void *mall_get(struct tcf_proto *tp, u32 handle)
......
...@@ -257,13 +257,19 @@ static int route4_init(struct tcf_proto *tp) ...@@ -257,13 +257,19 @@ static int route4_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __route4_delete_filter(struct route4_filter *f)
{
tcf_exts_destroy(&f->exts);
tcf_exts_put_net(&f->exts);
kfree(f);
}
static void route4_delete_filter_work(struct work_struct *work) static void route4_delete_filter_work(struct work_struct *work)
{ {
struct route4_filter *f = container_of(work, struct route4_filter, work); struct route4_filter *f = container_of(work, struct route4_filter, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->exts); __route4_delete_filter(f);
kfree(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -297,7 +303,10 @@ static void route4_destroy(struct tcf_proto *tp) ...@@ -297,7 +303,10 @@ static void route4_destroy(struct tcf_proto *tp)
next = rtnl_dereference(f->next); next = rtnl_dereference(f->next);
RCU_INIT_POINTER(b->ht[h2], next); RCU_INIT_POINTER(b->ht[h2], next);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, route4_delete_filter); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, route4_delete_filter);
else
__route4_delete_filter(f);
} }
} }
RCU_INIT_POINTER(head->table[h1], NULL); RCU_INIT_POINTER(head->table[h1], NULL);
...@@ -338,6 +347,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last) ...@@ -338,6 +347,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last)
/* Delete it */ /* Delete it */
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
tcf_exts_get_net(&f->exts);
call_rcu(&f->rcu, route4_delete_filter); call_rcu(&f->rcu, route4_delete_filter);
/* Strip RTNL protected tree */ /* Strip RTNL protected tree */
...@@ -541,6 +551,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, ...@@ -541,6 +551,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
*arg = f; *arg = f;
if (fold) { if (fold) {
tcf_unbind_filter(tp, &fold->res); tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
call_rcu(&fold->rcu, route4_delete_filter); call_rcu(&fold->rcu, route4_delete_filter);
} }
return 0; return 0;
......
...@@ -285,13 +285,19 @@ static int rsvp_init(struct tcf_proto *tp) ...@@ -285,13 +285,19 @@ static int rsvp_init(struct tcf_proto *tp)
return -ENOBUFS; return -ENOBUFS;
} }
static void __rsvp_delete_filter(struct rsvp_filter *f)
{
tcf_exts_destroy(&f->exts);
tcf_exts_put_net(&f->exts);
kfree(f);
}
static void rsvp_delete_filter_work(struct work_struct *work) static void rsvp_delete_filter_work(struct work_struct *work)
{ {
struct rsvp_filter *f = container_of(work, struct rsvp_filter, work); struct rsvp_filter *f = container_of(work, struct rsvp_filter, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->exts); __rsvp_delete_filter(f);
kfree(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -310,7 +316,10 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) ...@@ -310,7 +316,10 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
* grace period, since converted-to-rcu actions are relying on that * grace period, since converted-to-rcu actions are relying on that
* in cleanup() callback * in cleanup() callback
*/ */
call_rcu(&f->rcu, rsvp_delete_filter_rcu); if (tcf_exts_get_net(&f->exts))
call_rcu(&f->rcu, rsvp_delete_filter_rcu);
else
__rsvp_delete_filter(f);
} }
static void rsvp_destroy(struct tcf_proto *tp) static void rsvp_destroy(struct tcf_proto *tp)
......
...@@ -139,13 +139,19 @@ static int tcindex_init(struct tcf_proto *tp) ...@@ -139,13 +139,19 @@ static int tcindex_init(struct tcf_proto *tp)
return 0; return 0;
} }
static void __tcindex_destroy_rexts(struct tcindex_filter_result *r)
{
tcf_exts_destroy(&r->exts);
tcf_exts_put_net(&r->exts);
}
static void tcindex_destroy_rexts_work(struct work_struct *work) static void tcindex_destroy_rexts_work(struct work_struct *work)
{ {
struct tcindex_filter_result *r; struct tcindex_filter_result *r;
r = container_of(work, struct tcindex_filter_result, work); r = container_of(work, struct tcindex_filter_result, work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&r->exts); __tcindex_destroy_rexts(r);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -158,14 +164,20 @@ static void tcindex_destroy_rexts(struct rcu_head *head) ...@@ -158,14 +164,20 @@ static void tcindex_destroy_rexts(struct rcu_head *head)
tcf_queue_work(&r->work); tcf_queue_work(&r->work);
} }
static void __tcindex_destroy_fexts(struct tcindex_filter *f)
{
tcf_exts_destroy(&f->result.exts);
tcf_exts_put_net(&f->result.exts);
kfree(f);
}
static void tcindex_destroy_fexts_work(struct work_struct *work) static void tcindex_destroy_fexts_work(struct work_struct *work)
{ {
struct tcindex_filter *f = container_of(work, struct tcindex_filter, struct tcindex_filter *f = container_of(work, struct tcindex_filter,
work); work);
rtnl_lock(); rtnl_lock();
tcf_exts_destroy(&f->result.exts); __tcindex_destroy_fexts(f);
kfree(f);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -210,10 +222,17 @@ static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last) ...@@ -210,10 +222,17 @@ static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last)
* grace period, since converted-to-rcu actions are relying on that * grace period, since converted-to-rcu actions are relying on that
* in cleanup() callback * in cleanup() callback
*/ */
if (f) if (f) {
call_rcu(&f->rcu, tcindex_destroy_fexts); if (tcf_exts_get_net(&f->result.exts))
else call_rcu(&f->rcu, tcindex_destroy_fexts);
call_rcu(&r->rcu, tcindex_destroy_rexts); else
__tcindex_destroy_fexts(f);
} else {
if (tcf_exts_get_net(&r->exts))
call_rcu(&r->rcu, tcindex_destroy_rexts);
else
__tcindex_destroy_rexts(r);
}
*last = false; *last = false;
return 0; return 0;
......
...@@ -399,6 +399,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n, ...@@ -399,6 +399,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n,
bool free_pf) bool free_pf)
{ {
tcf_exts_destroy(&n->exts); tcf_exts_destroy(&n->exts);
tcf_exts_put_net(&n->exts);
if (n->ht_down) if (n->ht_down)
n->ht_down->refcnt--; n->ht_down->refcnt--;
#ifdef CONFIG_CLS_U32_PERF #ifdef CONFIG_CLS_U32_PERF
...@@ -476,6 +477,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) ...@@ -476,6 +477,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
RCU_INIT_POINTER(*kp, key->next); RCU_INIT_POINTER(*kp, key->next);
tcf_unbind_filter(tp, &key->res); tcf_unbind_filter(tp, &key->res);
tcf_exts_get_net(&key->exts);
call_rcu(&key->rcu, u32_delete_key_freepf_rcu); call_rcu(&key->rcu, u32_delete_key_freepf_rcu);
return 0; return 0;
} }
...@@ -588,7 +590,10 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) ...@@ -588,7 +590,10 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
rtnl_dereference(n->next)); rtnl_dereference(n->next));
tcf_unbind_filter(tp, &n->res); tcf_unbind_filter(tp, &n->res);
u32_remove_hw_knode(tp, n->handle); u32_remove_hw_knode(tp, n->handle);
call_rcu(&n->rcu, u32_delete_key_freepf_rcu); if (tcf_exts_get_net(&n->exts))
call_rcu(&n->rcu, u32_delete_key_freepf_rcu);
else
u32_destroy_key(n->tp, n, true);
} }
} }
} }
...@@ -949,6 +954,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -949,6 +954,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
u32_replace_knode(tp, tp_c, new); u32_replace_knode(tp, tp_c, new);
tcf_unbind_filter(tp, &n->res); tcf_unbind_filter(tp, &n->res);
tcf_exts_get_net(&n->exts);
call_rcu(&n->rcu, u32_delete_key_rcu); call_rcu(&n->rcu, u32_delete_key_rcu);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment