net: sched: set dedicated tcf_walker flag when tp is empty
authorVlad Buslov <vladbu@mellanox.com>
Mon, 25 Feb 2019 15:38:31 +0000 (17:38 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Feb 2019 18:18:17 +0000 (10:18 -0800)
Using tcf_walker->stop flag to determine when tcf_walker->fn() was called
at least once is unreliable. Some classifiers set 'stop' flag on error
before calling walker callback, other classifiers used to call it with NULL
filter pointer when empty. In order to prevent further regressions, extend
tcf_walker structure with dedicated 'nonempty' flag. Set this flag in
tcf_walker->fn() implementation that is used to check if classifier has
filters configured.

Fixes: 8b64678e0af8 ("net: sched: refactor tp insert/delete for concurrent execution")
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Suggested-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/pkt_cls.h
net/sched/cls_api.c

index 58ea48e1221c5303c5dffc71c4077384988b9362..d5e7a1af346fe06b6ad8f8e091a3ad8a77e7d399 100644 (file)
@@ -17,6 +17,7 @@ struct tcf_walker {
        int     stop;
        int     skip;
        int     count;
+       bool    nonempty;
        unsigned long cookie;
        int     (*fn)(struct tcf_proto *, void *node, struct tcf_walker *);
 };
index 6593c245f71427ab1976388e6689768012d5189c..97d9312c2ff90e2a907397fdeb9761e818d1ae2e 100644 (file)
@@ -238,18 +238,23 @@ static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held,
                tcf_proto_destroy(tp, rtnl_held, extack);
 }
 
-static int walker_noop(struct tcf_proto *tp, void *d, struct tcf_walker *arg)
+static int walker_check_empty(struct tcf_proto *tp, void *d,
+                             struct tcf_walker *arg)
 {
-       return -1;
+       if (tp) {
+               arg->nonempty = true;
+               return -1;
+       }
+       return 0;
 }
 
 static bool tcf_proto_is_empty(struct tcf_proto *tp, bool rtnl_held)
 {
-       struct tcf_walker walker = { .fn = walker_noop, };
+       struct tcf_walker walker = { .fn = walker_check_empty, };
 
        if (tp->ops->walk) {
                tp->ops->walk(tp, &walker, rtnl_held);
-               return !walker.stop;
+               return !walker.nonempty;
        }
        return true;
 }