]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
Merge branch 'net-sched-race-fix'
authorDavid S. Miller <davem@davemloft.net>
Thu, 9 Nov 2017 01:03:10 +0000 (10:03 +0900)
committerDavid S. Miller <davem@davemloft.net>
Thu, 9 Nov 2017 01:03:10 +0000 (10:03 +0900)
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: ddf97ccdd7cb ("net_sched: add network namespace support for tc actions")
Reported-by: Lucas 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: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
31 files changed:
include/net/act_api.h
include/net/pkt_cls.h
net/sched/act_api.c
net/sched/act_bpf.c
net/sched/act_connmark.c
net/sched/act_csum.c
net/sched/act_gact.c
net/sched/act_ife.c
net/sched/act_ipt.c
net/sched/act_mirred.c
net/sched/act_nat.c
net/sched/act_pedit.c
net/sched/act_police.c
net/sched/act_sample.c
net/sched/act_simple.c
net/sched/act_skbedit.c
net/sched/act_skbmod.c
net/sched/act_tunnel_key.c
net/sched/act_vlan.c
net/sched/cls_api.c
net/sched/cls_basic.c
net/sched/cls_bpf.c
net/sched/cls_cgroup.c
net/sched/cls_flow.c
net/sched/cls_flower.c
net/sched/cls_fw.c
net/sched/cls_matchall.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_tcindex.c
net/sched/cls_u32.c

index 1e6df0eb058f1eedd5b84a52457273b2020e1c80..a10a3b1813f36c18f698e609b8f09beaf43d8d1e 100644 (file)
@@ -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;
index 70ca2437740e41284d7b40b5c202d85fc2e6668b..8826747ef83e616156092f311282c4785621d923 100644 (file)
@@ -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)
 {
index ca2ff0b3123f9d37e4b0549e55254bf46efb5e1a..8f2c635149561e741bda88163df063f8cc70f957 100644 (file)
@@ -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 @@ err3:
        p->idrinfo = idrinfo;
        p->ops = ops;
        INIT_LIST_HEAD(&p->list);
-       get_net(idrinfo->net);
        *a = p;
        return 0;
 }
index 9bce8cc84cbb9cd99fa1812ef0ac99a4405a995a..c0c707eb2c962520fbc6da655c8b4151bf8a4462 100644 (file)
@@ -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)
index 34e52d01a5dde25ab4432979eaac01c730d733fd..10b7a8855a6c754640c66b21af3df1e4b17de63d 100644 (file)
@@ -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)
index 35171df2ebef38776dfd2f9fe2603de86d341872..1c40caadcff959ba0c6cec6b8e32f7b459c42cfa 100644 (file)
@@ -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);
 
-       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)
index ef7f7f39d26d27cff31f929f84dfed549ccb843d..e29a48ef7fc348aefdc720cf08d1509ac5b53559 100644 (file)
@@ -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);
 
-       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)
index f65e4b5058e0803d4e2f9a67f2cb1f7978ffc656..8ccd35825b6b97f48311c1060f88a41a34f18f9a 100644 (file)
@@ -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);
 
-       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)
index dbdf3b2470d53bebbd2f75f32889d4fda47e19fb..d9e399a7e3d59c8ec53e46e0862e2ca3c83988bc 100644 (file)
@@ -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);
 
-       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)
@@ -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);
 
-       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)
index 84759cfd5a3398555cba7012d0cc949b7b7e6033..416627c66f081f26ff9284de86411c3e05390b8e 100644 (file)
@@ -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);
 
-       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)
index 7eeaaf9217b69faa356a091409bbaa2c70f95242..c365d01b99c8b7892c16e43742a88744d7d84e54 100644 (file)
@@ -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);
 
-       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)
index b3d82c334a5f5285ad7309e5eb9b573e2f84e229..491fe5deb09ee7f38a6c0f892c43ffe0bce79fd1 100644 (file)
@@ -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);
 
-       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)
index 9ec42b26e4b97d298392e60ed7ca0a1eb427b6ea..3bb2ebf9e9aec2743033ecdc4f5763ce601bf653 100644 (file)
@@ -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);
 
-       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)
index e69a1e3a39bf700a5a729bd9234132397c7e55c7..8b5abcd2f32faeaa2a283bcc8fb388201f7a86e2 100644 (file)
@@ -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);
 
-       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)
index a8d0ea95f89454826e9d782846543f054a0dc5e5..e7b57e5071a365743de9d2c5aaa22dca445fa0d9 100644 (file)
@@ -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);
 
-       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)
index fbac62472e09cf72c5aac1731f9065843b48455e..59949d61f20da1031b8b47712f817c991c6bed60 100644 (file)
@@ -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);
 
-       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)
index 8e12d8897d2ff6449a410e037652a1aa95c63d35..b642ad3d39dd414d392e492bae091995b01cbd77 100644 (file)
@@ -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);
 
-       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)
index c33faa373cf2221c2798509a5f1f70c8bce69240..30c96274c63826520eec062a6a5609b141bab307 100644 (file)
@@ -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);
 
-       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)
index 115fc33cc6d8b321f9c02febf861b93fe9a85740..16eb067a8d8fa20c17894db8047571789c0b96e8 100644 (file)
@@ -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);
 
-       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)
index b2d31074548724a3b59defbcd827d6538ac9bea6..ecbb019efcbd310dd1caaaf336d2db2b523713dd 100644 (file)
@@ -927,6 +927,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                                exts->actions[i++] = act;
                        exts->nr_actions = i;
                }
+               exts->net = net;
        }
 #else
        if ((exts->action && tb[exts->action]) ||
index f177649a24192a144f261d4e4a40b52ce7cdc91a..e43c56d5b96a2943d59733185b51b8bafe19f8fa 100644 (file)
@@ -85,16 +85,21 @@ static int basic_init(struct tcf_proto *tp)
        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)
 {
        struct basic_filter *f = container_of(work, struct basic_filter, work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->exts);
-       tcf_em_tree_destroy(&f->ematches);
+       __basic_delete_filter(f);
        rtnl_unlock();
-
-       kfree(f);
 }
 
 static void basic_delete_filter(struct rcu_head *head)
@@ -113,7 +118,10 @@ static void basic_destroy(struct tcf_proto *tp)
        list_for_each_entry_safe(f, n, &head->flist, link) {
                list_del_rcu(&f->link);
                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);
 }
@@ -125,6 +133,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last)
 
        list_del_rcu(&f->link);
        tcf_unbind_filter(tp, &f->res);
+       tcf_exts_get_net(&f->exts);
        call_rcu(&f->rcu, basic_delete_filter);
        *last = list_empty(&head->flist);
        return 0;
@@ -219,6 +228,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
        if (fold) {
                list_replace_rcu(&fold->link, &fnew->link);
                tcf_unbind_filter(tp, &fold->res);
+               tcf_exts_get_net(&fold->exts);
                call_rcu(&fold->rcu, basic_delete_filter);
        } else {
                list_add_rcu(&fnew->link, &head->flist);
index 037a3ae86829946135e2154bd83ddc722b61af17..990eb4d91d54255ace7eb400298eb433edd47cbe 100644 (file)
@@ -249,6 +249,7 @@ static int cls_bpf_init(struct tcf_proto *tp)
 static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog)
 {
        tcf_exts_destroy(&prog->exts);
+       tcf_exts_put_net(&prog->exts);
 
        if (cls_bpf_is_ebpf(prog))
                bpf_prog_put(prog->filter);
@@ -282,7 +283,10 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog)
        cls_bpf_stop_offload(tp, prog);
        list_del_rcu(&prog->link);
        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)
@@ -516,6 +520,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
        if (oldprog) {
                list_replace_rcu(&oldprog->link, &prog->link);
                tcf_unbind_filter(tp, &oldprog->res);
+               tcf_exts_get_net(&oldprog->exts);
                call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu);
        } else {
                list_add_rcu(&prog->link, &head->plist);
index a97e069bee89f0010daaa33c610cc1aea7b97811..309d5899265f8f7dea731a2a8569867fa5077e09 100644 (file)
@@ -60,15 +60,21 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
        [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)
 {
        struct cls_cgroup_head *head = container_of(work,
                                                    struct cls_cgroup_head,
                                                    work);
        rtnl_lock();
-       tcf_exts_destroy(&head->exts);
-       tcf_em_tree_destroy(&head->ematches);
-       kfree(head);
+       __cls_cgroup_destroy(head);
        rtnl_unlock();
 }
 
@@ -124,8 +130,10 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                goto errout;
 
        rcu_assign_pointer(tp->root, new);
-       if (head)
+       if (head) {
+               tcf_exts_get_net(&head->exts);
                call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
+       }
        return 0;
 errout:
        tcf_exts_destroy(&new->exts);
@@ -138,8 +146,12 @@ static void cls_cgroup_destroy(struct tcf_proto *tp)
        struct cls_cgroup_head *head = rtnl_dereference(tp->root);
 
        /* Head can still be NULL due to cls_cgroup_init(). */
-       if (head)
-               call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
+       if (head) {
+               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)
index 67f3a2af6aab1aadb5b492265d8469a37a1e8fd5..85f765cff6972357c835611a8a29464ce7a3970d 100644 (file)
@@ -372,15 +372,21 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
        [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);
        tcf_exts_destroy(&f->exts);
        tcf_em_tree_destroy(&f->ematches);
+       tcf_exts_put_net(&f->exts);
        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();
 }
 
@@ -552,8 +558,10 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
 
        *arg = fnew;
 
-       if (fold)
+       if (fold) {
+               tcf_exts_get_net(&fold->exts);
                call_rcu(&fold->rcu, flow_destroy_filter);
+       }
        return 0;
 
 err2:
@@ -570,6 +578,7 @@ static int flow_delete(struct tcf_proto *tp, void *arg, bool *last)
        struct flow_filter *f = arg;
 
        list_del_rcu(&f->list);
+       tcf_exts_get_net(&f->exts);
        call_rcu(&f->rcu, flow_destroy_filter);
        *last = list_empty(&head->filters);
        return 0;
@@ -594,7 +603,10 @@ static void flow_destroy(struct tcf_proto *tp)
 
        list_for_each_entry_safe(f, next, &head->filters, 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);
 }
index 5b5722c8b32c1a276f7441fb67baa0d16ba24ff2..7a838d1c1c0059bddb3da24a59ad73689fec1877 100644 (file)
@@ -218,13 +218,19 @@ static int fl_init(struct tcf_proto *tp)
        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)
 {
        struct cls_fl_filter *f = container_of(work, struct cls_fl_filter, work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->exts);
-       kfree(f);
+       __fl_destroy_filter(f);
        rtnl_unlock();
 }
 
@@ -318,7 +324,10 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f)
        if (!tc_skip_hw(f->flags))
                fl_hw_destroy_filter(tp, f);
        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)
@@ -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);
                list_replace_rcu(&fold->list, &fnew->list);
                tcf_unbind_filter(tp, &fold->res);
+               tcf_exts_get_net(&fold->exts);
                call_rcu(&fold->rcu, fl_destroy_filter);
        } else {
                list_add_tail_rcu(&fnew->list, &head->filters);
index 99183b8621ecb0543af91020167d13c193cad214..7f45e5ab8afcddcb95e4c9e4ead3ec5a9899d3ec 100644 (file)
@@ -122,13 +122,19 @@ static int fw_init(struct tcf_proto *tp)
        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)
 {
        struct fw_filter *f = container_of(work, struct fw_filter, work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->exts);
-       kfree(f);
+       __fw_delete_filter(f);
        rtnl_unlock();
 }
 
@@ -154,7 +160,10 @@ static void fw_destroy(struct tcf_proto *tp)
                        RCU_INIT_POINTER(head->ht[h],
                                         rtnl_dereference(f->next));
                        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);
@@ -179,6 +188,7 @@ static int fw_delete(struct tcf_proto *tp, void *arg, bool *last)
                if (pfp == f) {
                        RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
                        tcf_unbind_filter(tp, &f->res);
+                       tcf_exts_get_net(&f->exts);
                        call_rcu(&f->rcu, fw_delete_filter);
                        ret = 0;
                        break;
@@ -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_assign_pointer(*fp, fnew);
                tcf_unbind_filter(tp, &f->res);
+               tcf_exts_get_net(&f->exts);
                call_rcu(&f->rcu, fw_delete_filter);
 
                *arg = fnew;
index c33f711b90198ab8b77bd314c289d09cbfe2d170..3684153cd8a9e1dfc680bcabf3c13e4fba2f507e 100644 (file)
@@ -44,13 +44,19 @@ static int mall_init(struct tcf_proto *tp)
        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)
 {
        struct cls_mall_head *head = container_of(work, struct cls_mall_head,
                                                  work);
        rtnl_lock();
-       tcf_exts_destroy(&head->exts);
-       kfree(head);
+       __mall_destroy(head);
        rtnl_unlock();
 }
 
@@ -109,7 +115,10 @@ static void mall_destroy(struct tcf_proto *tp)
        if (tc_should_offload(dev, head->flags))
                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)
index 4b14ccd8b8f271aabecb97427fa3fa18f7484747..ac9a5b8825b9d572a0e60453fc6c68e82bdab320 100644 (file)
@@ -257,13 +257,19 @@ static int route4_init(struct tcf_proto *tp)
        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)
 {
        struct route4_filter *f = container_of(work, struct route4_filter, work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->exts);
-       kfree(f);
+       __route4_delete_filter(f);
        rtnl_unlock();
 }
 
@@ -297,7 +303,10 @@ static void route4_destroy(struct tcf_proto *tp)
                                        next = rtnl_dereference(f->next);
                                        RCU_INIT_POINTER(b->ht[h2], next);
                                        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);
@@ -338,6 +347,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last)
 
                        /* Delete it */
                        tcf_unbind_filter(tp, &f->res);
+                       tcf_exts_get_net(&f->exts);
                        call_rcu(&f->rcu, route4_delete_filter);
 
                        /* Strip RTNL protected tree */
@@ -541,6 +551,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
        *arg = f;
        if (fold) {
                tcf_unbind_filter(tp, &fold->res);
+               tcf_exts_get_net(&fold->exts);
                call_rcu(&fold->rcu, route4_delete_filter);
        }
        return 0;
index bdbc541787f87d6bc0592122acef87a80b0b8d03..cf325625c99da83df0503a7a324d1b962540a34a 100644 (file)
@@ -285,13 +285,19 @@ static int rsvp_init(struct tcf_proto *tp)
        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)
 {
        struct rsvp_filter *f = container_of(work, struct rsvp_filter, work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->exts);
-       kfree(f);
+       __rsvp_delete_filter(f);
        rtnl_unlock();
 }
 
@@ -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
         * 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)
index beaa95e09c25c26ba94155f03375d27156e9cbc4..a76937ee0b2dbf08f68574f727aa91d3a496fb4b 100644 (file)
@@ -139,13 +139,19 @@ static int tcindex_init(struct tcf_proto *tp)
        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)
 {
        struct tcindex_filter_result *r;
 
        r = container_of(work, struct tcindex_filter_result, work);
        rtnl_lock();
-       tcf_exts_destroy(&r->exts);
+       __tcindex_destroy_rexts(r);
        rtnl_unlock();
 }
 
@@ -158,14 +164,20 @@ static void tcindex_destroy_rexts(struct rcu_head *head)
        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)
 {
        struct tcindex_filter *f = container_of(work, struct tcindex_filter,
                                                work);
 
        rtnl_lock();
-       tcf_exts_destroy(&f->result.exts);
-       kfree(f);
+       __tcindex_destroy_fexts(f);
        rtnl_unlock();
 }
 
@@ -210,10 +222,17 @@ found:
         * grace period, since converted-to-rcu actions are relying on that
         * in cleanup() callback
         */
-       if (f)
-               call_rcu(&f->rcu, tcindex_destroy_fexts);
-       else
-               call_rcu(&r->rcu, tcindex_destroy_rexts);
+       if (f) {
+               if (tcf_exts_get_net(&f->result.exts))
+                       call_rcu(&f->rcu, tcindex_destroy_fexts);
+               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;
        return 0;
index dadd1b3444970d1be4936e4f1a6cf8fd9a6d14a6..b58eccb21f039b676d051507ae971f6fc601654d 100644 (file)
@@ -399,6 +399,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n,
                           bool free_pf)
 {
        tcf_exts_destroy(&n->exts);
+       tcf_exts_put_net(&n->exts);
        if (n->ht_down)
                n->ht_down->refcnt--;
 #ifdef CONFIG_CLS_U32_PERF
@@ -476,6 +477,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
                                RCU_INIT_POINTER(*kp, key->next);
 
                                tcf_unbind_filter(tp, &key->res);
+                               tcf_exts_get_net(&key->exts);
                                call_rcu(&key->rcu, u32_delete_key_freepf_rcu);
                                return 0;
                        }
@@ -588,7 +590,10 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
                                         rtnl_dereference(n->next));
                        tcf_unbind_filter(tp, &n->res);
                        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,
 
                u32_replace_knode(tp, tp_c, new);
                tcf_unbind_filter(tp, &n->res);
+               tcf_exts_get_net(&n->exts);
                call_rcu(&n->rcu, u32_delete_key_rcu);
                return 0;
        }