net_sched: use tcf_queue_work() in tcindex filter
authorCong Wang <xiyou.wangcong@gmail.com>
Fri, 27 Oct 2017 01:24:39 +0000 (18:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 29 Oct 2017 13:49:31 +0000 (22:49 +0900)
Defer the tcf_exts_destroy() in RCU callback to
tc filter workqueue and get RTNL lock.

Reported-by: Chris Mi <chrism@mellanox.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: John Fastabend <john.fastabend@gmail.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/cls_tcindex.c

index 14a7e08b2fa9e2f8a3551f6dbc1fb38f1e8df579..beaa95e09c25c26ba94155f03375d27156e9cbc4 100644 (file)
 struct tcindex_filter_result {
        struct tcf_exts         exts;
        struct tcf_result       res;
-       struct rcu_head         rcu;
+       union {
+               struct work_struct      work;
+               struct rcu_head         rcu;
+       };
 };
 
 struct tcindex_filter {
        u16 key;
        struct tcindex_filter_result result;
        struct tcindex_filter __rcu *next;
-       struct rcu_head rcu;
+       union {
+               struct work_struct work;
+               struct rcu_head rcu;
+       };
 };
 
 
@@ -133,12 +139,34 @@ static int tcindex_init(struct tcf_proto *tp)
        return 0;
 }
 
+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);
+       rtnl_unlock();
+}
+
 static void tcindex_destroy_rexts(struct rcu_head *head)
 {
        struct tcindex_filter_result *r;
 
        r = container_of(head, struct tcindex_filter_result, rcu);
-       tcf_exts_destroy(&r->exts);
+       INIT_WORK(&r->work, tcindex_destroy_rexts_work);
+       tcf_queue_work(&r->work);
+}
+
+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);
+       rtnl_unlock();
 }
 
 static void tcindex_destroy_fexts(struct rcu_head *head)
@@ -146,8 +174,8 @@ static void tcindex_destroy_fexts(struct rcu_head *head)
        struct tcindex_filter *f = container_of(head, struct tcindex_filter,
                                                rcu);
 
-       tcf_exts_destroy(&f->result.exts);
-       kfree(f);
+       INIT_WORK(&f->work, tcindex_destroy_fexts_work);
+       tcf_queue_work(&f->work);
 }
 
 static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last)