Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[deliverable/linux.git] / net / core / rtnetlink.c
index 7aef62e53113475f343a053a3cae866c8b4a3394..5cf39cd7da85e85d7dc5e913a80735eb9acb2838 100644 (file)
@@ -60,7 +60,6 @@ struct rtnl_link {
 };
 
 static DEFINE_MUTEX(rtnl_mutex);
-static u16 min_ifinfo_dump_size;
 
 void rtnl_lock(void)
 {
@@ -724,10 +723,11 @@ static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)
 }
 
 /* All VF info */
-static inline int rtnl_vfinfo_size(const struct net_device *dev)
+static inline int rtnl_vfinfo_size(const struct net_device *dev,
+                                  u32 ext_filter_mask)
 {
-       if (dev->dev.parent && dev_is_pci(dev->dev.parent)) {
-
+       if (dev->dev.parent && dev_is_pci(dev->dev.parent) &&
+           (ext_filter_mask & RTEXT_FILTER_VF)) {
                int num_vfs = dev_num_vf(dev->dev.parent);
                size_t size = nla_total_size(sizeof(struct nlattr));
                size += nla_total_size(num_vfs * sizeof(struct nlattr));
@@ -766,7 +766,8 @@ static size_t rtnl_port_size(const struct net_device *dev)
                return port_self_size;
 }
 
-static noinline size_t if_nlmsg_size(const struct net_device *dev)
+static noinline size_t if_nlmsg_size(const struct net_device *dev,
+                                    u32 ext_filter_mask)
 {
        return NLMSG_ALIGN(sizeof(struct ifinfomsg))
               + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
@@ -784,8 +785,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev)
               + nla_total_size(4) /* IFLA_MASTER */
               + nla_total_size(1) /* IFLA_OPERSTATE */
               + nla_total_size(1) /* IFLA_LINKMODE */
-              + nla_total_size(4) /* IFLA_NUM_VF */
-              + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */
+              + nla_total_size(ext_filter_mask
+                               & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+              + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
               + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
               + rtnl_link_get_size(dev) /* IFLA_LINKINFO */
               + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */
@@ -868,7 +870,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev)
 
 static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                            int type, u32 pid, u32 seq, u32 change,
-                           unsigned int flags)
+                           unsigned int flags, u32 ext_filter_mask)
 {
        struct ifinfomsg *ifm;
        struct nlmsghdr *nlh;
@@ -941,10 +943,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                goto nla_put_failure;
        copy_rtnl_link_stats64(nla_data(attr), stats);
 
-       if (dev->dev.parent)
+       if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF))
                NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent));
 
-       if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) {
+       if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent
+           && (ext_filter_mask & RTEXT_FILTER_VF)) {
                int i;
 
                struct nlattr *vfinfo, *vf;
@@ -1048,6 +1051,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        struct net_device *dev;
        struct hlist_head *head;
        struct hlist_node *node;
+       struct nlattr *tb[IFLA_MAX+1];
+       u32 ext_filter_mask = 0;
 
        s_h = cb->args[0];
        s_idx = cb->args[1];
@@ -1055,6 +1060,12 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        rcu_read_lock();
        cb->seq = net->dev_base_seq;
 
+       nlmsg_parse(cb->nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX,
+                   ifla_policy);
+
+       if (tb[IFLA_EXT_MASK])
+               ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+
        for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
                idx = 0;
                head = &net->dev_index_head[h];
@@ -1064,7 +1075,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
                        if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
                                             NETLINK_CB(cb->skb).pid,
                                             cb->nlh->nlmsg_seq, 0,
-                                            NLM_F_MULTI) <= 0)
+                                            NLM_F_MULTI,
+                                            ext_filter_mask) <= 0)
                                goto out;
 
                        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@@ -1100,6 +1112,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_VF_PORTS]         = { .type = NLA_NESTED },
        [IFLA_PORT_SELF]        = { .type = NLA_NESTED },
        [IFLA_AF_SPEC]          = { .type = NLA_NESTED },
+       [IFLA_EXT_MASK]         = { .type = NLA_U32 },
 };
 EXPORT_SYMBOL(ifla_policy);
 
@@ -1509,8 +1522,6 @@ errout:
 
        if (send_addr_notify)
                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
-       min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev),
-                                    min_ifinfo_dump_size);
 
        return err;
 }
@@ -1842,6 +1853,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        struct net_device *dev = NULL;
        struct sk_buff *nskb;
        int err;
+       u32 ext_filter_mask = 0;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
        if (err < 0)
@@ -1850,6 +1862,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (tb[IFLA_IFNAME])
                nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
 
+       if (tb[IFLA_EXT_MASK])
+               ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
                dev = __dev_get_by_index(net, ifm->ifi_index);
@@ -1861,12 +1876,12 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (dev == NULL)
                return -ENODEV;
 
-       nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
+       nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);
        if (nskb == NULL)
                return -ENOBUFS;
 
        err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid,
-                              nlh->nlmsg_seq, 0, 0);
+                              nlh->nlmsg_seq, 0, 0, ext_filter_mask);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in if_nlmsg_size */
                WARN_ON(err == -EMSGSIZE);
@@ -1877,8 +1892,31 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        return err;
 }
 
-static u16 rtnl_calcit(struct sk_buff *skb)
+static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
+       struct net *net = sock_net(skb->sk);
+       struct net_device *dev;
+       struct nlattr *tb[IFLA_MAX+1];
+       u32 ext_filter_mask = 0;
+       u16 min_ifinfo_dump_size = 0;
+
+       nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX, ifla_policy);
+
+       if (tb[IFLA_EXT_MASK])
+               ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+
+       if (!ext_filter_mask)
+               return NLMSG_GOODSIZE;
+       /*
+        * traverse the list of net devices and compute the minimum
+        * buffer size based upon the filter mask.
+        */
+       list_for_each_entry(dev, &net->dev_base_head, dev_list) {
+               min_ifinfo_dump_size = max_t(u16, min_ifinfo_dump_size,
+                                            if_nlmsg_size(dev,
+                                                          ext_filter_mask));
+       }
+
        return min_ifinfo_dump_size;
 }
 
@@ -1913,13 +1951,11 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
        int err = -ENOBUFS;
        size_t if_info_size;
 
-       skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL);
+       skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL);
        if (skb == NULL)
                goto errout;
 
-       min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size);
-
-       err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0);
+       err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in if_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -1977,7 +2013,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        return -EOPNOTSUPP;
                calcit = rtnl_get_calcit(family, type);
                if (calcit)
-                       min_dump_alloc = calcit(skb);
+                       min_dump_alloc = calcit(skb, nlh);
 
                __rtnl_unlock();
                rtnl = net->rtnl;
This page took 0.053322 seconds and 5 git commands to generate.