Hold netns refcnt before call_rcu() and release it after
the tcf_exts_destroy() is done.

Note, on ->destroy() path we have to respect the return value
of tcf_exts_get_net(), on other paths it should always return
true, so we don't need to care.

Cc: Lucas Bates <luc...@mojatatu.com>
Cc: Jamal Hadi Salim <j...@mojatatu.com>
Cc: Jiri Pirko <j...@resnulli.us>
Signed-off-by: Cong Wang <xiyou.wangc...@gmail.com>
---
 net/sched/cls_u32.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index dadd1b344497..b58eccb21f03 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -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;
        }
-- 
2.13.0

Reply via email to