Now we have a set
of nested attributes:
    
  IFLA_VFINFO_LIST (NESTED)
    IFLA_VF_INFO (NESTED)
      IFLA_VF_MAC
      IFLA_VF_VLAN
      IFLA_VF_TX_RATE
    
This allows a single set to operate on multiple attributes if desired.
Among other things, it means a dump can be replayed to set state.
    
Signed-off-by: Greg Rose <gregory.v.rose@xxxxxxxxx>
CC: Chris Wright <chrisw@xxxxxxxxxxxx>
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index e77f2b4..d681cc9 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -82,10 +82,7 @@ enum
      IFLA_NET_NS_PID,
      IFLA_IFALIAS,
      IFLA_NUM_VF,            /*
Number of VFs if device is SR-IOV PF */
-     IFLA_VF_MAC,            /*
Hardware queue specific attributes */
-     IFLA_VF_VLAN,
-     IFLA_VF_TX_RATE,  /* TX Bandwidth Allocation */
-     IFLA_VFINFO,
+     IFLA_VFINFO_LIST,
      __IFLA_MAX
 };
 
@@ -197,6 +194,24 @@ struct ifla_vlan_qos_mapping
 
 /* SR-IOV virtual function managment section */
 
+enum {
+     IFLA_VF_INFO_UNSPEC,
+     IFLA_VF_INFO,
+     __IFLA_VF_INFO_MAX,
+};
+
+#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
+
+enum {
+     IFLA_VF_UNSPEC,
+     IFLA_VF_MAC,            /*
Hardware queue specific attributes */
+     IFLA_VF_VLAN,
+     IFLA_VF_TX_RATE,  /* TX Bandwidth Allocation */
+     __IFLA_VF_MAX,
+};
+
+#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
+
 struct ifla_vf_mac {
      __u32 vf;
      __u8 mac[32]; /* MAX_ADDR_LEN */
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index b8541e4..f58325b 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -583,12 +583,18 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats
*a,
      a->tx_compressed = b->tx_compressed;
 };
 
+/* All VF info */
 static inline int rtnl_vfinfo_size(const struct net_device *dev)
 {
-     if (dev->dev.parent &&
dev_is_pci(dev->dev.parent))
-           return dev_num_vf(dev->dev.parent)
*
-                 sizeof(struct
ifla_vf_info);
-     else
+     if (dev->dev.parent &&
dev_is_pci(dev->dev.parent)) {
+           int num_vfs =
dev_num_vf(dev->dev.parent);
+           size_t size =
nlmsg_total_size(sizeof(struct nlattr));
+           size +=
nlmsg_total_size(num_vfs * sizeof(struct nlattr));
+           size += num_vfs *
(sizeof(struct ifla_vf_mac) +
+                        
sizeof(struct ifla_vf_vlan) +
+                        
sizeof(struct ifla_vf_tx_rate));
+           return size;
+     } else
            return 0;
 }
 
@@ -610,7 +616,7 @@ static inline size_t if_nlmsg_size(const struct net_device
*dev)
             +
nla_total_size(1) /* IFLA_OPERSTATE */
             +
nla_total_size(1) /* IFLA_LINKMODE */
             +
nla_total_size(4) /* IFLA_NUM_VF */
-            +
nla_total_size(rtnl_vfinfo_size(dev)) /* IFLA_VFINFO */
+            + rtnl_vfinfo_size(dev)
/* IFLA_VFINFO_LIST */
             +
rtnl_link_get_size(dev); /* IFLA_LINKINFO */
 }
 
@@ -681,14 +687,37 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct
net_device *dev,
 
      if (dev->netdev_ops->ndo_get_vf_config
&& dev->dev.parent) {
            int i;
-           struct
ifla_vf_info ivi;
 
-           NLA_PUT_U32(skb,
IFLA_NUM_VF, dev_num_vf(dev->dev.parent));
-           for (i = 0; i
< dev_num_vf(dev->dev.parent); i++) {
+           struct nlattr
*vfinfo, *vf;
+           int num_vfs =
dev_num_vf(dev->dev.parent);
+
+           NLA_PUT_U32(skb,
IFLA_NUM_VF, num_vfs);
+           vfinfo =
nla_nest_start(skb, IFLA_VFINFO_LIST);
+           if (!vfinfo)
+                 goto
nla_put_failure;
+           for (i = 0; i
< num_vfs; i++) {
+                 struct
ifla_vf_info ivi;
+                 struct
ifla_vf_mac vf_mac;
+                 struct
ifla_vf_vlan vf_vlan;
+                 struct
ifla_vf_tx_rate vf_tx_rate;
                  if
(dev->netdev_ops->ndo_get_vf_config(dev, i, &ivi))
                        break;
-                 NLA_PUT(skb,
IFLA_VFINFO, sizeof(ivi), &ivi);
+                 vf_mac.vf
= vf_vlan.vf = vf_tx_rate.vf = ivi.vf;
+                 memcpy(vf_mac.mac,
ivi.mac, sizeof(ivi.mac));
+                 vf_vlan.vlan
= ivi.vlan;
+                 vf_vlan.qos
= ivi.qos;
+                 vf_tx_rate.rate
= ivi.tx_rate;
+                 vf
= nla_nest_start(skb, IFLA_VF_INFO);
+                 if
(!vf) {
+                       nla_nest_cancel(skb,
vfinfo);
+                       goto
nla_put_failure;
+                 }
+                 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_TX_RATE, sizeof(vf_tx_rate), &vf_tx_rate);
+                 nla_nest_end(skb,
vf);
            }
+           nla_nest_end(skb,
vfinfo);
      }
      if (dev->rtnl_link_ops) {
            if
(rtnl_link_fill(skb, dev) < 0)
@@ -739,6 +768,19 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
      [IFLA_LINKINFO]         =
{ .type = NLA_NESTED },
      [IFLA_NET_NS_PID] = { .type = NLA_U32 },
      [IFLA_IFALIAS]           
= { .type = NLA_STRING, .len = IFALIASZ-1 },
+     [IFLA_VFINFO_LIST]      = {.
type = NLA_NESTED },
+};
+
+static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
+     [IFLA_INFO_KIND]  = { .type = NLA_STRING },
+     [IFLA_INFO_DATA]  = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = {
+     [IFLA_VF_INFO]          =
{ .type = NLA_NESTED },
+};
+
+static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
      [IFLA_VF_MAC]           =
{ .type = NLA_BINARY,
                           
.len = sizeof(struct ifla_vf_mac) },
      [IFLA_VF_VLAN]          =
{ .type = NLA_BINARY,
@@ -747,11 +789,6 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
                           
.len = sizeof(struct ifla_vf_tx_rate) },
 };
 
-static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
-     [IFLA_INFO_KIND]  = { .type = NLA_STRING },
-     [IFLA_INFO_DATA]  = { .type = NLA_NESTED },
-};
-
 static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
 {
      if (dev) {
@@ -767,6 +804,52 @@ static int validate_linkmsg(struct net_device *dev, struct
nlattr *tb[])
      return 0;
 }
 
+static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
+{
+     int rem, err = -EINVAL;
+     struct nlattr *vf;
+     const struct net_device_ops *ops =
dev->netdev_ops;
+
+     nla_for_each_nested(vf, attr, rem) {
+           switch
(nla_type(vf)) {
+           case IFLA_VF_MAC:
{
+                 struct
ifla_vf_mac *ivm;
+                 ivm
= nla_data(vf);
+                 err
= -EOPNOTSUPP;
+                 if
(ops->ndo_set_vf_mac)
+                       err
= ops->ndo_set_vf_mac(dev, ivm->vf,
+                                          
ivm->mac);
+                 break;
+           }
+           case
IFLA_VF_VLAN: {
+                 struct
ifla_vf_vlan *ivv;
+                 ivv
= nla_data(vf);
+                 err
= -EOPNOTSUPP;
+                 if
(ops->ndo_set_vf_vlan)
+                       err
= ops->ndo_set_vf_vlan(dev, ivv->vf,
+                                           
ivv->vlan,
+                                           
ivv->qos);
+                 break;
+           }
+           case
IFLA_VF_TX_RATE: {
+                 struct
ifla_vf_tx_rate *ivt;
+                 ivt
= nla_data(vf);
+                 err
= -EOPNOTSUPP;
+                 if
(ops->ndo_set_vf_tx_rate)
+                       err
= ops->ndo_set_vf_tx_rate(dev, ivt->vf,
+                                              
ivt->rate);
+                 break;
+           }
+           default:
+                 err
= -EINVAL;
+                 break;
+           }
+           if (err)
+                 break;
+     }
+     return err;
+}
+
 static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
                 
struct nlattr **tb, char *ifname, int modified)
 {
@@ -904,43 +987,17 @@ static int do_setlink(struct net_device *dev, struct
ifinfomsg *ifm,
            write_unlock_bh(&dev_base_lock);
      }
 
-     if (tb[IFLA_VF_MAC]) {
-           struct
ifla_vf_mac *ivm;
-           ivm =
nla_data(tb[IFLA_VF_MAC]);
-           write_lock_bh(&dev_base_lock);
-           if
(ops->ndo_set_vf_mac)
-                 err
= ops->ndo_set_vf_mac(dev, ivm->vf, ivm->mac);
-           write_unlock_bh(&dev_base_lock);
-           if (err < 0)
-                 goto
errout;
-           modified = 1;
-     }
-
-     if (tb[IFLA_VF_VLAN]) {
-           struct
ifla_vf_vlan *ivv;
-           ivv =
nla_data(tb[IFLA_VF_VLAN]);
-           write_lock_bh(&dev_base_lock);
-           if
(ops->ndo_set_vf_vlan)
-                 err
= ops->ndo_set_vf_vlan(dev, ivv->vf,
-                                     
(u16)ivv->vlan,
-                                     
(u8)ivv->qos);
-           write_unlock_bh(&dev_base_lock);
-           if (err < 0)
-                 goto
errout;
-           modified = 1;
-     }
-     err = 0;
-
-     if (tb[IFLA_VF_TX_RATE]) {
-           struct
ifla_vf_tx_rate *ivt;
-           ivt =
nla_data(tb[IFLA_VF_TX_RATE]);
-           write_lock_bh(&dev_base_lock);
-           if
(ops->ndo_set_vf_tx_rate)
-                 err
= ops->ndo_set_vf_tx_rate(dev, ivt->vf, ivt->rate);
-           write_unlock_bh(&dev_base_lock);
-           if (err < 0)
-                 goto
errout;
-           modified = 1;
+     if (tb[IFLA_VFINFO_LIST]) {
+           struct nlattr
*attr;
+           int rem;
+           nla_for_each_nested(attr,
tb[IFLA_VFINFO_LIST], rem) {
+                 if
(nla_type(attr) != IFLA_VF_INFO)
+                       goto
errout;
+                 err
= do_setvfinfo(dev, attr);
+                 if
(err < 0)
+                       goto
errout;
+                 modified
= 1;
+           }
      }
      err = 0;