net/sched: sch_api: conditional netlink notifications
authorPedro Tammela <pctammela@mojatatu.com>
Fri, 29 Dec 2023 13:26:42 +0000 (10:26 -0300)
committerJakub Kicinski <kuba@kernel.org>
Thu, 4 Jan 2024 02:36:30 +0000 (18:36 -0800)
Implement conditional netlink notifications for Qdiscs and classes,
which were missing in the initial patches that targeted tc filters and
actions. Notifications will only be built after passing a check for
'rtnl_notify_needed()'.

For both Qdiscs and classes 'get' operations now call a dedicated
notification function as it was not possible to distinguish between
'create' and 'get' before. This distinction is necessary because 'get'
always send a notification.

Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Link: https://lore.kernel.org/r/20231229132642.1489088-2-pctammela@mojatatu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/sched/sch_api.c

index 299086bb6205ff4935112e9158ad4b657115b6ce..2a2a48838eb9d0ca10b5f0c91d949e9f559aeac4 100644 (file)
@@ -1003,6 +1003,32 @@ static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
        return false;
 }
 
+static int qdisc_get_notify(struct net *net, struct sk_buff *oskb,
+                           struct nlmsghdr *n, u32 clid, struct Qdisc *q,
+                           struct netlink_ext_ack *extack)
+{
+       struct sk_buff *skb;
+       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+
+       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       if (!tc_qdisc_dump_ignore(q, false)) {
+               if (tc_fill_qdisc(skb, q, clid, portid, n->nlmsg_seq, 0,
+                                 RTM_NEWQDISC, extack) < 0)
+                       goto err_out;
+       }
+
+       if (skb->len)
+               return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
+                                     n->nlmsg_flags & NLM_F_ECHO);
+
+err_out:
+       kfree_skb(skb);
+       return -EINVAL;
+}
+
 static int qdisc_notify(struct net *net, struct sk_buff *oskb,
                        struct nlmsghdr *n, u32 clid,
                        struct Qdisc *old, struct Qdisc *new,
@@ -1011,6 +1037,9 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
 
+       if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
+               return 0;
+
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb)
                return -ENOBUFS;
@@ -1583,7 +1612,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
                if (err != 0)
                        return err;
        } else {
-               qdisc_notify(net, skb, n, clid, NULL, q, NULL);
+               qdisc_get_notify(net, skb, n, clid, q, NULL);
        }
        return 0;
 }
@@ -1977,6 +2006,9 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb,
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
 
+       if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
+               return 0;
+
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb)
                return -ENOBUFS;
@@ -1990,6 +2022,27 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb,
                              n->nlmsg_flags & NLM_F_ECHO);
 }
 
+static int tclass_get_notify(struct net *net, struct sk_buff *oskb,
+                            struct nlmsghdr *n, struct Qdisc *q,
+                            unsigned long cl, struct netlink_ext_ack *extack)
+{
+       struct sk_buff *skb;
+       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+
+       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, RTM_NEWTCLASS,
+                          extack) < 0) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
+                             n->nlmsg_flags & NLM_F_ECHO);
+}
+
 static int tclass_del_notify(struct net *net,
                             const struct Qdisc_class_ops *cops,
                             struct sk_buff *oskb, struct nlmsghdr *n,
@@ -2003,14 +2056,18 @@ static int tclass_del_notify(struct net *net,
        if (!cops->delete)
                return -EOPNOTSUPP;
 
-       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!skb)
-               return -ENOBUFS;
+       if (rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC)) {
+               skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+               if (!skb)
+                       return -ENOBUFS;
 
-       if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
-                          RTM_DELTCLASS, extack) < 0) {
-               kfree_skb(skb);
-               return -EINVAL;
+               if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
+                                  RTM_DELTCLASS, extack) < 0) {
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+       } else {
+               skb = NULL;
        }
 
        err = cops->delete(q, cl, extack);
@@ -2019,8 +2076,8 @@ static int tclass_del_notify(struct net *net,
                return err;
        }
 
-       err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
-                            n->nlmsg_flags & NLM_F_ECHO);
+       err = rtnetlink_maybe_send(skb, net, portid, RTNLGRP_TC,
+                                  n->nlmsg_flags & NLM_F_ECHO);
        return err;
 }
 
@@ -2215,7 +2272,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
                        tc_bind_tclass(q, portid, clid, 0);
                        goto out;
                case RTM_GETTCLASS:
-                       err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS, extack);
+                       err = tclass_get_notify(net, skb, n, q, cl, extack);
                        goto out;
                default:
                        err = -EINVAL;