net: core: Add offload stats to if_stats_msg
authorNogah Frankel <nogahf@mellanox.com>
Fri, 16 Sep 2016 13:05:37 +0000 (15:05 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 19 Sep 2016 02:33:42 +0000 (22:33 -0400)
Add a nested attribute of offload stats to if_stats_msg
named IFLA_STATS_LINK_OFFLOAD_XSTATS.
Under it, add SW stats, meaning stats only per packets that went via
slowpath to the cpu, named IFLA_OFFLOAD_XSTATS_CPU_HIT.

Signed-off-by: Nogah Frankel <nogahf@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_link.h
net/core/rtnetlink.c

index 9bf3aecfe05b6f154bda36df1ad7443420d559b4..2351776a724f91fba17868eabf390541238756a9 100644 (file)
@@ -826,6 +826,7 @@ enum {
        IFLA_STATS_LINK_64,
        IFLA_STATS_LINK_XSTATS,
        IFLA_STATS_LINK_XSTATS_SLAVE,
+       IFLA_STATS_LINK_OFFLOAD_XSTATS,
        __IFLA_STATS_MAX,
 };
 
@@ -845,6 +846,14 @@ enum {
 };
 #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
 
+/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */
+enum {
+       IFLA_OFFLOAD_XSTATS_UNSPEC,
+       IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
+       __IFLA_OFFLOAD_XSTATS_MAX
+};
+#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
+
 /* XDP section */
 
 enum {
index 937e459bdaa96e56c3c07932b87a7acb74698a15..0dbae4244a8931d70e9837dede29296a676082d4 100644 (file)
@@ -3577,6 +3577,91 @@ static bool stats_attr_valid(unsigned int mask, int attrid, int idxattr)
               (!idxattr || idxattr == attrid);
 }
 
+#define IFLA_OFFLOAD_XSTATS_FIRST (IFLA_OFFLOAD_XSTATS_UNSPEC + 1)
+static int rtnl_get_offload_stats_attr_size(int attr_id)
+{
+       switch (attr_id) {
+       case IFLA_OFFLOAD_XSTATS_CPU_HIT:
+               return sizeof(struct rtnl_link_stats64);
+       }
+
+       return 0;
+}
+
+static int rtnl_get_offload_stats(struct sk_buff *skb, struct net_device *dev,
+                                 int *prividx)
+{
+       struct nlattr *attr = NULL;
+       int attr_id, size;
+       void *attr_data;
+       int err;
+
+       if (!(dev->netdev_ops && dev->netdev_ops->ndo_has_offload_stats &&
+             dev->netdev_ops->ndo_get_offload_stats))
+               return -ENODATA;
+
+       for (attr_id = IFLA_OFFLOAD_XSTATS_FIRST;
+            attr_id <= IFLA_OFFLOAD_XSTATS_MAX; attr_id++) {
+               if (attr_id < *prividx)
+                       continue;
+
+               size = rtnl_get_offload_stats_attr_size(attr_id);
+               if (!size)
+                       continue;
+
+               if (!dev->netdev_ops->ndo_has_offload_stats(attr_id))
+                       continue;
+
+               attr = nla_reserve_64bit(skb, attr_id, size,
+                                        IFLA_OFFLOAD_XSTATS_UNSPEC);
+               if (!attr)
+                       goto nla_put_failure;
+
+               attr_data = nla_data(attr);
+               memset(attr_data, 0, size);
+               err = dev->netdev_ops->ndo_get_offload_stats(attr_id, dev,
+                                                            attr_data);
+               if (err)
+                       goto get_offload_stats_failure;
+       }
+
+       if (!attr)
+               return -ENODATA;
+
+       *prividx = 0;
+       return 0;
+
+nla_put_failure:
+       err = -EMSGSIZE;
+get_offload_stats_failure:
+       *prividx = attr_id;
+       return err;
+}
+
+static int rtnl_get_offload_stats_size(const struct net_device *dev)
+{
+       int nla_size = 0;
+       int attr_id;
+       int size;
+
+       if (!(dev->netdev_ops && dev->netdev_ops->ndo_has_offload_stats &&
+             dev->netdev_ops->ndo_get_offload_stats))
+               return 0;
+
+       for (attr_id = IFLA_OFFLOAD_XSTATS_FIRST;
+            attr_id <= IFLA_OFFLOAD_XSTATS_MAX; attr_id++) {
+               if (!dev->netdev_ops->ndo_has_offload_stats(attr_id))
+                       continue;
+               size = rtnl_get_offload_stats_attr_size(attr_id);
+               nla_size += nla_total_size_64bit(size);
+       }
+
+       if (nla_size != 0)
+               nla_size += nla_total_size(0);
+
+       return nla_size;
+}
+
 static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
                               int type, u32 pid, u32 seq, u32 change,
                               unsigned int flags, unsigned int filter_mask,
@@ -3586,6 +3671,7 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
        struct nlmsghdr *nlh;
        struct nlattr *attr;
        int s_prividx = *prividx;
+       int err;
 
        ASSERT_RTNL();
 
@@ -3614,8 +3700,6 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
                const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
 
                if (ops && ops->fill_linkxstats) {
-                       int err;
-
                        *idxattr = IFLA_STATS_LINK_XSTATS;
                        attr = nla_nest_start(skb,
                                              IFLA_STATS_LINK_XSTATS);
@@ -3639,8 +3723,6 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
                if (master)
                        ops = master->rtnl_link_ops;
                if (ops && ops->fill_linkxstats) {
-                       int err;
-
                        *idxattr = IFLA_STATS_LINK_XSTATS_SLAVE;
                        attr = nla_nest_start(skb,
                                              IFLA_STATS_LINK_XSTATS_SLAVE);
@@ -3655,6 +3737,24 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
                }
        }
 
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS,
+                            *idxattr)) {
+               *idxattr = IFLA_STATS_LINK_OFFLOAD_XSTATS;
+               attr = nla_nest_start(skb, IFLA_STATS_LINK_OFFLOAD_XSTATS);
+               if (!attr)
+                       goto nla_put_failure;
+
+               err = rtnl_get_offload_stats(skb, dev, prividx);
+               if (err == -ENODATA)
+                       nla_nest_cancel(skb, attr);
+               else
+                       nla_nest_end(skb, attr);
+
+               if (err && err != -ENODATA)
+                       goto nla_put_failure;
+               *idxattr = 0;
+       }
+
        nlmsg_end(skb, nlh);
 
        return 0;
@@ -3708,6 +3808,9 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
                }
        }
 
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS, 0))
+               size += rtnl_get_offload_stats_size(dev);
+
        return size;
 }