rtnl: use nla_put_u64_64bit()
[cascardo/linux.git] / net / core / rtnetlink.c
index a75f7e9..9efc1f3 100644 (file)
@@ -808,11 +808,6 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
        a->rx_nohandler = b->rx_nohandler;
 }
 
-static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)
-{
-       memcpy(v, b, sizeof(*b));
-}
-
 /* All VF info */
 static inline int rtnl_vfinfo_size(const struct net_device *dev,
                                   u32 ext_filter_mask)
@@ -830,17 +825,17 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                         nla_total_size(sizeof(struct ifla_vf_link_state)) +
                         nla_total_size(sizeof(struct ifla_vf_rss_query_en)) +
                         /* IFLA_VF_STATS_RX_PACKETS */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         /* IFLA_VF_STATS_TX_PACKETS */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         /* IFLA_VF_STATS_RX_BYTES */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         /* IFLA_VF_STATS_TX_BYTES */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         /* IFLA_VF_STATS_BROADCAST */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         /* IFLA_VF_STATS_MULTICAST */
-                        nla_total_size(sizeof(__u64)) +
+                        nla_total_size_64bit(sizeof(__u64)) +
                         nla_total_size(sizeof(struct ifla_vf_trust)));
                return size;
        } else
@@ -883,7 +878,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
               + nla_total_size(IFNAMSIZ) /* IFLA_QDISC */
               + nla_total_size(sizeof(struct rtnl_link_ifmap))
               + nla_total_size(sizeof(struct rtnl_link_stats))
-              + nla_total_size(sizeof(struct rtnl_link_stats64))
+              + nla_total_size_64bit(sizeof(struct rtnl_link_stats64))
               + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
               + nla_total_size(MAX_ADDR_LEN) /* IFLA_BROADCAST */
               + nla_total_size(4) /* IFLA_TXQLEN */
@@ -1054,25 +1049,23 @@ static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
 static noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb,
                                              struct net_device *dev)
 {
-       const struct rtnl_link_stats64 *stats;
-       struct rtnl_link_stats64 temp;
+       struct rtnl_link_stats64 *sp;
        struct nlattr *attr;
 
-       stats = dev_get_stats(dev, &temp);
-
-       attr = nla_reserve(skb, IFLA_STATS,
-                          sizeof(struct rtnl_link_stats));
+       attr = nla_reserve_64bit(skb, IFLA_STATS64,
+                                sizeof(struct rtnl_link_stats64), IFLA_PAD);
        if (!attr)
                return -EMSGSIZE;
 
-       copy_rtnl_link_stats(nla_data(attr), stats);
+       sp = nla_data(attr);
+       dev_get_stats(dev, sp);
 
-       attr = nla_reserve(skb, IFLA_STATS64,
-                          sizeof(struct rtnl_link_stats64));
+       attr = nla_reserve(skb, IFLA_STATS,
+                          sizeof(struct rtnl_link_stats));
        if (!attr)
                return -EMSGSIZE;
 
-       copy_rtnl_link_stats64(nla_data(attr), stats);
+       copy_rtnl_link_stats(nla_data(attr), sp);
 
        return 0;
 }
@@ -1160,18 +1153,18 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
                nla_nest_cancel(skb, vfinfo);
                return -EMSGSIZE;
        }
-       if (nla_put_u64(skb, IFLA_VF_STATS_RX_PACKETS,
-                       vf_stats.rx_packets) ||
-           nla_put_u64(skb, IFLA_VF_STATS_TX_PACKETS,
-                       vf_stats.tx_packets) ||
-           nla_put_u64(skb, IFLA_VF_STATS_RX_BYTES,
-                       vf_stats.rx_bytes) ||
-           nla_put_u64(skb, IFLA_VF_STATS_TX_BYTES,
-                       vf_stats.tx_bytes) ||
-           nla_put_u64(skb, IFLA_VF_STATS_BROADCAST,
-                       vf_stats.broadcast) ||
-           nla_put_u64(skb, IFLA_VF_STATS_MULTICAST,
-                       vf_stats.multicast))
+       if (nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_PACKETS,
+                             vf_stats.rx_packets, IFLA_VF_STATS_PAD) ||
+           nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_PACKETS,
+                             vf_stats.tx_packets, IFLA_VF_STATS_PAD) ||
+           nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_BYTES,
+                             vf_stats.rx_bytes, IFLA_VF_STATS_PAD) ||
+           nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_BYTES,
+                             vf_stats.tx_bytes, IFLA_VF_STATS_PAD) ||
+           nla_put_u64_64bit(skb, IFLA_VF_STATS_BROADCAST,
+                             vf_stats.broadcast, IFLA_VF_STATS_PAD) ||
+           nla_put_u64_64bit(skb, IFLA_VF_STATS_MULTICAST,
+                             vf_stats.multicast, IFLA_VF_STATS_PAD))
                return -EMSGSIZE;
        nla_nest_end(skb, vfstats);
        nla_nest_end(skb, vf);
@@ -3451,6 +3444,154 @@ out:
        return err;
 }
 
+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)
+{
+       struct if_stats_msg *ifsm;
+       struct nlmsghdr *nlh;
+       struct nlattr *attr;
+
+       ASSERT_RTNL();
+
+       nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifsm), flags);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       ifsm = nlmsg_data(nlh);
+       ifsm->ifindex = dev->ifindex;
+       ifsm->filter_mask = filter_mask;
+
+       if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_64)) {
+               struct rtnl_link_stats64 *sp;
+
+               attr = nla_reserve_64bit(skb, IFLA_STATS_LINK_64,
+                                        sizeof(struct rtnl_link_stats64),
+                                        IFLA_STATS_UNSPEC);
+               if (!attr)
+                       goto nla_put_failure;
+
+               sp = nla_data(attr);
+               dev_get_stats(dev, sp);
+       }
+
+       nlmsg_end(skb, nlh);
+
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+
+       return -EMSGSIZE;
+}
+
+static const struct nla_policy ifla_stats_policy[IFLA_STATS_MAX + 1] = {
+       [IFLA_STATS_LINK_64]    = { .len = sizeof(struct rtnl_link_stats64) },
+};
+
+static size_t if_nlmsg_stats_size(const struct net_device *dev,
+                                 u32 filter_mask)
+{
+       size_t size = 0;
+
+       if (filter_mask & IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_64))
+               size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64));
+
+       return size;
+}
+
+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;
+       struct sk_buff *nskb;
+       u32 filter_mask;
+       int err;
+
+       ifsm = nlmsg_data(nlh);
+       if (ifsm->ifindex > 0)
+               dev = __dev_get_by_index(net, ifsm->ifindex);
+       else
+               return -EINVAL;
+
+       if (!dev)
+               return -ENODEV;
+
+       filter_mask = ifsm->filter_mask;
+       if (!filter_mask)
+               return -EINVAL;
+
+       nskb = nlmsg_new(if_nlmsg_stats_size(dev, filter_mask), GFP_KERNEL);
+       if (!nskb)
+               return -ENOBUFS;
+
+       err = rtnl_fill_statsinfo(nskb, dev, RTM_NEWSTATS,
+                                 NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
+                                 0, filter_mask);
+       if (err < 0) {
+               /* -EMSGSIZE implies BUG in if_nlmsg_stats_size */
+               WARN_ON(err == -EMSGSIZE);
+               kfree_skb(nskb);
+       } else {
+               err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid);
+       }
+
+       return err;
+}
+
+static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       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;
+       u32 filter_mask = 0;
+       int err;
+
+       s_h = cb->args[0];
+       s_idx = cb->args[1];
+
+       cb->seq = net->dev_base_seq;
+
+       ifsm = nlmsg_data(cb->nlh);
+       filter_mask = ifsm->filter_mask;
+       if (!filter_mask)
+               return -EINVAL;
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               hlist_for_each_entry(dev, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS,
+                                                 NETLINK_CB(cb->skb).portid,
+                                                 cb->nlh->nlmsg_seq, 0,
+                                                 flags, filter_mask);
+                       /* If we ran out of room on the first message,
+                        * we're in trouble
+                        */
+                       WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
+
+                       if (err < 0)
+                               goto out;
+
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+                       idx++;
+               }
+       }
+out:
+       cb->args[1] = idx;
+       cb->args[0] = h;
+
+       return skb->len;
+}
+
 /* Process one rtnetlink message. */
 
 static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -3600,4 +3741,7 @@ void __init rtnetlink_init(void)
        rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
        rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
        rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
+
+       rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump,
+                     NULL);
 }