net: rtnetlink: allow rtnl_fill_statsinfo to save private state counter

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>
This commit is contained in:
Nikolay Aleksandrov 2016-04-30 10:25:26 +02:00 committed by David S. Miller
parent d1ac3b16d5
commit e8872a25a0

View 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);
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 if_stats_msg *ifsm;
struct hlist_head *head;
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;