]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/core/rtnetlink.c
Merge tag 'xfs-for-linus-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[mirror_ubuntu-artful-kernel.git] / net / core / rtnetlink.c
index 1dfca1c3f8f5dce66ce4b29644310a1fa40db2b8..b06d2f46b83e98c4fb6a92cefd625c2918eafdeb 100644 (file)
@@ -843,7 +843,10 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                size += nla_total_size(num_vfs * sizeof(struct nlattr));
                size += num_vfs *
                        (nla_total_size(sizeof(struct ifla_vf_mac)) +
-                        nla_total_size(sizeof(struct ifla_vf_vlan)) +
+                        nla_total_size(MAX_VLAN_LIST_LEN *
+                                       sizeof(struct nlattr)) +
+                        nla_total_size(MAX_VLAN_LIST_LEN *
+                                       sizeof(struct ifla_vf_vlan_info)) +
                         nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
                         nla_total_size(sizeof(struct ifla_vf_rate)) +
                         nla_total_size(sizeof(struct ifla_vf_link_state)) +
@@ -1111,14 +1114,15 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
                                               struct nlattr *vfinfo)
 {
        struct ifla_vf_rss_query_en vf_rss_query_en;
+       struct nlattr *vf, *vfstats, *vfvlanlist;
        struct ifla_vf_link_state vf_linkstate;
+       struct ifla_vf_vlan_info vf_vlan_info;
        struct ifla_vf_spoofchk vf_spoofchk;
        struct ifla_vf_tx_rate vf_tx_rate;
        struct ifla_vf_stats vf_stats;
        struct ifla_vf_trust vf_trust;
        struct ifla_vf_vlan vf_vlan;
        struct ifla_vf_rate vf_rate;
-       struct nlattr *vf, *vfstats;
        struct ifla_vf_mac vf_mac;
        struct ifla_vf_info ivi;
 
@@ -1135,11 +1139,14 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
         * IFLA_VF_LINK_STATE_AUTO which equals zero
         */
        ivi.linkstate = 0;
+       /* VLAN Protocol by default is 802.1Q */
+       ivi.vlan_proto = htons(ETH_P_8021Q);
        if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
                return 0;
 
        vf_mac.vf =
                vf_vlan.vf =
+               vf_vlan_info.vf =
                vf_rate.vf =
                vf_tx_rate.vf =
                vf_spoofchk.vf =
@@ -1150,6 +1157,9 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
        vf_vlan.vlan = ivi.vlan;
        vf_vlan.qos = ivi.qos;
+       vf_vlan_info.vlan = ivi.vlan;
+       vf_vlan_info.qos = ivi.qos;
+       vf_vlan_info.vlan_proto = ivi.vlan_proto;
        vf_tx_rate.rate = ivi.max_tx_rate;
        vf_rate.min_tx_rate = ivi.min_tx_rate;
        vf_rate.max_tx_rate = ivi.max_tx_rate;
@@ -1158,10 +1168,8 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
        vf_rss_query_en.setting = ivi.rss_query_en;
        vf_trust.setting = ivi.trusted;
        vf = nla_nest_start(skb, IFLA_VF_INFO);
-       if (!vf) {
-               nla_nest_cancel(skb, vfinfo);
-               return -EMSGSIZE;
-       }
+       if (!vf)
+               goto nla_put_vfinfo_failure;
        if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) ||
            nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) ||
            nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate),
@@ -1177,17 +1185,23 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
                    &vf_rss_query_en) ||
            nla_put(skb, IFLA_VF_TRUST,
                    sizeof(vf_trust), &vf_trust))
-               return -EMSGSIZE;
+               goto nla_put_vf_failure;
+       vfvlanlist = nla_nest_start(skb, IFLA_VF_VLAN_LIST);
+       if (!vfvlanlist)
+               goto nla_put_vf_failure;
+       if (nla_put(skb, IFLA_VF_VLAN_INFO, sizeof(vf_vlan_info),
+                   &vf_vlan_info)) {
+               nla_nest_cancel(skb, vfvlanlist);
+               goto nla_put_vf_failure;
+       }
+       nla_nest_end(skb, vfvlanlist);
        memset(&vf_stats, 0, sizeof(vf_stats));
        if (dev->netdev_ops->ndo_get_vf_stats)
                dev->netdev_ops->ndo_get_vf_stats(dev, vfs_num,
                                                &vf_stats);
        vfstats = nla_nest_start(skb, IFLA_VF_STATS);
-       if (!vfstats) {
-               nla_nest_cancel(skb, vf);
-               nla_nest_cancel(skb, vfinfo);
-               return -EMSGSIZE;
-       }
+       if (!vfstats)
+               goto nla_put_vf_failure;
        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,
@@ -1199,11 +1213,19 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
            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;
+                             vf_stats.multicast, IFLA_VF_STATS_PAD)) {
+               nla_nest_cancel(skb, vfstats);
+               goto nla_put_vf_failure;
+       }
        nla_nest_end(skb, vfstats);
        nla_nest_end(skb, vf);
        return 0;
+
+nla_put_vf_failure:
+       nla_nest_cancel(skb, vf);
+nla_put_vfinfo_failure:
+       nla_nest_cancel(skb, vfinfo);
+       return -EMSGSIZE;
 }
 
 static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
@@ -1448,6 +1470,7 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
 static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
        [IFLA_VF_MAC]           = { .len = sizeof(struct ifla_vf_mac) },
        [IFLA_VF_VLAN]          = { .len = sizeof(struct ifla_vf_vlan) },
+       [IFLA_VF_VLAN_LIST]     = { .type = NLA_NESTED },
        [IFLA_VF_TX_RATE]       = { .len = sizeof(struct ifla_vf_tx_rate) },
        [IFLA_VF_SPOOFCHK]      = { .len = sizeof(struct ifla_vf_spoofchk) },
        [IFLA_VF_RATE]          = { .len = sizeof(struct ifla_vf_rate) },
@@ -1704,7 +1727,37 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
                err = -EOPNOTSUPP;
                if (ops->ndo_set_vf_vlan)
                        err = ops->ndo_set_vf_vlan(dev, ivv->vf, ivv->vlan,
-                                                  ivv->qos);
+                                                  ivv->qos,
+                                                  htons(ETH_P_8021Q));
+               if (err < 0)
+                       return err;
+       }
+
+       if (tb[IFLA_VF_VLAN_LIST]) {
+               struct ifla_vf_vlan_info *ivvl[MAX_VLAN_LIST_LEN];
+               struct nlattr *attr;
+               int rem, len = 0;
+
+               err = -EOPNOTSUPP;
+               if (!ops->ndo_set_vf_vlan)
+                       return err;
+
+               nla_for_each_nested(attr, tb[IFLA_VF_VLAN_LIST], rem) {
+                       if (nla_type(attr) != IFLA_VF_VLAN_INFO ||
+                           nla_len(attr) < NLA_HDRLEN) {
+                               return -EINVAL;
+                       }
+                       if (len >= MAX_VLAN_LIST_LEN)
+                               return -EOPNOTSUPP;
+                       ivvl[len] = nla_data(attr);
+
+                       len++;
+               }
+               if (len == 0)
+                       return -EINVAL;
+
+               err = ops->ndo_set_vf_vlan(dev, ivvl[0]->vf, ivvl[0]->vlan,
+                                          ivvl[0]->qos, ivvl[0]->vlan_proto);
                if (err < 0)
                        return err;
        }
@@ -3577,6 +3630,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 +3724,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 +3753,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 +3776,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 +3790,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;
@@ -3669,10 +3822,6 @@ nla_put_failure:
        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)
 {
@@ -3712,6 +3861,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;
 }