net: rtnetlink: Add UAPI toggle for IFLA_OFFLOAD_XSTATS_L3_STATS
authorPetr Machata <petrm@nvidia.com>
Wed, 2 Mar 2022 16:31:23 +0000 (18:31 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 Mar 2022 10:37:23 +0000 (10:37 +0000)
The offloaded HW stats are designed to allow per-netdevice enablement and
disablement. Add an attribute, IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS,
which should be carried by the RTM_SETSTATS message, and expresses a desire
to toggle L3 offload xstats on or off.

As part of the above, add an exported function rtnl_offload_xstats_notify()
that drivers can use when they have installed or deinstalled the counters
backing the HW stats.

At this point, it is possible to enable, disable and query L3 offload
xstats on netdevices. (However there is no driver actually implementing
these.)

Signed-off-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/rtnetlink.h
include/uapi/linux/if_link.h
include/uapi/linux/rtnetlink.h
net/core/rtnetlink.c

index bb9cb84114c151059ccac74ca07a113ea22c347a..7f970b16da3a26863559f1254b310bf4796e566b 100644 (file)
@@ -134,4 +134,7 @@ extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                   int (*vlan_fill)(struct sk_buff *skb,
                                                    struct net_device *dev,
                                                    u32 filter_mask));
+
+extern void rtnl_offload_xstats_notify(struct net_device *dev);
+
 #endif /* __LINUX_RTNETLINK_H */
index b1031f481d2f277582049fad8bc5d272f7fadd24..ddca20357e7e89b5f204b3117ff3838735535470 100644 (file)
@@ -1227,6 +1227,7 @@ enum {
        IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with
                                 * a filter mask for the corresponding group.
                                 */
+       IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */
        __IFLA_STATS_GETSET_MAX,
 };
 
index 14462dc159fd1aa7a96c1fd0a6d20596a569e77d..51530aade46e0160b1b3496897da25ec32a059e8 100644 (file)
@@ -767,6 +767,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_MCTP_IFADDR    RTNLGRP_MCTP_IFADDR
        RTNLGRP_TUNNEL,
 #define RTNLGRP_TUNNEL         RTNLGRP_TUNNEL
+       RTNLGRP_STATS,
+#define RTNLGRP_STATS          RTNLGRP_STATS
        __RTNLGRP_MAX
 };
 #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
index d093545143557f9a5d7f7caeb67504270a3556b3..a66b6761b88b1c63c916fc085f4d9e8523bb0659 100644 (file)
@@ -5566,6 +5566,7 @@ rtnl_stats_get_policy[IFLA_STATS_GETSET_MAX + 1] = {
 
 static const struct nla_policy
 ifla_stats_set_policy[IFLA_STATS_GETSET_MAX + 1] = {
+       [IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS] = NLA_POLICY_MAX(NLA_U8, 1),
 };
 
 static int rtnl_stats_get_parse_filters(struct nlattr *ifla_filters,
@@ -5773,16 +5774,51 @@ out:
        return skb->len;
 }
 
+void rtnl_offload_xstats_notify(struct net_device *dev)
+{
+       struct rtnl_stats_dump_filters response_filters = {};
+       struct net *net = dev_net(dev);
+       int idxattr = 0, prividx = 0;
+       struct sk_buff *skb;
+       int err = -ENOBUFS;
+
+       ASSERT_RTNL();
+
+       response_filters.mask[0] |=
+               IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS);
+       response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |=
+               IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+
+       skb = nlmsg_new(if_nlmsg_stats_size(dev, &response_filters),
+                       GFP_KERNEL);
+       if (!skb)
+               goto errout;
+
+       err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS, 0, 0, 0, 0,
+                                 &response_filters, &idxattr, &prividx, NULL);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto errout;
+       }
+
+       rtnl_notify(skb, net, 0, RTNLGRP_STATS, NULL, GFP_KERNEL);
+       return;
+
+errout:
+       rtnl_set_sk_err(net, RTNLGRP_STATS, err);
+}
+EXPORT_SYMBOL(rtnl_offload_xstats_notify);
+
 static int rtnl_stats_set(struct sk_buff *skb, struct nlmsghdr *nlh,
                          struct netlink_ext_ack *extack)
 {
+       enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
        struct rtnl_stats_dump_filters response_filters = {};
        struct nlattr *tb[IFLA_STATS_GETSET_MAX + 1];
        struct net *net = sock_net(skb->sk);
        struct net_device *dev = NULL;
-       int idxattr = 0, prividx = 0;
        struct if_stats_msg *ifsm;
-       struct sk_buff *nskb;
+       bool notify = false;
        int err;
 
        err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb),
@@ -5814,24 +5850,29 @@ static int rtnl_stats_set(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (err < 0)
                return err;
 
-       nskb = nlmsg_new(if_nlmsg_stats_size(dev, &response_filters),
-                        GFP_KERNEL);
-       if (!nskb)
-               return -ENOBUFS;
+       if (tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]) {
+               u8 req = nla_get_u8(tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]);
 
-       err = rtnl_fill_statsinfo(nskb, dev, RTM_NEWSTATS,
-                                 NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
-                                 0, &response_filters, &idxattr, &prividx,
-                                 extack);
-       if (err < 0) {
-               /* -EMSGSIZE implies BUG in if_nlmsg_stats_size */
-               WARN_ON(err == -EMSGSIZE);
-               kfree_skb(nskb);
-       } else {
-               err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid);
+               if (req)
+                       err = netdev_offload_xstats_enable(dev, t_l3, extack);
+               else
+                       err = netdev_offload_xstats_disable(dev, t_l3);
+
+               if (!err)
+                       notify = true;
+               else if (err != -EALREADY)
+                       return err;
+
+               response_filters.mask[0] |=
+                       IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS);
+               response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |=
+                       IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO);
        }
 
-       return err;
+       if (notify)
+               rtnl_offload_xstats_notify(dev);
+
+       return 0;
 }
 
 /* Process one rtnetlink message. */