net: rtnetlink: add linkxstats callbacks and attribute
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Sat, 30 Apr 2016 08:25:27 +0000 (10:25 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 May 2016 02:27:06 +0000 (22:27 -0400)
Add callbacks to calculate the size and fill link extended statistics
which can be split into multiple messages and are dumped via the new
rtnl stats API (RTM_GETSTATS) with the IFLA_STATS_LINK_XSTATS attribute.
Also add that attribute to the idx mask check since it is expected to
be able to save state and resume dumping (e.g. future bridge per-vlan
stats will be dumped via this attribute and callbacks).
Each link type should nest its private attributes under the per-link type
attribute. This allows to have any number of separated private attributes
and to avoid one call to get the dev link type.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/rtnetlink.h
include/uapi/linux/if_link.h
net/core/rtnetlink.c

index 2f87c1ba13de639df7b733ad3905c7e47b8d901b..006a7b81d758a29119a3d7e6458db15f83aa66e6 100644 (file)
@@ -47,6 +47,9 @@ static inline int rtnl_msg_family(const struct nlmsghdr *nlh)
  *     @get_num_rx_queues: Function to determine number of receive queues
  *                         to create when creating a new device.
  *     @get_link_net: Function to get the i/o netns of the device
+ *     @get_linkxstats_size: Function to calculate the required room for
+ *                           dumping device-specific extended link stats
+ *     @fill_linkxstats: Function to dump device-specific extended link stats
  */
 struct rtnl_link_ops {
        struct list_head        list;
@@ -95,6 +98,10 @@ struct rtnl_link_ops {
                                                   const struct net_device *dev,
                                                   const struct net_device *slave_dev);
        struct net              *(*get_link_net)(const struct net_device *dev);
+       size_t                  (*get_linkxstats_size)(const struct net_device *dev);
+       int                     (*fill_linkxstats)(struct sk_buff *skb,
+                                                  const struct net_device *dev,
+                                                  int *prividx);
 };
 
 int __rtnl_link_register(struct rtnl_link_ops *ops);
index 3e80974566bba7f9dfa55774657c6c77d473d20c..2bfdb9c583424c79e0f20f29f8e928dab519f094 100644 (file)
@@ -810,6 +810,7 @@ struct if_stats_msg {
 enum {
        IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
        IFLA_STATS_LINK_64,
+       IFLA_STATS_LINK_XSTATS,
        __IFLA_STATS_MAX,
 };
 
@@ -817,4 +818,15 @@ enum {
 
 #define IFLA_STATS_FILTER_BIT(ATTR)    (1 << (ATTR - 1))
 
+/* These are embedded into IFLA_STATS_LINK_XSTATS:
+ * [IFLA_STATS_LINK_XSTATS]
+ * -> [LINK_XSTATS_TYPE_xxx]
+ *    -> [rtnl link type specific attributes]
+ */
+enum {
+       LINK_XSTATS_TYPE_UNSPEC,
+       __LINK_XSTATS_TYPE_MAX
+};
+#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
+
 #endif /* _UAPI_LINUX_IF_LINK_H */
index de529a20cd186e0495944ffa35981d37df461e08..d471f097c739f09eb2034c79e800f1d64a936c0b 100644 (file)
@@ -3483,6 +3483,26 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
                dev_get_stats(dev, sp);
        }
 
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, *idxattr)) {
+               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);
+                       if (!attr)
+                               goto nla_put_failure;
+
+                       err = ops->fill_linkxstats(skb, dev, prividx);
+                       nla_nest_end(skb, attr);
+                       if (err)
+                               goto nla_put_failure;
+                       *idxattr = 0;
+               }
+       }
+
        nlmsg_end(skb, nlh);
 
        return 0;
@@ -3509,6 +3529,16 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
        if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, 0))
                size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64));
 
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) {
+               const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
+
+               if (ops && ops->get_linkxstats_size) {
+                       size += nla_total_size(ops->get_linkxstats_size(dev));
+                       /* for IFLA_STATS_LINK_XSTATS */
+                       size += nla_total_size(0);
+               }
+       }
+
        return size;
 }