net: rtnetlink: allow rtnl_fill_statsinfo to save private state counter
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Sat, 30 Apr 2016 08:25:26 +0000 (10:25 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 May 2016 02:27:06 +0000 (22:27 -0400)
The new prividx argument allows the current dumping device to save a
private state counter which would enable it to continue dumping from
where it left off. And the idxattr is used to save the current idx user
so multiple prividx using attributes can be requested at the same time
as suggested by Roopa Prabhu.

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

index 5503dfe6a050136a45182c0a86914e732e392381..de529a20cd186e0495944ffa35981d37df461e08 100644 (file)
@@ -3444,13 +3444,21 @@ out:
        return err;
 }
 
+static bool stats_attr_valid(unsigned int mask, int attrid, int idxattr)
+{
+       return (mask & IFLA_STATS_FILTER_BIT(attrid)) &&
+              (!idxattr || idxattr == attrid);
+}
+
 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)
+                              unsigned int flags, unsigned int filter_mask,
+                              int *idxattr, int *prividx)
 {
        struct if_stats_msg *ifsm;
        struct nlmsghdr *nlh;
        struct nlattr *attr;
+       int s_prividx = *prividx;
 
        ASSERT_RTNL();
 
@@ -3462,7 +3470,7 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
        ifsm->ifindex = dev->ifindex;
        ifsm->filter_mask = filter_mask;
 
-       if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_64)) {
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, *idxattr)) {
                struct rtnl_link_stats64 *sp;
 
                attr = nla_reserve_64bit(skb, IFLA_STATS_LINK_64,
@@ -3480,7 +3488,11 @@ static int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
        return 0;
 
 nla_put_failure:
-       nlmsg_cancel(skb, nlh);
+       /* not a multi message or no progress mean a real error */
+       if (!(flags & NLM_F_MULTI) || s_prividx == *prividx)
+               nlmsg_cancel(skb, nlh);
+       else
+               nlmsg_end(skb, nlh);
 
        return -EMSGSIZE;
 }
@@ -3494,7 +3506,7 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
 {
        size_t size = 0;
 
-       if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_64))
+       if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, 0))
                size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64));
 
        return size;
@@ -3503,8 +3515,9 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev,
 static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
-       struct if_stats_msg *ifsm;
        struct net_device *dev = NULL;
+       int idxattr = 0, prividx = 0;
+       struct if_stats_msg *ifsm;
        struct sk_buff *nskb;
        u32 filter_mask;
        int err;
@@ -3528,7 +3541,7 @@ static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        err = rtnl_fill_statsinfo(nskb, dev, RTM_NEWSTATS,
                                  NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
-                                 0, filter_mask);
+                                 0, filter_mask, &idxattr, &prividx);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in if_nlmsg_stats_size */
                WARN_ON(err == -EMSGSIZE);
@@ -3542,18 +3555,19 @@ static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh)
 
 static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       int h, s_h, err, s_idx, s_idxattr, s_prividx;
        struct net *net = sock_net(skb->sk);
+       unsigned int flags = NLM_F_MULTI;
        struct if_stats_msg *ifsm;
-       int h, s_h;
-       int idx = 0, s_idx;
-       struct net_device *dev;
        struct hlist_head *head;
-       unsigned int flags = NLM_F_MULTI;
+       struct net_device *dev;
        u32 filter_mask = 0;
-       int err;
+       int idx = 0;
 
        s_h = cb->args[0];
        s_idx = cb->args[1];
+       s_idxattr = cb->args[2];
+       s_prividx = cb->args[3];
 
        cb->seq = net->dev_base_seq;
 
@@ -3571,7 +3585,8 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS,
                                                  NETLINK_CB(cb->skb).portid,
                                                  cb->nlh->nlmsg_seq, 0,
-                                                 flags, filter_mask);
+                                                 flags, filter_mask,
+                                                 &s_idxattr, &s_prividx);
                        /* If we ran out of room on the first message,
                         * we're in trouble
                         */
@@ -3579,13 +3594,16 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
                        if (err < 0)
                                goto out;
-
+                       s_prividx = 0;
+                       s_idxattr = 0;
                        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
 cont:
                        idx++;
                }
        }
 out:
+       cb->args[3] = s_prividx;
+       cb->args[2] = s_idxattr;
        cb->args[1] = idx;
        cb->args[0] = h;