[PATCH 1/5] lib/route: SRIOV Parse and Read support

Jef Oliver jef.oliver at intel.com
Tue Nov 1 13:01:36 PDT 2016


* This patch adds support for parsing SRIOV VF specific
  information on a link.
  * Adds LINK_ATTR_VF_LIST to add to link->ce_mask.
  * Extends the rtnl_link object to include 'l_vf_list', a member
    to carry information for SRIOV VFs.
  * Adds rtnl_link_sriov, a private structure to fill link->l_vf_list.

* This patch adds support for reading parsed SRIOV VF specific
  informatino on a link.

* This patch adds support for freeing stored SRIOV VF specific
  information on a link.

Signed-off-by: Jef Oliver <jef.oliver at intel.com>
---
 include/Makefile.am                        |   2 +
 include/linux-private/linux/if_ether.h     |   1 +
 include/linux-private/linux/if_link.h      |  44 ++
 include/netlink-private/route/link/sriov.h |  30 ++
 include/netlink-private/types.h            |  23 +
 include/netlink/route/link.h               |   4 +
 include/netlink/route/link/sriov.h         | 112 +++++
 lib/Makefile.am                            |   2 +-
 lib/route/link.c                           |  37 ++
 lib/route/link/sriov.c                     | 753 +++++++++++++++++++++++++++++
 libnl-route-3.sym                          |  21 +
 11 files changed, 1028 insertions(+), 1 deletion(-)
 create mode 100644 include/netlink-private/route/link/sriov.h
 create mode 100644 include/netlink/route/link/sriov.h
 create mode 100644 lib/route/link/sriov.c

diff --git a/include/Makefile.am b/include/Makefile.am
index fafb90a..9b0bc39 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -64,6 +64,7 @@ nobase_libnlinclude_HEADERS = \
 	netlink/route/link/sit.h \
 	netlink/route/link/ipvlan.h \
 	netlink/route/link/vrf.h \
+	netlink/route/link/sriov.h \
 	netlink/route/qdisc/cbq.h \
 	netlink/route/qdisc/dsmark.h \
 	netlink/route/qdisc/fifo.h \
@@ -171,4 +172,5 @@ noinst_HEADERS = \
 	netlink-private/cache-api.h \
 	netlink-private/object-api.h \
 	netlink-private/route/link/api.h \
+	netlink-private/route/link/sriov.h \
 	netlink-private/route/tc-api.h
diff --git a/include/linux-private/linux/if_ether.h b/include/linux-private/linux/if_ether.h
index a6af32d..7d82bd7 100644
--- a/include/linux-private/linux/if_ether.h
+++ b/include/linux-private/linux/if_ether.h
@@ -78,6 +78,7 @@
 					 */
 #define ETH_P_PAE	0x888E		/* Port Access Entity (IEEE 802.1X) */
 #define ETH_P_AOE	0x88A2		/* ATA over Ethernet		*/
+#define ETH_P_8021AD	0x88A8		/* 802.1ad Service VLAN		*/
 #define ETH_P_TIPC	0x88CA		/* TIPC 			*/
 #define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
 #define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
diff --git a/include/linux-private/linux/if_link.h b/include/linux-private/linux/if_link.h
index cd7213d..9871073 100644
--- a/include/linux-private/linux/if_link.h
+++ b/include/linux-private/linux/if_link.h
@@ -505,6 +505,11 @@ enum {
 	IFLA_VF_RSS_QUERY_EN,	/* RSS Redirection Table and Hash Key query
 				 * on/off switch
 				 */
+	IFLA_VF_STATS,		/* network device statistics */
+	IFLA_VF_TRUST,		/* Trust VF */
+	IFLA_VF_IB_NODE_GUID,	/* VF Infiniband node GUID */
+	IFLA_VF_IB_PORT_GUID,	/* VF Infiniband port GUID */
+	IFLA_VF_VLAN_LIST,	/* nested list of vlans, option for QinQ */
 	__IFLA_VF_MAX,
 };
 
@@ -521,6 +526,22 @@ struct ifla_vf_vlan {
 	__u32 qos;
 };
 
+enum {
+	IFLA_VF_VLAN_INFO_UNSPEC,
+	IFLA_VF_VLAN_INFO,	/* VLAN ID, QoS and VLAN protocol */
+	__IFLA_VF_VLAN_INFO_MAX,
+};
+
+#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1)
+#define MAX_VLAN_LIST_LEN 1
+
+struct ifla_vf_vlan_info {
+	__u32 vf;
+	__u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+	__u32 qos;
+	__be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */
+};
+
 struct ifla_vf_tx_rate {
 	__u32 vf;
 	__u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
@@ -537,6 +558,11 @@ struct ifla_vf_spoofchk {
 	__u32 setting;
 };
 
+struct ifla_vf_guid {
+	__u32 vf;
+	__u64 guid;
+};
+
 enum {
 	IFLA_VF_LINK_STATE_AUTO,	/* link state of the uplink */
 	IFLA_VF_LINK_STATE_ENABLE,	/* link always up */
@@ -554,6 +580,24 @@ struct ifla_vf_rss_query_en {
 	__u32 setting;
 };
 
+enum {
+	IFLA_VF_STATS_RX_PACKETS,
+	IFLA_VF_STATS_TX_PACKETS,
+	IFLA_VF_STATS_RX_BYTES,
+	IFLA_VF_STATS_TX_BYTES,
+	IFLA_VF_STATS_BROADCAST,
+	IFLA_VF_STATS_MULTICAST,
+	IFLA_VF_STATS_PAD,
+	__IFLA_VF_STATS_MAX,
+};
+
+#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
+
+struct ifla_vf_trust {
+	__u32 vf;
+	__u32 setting;
+};
+
 /* VF ports management section
  *
  *	Nested layout of set/get msg is:
diff --git a/include/netlink-private/route/link/sriov.h b/include/netlink-private/route/link/sriov.h
new file mode 100644
index 0000000..49e601b
--- /dev/null
+++ b/include/netlink-private/route/link/sriov.h
@@ -0,0 +1,30 @@
+/*
+ * include/netlink-private/route/link/sriov.h      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver at intel.com>
+ */
+
+#ifndef NETLINK_PRIV_LINK_SRIOV_H_
+#define NETLINK_PRIV_LINK_SRIOV_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link/sriov.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void rtnl_link_sriov_free_data(struct rtnl_link *);
+extern int rtnl_link_sriov_parse_vflist(struct rtnl_link *, struct nlattr **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index e80c8a1..3ec6661 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -22,6 +22,7 @@
 #include <netlink/netfilter/ct.h>
 #include <netlink-private/object-api.h>
 #include <netlink-private/route/tc-api.h>
+#include <netlink-private/route/link/sriov.h>
 #include <linux/tc_act/tc_mirred.h>
 #include <linux/tc_act/tc_skbedit.h>
 
@@ -36,6 +37,7 @@ struct nl_cache_ops;
 struct nl_sock;
 struct nl_object;
 struct nl_hash_table;
+struct nl_vf_vlans;
 
 struct nl_cb
 {
@@ -154,6 +156,26 @@ struct rtnl_link_map
 	uint8_t  lm_port;
 };
 
+struct rtnl_link_vf
+{
+	struct nl_list_head	vf_list;
+	int			ce_refcnt;
+	uint32_t		ce_mask;
+	uint32_t		vf_index;
+	uint64_t		vf_guid_node;
+	uint64_t		vf_guid_port;
+	uint32_t		vf_linkstate;
+	struct nl_addr *	vf_lladdr;
+	uint32_t		vf_max_tx_rate;
+	uint32_t		vf_min_tx_rate;
+	uint32_t		vf_rate;
+	uint32_t		vf_rss_query_en;
+	uint32_t		vf_spoofchk;
+	uint64_t		vf_stats[RTNL_LINK_VF_STATS_MAX+1];
+	uint32_t		vf_trust;
+	struct nl_vf_vlans *	vf_vlans;
+};
+
 #define IFQDISCSIZ	32
 
 struct rtnl_link
@@ -197,6 +219,7 @@ struct rtnl_link
 	struct nl_data *		l_phys_port_id;
 	int				l_ns_fd;
 	pid_t				l_ns_pid;
+	struct rtnl_link_vf *		l_vf_list;
 };
 
 struct rtnl_ncacheinfo
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index 23b0183..42eb17f 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -244,6 +244,10 @@ extern int	rtnl_link_release(struct nl_sock *, struct rtnl_link *);
 extern int	rtnl_link_fill_info(struct nl_msg *, struct rtnl_link *);
 extern int	rtnl_link_info_parse(struct rtnl_link *, struct nlattr **);
 
+extern int rtnl_link_has_vf_list(struct rtnl_link *);
+extern void rtnl_link_set_vf_list(struct rtnl_link *);
+extern void rtnl_link_unset_vf_list(struct rtnl_link *);
+
 
 /* deprecated */
 extern int	rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated));
diff --git a/include/netlink/route/link/sriov.h b/include/netlink/route/link/sriov.h
new file mode 100644
index 0000000..71d0dbe
--- /dev/null
+++ b/include/netlink/route/link/sriov.h
@@ -0,0 +1,112 @@
+/*
+ * include/netlink/route/link/sriov.h      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver at intel.com>
+ */
+
+#ifndef NETLINK_LINK_SRIOV_H_
+#define NETLINK_LINK_SRIOV_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup sriov
+ */
+typedef enum {
+	RTNL_LINK_VF_RATE_API_UNSPEC,	/*!< Unspecified API type */
+	RTNL_LINK_VF_RATE_API_OLD,	/*!< Old Rate setting API */
+	RTNL_LINK_VF_RATE_API_NEW,	/*!< New Rate setting API */
+	__RTNL_LINK_VF_RATE_API_MAX,
+} rtnl_link_rate_api_t;
+
+#define RTNL_LINK_VF_RATE_API_MAX (__RTNL_LINK_VF_RATE_API_MAX - 1)
+
+/**
+ * @ingroup sriov
+ */
+typedef enum {
+	RTNL_LINK_VF_STATS_RX_PACKETS,	/*!< Packets Received */
+	RTNL_LINK_VF_STATS_TX_PACKETS,	/*!< Packets Sent */
+	RTNL_LINK_VF_STATS_RX_BYTES,	/*!< Bytes Recieved */
+	RTNL_LINK_VF_STATS_TX_BYTES,	/*!< Bytes Sent */
+	RTNL_LINK_VF_STATS_BROADCAST,	/*!< Broadcast packets received */
+	RTNL_LINK_VF_STATS_MULTICAST,	/*!< Multicast packets received */
+	__RTNL_LINK_VF_STATS_MAX,
+} rtnl_link_vf_stats_t;
+
+#define RTNL_LINK_VF_STATS_MAX (__RTNL_LINK_VF_STATS_MAX - 1)
+
+/**
+ * @struct rtnl_link_vf sriov.h "netlink/route/link/sriov.h"
+ * @brief SRIOV VF object
+ * @ingroup sriov
+ *
+ * @copydoc private_struct
+ */
+struct rtnl_link_vf;
+
+/**
+ * @brief SRIOV VF VFLAN settings
+ * @ingroup sriov
+ */
+typedef struct nl_vf_vlan_info {
+	uint32_t	vf_vlan;	/*!< VLAN number */
+	uint32_t	vf_vlan_qos;	/*!< VLAN QOS value */
+	uint16_t	vf_vlan_proto;	/*!< VLAN protocol */
+} nl_vf_vlan_info_t;
+
+/**
+ * @brief SRIOV VF VLANs information
+ * @ingroup sriov
+ */
+typedef struct nl_vf_vlans {
+	int			ce_refcnt;	/*!< Reference counter. Don't change this value */
+	int			size;		/*!< Number of VLANs on the SRIOV VF */
+	nl_vf_vlan_info_t *	vlans;		/*!< nl_vf_vlan_info_t array of SRIOV VF VLANs */
+} nl_vf_vlans_t;
+
+/**
+ * @brief VF Rate information structure
+ * @ingroup sriov
+ */
+struct nl_vf_rate {
+	int		api;		/*!< rtnl_link_rate_api_t API Version to use */
+	uint32_t	rate;		/*!< Old API Max Rate in Mbps */
+	uint32_t	max_tx_rate;	/*!< New API Max Rate in Mbps */
+	uint32_t	min_tx_rate;	/*!< New API Mix Rate in Mbps */
+};
+
+extern struct rtnl_link_vf *rtnl_link_vf_alloc(void);
+extern void rtnl_link_vf_free(struct rtnl_link_vf *);
+extern struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *, uint32_t);
+extern void rtnl_link_vf_put(struct rtnl_link_vf *);
+extern int rtnl_link_vf_get_addr(struct rtnl_link_vf *, struct nl_addr **);
+extern int rtnl_link_vf_get_index(struct rtnl_link_vf *, uint32_t *);
+extern int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *, uint32_t *);
+extern int rtnl_link_vf_get_rate(struct rtnl_link_vf *, struct nl_vf_rate *);
+extern int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *, uint32_t *);
+extern int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *, uint32_t *);
+extern int rtnl_link_vf_get_stat(struct rtnl_link_vf *, rtnl_link_vf_stats_t,
+				 uint64_t *);
+extern int rtnl_link_vf_get_trust(struct rtnl_link_vf *, uint32_t *);
+extern int rtnl_link_vf_get_vlans(struct rtnl_link_vf *, nl_vf_vlans_t **);
+
+extern int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **, int);
+extern void rtnl_link_vf_vlan_put(nl_vf_vlans_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 21d28ca..b14a991 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -92,7 +92,7 @@ libnl_route_3_la_SOURCES = \
 	route/addr.c route/class.c route/cls.c route/act.c route/link.c \
 	route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \
 	route/route.c route/route_obj.c route/route_utils.c route/rtnl.c \
-	route/rule.c route/tc.c route/classid.c \
+	route/rule.c route/tc.c route/classid.c route/link/sriov.c \
 	\
 	route/cls/fw.c route/cls/police.c route/cls/u32.c route/cls/basic.c \
 	route/cls/cgroup.c \
diff --git a/lib/route/link.c b/lib/route/link.c
index 90d7a55..ba871af 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -28,6 +28,7 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/link.h>
 #include <netlink-private/route/link/api.h>
+#include <netlink-private/route/link/sriov.h>
 
 /** @cond SKIP */
 #define LINK_ATTR_MTU		(1 <<  0)
@@ -63,6 +64,7 @@
 #define LINK_ATTR_NS_PID	(1 << 30)
 /* 31 used by 32-bit api */
 #define LINK_ATTR_LINK_NETNSID  ((uint64_t) 1 << 32)
+#define LINK_ATTR_VF_LIST	((uint64_t) 1 << 33)
 
 static struct nl_cache_ops rtnl_link_ops;
 static struct nl_object_ops link_obj_ops;
@@ -272,6 +274,9 @@ static void link_free_data(struct nl_object *c)
 		do_foreach_af(link, af_free, NULL);
 
 		nl_data_free(link->l_phys_port_id);
+
+		if (link->ce_mask & LINK_ATTR_VF_LIST)
+			rtnl_link_sriov_free_data(link);
 	}
 }
 
@@ -331,6 +336,7 @@ struct nla_policy rtln_link_policy[IFLA_MAX+1] = {
 	[IFLA_MAP]		= { .minlen = sizeof(struct rtnl_link_ifmap) },
 	[IFLA_IFALIAS]		= { .type = NLA_STRING, .maxlen = IFALIASZ },
 	[IFLA_NUM_VF]		= { .type = NLA_U32 },
+	[IFLA_VFINFO_LIST]	= { .type = NLA_NESTED },
 	[IFLA_AF_SPEC]		= { .type = NLA_NESTED },
 	[IFLA_PROMISCUITY]	= { .type = NLA_U32 },
 	[IFLA_NUM_TX_QUEUES]	= { .type = NLA_U32 },
@@ -584,6 +590,12 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 	if (tb[IFLA_NUM_VF]) {
 		link->l_num_vf = nla_get_u32(tb[IFLA_NUM_VF]);
 		link->ce_mask |= LINK_ATTR_NUM_VF;
+		if (link->l_num_vf && tb[IFLA_VFINFO_LIST]) {
+			if ((err = rtnl_link_sriov_parse_vflist(link, tb)) < 0) {
+				goto errout;
+			}
+			link->ce_mask |= LINK_ATTR_VF_LIST;
+		}
 	}
 
 	if (tb[IFLA_LINKINFO]) {
@@ -2882,6 +2894,31 @@ int rtnl_link_str2carrier(const char *name)
 	return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states));
 }
 
+int rtnl_link_has_vf_list(struct rtnl_link *link) {
+	if (link->ce_mask & LINK_ATTR_VF_LIST)
+		return 1;
+	else
+		return 0;
+}
+
+void rtnl_link_set_vf_list(struct rtnl_link *link) {
+	int err;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		link->ce_mask |= LINK_ATTR_VF_LIST;
+
+	return;
+}
+
+void rtnl_link_unset_vf_list(struct rtnl_link *link) {
+	int err;
+
+	if ((err = rtnl_link_has_vf_list(link)))
+		link->ce_mask &= ~LINK_ATTR_VF_LIST;
+
+	return;
+}
+
 /** @} */
 
 /**
diff --git a/lib/route/link/sriov.c b/lib/route/link/sriov.c
new file mode 100644
index 0000000..528de83
--- /dev/null
+++ b/lib/route/link/sriov.c
@@ -0,0 +1,753 @@
+/*
+ * lib/route/link/sriov.c      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver at intel.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup sriov SRIOV
+ * SR-IOV VF link module
+ *
+ * @details
+ * SR-IOV (Single Root Input/Output Virtualization) is a network interface
+ * that allows for the isolation of the PCI Express resources. In a virtual
+ * environment, SR-IOV allows multiple virtual machines can share a single
+ * PCI Express hardware interface. This is done via VFs (Virtual Functions),
+ * virtual hardware devices with their own PCI address.
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <netlink-private/route/link/sriov.h>
+#include <netlink/route/link/sriov.h>
+
+/** @cond SKIP */
+
+#define SET_VF_STAT(link, vf_num, stb, stat, attr) \
+	vf_data->vf_stats[stat] = nla_get_u64(stb[attr])
+
+/* SRIOV-VF Attributes */
+#define SRIOV_ATTR_INDEX 		(1 <<  0)
+#define SRIOV_ATTR_ADDR 		(1 <<  1)
+#define SRIOV_ATTR_VLAN 		(1 <<  2)
+#define SRIOV_ATTR_TX_RATE 		(1 <<  3)
+#define SRIOV_ATTR_SPOOFCHK 		(1 <<  4)
+#define SRIOV_ATTR_RATE_MAX 		(1 <<  5)
+#define SRIOV_ATTR_RATE_MIN 		(1 <<  6)
+#define SRIOV_ATTR_LINK_STATE 		(1 <<  7)
+#define SRIOV_ATTR_RSS_QUERY_EN 	(1 <<  8)
+#define SRIOV_ATTR_STATS 		(1 <<  9)
+#define SRIOV_ATTR_TRUST 		(1 << 10)
+
+static struct nla_policy sriov_info_policy[IFLA_VF_MAX+1] = {
+	[IFLA_VF_MAC]		= { .minlen = sizeof(struct ifla_vf_mac) },
+	[IFLA_VF_VLAN]		= { .minlen = sizeof(struct ifla_vf_vlan) },
+	[IFLA_VF_VLAN_LIST]     = { .type = NLA_NESTED },
+	[IFLA_VF_TX_RATE]	= { .minlen = sizeof(struct ifla_vf_tx_rate) },
+	[IFLA_VF_SPOOFCHK]	= { .minlen = sizeof(struct ifla_vf_spoofchk) },
+	[IFLA_VF_RATE]		= { .minlen = sizeof(struct ifla_vf_rate) },
+	[IFLA_VF_LINK_STATE]	= { .minlen = sizeof(struct ifla_vf_link_state) },
+	[IFLA_VF_RSS_QUERY_EN]	= { .minlen = sizeof(struct ifla_vf_rss_query_en) },
+	[IFLA_VF_STATS]		= { .type = NLA_NESTED },
+	[IFLA_VF_TRUST]		= { .minlen = sizeof(struct ifla_vf_trust) },
+	[IFLA_VF_IB_NODE_GUID]	= { .minlen = sizeof(struct ifla_vf_guid) },
+	[IFLA_VF_IB_PORT_GUID]	= { .minlen = sizeof(struct ifla_vf_guid) },
+};
+
+static struct nla_policy sriov_stats_policy[IFLA_VF_STATS_MAX+1] = {
+	[IFLA_VF_STATS_RX_PACKETS]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_TX_PACKETS]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_RX_BYTES]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_TX_BYTES]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_BROADCAST]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_MULTICAST]	= { .type = NLA_U64 },
+};
+
+/** @endcond */
+
+/* Free stored SRIOV VF data */
+void rtnl_link_sriov_free_data(struct rtnl_link *link) {
+	int err = 0;
+	struct rtnl_link_vf *list, *vf, *next;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		return;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) {
+		nl_list_del(&vf->vf_list);
+		rtnl_link_vf_put(vf);
+	}
+
+	rtnl_link_vf_put(link->l_vf_list);
+
+	return;
+}
+
+/* Fill VLAN info array */
+static int rtnl_link_vf_vlan_info(int len, struct ifla_vf_vlan_info **vi,
+				  nl_vf_vlans_t **nvi) {
+	int cur = 0, err;
+	nl_vf_vlans_t *vlans;
+
+	if (len <= 0)
+		return 0;
+
+	if ((err = rtnl_link_vf_vlan_alloc(&vlans, len)) < 0)
+		return err;
+
+	cur = 0;
+	while (cur < len) {
+		vlans->vlans[cur].vf_vlan = vi[cur]->vlan ? vi[cur]->vlan : 0;
+		vlans->vlans[cur].vf_vlan_qos = vi[cur]->qos ? vi[cur]->qos : 0;
+		if (vi[cur]->vlan_proto) {
+			vlans->vlans[cur].vf_vlan_proto = ntohs(vi[cur]->vlan_proto);
+		} else {
+			vlans->vlans[cur].vf_vlan_proto = ETH_P_8021Q;
+		}
+		cur++;
+	}
+
+	*nvi = vlans;
+	return 0;
+}
+
+/* Parse IFLA_VFINFO_LIST and IFLA_VF_INFO attributes */
+int rtnl_link_sriov_parse_vflist(struct rtnl_link *link, struct nlattr **tb) {
+	int err, len, list_len, list_rem;
+	struct ifla_vf_mac *vf_lladdr;
+	struct ifla_vf_vlan *vf_vlan;
+	struct ifla_vf_vlan_info *vf_vlan_info[MAX_VLAN_LIST_LEN];
+	struct ifla_vf_tx_rate *vf_tx_rate;
+	struct ifla_vf_spoofchk *vf_spoofchk;
+	struct ifla_vf_link_state *vf_linkstate;
+	struct ifla_vf_rate *vf_rate;
+	struct ifla_vf_rss_query_en *vf_rss_query;
+	struct ifla_vf_trust *vf_trust;
+	struct nlattr *nla, *nla_list, *t[IFLA_VF_MAX+1],
+		*stb[RTNL_LINK_VF_STATS_MAX+1];
+	nl_vf_vlans_t *vf_vlans = NULL;
+	struct rtnl_link_vf *vf_data, *vf_head = NULL;
+
+	len = nla_len(tb[IFLA_VFINFO_LIST]);
+	link->l_vf_list = rtnl_link_vf_alloc();
+	if (!link->l_vf_list)
+		return -NLE_NOMEM;
+	vf_head = link->l_vf_list;
+
+	for (nla = nla_data(tb[IFLA_VFINFO_LIST]); nla_ok(nla, len);
+	     nla = nla_next(nla, &len)) {
+		err = nla_parse(t, IFLA_VF_MAX, nla_data(nla), nla_len(nla),
+				sriov_info_policy);
+		if (err < 0)
+			return err;
+
+		vf_data = rtnl_link_vf_alloc();
+		if (!vf_data)
+			return -NLE_NOMEM;
+
+		if (t[IFLA_VF_MAC]) {
+			vf_lladdr = nla_data(t[IFLA_VF_MAC]);
+
+			vf_data->vf_index = vf_lladdr->vf;
+			vf_data->ce_mask |= SRIOV_ATTR_INDEX;
+
+			vf_data->vf_lladdr = nl_addr_build(AF_LLC,
+							   vf_lladdr->mac, 6);
+			if (vf_data->vf_lladdr == NULL)
+				return -NLE_NOMEM;
+			nl_addr_set_family(vf_data->vf_lladdr, AF_LLC);
+			vf_data->ce_mask |= SRIOV_ATTR_ADDR;
+		}
+
+		if (t[IFLA_VF_VLAN_LIST]) {
+			list_len = 0;
+			nla_for_each_nested(nla_list, t[IFLA_VF_VLAN_LIST],
+					    list_rem) {
+				vf_vlan_info[len] = nla_data(nla_list);
+				list_len++;
+			}
+
+			err = rtnl_link_vf_vlan_info(list_len, vf_vlan_info,
+						     &vf_vlans);
+			if (err < 0)
+				return err;
+
+			vf_data->vf_vlans = vf_vlans;
+			vf_data->ce_mask |= SRIOV_ATTR_VLAN;
+		} else if (t[IFLA_VF_VLAN]) {
+			vf_vlan = nla_data(t[IFLA_VF_VLAN]);
+
+			if (vf_vlan->vlan) {
+				err = rtnl_link_vf_vlan_alloc(&vf_vlans, 1);
+				if (err < 0)
+					return err;
+
+				vf_vlans->vlans[0].vf_vlan = vf_vlan->vlan;
+				vf_vlans->vlans[0].vf_vlan_qos = vf_vlan->qos;
+				vf_vlans->vlans[0].vf_vlan_proto = ETH_P_8021Q;
+
+				vf_data->vf_vlans = vf_vlans;
+				vf_data->ce_mask |= SRIOV_ATTR_VLAN;
+			}
+		}
+
+		if (t[IFLA_VF_TX_RATE]) {
+			vf_tx_rate = nla_data(t[IFLA_VF_TX_RATE]);
+
+			if (vf_tx_rate->rate) {
+				vf_data->vf_rate = vf_tx_rate->rate;
+				vf_data->ce_mask |= SRIOV_ATTR_TX_RATE;
+			}
+		}
+
+		if (t[IFLA_VF_SPOOFCHK]) {
+			vf_spoofchk = nla_data(t[IFLA_VF_SPOOFCHK]);
+
+			if (vf_spoofchk->setting != -1) {
+				vf_data->vf_spoofchk = vf_spoofchk->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK;
+			}
+		}
+
+		if (t[IFLA_VF_LINK_STATE]) {
+			vf_linkstate = nla_data(t[IFLA_VF_LINK_STATE]);
+
+			vf_data->vf_linkstate = vf_linkstate->link_state;
+			vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE;
+		}
+
+		if (t[IFLA_VF_RATE]) {
+			vf_rate = nla_data(t[IFLA_VF_RATE]);
+
+			if (vf_rate->max_tx_rate) {
+				vf_data->vf_max_tx_rate = vf_rate->max_tx_rate;
+				vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX;
+			}
+			if (vf_rate->min_tx_rate) {
+				vf_data->vf_min_tx_rate = vf_rate->min_tx_rate;
+				vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN;
+			}
+		}
+
+		if (t[IFLA_VF_RSS_QUERY_EN]) {
+			vf_rss_query = nla_data(t[IFLA_VF_RSS_QUERY_EN]);
+
+			if (vf_rss_query->setting != -1) {
+				vf_data->vf_rss_query_en = vf_rss_query->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN;
+			}
+		}
+
+		if (t[IFLA_VF_STATS]) {
+			err = nla_parse_nested(stb, IFLA_VF_STATS_MAX,
+					       t[IFLA_VF_STATS],
+					       sriov_stats_policy);
+			if (err < 0)
+				return err;
+
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_RX_PACKETS,
+				    IFLA_VF_STATS_RX_PACKETS);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_TX_PACKETS,
+				    IFLA_VF_STATS_TX_PACKETS);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_RX_BYTES,
+				    IFLA_VF_STATS_RX_BYTES);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_TX_BYTES,
+				    IFLA_VF_STATS_TX_BYTES);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_BROADCAST,
+				    IFLA_VF_STATS_BROADCAST);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_MULTICAST,
+				    IFLA_VF_STATS_MULTICAST);
+
+			vf_data->ce_mask |= IFLA_VF_STATS;
+		}
+
+		if (t[IFLA_VF_TRUST]) {
+			vf_trust = nla_data(t[IFLA_VF_TRUST]);
+
+			if (vf_trust->setting != -1) {
+				vf_data->vf_trust = vf_trust->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_TRUST;
+			}
+		}
+
+		nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list);
+		vf_head = vf_data;
+	}
+
+	return 0;
+}
+
+/**
+ * @name SR-IOV Sub-Object
+ * @{
+ */
+
+/**
+ * Allocate a new SRIOV VF object
+ *
+ * @return NULL if out of memory
+ * @return New VF Object
+ *
+ * @see rtnl_link_vf_put()
+ *
+ * The SRIOV VF object must be returned to the link object with
+ * rtnl_link_vf_put() when operations are done to prevent memory leaks.
+ */
+struct rtnl_link_vf *rtnl_link_vf_alloc(void) {
+	struct rtnl_link_vf *vf;
+
+	if (!(vf = calloc(1, sizeof(*vf))))
+		return NULL;
+
+	NL_INIT_LIST_HEAD(&vf->vf_list);
+	vf->ce_refcnt = 1;
+
+	NL_DBG(4, "Allocated new SRIOV VF object %p\n", vf);
+
+	return vf;
+}
+
+/**
+ * Free SRIOV VF object.
+ * @arg vf_data 	SRIOV VF data object
+ */
+void rtnl_link_vf_free(struct rtnl_link_vf *vf_data) {
+	if (!vf_data)
+		return;
+
+	if (vf_data->ce_refcnt > 0)
+		NL_DBG(1, "Warning: Freeing SRIOV VF object in use...\n");
+
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR)
+		nl_addr_put(vf_data->vf_lladdr);
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN)
+		rtnl_link_vf_vlan_put(vf_data->vf_vlans);
+
+	NL_DBG(4, "Freed SRIOV VF object %p\n", vf_data);
+	free(vf_data);
+
+	return;
+}
+
+/**
+ * Lookup SRIOV VF in link object by VF index.
+ *
+ * @return NULL if VF not found
+ * @return VF Object
+ *
+ * @see rtnl_link_vf_put()
+ *
+ * The SRIOV VF object must be returned to the link object with
+ * rtnl_link_vf_put() when operations are done to prevent memory leaks.
+ */
+struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *link, uint32_t vf_num) {
+	struct rtnl_link_vf *list, *vf, *next, *ret = NULL;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) {
+		if (vf->vf_index == vf_num) {
+			ret = vf;
+			break;
+		}
+	}
+
+	if (ret) {
+		ret->ce_refcnt++;
+		NL_DBG(4, "New reference to SRIOV VF object %p, total %i\n",
+		       ret, ret->ce_refcnt);
+	}
+
+	return ret;
+}
+
+/**
+ * Return SRIOV VF object to the owning link object.
+ * @arg vf_data 	SRIOV VF data object
+ *
+ * @see rtnl_link_vf_alloc()
+ * @see rtnl_link_vf_get()
+ */
+void rtnl_link_vf_put(struct rtnl_link_vf *vf_data) {
+	if (!vf_data)
+		return;
+
+	vf_data->ce_refcnt--;
+	NL_DBG(4, "Returned SRIOV VF object reference %p, %i remaining\n",
+	       vf_data, vf_data->ce_refcnt);
+
+	if (vf_data->ce_refcnt < 0)
+		BUG();
+
+	if (vf_data->ce_refcnt <= 0)
+		rtnl_link_vf_free(vf_data);
+
+	return;
+}
+
+/**
+ * Get link layer address of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg addr 		Pointer to store Link Layer address
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @copydoc pointer_lifetime_warning
+ * @return 0 if addr is present and addr is set to pointer containing address
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the link layer address is not set
+ */
+int rtnl_link_vf_get_addr(struct rtnl_link_vf *vf_data, struct nl_addr **addr)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR)
+		*addr = vf_data->vf_lladdr;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get index of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_index 	Pointer to store VF index
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if index is present and vf_index is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF index is not set
+ */
+int rtnl_link_vf_get_index(struct rtnl_link_vf *vf_data, uint32_t *vf_index)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_INDEX)
+		*vf_index = vf_data->vf_index;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get link state of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_linkstate 	Pointer to store VF link state
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if link state is present and vf_linkstate is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF link state is not set
+ */
+int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *vf_data,
+			       uint32_t *vf_linkstate)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE)
+		*vf_linkstate = vf_data->vf_linkstate;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get TX Rate Limit of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_rate 	Pointer to store VF rate limiting data
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * When the older rate API has been implemented, the rate member of the struct
+ * will be set, and the api member will be set to RTNL_LINK_VF_API_OLD.
+ * When the newer rate API has been implemented, the max_tx_rate
+ * and/or the minx_tx_rate will be set, and the api member will be set to
+ * RTNL_LINK_VF_API_NEW.
+ *
+ * @return 0 if rate is present and vf_rate is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF rate is not set
+ */
+int rtnl_link_vf_get_rate(struct rtnl_link_vf *vf_data,
+			  struct nl_vf_rate *vf_rate)
+{
+	int set = 0;
+
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	vf_rate->api = RTNL_LINK_VF_RATE_API_UNSPEC;
+	vf_rate->rate = 0;
+	vf_rate->max_tx_rate = 0;
+	vf_rate->min_tx_rate = 0;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) {
+		if (vf_data->vf_max_tx_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_NEW;
+			vf_rate->max_tx_rate = vf_data->vf_max_tx_rate;
+			set = 1;
+		}
+	}
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) {
+		if (vf_data->vf_min_tx_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_NEW;
+			vf_rate->min_tx_rate = vf_data->vf_min_tx_rate;
+			set = 1;
+		}
+	}
+	if ((!set) && (vf_data->ce_mask & SRIOV_ATTR_TX_RATE)) {
+		if (vf_data->vf_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_OLD;
+			vf_rate->rate = vf_data->vf_rate;
+			set = 1;
+		}
+	}
+
+	if (!set)
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get RSS Query EN value of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_rss_query_en	Pointer to store VF RSS Query value
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if rss_query_en is present and vf_rss_query_en is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF RSS Query EN value is not set
+ */
+int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *vf_data,
+				  uint32_t *vf_rss_query_en)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN)
+		*vf_rss_query_en = vf_data->vf_rss_query_en;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get spoof checking value of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_spoofchk 	Pointer to store VF spoofchk value
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if spoofchk is present and vf_spoofchk is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF spoofcheck is not set
+ */
+int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *vf_data,
+			      uint32_t *vf_spoofchk)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK)
+		*vf_spoofchk = vf_data->vf_spoofchk;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get value of stat counter for SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg stat 		Identifier of statistical counter
+ * @arg vf_stat 	Pointer to store VF stat value in
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if stat is present and vf_stat is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF stat is not set
+ */
+int rtnl_link_vf_get_stat(struct rtnl_link_vf *vf_data,
+			  rtnl_link_vf_stats_t stat, uint64_t *vf_stat)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_STATS)
+		*vf_stat = vf_data->vf_stats[stat];
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get trust setting of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_trust 	Pointer to store VF trust value
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if trust is present and vf_trust is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF trust setting is not set
+ */
+int rtnl_link_vf_get_trust(struct rtnl_link_vf *vf_data, uint32_t *vf_trust)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_TRUST)
+		*vf_trust = vf_data->vf_trust;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get an array of VLANS on SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_vlans 	Pointer to nl_vf_vlans_t struct to store vlan info.
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * The SRIOV VF VLANs object must be returned to the SRIOV VF object with
+ * rtnl_link_vf_vlans_put() when operations are done to prevent memory leaks.
+ *
+ * @copydoc pointer_lifetime_warning
+ * @return 0 if VLAN info is present and vf_vlans is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF vlans is not set
+ */
+int rtnl_link_vf_get_vlans(struct rtnl_link_vf *vf_data,
+			   nl_vf_vlans_t **vf_vlans) {
+	nl_vf_vlans_t *vf;
+
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN) {
+		vf = vf_data->vf_vlans;
+		vf->ce_refcnt++;
+		*vf_vlans = vf;
+	} else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Allocate a SRIOV VF VLAN object
+ * @param vf_vlans 	Pointer to store VLAN object at
+ * @param vlan_count 	Number of VLANs that will be stored in VLAN object
+ *
+ * The SRIOV VF VLANs object must be returned to the sRIOV VF object with
+ * rtnl_link_vf_vlan_put() when operations are done to prevent memory leaks.
+ *
+ * @return 0 if VLAN object is created and vf_vlans is set.
+ * @return -NLE_NOMEM if object could not be allocated.
+ * @return -NLE_INVAL if vlan_count is more than supported by SRIOV VF
+ */
+int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **vf_vlans, int vlan_count) {
+	nl_vf_vlans_t *vlans;
+	nl_vf_vlan_info_t *vlan_info;
+
+	if (vlan_count > MAX_VLAN_LIST_LEN)
+		return -NLE_INVAL;
+
+	vlans = calloc(1, sizeof(*vlans));
+	if (!vf_vlans)
+		return -NLE_NOMEM;
+
+	vlan_info = calloc(vlan_count+1, sizeof(*vlan_info));
+	if (!vlan_info) {
+		free(vlans);
+		return -NLE_NOMEM;
+	}
+
+	NL_DBG(4, "Allocated new SRIOV VF VLANs object %p\n", vlans);
+
+	vlans->ce_refcnt = 1;
+	vlans->size = vlan_count;
+	vlans->vlans = vlan_info;
+	*vf_vlans = vlans;
+
+	return 0;
+}
+
+/**
+ * Free an allocated SRIOV VF VLANs object
+ * @param vf_vlans 	SRIOV VF VLANs object
+ */
+void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans) {
+	if (!vf_vlans)
+		return;
+
+	if (vf_vlans->ce_refcnt > 0)
+		NL_DBG(1, "Warning: Freeing SRIOV VF VLANs object in use...\n");
+
+	NL_DBG(4, "Freed SRIOV VF object %p\n", vf_vlans);
+	free(vf_vlans->vlans);
+	free(vf_vlans);
+
+	return;
+}
+
+/**
+ * Return SRIOV VF VLANs object to the owning SRIOV VF object.
+ * @param vf_vlans 	SRIOV VF VLANs object
+ */
+void rtnl_link_vf_vlan_put(nl_vf_vlans_t *vf_vlans) {
+	if (!vf_vlans)
+		return;
+
+	vf_vlans->ce_refcnt--;
+	NL_DBG(4, "Returned SRIOV VF VLANs object reference %p, %i remaining\n",
+	       vf_vlans, vf_vlans->ce_refcnt);
+
+	if (vf_vlans->ce_refcnt < 0)
+		BUG();
+
+	if (vf_vlans->ce_refcnt <= 0)
+		rtnl_link_vf_vlan_free(vf_vlans);
+
+	return;
+}
+
+
+/** @} */
+
+/** @} */
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 448a617..819b82b 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -964,5 +964,26 @@ global:
 	rtnl_link_macvlan_macmode2str;
 	rtnl_link_macvlan_set_macmode;
 	rtnl_link_macvlan_str2macmode;
+	rtnl_link_has_vf_list;
+	rtnl_link_set_vf_list;
+	rtnl_link_unset_vf_list;
+	rtnl_link_sriov_free_data;
+	rtnl_link_sriov_parse_vflist;
+	rtnl_link_vf_alloc;
+	rtnl_link_vf_free;
+	rtnl_link_vf_get;
+	rtnl_link_vf_put;
+	rtnl_link_vf_get_addr;
+	rtnl_link_vf_get_index;
+	rtnl_link_vf_get_linkstate;
+	rtnl_link_vf_get_rate;
+	rtnl_link_vf_get_rss_query_en;
+	rtnl_link_vf_get_spoofchk;
+	rtnl_link_vf_get_stat;
+	rtnl_link_vf_get_trust;
+	rtnl_link_vf_get_vlans;
+	rtnl_link_vf_vlan_alloc;
+	rtnl_link_vf_vlan_free;
+	rtnl_link_vf_vlan_put;
 } libnl_3_2_28;
 
-- 
2.10.1




More information about the libnl mailing list