net: rtnetlink: Add UAPI for obtaining L3 offload xstats
authorPetr Machata <petrm@nvidia.com>
Wed, 2 Mar 2022 16:31:21 +0000 (18:31 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 Mar 2022 10:37:23 +0000 (10:37 +0000)
Add a new IFLA_STATS_LINK_OFFLOAD_XSTATS child attribute,
IFLA_OFFLOAD_XSTATS_L3_STATS, to carry statistics for traffic that takes
place in a HW router.

The offloaded HW stats are designed to allow per-netdevice enablement and
disablement. Additionally, as a netdevice is configured, it may become or
cease being suitable for binding of a HW counter. Both of these aspects
need to be communicated to the userspace. To that end, add another child
attribute, IFLA_OFFLOAD_XSTATS_HW_S_INFO:

    - attr nest IFLA_OFFLOAD_XSTATS_HW_S_INFO
- attr nest IFLA_OFFLOAD_XSTATS_L3_STATS
      - attr IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST
      - {0,1} as u8
      - attr IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED
      - {0,1} as u8

Thus this one attribute is a nest that can be used to carry information
about various types of HW statistics, and indexing is very simply done by
wrapping the information for a given statistics suite into the attribute
that carries the suite is the RTM_GETSTATS query. At the same time, because
_HW_S_INFO is nested directly below IFLA_STATS_LINK_OFFLOAD_XSTATS, it is
possible through filtering to request only the metadata about individual
statistics suites, without having to hit the HW to get the actual counters.

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/uapi/linux/if_link.h
net/core/rtnetlink.c

index ef6a62a..b1031f4 100644 (file)
@@ -1249,10 +1249,21 @@ enum {
 enum {
        IFLA_OFFLOAD_XSTATS_UNSPEC,
        IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
+       IFLA_OFFLOAD_XSTATS_HW_S_INFO,  /* HW stats info. A nest */
+       IFLA_OFFLOAD_XSTATS_L3_STATS,   /* struct rtnl_hw_stats64 */
        __IFLA_OFFLOAD_XSTATS_MAX
 };
 #define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
 
+enum {
+       IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC,
+       IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST,          /* u8 */
+       IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED,             /* u8 */
+       __IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX,
+};
+#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \
+       (__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1)
+
 /* XDP section */
 
 #define XDP_FLAGS_UPDATE_IF_NOEXIST    (1U << 0)
index 4db1d6c..9ce894a 100644 (file)
@@ -5091,10 +5091,110 @@ rtnl_offload_xstats_fill_ndo(struct net_device *dev, int attr_id,
        return 0;
 }
 
+static unsigned int
+rtnl_offload_xstats_get_size_stats(const struct net_device *dev,
+                                  enum netdev_offload_xstats_type type)
+{
+       bool enabled = netdev_offload_xstats_enabled(dev, type);
+
+       return enabled ? sizeof(struct rtnl_hw_stats64) : 0;
+}
+
+struct rtnl_offload_xstats_request_used {
+       bool request;
+       bool used;
+};
+
+static int
+rtnl_offload_xstats_get_stats(struct net_device *dev,
+                             enum netdev_offload_xstats_type type,
+                             struct rtnl_offload_xstats_request_used *ru,
+                             struct rtnl_hw_stats64 *stats,
+                             struct netlink_ext_ack *extack)
+{
+       bool request;
+       bool used;
+       int err;
+
+       request = netdev_offload_xstats_enabled(dev, type);
+       if (!request) {
+               used = false;
+               goto out;
+       }
+
+       err = netdev_offload_xstats_get(dev, type, stats, &used, extack);
+       if (err)
+               return err;
+
+out:
+       if (ru) {
+               ru->request = request;
+               ru->used = used;
+       }
+       return 0;
+}
+
+static int
+rtnl_offload_xstats_fill_hw_s_info_one(struct sk_buff *skb, int attr_id,
+                                      struct rtnl_offload_xstats_request_used *ru)
+{
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, attr_id);
+       if (!nest)
+               return -EMSGSIZE;
+
+       if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, ru->request))
+               goto nla_put_failure;
+
+       if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, ru->used))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
+static int
+rtnl_offload_xstats_fill_hw_s_info(struct sk_buff *skb, struct net_device *dev,
+                                  struct netlink_ext_ack *extack)
+{
+       enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
+       struct rtnl_offload_xstats_request_used ru_l3;
+       struct nlattr *nest;
+       int err;
+
+       err = rtnl_offload_xstats_get_stats(dev, t_l3, &ru_l3, NULL, extack);
+       if (err)
+               return err;
+
+       nest = nla_nest_start(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+       if (!nest)
+               return -EMSGSIZE;
+
+       if (rtnl_offload_xstats_fill_hw_s_info_one(skb,
+                                                  IFLA_OFFLOAD_XSTATS_L3_STATS,
+                                                  &ru_l3))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
 static int rtnl_offload_xstats_fill(struct sk_buff *skb, struct net_device *dev,
                                    int *prividx, u32 off_filter_mask,
                                    struct netlink_ext_ack *extack)
 {
+       enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
+       int attr_id_hw_s_info = IFLA_OFFLOAD_XSTATS_HW_S_INFO;
+       int attr_id_l3_stats = IFLA_OFFLOAD_XSTATS_L3_STATS;
        int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT;
        bool have_data = false;
        int err;
@@ -5111,6 +5211,40 @@ static int rtnl_offload_xstats_fill(struct sk_buff *skb, struct net_device *dev,
                }
        }
 
+       if (*prividx <= attr_id_hw_s_info &&
+           (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_hw_s_info))) {
+               *prividx = attr_id_hw_s_info;
+
+               err = rtnl_offload_xstats_fill_hw_s_info(skb, dev, extack);
+               if (err)
+                       return err;
+
+               have_data = true;
+               *prividx = 0;
+       }
+
+       if (*prividx <= attr_id_l3_stats &&
+           (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_l3_stats))) {
+               unsigned int size_l3;
+               struct nlattr *attr;
+
+               *prividx = attr_id_l3_stats;
+
+               size_l3 = rtnl_offload_xstats_get_size_stats(dev, t_l3);
+               attr = nla_reserve_64bit(skb, attr_id_l3_stats, size_l3,
+                                        IFLA_OFFLOAD_XSTATS_UNSPEC);
+               if (!attr)
+                       return -EMSGSIZE;
+
+               err = rtnl_offload_xstats_get_stats(dev, t_l3, NULL,
+                                                   nla_data(attr), extack);
+               if (err)
+                       return err;
+
+               have_data = true;
+               *prividx = 0;
+       }
+
        if (!have_data)
                return -ENODATA;
 
@@ -5118,9 +5252,35 @@ static int rtnl_offload_xstats_fill(struct sk_buff *skb, struct net_device *dev,
        return 0;
 }
 
+static unsigned int
+rtnl_offload_xstats_get_size_hw_s_info_one(const struct net_device *dev,
+                                          enum netdev_offload_xstats_type type)
+{
+       bool enabled = netdev_offload_xstats_enabled(dev, type);
+
+       return nla_total_size(0) +
+               /* IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST */
+               nla_total_size(sizeof(u8)) +
+               /* IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED */
+               (enabled ? nla_total_size(sizeof(u8)) : 0) +
+               0;
+}
+
+static unsigned int
+rtnl_offload_xstats_get_size_hw_s_info(const struct net_device *dev)
+{
+       enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
+
+       return nla_total_size(0) +
+               /* IFLA_OFFLOAD_XSTATS_L3_STATS */
+               rtnl_offload_xstats_get_size_hw_s_info_one(dev, t_l3) +
+               0;
+}
+
 static int rtnl_offload_xstats_get_size(const struct net_device *dev,
                                        u32 off_filter_mask)
 {
+       enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
        int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT;
        int nla_size = 0;
        int size;
@@ -5131,6 +5291,16 @@ static int rtnl_offload_xstats_get_size(const struct net_device *dev,
                nla_size += nla_total_size_64bit(size);
        }
 
+       if (off_filter_mask &
+           IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO))
+               nla_size += rtnl_offload_xstats_get_size_hw_s_info(dev);
+
+       if (off_filter_mask &
+           IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_L3_STATS)) {
+               size = rtnl_offload_xstats_get_size_stats(dev, t_l3);
+               nla_size += nla_total_size_64bit(size);
+       }
+
        if (nla_size != 0)
                nla_size += nla_total_size(0);