Register netlink protocol handlers for message types RTM_NEWTFILTER,
RTM_DELTFILTER, RTM_GETTFILTER as unlocked. Set rtnl_held variable that
tracks rtnl mutex state to be false by default.

Modify tcf_block_release() to release rtnl lock if it was taken before.
Move code that releases block and qdisc to function __tcf_block_release()
that is used internally by regular block release and by chain update
function, which is not unlocked and doesn't need to release rtnl.

Signed-off-by: Vlad Buslov <vla...@mellanox.com>
Acked-by: Jiri Pirko <j...@mellanox.com>
---
 net/sched/cls_api.c | 43 ++++++++++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 848f148f1019..a23aeac8ea4e 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -1024,13 +1024,28 @@ static struct tcf_block *tcf_block_find(struct net 
*net, struct Qdisc **q,
        return ERR_PTR(err);
 }
 
-static void tcf_block_release(struct Qdisc *q, struct tcf_block *block)
+static void __tcf_block_release(struct Qdisc *q, struct tcf_block *block,
+                               bool rtnl_held)
 {
        if (!IS_ERR_OR_NULL(block))
                tcf_block_refcnt_put(block);
 
-       if (q)
-               qdisc_put(q);
+       if (q) {
+               if (rtnl_held)
+                       qdisc_put(q);
+               else
+                       qdisc_put_unlocked(q);
+       }
+}
+
+static void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
+                             bool *rtnl_held)
+{
+       if (*rtnl_held) {
+               rtnl_unlock();
+               *rtnl_held = false;
+       }
+       __tcf_block_release(q, block, false);
 }
 
 struct tcf_block_owner_item {
@@ -1706,7 +1721,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
        void *fh;
        int err;
        int tp_created;
-       bool rtnl_held = true;
+       bool rtnl_held = false;
 
        if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -1865,7 +1880,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
                if (!tp_created)
                        tcf_chain_put(chain);
        }
-       tcf_block_release(q, block);
+       tcf_block_release(q, block, &rtnl_held);
        if (err == -EAGAIN) {
                /* Take rtnl lock in case EAGAIN is caused by concurrent flush
                 * of target chain.
@@ -1899,7 +1914,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
        unsigned long cl = 0;
        void *fh = NULL;
        int err;
-       bool rtnl_held = true;
+       bool rtnl_held = false;
 
        if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -2011,7 +2026,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
                        tcf_proto_put(tp, NULL);
                tcf_chain_put(chain);
        }
-       tcf_block_release(q, block);
+       tcf_block_release(q, block, &rtnl_held);
        return err;
 
 errout_locked:
@@ -2037,7 +2052,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
        unsigned long cl = 0;
        void *fh = NULL;
        int err;
-       bool rtnl_held = true;
+       bool rtnl_held = false;
 
        err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack);
        if (err < 0)
@@ -2112,7 +2127,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct 
nlmsghdr *n,
                        tcf_proto_put(tp, NULL);
                tcf_chain_put(chain);
        }
-       tcf_block_release(q, block);
+       tcf_block_release(q, block, &rtnl_held);
        return err;
 }
 
@@ -2561,7 +2576,7 @@ static int tc_ctl_chain(struct sk_buff *skb, struct 
nlmsghdr *n,
 errout:
        tcf_chain_put(chain);
 errout_block:
-       tcf_block_release(q, block);
+       __tcf_block_release(q, block, true);
        if (err == -EAGAIN)
                /* Replay the request. */
                goto replay;
@@ -2899,10 +2914,12 @@ static int __init tc_filter_init(void)
        if (err)
                goto err_register_pernet_subsys;
 
-       rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 0);
-       rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0);
+       rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL,
+                     RTNL_FLAG_DOIT_UNLOCKED);
+       rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL,
+                     RTNL_FLAG_DOIT_UNLOCKED);
        rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
-                     tc_dump_tfilter, 0);
+                     tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
        rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
-- 
2.7.5

Reply via email to