DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 14/26] net/i40e: set VF VLAN insertion from PF
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Bernard Iremonger
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Bernard Iremonger <bernard.iremonger@intel.com>

Support inserting VF VLAN id from PF.
User can call the API on PF to insert a VLAN id to a
specific VF.

Signed-off-by: Bernard Iremonger <bernard.iremonger@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 62 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 19 ++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 82 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4851978..c173a1c 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10293,3 +10293,65 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+int rte_pmd_i40e_set_vf_vlan_insert(uint8_t port, uint16_t vf_id,
+				    uint16_t vlan_id)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_hw *hw;
+	struct i40e_vsi *vsi;
+	struct i40e_vsi_context ctxt;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	if (vlan_id > ETHER_MAX_VLAN_ID) {
+		PMD_DRV_LOG(ERR, "Invalid VLAN ID.");
+		return -EINVAL;
+	}
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	hw = I40E_PF_TO_HW(pf);
+
+	/**
+	 * return -ENODEV if SRIOV not enabled, VF number not configured
+	 * or no queue assigned.
+	 */
+	if (!hw->func_caps.sr_iov_1_1 || pf->vf_num == 0 ||
+	    pf->vf_nb_qps == 0)
+		return -ENODEV;
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid VF ID.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID);
+	vsi->info.pvid = vlan_id;
+	if (vlan_id > 0)
+		vsi->info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_INSERT_PVID;
+	else
+		vsi->info.port_vlan_flags &= ~I40E_AQ_VSI_PVLAN_INSERT_PVID;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	(void)rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+	ctxt.seid = vsi->seid;
+
+	hw = I40E_VSI_TO_HW(vsi);
+	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to update VSI params");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index a13354f..ce89ffa 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -213,4 +213,23 @@ int rte_pmd_i40e_set_vf_mac_addr(uint8_t port, uint16_t vf_id,
 int
 rte_pmd_i40e_set_vf_vlan_stripq(uint8_t port, uint16_t vf, uint8_t on);
 
+/**
+ * Enable/Disable vf vlan insert
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    ID specifying VF.
+ * @param vlan_id
+ *    0 - Disable VF's vlan insert.
+ *    n - Enable; n is inserted as the vlan id.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_vf_vlan_insert(uint8_t port, uint16_t vf_id,
+				    uint16_t vlan_id);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index d4e7acd..d851f6c 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -13,6 +13,7 @@ DPDK_17.02 {
 	rte_pmd_i40e_set_vf_multicast_promisc;
 	rte_pmd_i40e_set_vf_unicast_promisc;
 	rte_pmd_i40e_set_vf_vlan_anti_spoof;
+	rte_pmd_i40e_set_vf_vlan_insert;
 	rte_pmd_i40e_set_vf_vlan_stripq;
 
 } DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 13/26] net/i40e: set VF VLAN strip from PF
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Chen Jing D(Mark)
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: "Chen Jing D(Mark)" <jing.d.chen@intel.com>

Add a function to configure vlan strip enable/disable for specific
SRIOV VF device.

Signed-off-by: Chen Jing D(Mark) <jing.d.chen@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 33 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 19 ++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 53 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 0467f3e..4851978 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10260,3 +10260,36 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return 0;
 }
+
+/* Set vlan strip on/off for specific VF from host */
+int
+rte_pmd_i40e_set_vf_vlan_stripq(uint8_t port, uint16_t vf_id, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+
+	if (!vsi)
+		return -EINVAL;
+
+	ret = i40e_vsi_config_vlan_stripping(vsi, !!on);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to set VLAN stripping!");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index 38f6030..a13354f 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -194,4 +194,23 @@ int rte_pmd_i40e_set_vf_multicast_promisc(uint8_t port,
 int rte_pmd_i40e_set_vf_mac_addr(uint8_t port, uint16_t vf_id,
 				 struct ether_addr *mac_addr);
 
+/**
+ * Enable/Disable vf vlan strip for all queues in a pool
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    ID specifying VF.
+ * @param on
+ *    1 - Enable VF's vlan strip on RX queues.
+ *    0 - Disable VF's vlan strip on RX queues.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int
+rte_pmd_i40e_set_vf_vlan_stripq(uint8_t port, uint16_t vf, uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 2d53b87..d4e7acd 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -13,5 +13,6 @@ DPDK_17.02 {
 	rte_pmd_i40e_set_vf_multicast_promisc;
 	rte_pmd_i40e_set_vf_unicast_promisc;
 	rte_pmd_i40e_set_vf_vlan_anti_spoof;
+	rte_pmd_i40e_set_vf_vlan_stripq;
 
 } DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 11/26] net/i40e: set VF MAC from VF support
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Ferruh Yigit
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Ferruh Yigit <ferruh.yigit@intel.com>

Support changing VF default MAC address.
This function is not supported if PF set the MAC
address for the PF.

Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
---
 drivers/net/i40e/i40e_ethdev.h    |  4 +++-
 drivers/net/i40e/i40e_ethdev_vf.c | 49 +++++++++++++++++++++++++++++++++------
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 0db140b..b687b0c 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -126,6 +126,7 @@ enum i40e_flxpld_layer_idx {
 #define I40E_FLAG_FDIR                  (1ULL << 6)
 #define I40E_FLAG_VXLAN                 (1ULL << 7)
 #define I40E_FLAG_RSS_AQ_CAPABLE        (1ULL << 8)
+#define I40E_FLAG_VF_MAC_BY_PF          (1ULL << 9)
 #define I40E_FLAG_ALL (I40E_FLAG_RSS | \
 		       I40E_FLAG_DCB | \
 		       I40E_FLAG_VMDQ | \
@@ -134,7 +135,8 @@ enum i40e_flxpld_layer_idx {
 		       I40E_FLAG_HEADER_SPLIT_ENABLED | \
 		       I40E_FLAG_FDIR | \
 		       I40E_FLAG_VXLAN | \
-		       I40E_FLAG_RSS_AQ_CAPABLE)
+		       I40E_FLAG_RSS_AQ_CAPABLE | \
+		       I40E_FLAG_VF_MAC_BY_PF)
 
 #define I40E_RSS_OFFLOAD_ALL ( \
 	ETH_RSS_FRAG_IPV4 | \
diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
index bce01d0..5016249 100644
--- a/drivers/net/i40e/i40e_ethdev_vf.c
+++ b/drivers/net/i40e/i40e_ethdev_vf.c
@@ -152,6 +152,8 @@ static int i40evf_dev_rss_hash_update(struct rte_eth_dev *dev,
 static int i40evf_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
 					struct rte_eth_rss_conf *rss_conf);
 static int i40evf_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
+static void i40evf_set_default_mac_addr(struct rte_eth_dev *dev,
+					struct ether_addr *mac_addr);
 static int
 i40evf_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id);
 static int
@@ -227,6 +229,7 @@ struct rte_i40evf_xstats_name_off {
 	.rss_hash_update      = i40evf_dev_rss_hash_update,
 	.rss_hash_conf_get    = i40evf_dev_rss_hash_conf_get,
 	.mtu_set              = i40evf_dev_mtu_set,
+	.mac_addr_set         = i40evf_set_default_mac_addr,
 };
 
 /*
@@ -888,19 +891,16 @@ struct rte_i40evf_xstats_name_off {
 }
 
 static void
-i40evf_del_mac_addr(struct rte_eth_dev *dev, uint32_t index)
+i40evf_del_mac_addr_by_addr(struct rte_eth_dev *dev,
+			    struct ether_addr *addr)
 {
 	struct i40e_virtchnl_ether_addr_list *list;
 	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
-	struct rte_eth_dev_data *data = dev->data;
-	struct ether_addr *addr;
 	uint8_t cmd_buffer[sizeof(struct i40e_virtchnl_ether_addr_list) + \
 			sizeof(struct i40e_virtchnl_ether_addr)];
 	int err;
 	struct vf_cmd_info args;
 
-	addr = &(data->mac_addrs[index]);
-
 	if (i40e_validate_mac_addr(addr->addr_bytes) != I40E_SUCCESS) {
 		PMD_DRV_LOG(ERR, "Invalid mac:%x-%x-%x-%x-%x-%x",
 			    addr->addr_bytes[0], addr->addr_bytes[1],
@@ -927,6 +927,17 @@ struct rte_i40evf_xstats_name_off {
 	return;
 }
 
+static void
+i40evf_del_mac_addr(struct rte_eth_dev *dev, uint32_t index)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	struct ether_addr *addr;
+
+	addr = &data->mac_addrs[index];
+
+	i40evf_del_mac_addr_by_addr(dev, addr);
+}
+
 static int
 i40evf_update_stats(struct rte_eth_dev *dev, struct i40e_eth_stats **pstats)
 {
@@ -1260,10 +1271,12 @@ static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
 
 	/* Store the MAC address configured by host, or generate random one */
 	p_mac_addr = (struct ether_addr *)(vf->vsi_res->default_mac_addr);
-	if (is_valid_assigned_ether_addr(p_mac_addr)) /* Configured by host */
+	if (is_valid_assigned_ether_addr(p_mac_addr)) { /* Configured by host */
 		ether_addr_copy(p_mac_addr, (struct ether_addr *)hw->mac.addr);
-	else
+		vf->flags |= I40E_FLAG_VF_MAC_BY_PF;
+	} else {
 		eth_random_addr(hw->mac.addr); /* Generate a random one */
+	}
 
 	/* If the PF host is not DPDK, set the interval of ITR0 to max*/
 	if (vf->version_major != I40E_DPDK_VERSION_MAJOR) {
@@ -2674,3 +2687,25 @@ static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+static void
+i40evf_set_default_mac_addr(struct rte_eth_dev *dev,
+			    struct ether_addr *mac_addr)
+{
+	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+
+	if (!is_valid_assigned_ether_addr(mac_addr)) {
+		PMD_DRV_LOG(ERR, "Tried to set invalid MAC address.");
+		return;
+	}
+
+	if (is_same_ether_addr(mac_addr, dev->data->mac_addrs))
+		return;
+
+	if (vf->flags & I40E_FLAG_VF_MAC_BY_PF)
+		return;
+
+	i40evf_del_mac_addr_by_addr(dev, dev->data->mac_addrs);
+
+	i40evf_add_mac_addr(dev, mac_addr, 0, 0);
+}
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 10/26] net/i40e: set VF MAC from PF support
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Ferruh Yigit
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Ferruh Yigit <ferruh.yigit@intel.com>

Support setting VF MAC address from PF.
User can call the API on PF to set a specific
VF's MAC address.

PF should set MAC address before VF initialized,
if PF sets the MAC address after VF initialized,
new MAC address won't be effective until VF
reinitialized.

This will remove all existing MAC filters.

Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 39 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 23 ++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 63 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index bd4b710..0467f3e 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10221,3 +10221,42 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+int
+rte_pmd_i40e_set_vf_mac_addr(uint8_t port, uint16_t vf_id,
+			     struct ether_addr *mac_addr)
+{
+	struct i40e_mac_filter *f;
+	struct rte_eth_dev *dev;
+	struct i40e_pf_vf *vf;
+	struct i40e_vsi *vsi;
+	struct i40e_pf *pf;
+	void *temp;
+
+	if (i40e_validate_mac_addr((u8 *)mac_addr) != I40E_SUCCESS)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs)
+		return -EINVAL;
+
+	vf = &pf->vfs[vf_id];
+	vsi = vf->vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	ether_addr_copy(mac_addr, &vf->mac_addr);
+
+	/* Remove all existing mac */
+	TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp)
+		i40e_vsi_delete_mac(vsi, &f->mac_info.mac_addr);
+
+	return 0;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index f8aa1f8..38f6030 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -171,4 +171,27 @@ int rte_pmd_i40e_set_vf_multicast_promisc(uint8_t port,
 					  uint16_t vf_id,
 					  uint8_t on);
 
+/**
+ * Set the VF MAC address.
+ *
+ * PF should set MAC address before VF initialized, if PF sets the MAC
+ * address after VF initialized, new MAC address won't be effective until
+ * VF reinitialize.
+ *
+ * This will remove all existing MAC filters.
+ *
+ * @param port
+ *   The port identifier of the Ethernet device.
+ * @param vf_id
+ *   VF id.
+ * @param mac_addr
+ *   VF MAC address.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if *vf* or *mac_addr* is invalid.
+ */
+int rte_pmd_i40e_set_vf_mac_addr(uint8_t port, uint16_t vf_id,
+				 struct ether_addr *mac_addr);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 32939b2..2d53b87 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -8,6 +8,7 @@ DPDK_17.02 {
 
 	rte_pmd_i40e_ping_vfs;
 	rte_pmd_i40e_set_tx_loopback;
+	rte_pmd_i40e_set_vf_mac_addr;
 	rte_pmd_i40e_set_vf_mac_anti_spoof;
 	rte_pmd_i40e_set_vf_multicast_promisc;
 	rte_pmd_i40e_set_vf_unicast_promisc;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 08/26] net/i40e: enable VF MTU change
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Qi Zhang
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Qi Zhang <qi.z.zhang@intel.com>

This patch implement mtu_set ops for i40e VF.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 drivers/net/i40e/i40e_ethdev_vf.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
index 12da0ec..bce01d0 100644
--- a/drivers/net/i40e/i40e_ethdev_vf.c
+++ b/drivers/net/i40e/i40e_ethdev_vf.c
@@ -151,6 +151,7 @@ static int i40evf_dev_rss_hash_update(struct rte_eth_dev *dev,
 				      struct rte_eth_rss_conf *rss_conf);
 static int i40evf_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
 					struct rte_eth_rss_conf *rss_conf);
+static int i40evf_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
 static int
 i40evf_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id);
 static int
@@ -225,6 +226,7 @@ struct rte_i40evf_xstats_name_off {
 	.reta_query           = i40evf_dev_rss_reta_query,
 	.rss_hash_update      = i40evf_dev_rss_hash_update,
 	.rss_hash_conf_get    = i40evf_dev_rss_hash_conf_get,
+	.mtu_set              = i40evf_dev_mtu_set,
 };
 
 /*
@@ -2641,3 +2643,34 @@ static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
 
 	return 0;
 }
+
+static int
+i40evf_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+	struct rte_eth_dev_data *dev_data = vf->dev_data;
+	uint32_t frame_size = mtu + ETHER_HDR_LEN
+			      + ETHER_CRC_LEN + I40E_VLAN_TAG_SIZE;
+	int ret = 0;
+
+	/* check if mtu is within the allowed range */
+	if ((mtu < ETHER_MIN_MTU) || (frame_size > I40E_FRAME_SIZE_MAX))
+		return -EINVAL;
+
+	/* mtu setting is forbidden if port is start */
+	if (dev_data->dev_started) {
+		PMD_DRV_LOG(ERR,
+			    "port %d must be stopped before configuration\n",
+			    dev_data->port_id);
+		return -EBUSY;
+	}
+
+	if (frame_size > ETHER_MAX_LEN)
+		dev_data->dev_conf.rxmode.jumbo_frame = 1;
+	else
+		dev_data->dev_conf.rxmode.jumbo_frame = 0;
+
+	dev_data->dev_conf.rxmode.max_rx_pkt_len = frame_size;
+
+	return ret;
+}
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 07/26] net/i40e: set VF multicast promisc mode from PF
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Support enabling/disabling VF multicast promiscuous mode from
PF.
User can call the API on PF to enable/disable a specific
VF's multicast promiscuous mode.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 38 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 19 ++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 58 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 175d9e0..bd4b710 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10183,3 +10183,41 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+int
+rte_pmd_i40e_set_vf_multicast_promisc(uint8_t port, uint16_t vf_id, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	struct i40e_hw *hw;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	hw = I40E_VSI_TO_HW(vsi);
+
+	ret = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid,
+						    on, NULL);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to set multicast promiscuous mode");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index 1f9d3b4..f8aa1f8 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -152,4 +152,23 @@ int rte_pmd_i40e_set_vf_unicast_promisc(uint8_t port,
 					uint16_t vf_id,
 					uint8_t on);
 
+/**
+ * Enable/Disable VF multicast promiscuous mode.
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    VF on which to set.
+ * @param on
+ *    1 - Enable.
+ *    0 - Disable.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_vf_multicast_promisc(uint8_t port,
+					  uint16_t vf_id,
+					  uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 35757a9..32939b2 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -9,6 +9,7 @@ DPDK_17.02 {
 	rte_pmd_i40e_ping_vfs;
 	rte_pmd_i40e_set_tx_loopback;
 	rte_pmd_i40e_set_vf_mac_anti_spoof;
+	rte_pmd_i40e_set_vf_multicast_promisc;
 	rte_pmd_i40e_set_vf_unicast_promisc;
 	rte_pmd_i40e_set_vf_vlan_anti_spoof;
 
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 06/26] net/i40e: set VF unicast promisc mode from PF
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Support enabling/disabling VF unicast promiscuous mode from
PF.
User can call the API on PF to enable/disable a specific
VF's unicast promiscuous mode.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 38 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 19 ++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 58 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 1c02db7..175d9e0 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10145,3 +10145,41 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+int
+rte_pmd_i40e_set_vf_unicast_promisc(uint8_t port, uint16_t vf_id, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	struct i40e_hw *hw;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	hw = I40E_VSI_TO_HW(vsi);
+
+	ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid,
+						  on, NULL, true);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to set unicast promiscuous mode");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index 8befb06..1f9d3b4 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -133,4 +133,23 @@ int rte_pmd_i40e_set_vf_vlan_anti_spoof(uint8_t port,
 int rte_pmd_i40e_set_tx_loopback(uint8_t port,
 				 uint8_t on);
 
+/**
+ * Enable/Disable VF unicast promiscuous mode.
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    VF on which to set.
+ * @param on
+ *    1 - Enable.
+ *    0 - Disable.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_vf_unicast_promisc(uint8_t port,
+					uint16_t vf_id,
+					uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 51eda65..35757a9 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -9,6 +9,7 @@ DPDK_17.02 {
 	rte_pmd_i40e_ping_vfs;
 	rte_pmd_i40e_set_tx_loopback;
 	rte_pmd_i40e_set_vf_mac_anti_spoof;
+	rte_pmd_i40e_set_vf_unicast_promisc;
 	rte_pmd_i40e_set_vf_vlan_anti_spoof;
 
 } DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 05/26] net/i40e: set Tx loopback from PF
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Support enabling/disabling TX loopback from PF.
User can call the API on PF to enable/disable TX loopback
for all the PF and VFs.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 238 ++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           |  16 ++
 drivers/net/i40e/rte_pmd_i40e_version.map |   1 +
 3 files changed, 255 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 3d126a1..1c02db7 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -9907,3 +9907,241 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+static int
+i40e_vsi_rm_mac_filter(struct i40e_vsi *vsi)
+{
+	struct i40e_mac_filter *f;
+	struct i40e_macvlan_filter *mv_f;
+	int i, vlan_num;
+	enum rte_mac_filter_type filter_type;
+	int ret = I40E_SUCCESS;
+	void *temp;
+
+	/* remove all the MACs */
+	TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp) {
+		vlan_num = vsi->vlan_num;
+		filter_type = f->mac_info.filter_type;
+		if (filter_type == RTE_MACVLAN_PERFECT_MATCH ||
+		    filter_type == RTE_MACVLAN_HASH_MATCH) {
+			if (vlan_num == 0) {
+				PMD_DRV_LOG(ERR,
+					    "VLAN number shouldn't be 0\n");
+				return I40E_ERR_PARAM;
+			}
+		} else if (filter_type == RTE_MAC_PERFECT_MATCH ||
+			   filter_type == RTE_MAC_HASH_MATCH)
+			vlan_num = 1;
+
+		mv_f = rte_zmalloc("macvlan_data", vlan_num * sizeof(*mv_f), 0);
+		if (!mv_f) {
+			PMD_DRV_LOG(ERR, "failed to allocate memory");
+			return I40E_ERR_NO_MEMORY;
+		}
+
+		for (i = 0; i < vlan_num; i++) {
+			mv_f[i].filter_type = filter_type;
+			(void)rte_memcpy(&mv_f[i].macaddr,
+					 &f->mac_info.mac_addr,
+					 ETH_ADDR_LEN);
+		}
+		if (filter_type == RTE_MACVLAN_PERFECT_MATCH ||
+		    filter_type == RTE_MACVLAN_HASH_MATCH) {
+			ret = i40e_find_all_vlan_for_mac(vsi, mv_f, vlan_num,
+							 &f->mac_info.mac_addr);
+			if (ret != I40E_SUCCESS) {
+				rte_free(mv_f);
+				return ret;
+			}
+		}
+
+		ret = i40e_remove_macvlan_filters(vsi, mv_f, vlan_num);
+		if (ret != I40E_SUCCESS) {
+			rte_free(mv_f);
+			return ret;
+		}
+
+		rte_free(mv_f);
+		ret = I40E_SUCCESS;
+	}
+
+	return ret;
+}
+
+static int
+i40e_vsi_restore_mac_filter(struct i40e_vsi *vsi)
+{
+	struct i40e_mac_filter *f;
+	struct i40e_macvlan_filter *mv_f;
+	int i, vlan_num = 0;
+	int ret = I40E_SUCCESS;
+	void *temp;
+
+	/* restore all the MACs */
+	TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp) {
+		if ((f->mac_info.filter_type == RTE_MACVLAN_PERFECT_MATCH) ||
+		    (f->mac_info.filter_type == RTE_MACVLAN_HASH_MATCH)) {
+			/**
+			 * If vlan_num is 0, that's the first time to add mac,
+			 * set mask for vlan_id 0.
+			 */
+			if (vsi->vlan_num == 0) {
+				i40e_set_vlan_filter(vsi, 0, 1);
+				vsi->vlan_num = 1;
+			}
+			vlan_num = vsi->vlan_num;
+		} else if ((f->mac_info.filter_type == RTE_MAC_PERFECT_MATCH) ||
+			   (f->mac_info.filter_type == RTE_MAC_HASH_MATCH))
+			vlan_num = 1;
+
+		mv_f = rte_zmalloc("macvlan_data", vlan_num * sizeof(*mv_f), 0);
+		if (!mv_f) {
+			PMD_DRV_LOG(ERR, "failed to allocate memory");
+			return I40E_ERR_NO_MEMORY;
+		}
+
+		for (i = 0; i < vlan_num; i++) {
+			mv_f[i].filter_type = f->mac_info.filter_type;
+			(void)rte_memcpy(&mv_f[i].macaddr,
+					 &f->mac_info.mac_addr,
+					 ETH_ADDR_LEN);
+		}
+
+		if (f->mac_info.filter_type == RTE_MACVLAN_PERFECT_MATCH ||
+		    f->mac_info.filter_type == RTE_MACVLAN_HASH_MATCH) {
+			ret = i40e_find_all_vlan_for_mac(vsi, mv_f, vlan_num,
+							 &f->mac_info.mac_addr);
+			if (ret != I40E_SUCCESS) {
+				rte_free(mv_f);
+				return ret;
+			}
+		}
+
+		ret = i40e_add_macvlan_filters(vsi, mv_f, vlan_num);
+		if (ret != I40E_SUCCESS) {
+			rte_free(mv_f);
+			return ret;
+		}
+
+		rte_free(mv_f);
+		ret = I40E_SUCCESS;
+	}
+
+	return ret;
+}
+
+static int
+i40e_vsi_set_tx_loopback(struct i40e_vsi *vsi, uint8_t on)
+{
+	struct i40e_vsi_context ctxt;
+	struct i40e_hw *hw;
+	int ret;
+
+	if (!vsi)
+		return -EINVAL;
+
+	hw = I40E_VSI_TO_HW(vsi);
+
+	/* Use the FW API if FW >= v5.0 */
+	if (hw->aq.fw_maj_ver < 5) {
+		PMD_INIT_LOG(ERR, "FW < v5.0, cannot enable loopback");
+		return -ENOTSUP;
+	}
+
+	/* Check if it has been already on or off */
+	if (vsi->info.valid_sections &
+		rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SWITCH_VALID)) {
+		if (on) {
+			if ((vsi->info.switch_id &
+			     I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB) ==
+			    I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB)
+				return 0; /* already on */
+		} else {
+			if ((vsi->info.switch_id &
+			     I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB) == 0)
+				return 0; /* already off */
+		}
+	}
+
+	/* remove all the MAC and VLAN first */
+	ret = i40e_vsi_rm_mac_filter(vsi);
+	if (ret) {
+		PMD_INIT_LOG(ERR, "Failed to remove MAC filters.");
+		return ret;
+	}
+	if (vsi->vlan_anti_spoof_on) {
+		ret = i40e_add_rm_all_vlan_filter(vsi, 0);
+		if (ret) {
+			PMD_INIT_LOG(ERR, "Failed to remove VLAN filters.");
+			return ret;
+		}
+	}
+
+	vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
+	if (on)
+		vsi->info.switch_id |= I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB;
+	else
+		vsi->info.switch_id &= ~I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	(void)rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+	ctxt.seid = vsi->seid;
+
+	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+	if (ret != I40E_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to update VSI params");
+		return ret;
+	}
+
+	/* add all the MAC and VLAN back */
+	ret = i40e_vsi_restore_mac_filter(vsi);
+	if (ret)
+		return ret;
+	if (vsi->vlan_anti_spoof_on) {
+		ret = i40e_add_rm_all_vlan_filter(vsi, 1);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+int
+rte_pmd_i40e_set_tx_loopback(uint8_t port, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_pf_vf *vf;
+	struct i40e_vsi *vsi;
+	uint16_t vf_id;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	/* setup PF TX loopback */
+	vsi = pf->main_vsi;
+	ret = i40e_vsi_set_tx_loopback(vsi, on);
+	if (ret)
+		return -ENOTSUP;
+
+	/* setup TX loopback for all the VFs */
+	if (!pf->vfs) {
+		/* if no VF, do nothing. */
+		return 0;
+	}
+
+	for (vf_id = 0; vf_id < pf->vf_num; vf_id++) {
+		vf = &pf->vfs[vf_id];
+		vsi = vf->vsi;
+
+		ret = i40e_vsi_set_tx_loopback(vsi, on);
+		if (ret)
+			return -ENOTSUP;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index 849eb47..8befb06 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -117,4 +117,20 @@ int rte_pmd_i40e_set_vf_vlan_anti_spoof(uint8_t port,
 					uint16_t vf_id,
 					uint8_t on);
 
+/**
+ * Enable/Disable TX loopback on all the PF and VFs.
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param on
+ *    1 - Enable TX loopback.
+ *    0 - Disable TX loopback.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_tx_loopback(uint8_t port,
+				 uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 028f0ef..51eda65 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -7,6 +7,7 @@ DPDK_17.02 {
 	global:
 
 	rte_pmd_i40e_ping_vfs;
+	rte_pmd_i40e_set_tx_loopback;
 	rte_pmd_i40e_set_vf_mac_anti_spoof;
 	rte_pmd_i40e_set_vf_vlan_anti_spoof;
 
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 04/26] net/i40e: set VF VLAN anti-spoofing from PF
From: Wenzhuo Lu @ 2017-01-13  6:52 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Support enabling/disabling VF VLAN anti-spoofing from
PF.
User can call the API on PF to enable/disable a specific
VF's VLAN anti-spoofing.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 137 ++++++++++++++++++++++++++++--
 drivers/net/i40e/i40e_ethdev.h            |   1 +
 drivers/net/i40e/rte_pmd_i40e.h           |  19 +++++
 drivers/net/i40e/rte_pmd_i40e_version.map |   1 +
 4 files changed, 153 insertions(+), 5 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index eba5168..3d126a1 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -4418,6 +4418,7 @@ struct i40e_vsi *
 	vsi->max_macaddrs = I40E_NUM_MACADDR_MAX;
 	vsi->parent_vsi = uplink_vsi ? uplink_vsi : pf->main_vsi;
 	vsi->user_param = user_param;
+	vsi->vlan_anti_spoof_on = 0;
 	/* Allocate queues */
 	switch (vsi->type) {
 	case I40E_VSI_MAIN  :
@@ -5757,14 +5758,11 @@ struct i40e_vsi *
 }
 
 static void
-i40e_set_vlan_filter(struct i40e_vsi *vsi,
-			 uint16_t vlan_id, bool on)
+i40e_store_vlan_filter(struct i40e_vsi *vsi,
+		       uint16_t vlan_id, bool on)
 {
 	uint32_t vid_idx, vid_bit;
 
-	if (vlan_id > ETH_VLAN_ID_MAX)
-		return;
-
 	vid_idx = I40E_VFTA_IDX(vlan_id);
 	vid_bit = I40E_VFTA_BIT(vlan_id);
 
@@ -5774,6 +5772,38 @@ struct i40e_vsi *
 		vsi->vfta[vid_idx] &= ~vid_bit;
 }
 
+static void
+i40e_set_vlan_filter(struct i40e_vsi *vsi,
+		     uint16_t vlan_id, bool on)
+{
+	struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+	struct i40e_aqc_add_remove_vlan_element_data vlan_data = {0};
+	int ret;
+
+	if (vlan_id > ETH_VLAN_ID_MAX)
+		return;
+
+	i40e_store_vlan_filter(vsi, vlan_id, on);
+
+	if (!vsi->vlan_anti_spoof_on || !vlan_id)
+		return;
+
+	vlan_data.vlan_tag = rte_cpu_to_le_16(vlan_id);
+
+	if (on) {
+		ret = i40e_aq_add_vlan(hw, vsi->seid,
+				       &vlan_data, 1, NULL);
+		if (ret != I40E_SUCCESS)
+			PMD_DRV_LOG(ERR, "Failed to add vlan filter");
+	} else {
+		ret = i40e_aq_remove_vlan(hw, vsi->seid,
+					  &vlan_data, 1, NULL);
+		if (ret != I40E_SUCCESS)
+			PMD_DRV_LOG(ERR,
+				    "Failed to remove vlan filter");
+	}
+}
+
 /**
  * Find all vlan options for specific mac addr,
  * return with actual vlan found.
@@ -9780,3 +9810,100 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+static int
+i40e_add_rm_all_vlan_filter(struct i40e_vsi *vsi, uint8_t add)
+{
+	uint32_t j, k;
+	uint16_t vlan_id;
+	struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+	struct i40e_aqc_add_remove_vlan_element_data vlan_data = {0};
+	int ret;
+
+	for (j = 0; j < I40E_VFTA_SIZE; j++) {
+		if (!vsi->vfta[j])
+			continue;
+
+		for (k = 0; k < I40E_UINT32_BIT_SIZE; k++) {
+			if (!(vsi->vfta[j] & (1 << k)))
+				continue;
+
+			vlan_id = j * I40E_UINT32_BIT_SIZE + k;
+			if (!vlan_id)
+				continue;
+
+			vlan_data.vlan_tag = rte_cpu_to_le_16(vlan_id);
+			if (add)
+				ret = i40e_aq_add_vlan(hw, vsi->seid,
+						       &vlan_data, 1, NULL);
+			else
+				ret = i40e_aq_remove_vlan(hw, vsi->seid,
+							  &vlan_data, 1, NULL);
+			if (ret != I40E_SUCCESS) {
+				PMD_DRV_LOG(ERR,
+					    "Failed to add/rm vlan filter");
+				return ret;
+			}
+		}
+	}
+
+	return I40E_SUCCESS;
+}
+
+int
+rte_pmd_i40e_set_vf_vlan_anti_spoof(uint8_t port, uint16_t vf_id, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	struct i40e_hw *hw;
+	struct i40e_vsi_context ctxt;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	/* Check if it has been already on or off */
+	if (vsi->vlan_anti_spoof_on == on)
+		return 0; /* already on or off */
+
+	vsi->vlan_anti_spoof_on = on;
+	ret = i40e_add_rm_all_vlan_filter(vsi, on);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Failed to remove VLAN filters.");
+		return -ENOTSUP;
+	}
+
+	vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+	if (on)
+		vsi->info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK;
+	else
+		vsi->info.sec_flags &= ~I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	(void)rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+	ctxt.seid = vsi->seid;
+
+	hw = I40E_VSI_TO_HW(vsi);
+	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to update VSI params");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 298cef4..0db140b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -300,6 +300,7 @@ struct i40e_vsi {
 	uint16_t msix_intr; /* The MSIX interrupt binds to VSI */
 	uint16_t nb_msix;   /* The max number of msix vector */
 	uint8_t enabled_tc; /* The traffic class enabled */
+	uint8_t vlan_anti_spoof_on; /* The VLAN anti-spoofing enabled */
 	struct i40e_bw_info bw_info; /* VSI bandwidth information */
 };
 
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index d13736f..849eb47 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -98,4 +98,23 @@ int rte_pmd_i40e_set_vf_mac_anti_spoof(uint8_t port,
 				       uint16_t vf_id,
 				       uint8_t on);
 
+/**
+ * Enable/Disable VF VLAN anti spoofing.
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    VF on which to set VLAN anti spoofing.
+ * @param on
+ *    1 - Enable VFs VLAN anti spoofing.
+ *    0 - Disable VFs VLAN anti spoofing.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_vf_vlan_anti_spoof(uint8_t port,
+					uint16_t vf_id,
+					uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 0581209..028f0ef 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -8,5 +8,6 @@ DPDK_17.02 {
 
 	rte_pmd_i40e_ping_vfs;
 	rte_pmd_i40e_set_vf_mac_anti_spoof;
+	rte_pmd_i40e_set_vf_vlan_anti_spoof;
 
 } DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 02/26] net/i40e: add callback to user on VF to PF mbox msg
From: Wenzhuo Lu @ 2017-01-13  6:52 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

The callback asks the user application if it is allowed to
perform the mailbox messages.

If the return value from user is RTE_PMD_I40E_MB_EVENT_PROCEED
then continue. If ACK or NACK, do nothing and send
not_supported to VF.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_pf.c      | 230 ++++++++++++++++++++++++++++++++++------
 drivers/net/i40e/rte_pmd_i40e.h |  21 ++++
 2 files changed, 216 insertions(+), 35 deletions(-)

diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index f606c2a..5c58424 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -55,6 +55,7 @@
 #include "i40e_ethdev.h"
 #include "i40e_rxtx.h"
 #include "i40e_pf.h"
+#include "rte_pmd_i40e.h"
 
 #define I40E_CFG_CRCSTRIP_DEFAULT 1
 
@@ -272,14 +273,23 @@
 }
 
 static void
-i40e_pf_host_process_cmd_version(struct i40e_pf_vf *vf)
+i40e_pf_host_process_cmd_version(struct i40e_pf_vf *vf, bool b_op)
 {
 	struct i40e_virtchnl_version_info info;
 
 	info.major = I40E_DPDK_VERSION_MAJOR;
 	info.minor = I40E_DPDK_VERSION_MINOR;
-	i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_VERSION,
-		I40E_SUCCESS, (uint8_t *)&info, sizeof(info));
+
+	if (b_op)
+		i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_VERSION,
+					    I40E_SUCCESS,
+					    (uint8_t *)&info,
+					    sizeof(info));
+	else
+		i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_VERSION,
+					    I40E_NOT_SUPPORTED,
+					    (uint8_t *)&info,
+					    sizeof(info));
 }
 
 static int
@@ -292,13 +302,20 @@
 }
 
 static int
-i40e_pf_host_process_cmd_get_vf_resource(struct i40e_pf_vf *vf)
+i40e_pf_host_process_cmd_get_vf_resource(struct i40e_pf_vf *vf, bool b_op)
 {
 	struct i40e_virtchnl_vf_resource *vf_res = NULL;
 	struct i40e_hw *hw = I40E_PF_TO_HW(vf->pf);
 	uint32_t len = 0;
 	int ret = I40E_SUCCESS;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(vf,
+					    I40E_VIRTCHNL_OP_GET_VF_RESOURCES,
+					    I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	/* only have 1 VSI by default */
 	len =  sizeof(struct i40e_virtchnl_vf_resource) +
 				I40E_DEFAULT_VF_VSI_NUM *
@@ -423,7 +440,8 @@
 static int
 i40e_pf_host_process_cmd_config_vsi_queues(struct i40e_pf_vf *vf,
 					   uint8_t *msg,
-					   uint16_t msglen)
+					   uint16_t msglen,
+					   bool b_op)
 {
 	struct i40e_hw *hw = I40E_PF_TO_HW(vf->pf);
 	struct i40e_vsi *vsi = vf->vsi;
@@ -432,6 +450,13 @@
 	struct i40e_virtchnl_queue_pair_info *vc_qpi;
 	int i, ret = I40E_SUCCESS;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(vf,
+					    I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES,
+					    I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
 		vc_vqci->num_queue_pairs > I40E_MAX_VSI_QP ||
 		msglen < I40E_VIRTCHNL_CONFIG_VSI_QUEUES_SIZE(vc_vqci,
@@ -482,7 +507,8 @@
 static int
 i40e_pf_host_process_cmd_config_vsi_queues_ext(struct i40e_pf_vf *vf,
 					       uint8_t *msg,
-					       uint16_t msglen)
+					       uint16_t msglen,
+					       bool b_op)
 {
 	struct i40e_hw *hw = I40E_PF_TO_HW(vf->pf);
 	struct i40e_vsi *vsi = vf->vsi;
@@ -491,6 +517,14 @@
 	struct i40e_virtchnl_queue_pair_ext_info *vc_qpei;
 	int i, ret = I40E_SUCCESS;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES_EXT,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (!msg || vc_vqcei->num_queue_pairs > vsi->nb_qps ||
 		vc_vqcei->num_queue_pairs > I40E_MAX_VSI_QP ||
 		msglen < I40E_VIRTCHNL_CONFIG_VSI_QUEUES_SIZE(vc_vqcei,
@@ -539,12 +573,21 @@
 
 static int
 i40e_pf_host_process_cmd_config_irq_map(struct i40e_pf_vf *vf,
-					uint8_t *msg, uint16_t msglen)
+					uint8_t *msg, uint16_t msglen,
+					bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_irq_map_info *irqmap =
 	    (struct i40e_virtchnl_irq_map_info *)msg;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen < sizeof(struct i40e_virtchnl_irq_map_info)) {
 		PMD_DRV_LOG(ERR, "buffer too short");
 		ret = I40E_ERR_PARAM;
@@ -646,12 +689,21 @@
 static int
 i40e_pf_host_process_cmd_disable_queues(struct i40e_pf_vf *vf,
 					uint8_t *msg,
-					uint16_t msglen)
+					uint16_t msglen,
+					bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_queue_select *q_sel =
 		(struct i40e_virtchnl_queue_select *)msg;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_DISABLE_QUEUES,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
 		ret = I40E_ERR_PARAM;
 		goto send_msg;
@@ -669,7 +721,8 @@
 static int
 i40e_pf_host_process_cmd_add_ether_address(struct i40e_pf_vf *vf,
 					   uint8_t *msg,
-					   uint16_t msglen)
+					   uint16_t msglen,
+					   bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_ether_addr_list *addr_list =
@@ -678,6 +731,14 @@
 	int i;
 	struct ether_addr *mac;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -707,7 +768,8 @@
 static int
 i40e_pf_host_process_cmd_del_ether_address(struct i40e_pf_vf *vf,
 					   uint8_t *msg,
-					   uint16_t msglen)
+					   uint16_t msglen,
+					   bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_ether_addr_list *addr_list =
@@ -715,6 +777,14 @@
 	int i;
 	struct ether_addr *mac;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
 		PMD_DRV_LOG(ERR, "delete_ether_address argument too short");
 		ret = I40E_ERR_PARAM;
@@ -739,7 +809,8 @@
 
 static int
 i40e_pf_host_process_cmd_add_vlan(struct i40e_pf_vf *vf,
-				uint8_t *msg, uint16_t msglen)
+				uint8_t *msg, uint16_t msglen,
+				bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_vlan_filter_list *vlan_filter_list =
@@ -747,6 +818,14 @@
 	int i;
 	uint16_t *vid;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_ADD_VLAN,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
 		PMD_DRV_LOG(ERR, "add_vlan argument too short");
 		ret = I40E_ERR_PARAM;
@@ -771,7 +850,8 @@
 static int
 i40e_pf_host_process_cmd_del_vlan(struct i40e_pf_vf *vf,
 				  uint8_t *msg,
-				  uint16_t msglen)
+				  uint16_t msglen,
+				  bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_vlan_filter_list *vlan_filter_list =
@@ -779,6 +859,14 @@
 	int i;
 	uint16_t *vid;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_DEL_VLAN,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
 		PMD_DRV_LOG(ERR, "delete_vlan argument too short");
 		ret = I40E_ERR_PARAM;
@@ -803,7 +891,8 @@
 i40e_pf_host_process_cmd_config_promisc_mode(
 					struct i40e_pf_vf *vf,
 					uint8_t *msg,
-					uint16_t msglen)
+					uint16_t msglen,
+					bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_promisc_info *promisc =
@@ -811,6 +900,14 @@
 	struct i40e_hw *hw = I40E_PF_TO_HW(vf->pf);
 	bool unicast = FALSE, multicast = FALSE;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen != sizeof(*promisc)) {
 		ret = I40E_ERR_PARAM;
 		goto send_msg;
@@ -836,13 +933,20 @@
 }
 
 static int
-i40e_pf_host_process_cmd_get_stats(struct i40e_pf_vf *vf)
+i40e_pf_host_process_cmd_get_stats(struct i40e_pf_vf *vf, bool b_op)
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_GET_STATS,
-		I40E_SUCCESS, (uint8_t *)&vf->vsi->eth_stats,
-				sizeof(vf->vsi->eth_stats));
+	if (b_op)
+		i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_GET_STATS,
+					    I40E_SUCCESS,
+					    (uint8_t *)&vf->vsi->eth_stats,
+					    sizeof(vf->vsi->eth_stats));
+	else
+		i40e_pf_host_send_msg_to_vf(vf, I40E_VIRTCHNL_OP_GET_STATS,
+					    I40E_NOT_SUPPORTED,
+					    (uint8_t *)&vf->vsi->eth_stats,
+					    sizeof(vf->vsi->eth_stats));
 
 	return I40E_SUCCESS;
 }
@@ -851,12 +955,21 @@
 i40e_pf_host_process_cmd_cfg_vlan_offload(
 					struct i40e_pf_vf *vf,
 					uint8_t *msg,
-					uint16_t msglen)
+					uint16_t msglen,
+					bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_vlan_offload_info *offload =
 			(struct i40e_virtchnl_vlan_offload_info *)msg;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_CFG_VLAN_OFFLOAD,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen != sizeof(*offload)) {
 		ret = I40E_ERR_PARAM;
 		goto send_msg;
@@ -877,12 +990,21 @@
 static int
 i40e_pf_host_process_cmd_cfg_pvid(struct i40e_pf_vf *vf,
 					uint8_t *msg,
-					uint16_t msglen)
+					uint16_t msglen,
+					bool b_op)
 {
 	int ret = I40E_SUCCESS;
 	struct i40e_virtchnl_pvid_info  *tpid_info =
 			(struct i40e_virtchnl_pvid_info *)msg;
 
+	if (!b_op) {
+		i40e_pf_host_send_msg_to_vf(
+			vf,
+			I40E_VIRTCHNL_OP_CFG_VLAN_PVID,
+			I40E_NOT_SUPPORTED, NULL, 0);
+		return ret;
+	}
+
 	if (msg == NULL || msglen != sizeof(*tpid_info)) {
 		ret = I40E_ERR_PARAM;
 		goto send_msg;
@@ -923,6 +1045,8 @@
 	struct i40e_pf_vf *vf;
 	/* AdminQ will pass absolute VF id, transfer to internal vf id */
 	uint16_t vf_id = abs_vf_id - hw->func_caps.vf_base_id;
+	struct rte_pmd_i40e_mb_event_param cb_param;
+	bool b_op = TRUE;
 
 	if (vf_id > pf->vf_num - 1 || !pf->vfs) {
 		PMD_DRV_LOG(ERR, "invalid argument");
@@ -937,10 +1061,35 @@
 		return;
 	}
 
+	/**
+	 * initialise structure to send to user application
+	 * will return response from user in retval field
+	 */
+	cb_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
+	cb_param.vfid = vf_id;
+	cb_param.msg_type = opcode;
+	cb_param.msg = (void *)msg;
+	cb_param.msglen = msglen;
+
+	/**
+	 * Ask user application if we're allowed to perform those functions.
+	 * If we get cb_param.retval == RTE_PMD_I40E_MB_EVENT_PROCEED,
+	 * then business as usual.
+	 * If RTE_PMD_I40E_MB_EVENT_NOOP_ACK or RTE_PMD_I40E_MB_EVENT_NOOP_NACK,
+	 * do nothing and send not_supported to VF. As PF must send a response
+	 * to VF and ACK/NACK is not defined.
+	 */
+	_rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_VF_MBOX, &cb_param);
+	if (cb_param.retval != RTE_PMD_I40E_MB_EVENT_PROCEED) {
+		PMD_DRV_LOG(WARNING, "VF to PF message(%d) is not permitted!",
+			    opcode);
+		b_op = FALSE;
+	}
+
 	switch (opcode) {
 	case I40E_VIRTCHNL_OP_VERSION :
 		PMD_DRV_LOG(INFO, "OP_VERSION received");
-		i40e_pf_host_process_cmd_version(vf);
+		i40e_pf_host_process_cmd_version(vf, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_RESET_VF :
 		PMD_DRV_LOG(INFO, "OP_RESET_VF received");
@@ -948,61 +1097,72 @@
 		break;
 	case I40E_VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf);
+		i40e_pf_host_process_cmd_get_vf_resource(vf, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg, msglen);
+		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+							   msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES_EXT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES_EXT received");
 		i40e_pf_host_process_cmd_config_vsi_queues_ext(vf, msg,
-								msglen);
+							       msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen);
+		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
-		i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
-		i40e_notify_vf_link_status(dev, vf);
+		if (b_op) {
+			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			i40e_notify_vf_link_status(dev, vf);
+		} else {
+			i40e_pf_host_send_msg_to_vf(
+				vf, I40E_VIRTCHNL_OP_ENABLE_QUEUES,
+				I40E_NOT_SUPPORTED, NULL, 0);
+		}
 		break;
 	case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen);
+		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg, msglen);
+		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+							   msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg, msglen);
+		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+							   msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen);
+		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen);
+		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg, msglen);
+		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+							     msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf);
+		i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CFG_VLAN_OFFLOAD:
 		PMD_DRV_LOG(INFO, "OP_CFG_VLAN_OFFLOAD received");
-		i40e_pf_host_process_cmd_cfg_vlan_offload(vf, msg, msglen);
+		i40e_pf_host_process_cmd_cfg_vlan_offload(vf, msg,
+							  msglen, b_op);
 		break;
 	case I40E_VIRTCHNL_OP_CFG_VLAN_PVID:
 		PMD_DRV_LOG(INFO, "OP_CFG_VLAN_PVID received");
-		i40e_pf_host_process_cmd_cfg_pvid(vf, msg, msglen);
+		i40e_pf_host_process_cmd_cfg_pvid(vf, msg, msglen, b_op);
 		break;
 	/* Don't add command supported below, which will
 	 * return an error code.
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index da79585..84c9291 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -45,6 +45,27 @@
 #include <rte_ethdev.h>
 
 /**
+ * Response sent back to i40e driver from user app after callback
+ */
+enum rte_pmd_i40e_mb_event_rsp {
+	RTE_PMD_I40E_MB_EVENT_NOOP_ACK,  /**< skip mbox request and ACK */
+	RTE_PMD_I40E_MB_EVENT_NOOP_NACK, /**< skip mbox request and NACK */
+	RTE_PMD_I40E_MB_EVENT_PROCEED,  /**< proceed with mbox request  */
+	RTE_PMD_I40E_MB_EVENT_MAX       /**< max value of this enum */
+};
+
+/**
+ * Data sent to the user application when the callback is executed.
+ */
+struct rte_pmd_i40e_mb_event_param {
+	uint16_t vfid;     /**< Virtual Function number */
+	uint16_t msg_type; /**< VF to PF message type, see i40e_virtchnl_ops */
+	uint16_t retval;   /**< return value */
+	void *msg;         /**< pointer to message */
+	uint16_t msglen;   /**< length of the message */
+};
+
+/**
  * Notify VF when PF link status changes.
  *
  * @param port
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 03/26] net/i40e: set VF MAC anti-spoofing from PF
From: Wenzhuo Lu @ 2017-01-13  6:52 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Support enabling/disabling VF MAC anti-spoofing from
PF.
User can call the API on PF to enable/disable a specific
VF's MAC anti-spoofing.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c            | 62 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e.h           | 19 ++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  1 +
 3 files changed, 82 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index a13d96b..eba5168 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -9718,3 +9718,65 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return 0;
 }
+
+int
+rte_pmd_i40e_set_vf_mac_anti_spoof(uint8_t port, uint16_t vf_id, uint8_t on)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	struct i40e_hw *hw;
+	struct i40e_vsi_context ctxt;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf_id >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	vsi = pf->vfs[vf_id].vsi;
+	if (!vsi) {
+		PMD_DRV_LOG(ERR, "Invalid VSI.");
+		return -EINVAL;
+	}
+
+	/* Check if it has been already on or off */
+	if (vsi->info.valid_sections &
+		rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SECURITY_VALID)) {
+		if (on) {
+			if ((vsi->info.sec_flags &
+			     I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK) ==
+			    I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK)
+				return 0; /* already on */
+		} else {
+			if ((vsi->info.sec_flags &
+			     I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK) == 0)
+				return 0; /* already off */
+		}
+	}
+
+	vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+	if (on)
+		vsi->info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK;
+	else
+		vsi->info.sec_flags &= ~I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	(void)rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+	ctxt.seid = vsi->seid;
+
+	hw = I40E_VSI_TO_HW(vsi);
+	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+	if (ret != I40E_SUCCESS) {
+		ret = -ENOTSUP;
+		PMD_DRV_LOG(ERR, "Failed to update VSI params");
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
index 84c9291..d13736f 100644
--- a/drivers/net/i40e/rte_pmd_i40e.h
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -79,4 +79,23 @@ struct rte_pmd_i40e_mb_event_param {
  */
 int rte_pmd_i40e_ping_vfs(uint8_t port, uint16_t vf);
 
+/**
+ * Enable/Disable VF MAC anti spoofing.
+ *
+ * @param port
+ *    The port identifier of the Ethernet device.
+ * @param vf
+ *    VF on which to set MAC anti spoofing.
+ * @param on
+ *    1 - Enable VFs MAC anti spoofing.
+ *    0 - Disable VFs MAC anti spoofing.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+int rte_pmd_i40e_set_vf_mac_anti_spoof(uint8_t port,
+				       uint16_t vf_id,
+				       uint8_t on);
+
 #endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index 3c6a192..0581209 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -7,5 +7,6 @@ DPDK_17.02 {
 	global:
 
 	rte_pmd_i40e_ping_vfs;
+	rte_pmd_i40e_set_vf_mac_anti_spoof;
 
 } DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 01/26] net/i40e: support link status notification
From: Wenzhuo Lu @ 2017-01-13  6:52 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

Add an API to expose the ability, that PF can notify VF
when link status changes, to APP.
So if PF APP doesn't want to enable interruption but check
link status by itself, PF APP can let VF know link status
changed.

Signed-off-by: Wenzhuo Lu <wenzhuo.lu@intel.com>
---
 MAINTAINERS                               |  1 +
 drivers/net/i40e/Makefile                 |  4 +-
 drivers/net/i40e/i40e_ethdev.c            | 23 ++++++++++++
 drivers/net/i40e/i40e_pf.c                |  4 +-
 drivers/net/i40e/i40e_pf.h                |  4 +-
 drivers/net/i40e/rte_pmd_i40e.h           | 61 +++++++++++++++++++++++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map |  7 ++++
 7 files changed, 100 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/i40e/rte_pmd_i40e.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 32d41b0..9531ff7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -328,6 +328,7 @@ Intel i40e
 M: Helin Zhang <helin.zhang@intel.com>
 M: Jingjing Wu <jingjing.wu@intel.com>
 F: drivers/net/i40e/
+F: drivers/net/i40e/rte_pmd_i40e.h - EXPERIMENTAL
 F: doc/guides/nics/i40e.rst
 F: doc/guides/nics/intel_vf.rst
 
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 66997b6..4e54e92 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -1,6 +1,6 @@
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
@@ -111,6 +111,8 @@ ifeq ($(findstring RTE_MACHINE_CPUFLAG_SSE4_1,$(CFLAGS)),)
 CFLAGS_i40e_rxtx_vec_sse.o += -msse4.1
 endif
 
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_I40E_PMD)-include := rte_pmd_i40e.h
 
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index f42f4ba..a13d96b 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -62,6 +62,7 @@
 #include "i40e_rxtx.h"
 #include "i40e_pf.h"
 #include "i40e_regs.h"
+#include "rte_pmd_i40e.h"
 
 #define ETH_I40E_FLOATING_VEB_ARG	"enable_floating_veb"
 #define ETH_I40E_FLOATING_VEB_LIST_ARG	"floating_veb_list"
@@ -9695,3 +9696,25 @@ static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+int
+rte_pmd_i40e_ping_vfs(uint8_t port, uint16_t vf)
+{
+	struct rte_eth_dev *dev;
+	struct i40e_pf *pf;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+
+	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	if (vf >= pf->vf_num || !pf->vfs) {
+		PMD_DRV_LOG(ERR, "Invalid argument.");
+		return -EINVAL;
+	}
+
+	i40e_notify_vf_link_status(dev, &pf->vfs[vf]);
+
+	return 0;
+}
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index ddfc140..f606c2a 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -897,7 +897,7 @@
 	return ret;
 }
 
-static void
+void
 i40e_notify_vf_link_status(struct rte_eth_dev *dev, struct i40e_pf_vf *vf)
 {
 	struct i40e_virtchnl_pf_event event;
diff --git a/drivers/net/i40e/i40e_pf.h b/drivers/net/i40e/i40e_pf.h
index cddc45c..47a9c34 100644
--- a/drivers/net/i40e/i40e_pf.h
+++ b/drivers/net/i40e/i40e_pf.h
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -123,5 +123,7 @@ void i40e_pf_host_handle_vf_msg(struct rte_eth_dev *dev,
 				uint8_t *msg, uint16_t msglen);
 int i40e_pf_host_init(struct rte_eth_dev *dev);
 int i40e_pf_host_uninit(struct rte_eth_dev *dev);
+void i40e_notify_vf_link_status(struct rte_eth_dev *dev,
+				struct i40e_pf_vf *vf);
 
 #endif /* _I40E_PF_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e.h b/drivers/net/i40e/rte_pmd_i40e.h
new file mode 100644
index 0000000..da79585
--- /dev/null
+++ b/drivers/net/i40e/rte_pmd_i40e.h
@@ -0,0 +1,61 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright (c) 2017 Intel Corporation. All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PMD_I40E_H_
+#define _PMD_I40E_H_
+
+/**
+ * @file rte_pmd_i40e.h
+ *
+ * i40e PMD specific functions.
+ *
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ */
+
+#include <rte_ethdev.h>
+
+/**
+ * Notify VF when PF link status changes.
+ *
+ * @param port
+ *   The port identifier of the Ethernet device.
+ * @param vf
+ *   VF id.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port* invalid.
+ *   - (-EINVAL) if *vf* invalid.
+ */
+int rte_pmd_i40e_ping_vfs(uint8_t port, uint16_t vf);
+
+#endif /* _PMD_I40E_H_ */
diff --git a/drivers/net/i40e/rte_pmd_i40e_version.map b/drivers/net/i40e/rte_pmd_i40e_version.map
index ef35398..3c6a192 100644
--- a/drivers/net/i40e/rte_pmd_i40e_version.map
+++ b/drivers/net/i40e/rte_pmd_i40e_version.map
@@ -2,3 +2,10 @@ DPDK_2.0 {
 
 	local: *;
 };
+
+DPDK_17.02 {
+	global:
+
+	rte_pmd_i40e_ping_vfs;
+
+} DPDK_2.0;
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 00/26] Support VFD on i40e - EXPERIMENTAL
From: Wenzhuo Lu @ 2017-01-13  6:52 UTC (permalink / raw)
  To: dev; +Cc: Wenzhuo Lu
In-Reply-To: <1480637533-37425-1-git-send-email-wenzhuo.lu@intel.com>

1, VF Daemon (VFD)
VFD is an idea to control all the VFs from PF.
As we need to support the scenario kernel PF + DPDK VF, DPDK follows
the interface between kernel PF + kernel VF.
We don't want to introduce too many new messages between PF and VF.
So this patch set adds some new APIs to control VFs directly from PF.
The new APIs include,
1) set VF MAC anti-spoofing
2) set VF VLAN anti-spoofing
3) set TX loopback
4) set VF unicast promiscuous mode
5) set VF multicast promiscuous mode
6) set VF MTU
7) get/reset VF stats
8) set VF MAC address
9) set VF VLAN stripping
10) VF VLAN insertion
12) set VF broadcast mode
13) set VF VLAN tag
14) set VF VLAN filter
VFD also includes VF to PF mailbox message management by APP.
When PF receives mailbox messages from VF, PF should call the callback
provided by APP to know if they're permitted to be processed.

Please aware VFD is an EXPERIMENTAL feature, it can be changed or even
removed without prior notice.

2, Implement VF MAC address setting on VF.

v9:
- upadte the doc and announce VFD is an experimental feature.

v8:
- remove unnecessary VF check.
- add more debug logs.
- split VMDq related code to a separate patch set.
- some minor change.

v7:
- fix 32 bit compile error in patch 23.
- add new patches to configure VMDq.

v6:
- remove the support of DPDK PF + kernel VF. Will create a new patch set for it.
- reword the tittles of some patches.
- add sanity check for vsi, and other minor change.

v5:
- fix testpmd build error(s)
- fix i40e vf_rx_vlan
- remove redundant memset on dev_info
- add functions to .map file sorted

v4:
- rebase on latest next-net
- move patch 10/29 testpmd part to patch 18/29

v3:
- fix issue that VF does not work for i40e
- remove patch for VDMq receive mode init
- move get/reset VF stats API into rte_pmd_i40

v2:
- fix the compile issues.
- fix the checkpatch warning and typo.
- update the commit log of some patches.
- fix the invalid port ID issue of testpmd.

Bernard Iremonger (7):
  net/i40e: set VF VLAN insertion from PF
  net/i40e: set VF broadcast mode from PF
  net/i40e: set VF VLAN tag from PF
  net/i40e: set VF VLAN filter from PF
  app/testpmd: add command to test VF broadcast mode on i40e
  app/testpmd: add command to test VF VLAN tag on i40e
  app/testpmd: handle i40e in VF VLAN filter command

Chen Jing D(Mark) (2):
  net/i40e: set VF VLAN strip from PF
  net/i40e: enhance in sanity check of MAC

Ferruh Yigit (3):
  net/i40e: set VF MAC from PF support
  net/i40e: set VF MAC from VF support
  net/i40e: fix VF MAC address assignment

Qi Zhang (3):
  net/i40e: enable VF MTU change
  net/i40e: fix VF reset flow
  net/i40e: set/clear VF stats from PF

Wenzhuo Lu (11):
  net/i40e: support link status notification
  net/i40e: add callback to user on VF to PF mbox msg
  net/i40e: set VF MAC anti-spoofing from PF
  net/i40e: set VF VLAN anti-spoofing from PF
  net/i40e: set Tx loopback from PF
  net/i40e: set VF unicast promisc mode from PF
  net/i40e: set VF multicast promisc mode from PF
  app/testpmd: use VFD APIs on i40e
  app/testpmd: use unicast promiscuous mode on i40e
  app/testpmd: use multicast promiscuous mode on i40e
  doc: update doc for VFD

 MAINTAINERS                                 |   1 +
 app/test-pmd/Makefile                       |   3 +
 app/test-pmd/cmdline.c                      | 587 ++++++++++++++++--
 app/test-pmd/config.c                       |  13 -
 app/test-pmd/testpmd.h                      |   2 -
 doc/guides/nics/i40e.rst                    |   1 +
 doc/guides/rel_notes/release_17_02.rst      |  29 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  32 +
 drivers/net/i40e/Makefile                   |   4 +-
 drivers/net/i40e/i40e_ethdev.c              | 921 +++++++++++++++++++++++++++-
 drivers/net/i40e/i40e_ethdev.h              |   5 +-
 drivers/net/i40e/i40e_ethdev_vf.c           |  82 ++-
 drivers/net/i40e/i40e_pf.c                  | 244 ++++++--
 drivers/net/i40e/i40e_pf.h                  |   9 +-
 drivers/net/i40e/rte_pmd_i40e.h             | 335 ++++++++++
 drivers/net/i40e/rte_pmd_i40e_version.map   |  20 +
 16 files changed, 2173 insertions(+), 115 deletions(-)
 create mode 100644 drivers/net/i40e/rte_pmd_i40e.h

Series-Acked-by: Helin Zhang <helin.zhang@intel.com>
-- 
1.9.3

^ permalink raw reply

* [PATCH v9 12/26] net/i40e: fix VF MAC address assignment
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Ferruh Yigit, stable
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Ferruh Yigit <ferruh.yigit@intel.com>

If PF sets vf->mac_addr, in VF initialization hw->mac.addr will be set
to that same value. It is possible to check if PF set a MAC address or
not through the hw->mac.addr variable.

hw->mac.addr set by i40e_vf_parse_hw_config(), call stack is:

In PF side
i40e_pf_host_process_cmd_get_vf_resources()
    eth_addr_copy(vf->mac_addr, vf_res->vsi_res[0].default_mac_address)

In VF sise
i40evf_init_vf()
    i40evf_get_vf_resources()
            i40e_vf_parse_hw_config()
                    memcpy(hw->mac.addr, vsi_res->default_mac_addr)

Updated code is after i40evf_get_vf_resources() and can benefit from
hw->mac.addr variable.

Fixes: 89e6b86384bb ("i40evf: rework MAC address validation")

CC: stable@dpdk.org
Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
---
 drivers/net/i40e/i40e_ethdev_vf.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
index 5016249..0977095 100644
--- a/drivers/net/i40e/i40e_ethdev_vf.c
+++ b/drivers/net/i40e/i40e_ethdev_vf.c
@@ -1193,7 +1193,6 @@ static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
 	int i, err, bufsz;
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
-	struct ether_addr *p_mac_addr;
 	uint16_t interval =
 		i40e_calc_itr_interval(I40E_QUEUE_ITR_INTERVAL_MAX);
 
@@ -1270,13 +1269,10 @@ static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
 	vf->vsi.adapter = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 
 	/* Store the MAC address configured by host, or generate random one */
-	p_mac_addr = (struct ether_addr *)(vf->vsi_res->default_mac_addr);
-	if (is_valid_assigned_ether_addr(p_mac_addr)) { /* Configured by host */
-		ether_addr_copy(p_mac_addr, (struct ether_addr *)hw->mac.addr);
+	if (is_valid_assigned_ether_addr((struct ether_addr *)hw->mac.addr))
 		vf->flags |= I40E_FLAG_VF_MAC_BY_PF;
-	} else {
+	else
 		eth_random_addr(hw->mac.addr); /* Generate a random one */
-	}
 
 	/* If the PF host is not DPDK, set the interval of ITR0 to max*/
 	if (vf->version_major != I40E_DPDK_VERSION_MAJOR) {
-- 
1.9.3

^ permalink raw reply related

* [PATCH v9 09/26] net/i40e: fix VF reset flow
From: Wenzhuo Lu @ 2017-01-13  6:53 UTC (permalink / raw)
  To: dev; +Cc: Qi Zhang, stable
In-Reply-To: <1484290401-1404-1-git-send-email-wenzhuo.lu@intel.com>

From: Qi Zhang <qi.z.zhang@intel.com>

Add missing step during VF reset: PF should
set I40E_VFGEN_RSTAT to ACTIVE at end of the
VF reset operation or VF driver may not able
to detect that reset is already completed.
This patch also remove the unnecessary enum
for vfr state.

Fixes: 4861cde46116 ("i40e: new poll mode driver")

CC: stable@dpdk.org
Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 drivers/net/i40e/i40e_pf.c | 6 ++++--
 drivers/net/i40e/i40e_pf.h | 5 -----
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index 5c58424..8a6733c 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -139,7 +139,7 @@
 	abs_vf_id = vf_id + hw->func_caps.vf_base_id;
 
 	/* Notify VF that we are in VFR progress */
-	I40E_WRITE_REG(hw, I40E_VFGEN_RSTAT1(vf_id), I40E_PF_VFR_INPROGRESS);
+	I40E_WRITE_REG(hw, I40E_VFGEN_RSTAT1(vf_id), I40E_VFR_INPROGRESS);
 
 	/*
 	 * If require a SW VF reset, a VFLR interrupt will be generated,
@@ -220,7 +220,7 @@
 	}
 
 	/* Reset done, Set COMPLETE flag and clear reset bit */
-	I40E_WRITE_REG(hw, I40E_VFGEN_RSTAT1(vf_id), I40E_PF_VFR_COMPLETED);
+	I40E_WRITE_REG(hw, I40E_VFGEN_RSTAT1(vf_id), I40E_VFR_COMPLETED);
 	val = I40E_READ_REG(hw, I40E_VPGEN_VFRTRIG(vf_id));
 	val &= ~I40E_VPGEN_VFRTRIG_VFSWR_MASK;
 	I40E_WRITE_REG(hw, I40E_VPGEN_VFRTRIG(vf_id), val);
@@ -248,6 +248,8 @@
 		return -EFAULT;
 	}
 
+	I40E_WRITE_REG(hw, I40E_VFGEN_RSTAT1(vf_id), I40E_VFR_VFACTIVE);
+
 	return ret;
 }
 
diff --git a/drivers/net/i40e/i40e_pf.h b/drivers/net/i40e/i40e_pf.h
index 47a9c34..b4c2287 100644
--- a/drivers/net/i40e/i40e_pf.h
+++ b/drivers/net/i40e/i40e_pf.h
@@ -48,11 +48,6 @@
 
 #define I40E_DPDK_OFFSET  0x100
 
-enum i40e_pf_vfr_state {
-	I40E_PF_VFR_INPROGRESS = 0,
-	I40E_PF_VFR_COMPLETED = 1,
-};
-
 /* DPDK pf driver specific command to VF */
 enum i40e_virtchnl_ops_dpdk {
 	/*
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCH v5 00/18] net/ixgbe: Consistent filter API
From: Dai, Wei @ 2017-01-13  6:27 UTC (permalink / raw)
  To: Xing, Beilei, Zhao1, Wei, dev@dpdk.org
In-Reply-To: <94479800C636CB44BD422CB454846E0131590B00@SHSMSX101.ccr.corp.intel.com>

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Xing, Beilei
> Sent: Thursday, January 12, 2017 7:39 PM
> To: Zhao1, Wei <wei.zhao1@intel.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 00/18] net/ixgbe: Consistent filter API
> 
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Wei Zhao
> > Sent: Thursday, January 12, 2017 5:17 PM
> > To: dev@dpdk.org
> > Subject: [dpdk-dev] [PATCH v5 00/18] net/ixgbe: Consistent filter API
> >
> > The patches mainly finish following functions:
> > 1) Store and restore all kinds of filters.
> > 2) Parse all kinds of filters.
> > 3) Add flow validate function.
> > 4) Add flow create function.
> > 5) Add flow destroy function.
> > 6) Add flow flush function.
> >
> > v2 changes:
> >  fix git log error
> >  Modify some function call relationship  Change return value type of
> > all parse flow functions  Update error info for all flow ops  Add
> > ixgbe_filterlist_flush to flush flows and rules created
> >
> > v3 change:
> >  add new file ixgbe_flow.c to store generic API parser related
> > functions  add more comment about pattern and action rules  add attr
> > check in parser functions  change struct name ixgbe_flow to rte_flow
> > change SYN to TCP SYN  change to use memset initizlize struct
> > ixgbe_filter_info  break down filter uninit process to 3 indepedent
> > functions in eth_ixgbe_dev_uninit() change struct rte_flow_item_nvgre
> > definition  change struct rte_flow_item_e_tag definition  fix one bug
> > in function ixgbe_dev_filter_ctrl add goto in function
> > ixgbe_flow_create  delete some useless initialization eliminate some
> > git log check warning
> >
> > v4 change:
> >  fix some check patch warning
> >
> > v5 change:
> >  fix some git log warning
> >
> > zhao wei (18):
> >   net/ixgbe: store TCP SYN filter
> >   net/ixgbe: store flow director filter
> >   net/ixgbe: store L2 tunnel filter
> >   net/ixgbe: restore n-tuple filter
> >   net/ixgbe: restore ether type filter
> >   net/ixgbe: restore TCP SYN filter
> >   net/ixgbe: restore flow director filter
> >   net/ixgbe: restore L2 tunnel filter
> >   net/ixgbe: store and restore L2 tunnel configuration
> >   net/ixgbe: flush all the filters
> >   net/ixgbe: parse n-tuple filter
> >   net/ixgbe: parse ethertype filter
> >   net/ixgbe: parse TCP SYN filter
> >   net/ixgbe: parse L2 tunnel filter
> >   net/ixgbe: parse flow director filter
> >   net/ixgbe: create consistent filter
> >   net/ixgbe: destroy consistent filter
> >   net/ixgbe: flush all the filter list
> >
> >  drivers/net/ixgbe/Makefile       |    2 +
> >  drivers/net/ixgbe/ixgbe_ethdev.c |  667 +++++++--
> > drivers/net/ixgbe/ixgbe_ethdev.h |  203 ++-
> >  drivers/net/ixgbe/ixgbe_fdir.c   |  407 ++++--
> >  drivers/net/ixgbe/ixgbe_flow.c   | 2808
> > ++++++++++++++++++++++++++++++++++++++
> >  drivers/net/ixgbe/ixgbe_pf.c     |   26 +-
> >  lib/librte_ether/rte_flow.h      |   48 +
> >  7 files changed, 3950 insertions(+), 211 deletions(-)  create mode
> > 100644 drivers/net/ixgbe/ixgbe_flow.c
> >
> Acked-by: Beilei Xing <beilei.xing@intel.com>
Acked-by: Wei Dai <wei.dai@intel.com>

^ permalink raw reply

* Re: [dpdk-stable] [PATCH 1/2] net/virtio: fix performance regression due to TSO enabling
From: Yuanhan Liu @ 2017-01-13  6:13 UTC (permalink / raw)
  To: Jan Viktorin
  Cc: Thomas Monjalon, Jianbo Liu, Jerin Jacob, Chao Zhu, dev,
	Tan Jianfeng, Wang Zhihong, Olivier Matz, Maxime Coquelin,
	Michael S. Tsirkin, Orsák Michal
In-Reply-To: <20170112160256.6915ff12.viktorin@rehivetech.com>

On Thu, Jan 12, 2017 at 04:02:56PM +0100, Jan Viktorin wrote:
> On Thu, 12 Jan 2017 10:30:58 +0800
> Yuanhan Liu <yuanhan.liu@linux.intel.com> wrote:
> 
> > On Wed, Jan 11, 2017 at 03:51:22PM +0100, Thomas Monjalon wrote:
> > > 2017-01-11 12:27, Yuanhan Liu:  
> > > > The fact that virtio net header is initiated to zero in PMD driver
> > > > init stage means that these costly writes are unnecessary and could
> > > > be avoided:
> > > > 
> > > >     if (hdr->csum_start != 0)
> > > >         hdr->csum_start = 0;
> > > > 
> > > > And that's what the macro ASSIGN_UNLESS_EQUAL does. With this, the
> > > > performance drop introduced by TSO enabling is recovered: it could
> > > > be up to 20% in micro benchmarking.  
> > > 
> > > This patch is adding a condition to assignments.
> > > We need a benchmark on other architectures like ARM. Please anyone?  
> > 
> > I think the cost of condition should be way lower than the cost from the
> > penalty introduced by the cache issue, that I don't see it would perform
> > bad on other platforms.
> > 
> > But, of course, testing is always welcome!
> > 
> > 	--yliu
> 
> Hello,
> 
> we've done a synthetic measurement, principle briefly:

Thanks!

> 
> == Without condition check ==
> 
> start = gettimeofday();
> 
> for (i = 0; i < 1024*1024*128; ++i) {
> 	hdr->csum_start = 0;
> 	hdr->csum_offset = 0;
> 	hdr->flags = 0;
> }
> 
> end = gettimeofday();
> 
> 
> == With condition check ==
> 
> start = gettimeofday();
> 
> for (i = 0; i < 1024*1024*128; ++i) {
> 	ASSIGN_UNLESS_EQUAL(hdr->csum_start, 0);
> 	ASSIGN_UNLESS_EQUAL(hdr->csum_offset, 0);
> 	ASSIGN_UNLESS_EQUAL(hdr->flags, 0);
> }
> 
> end = gettimeofday();

But it's not the test methodology I'd expect. You are purely testing
the instruction cycles. The drop on ARM is something more like "the
if instruction takes more cycles than the simple assignment".

This macro is used in the case that one process is heavily writing
same value (0 here) again and again while another process is heavily
read it also again and again. That means cache violation always
happen. With this macro, however, this cache issue could be avoided,
since no write happens.

For such workload, I don't think it would behaviour worse on ARM.

	--yliu

> == Results ==
> 
> Computed as total time of all threads:
> 
> for i = 1..THREAD_COUNT:
> 	result += end[i] - start[i]
> 
> cpu           threads  without-check (ms)  with-check
> Xeon E5-2670        1            516              529
> Xeon E5-2670        2           1155              953
> Xeon E5-2670        8           8947             5044
> Xeon E5-2670       16          23335            16836
> Zynq-7020 (armv7)   1           6735             7205
> Zynq-7020 (armv7)   2          13753            14418
> 
> The advantage for Intel is evident when increasing the number
> of threads.
> 
> However, on 32-bit ARMs we might expect some performance drop.
> 
> Regards
> Jan
> 
> > > 
> > > 
> > > [...]  
> > > > +/* avoid write operation when necessary, to lessen cache issues */
> > > > +#define ASSIGN_UNLESS_EQUAL(var, val) do {	\
> > > > +	if ((var) != (val))			\
> > > > +		(var) = (val);			\
> > > > +} while (0)  

^ permalink raw reply

* Re: [dpdk-users]  IGB_UIO: PCI Resources Management
From: Tan, Jianfeng @ 2017-01-13  5:33 UTC (permalink / raw)
  To: Yigit, Ferruh, Alejandro Lucero; +Cc: Gregory Etelson, dev, users@dpdk.org
In-Reply-To: <7605618f-dc86-3060-473e-aff5545cac72@intel.com>



> -----Original Message-----
> From: Yigit, Ferruh
> Sent: Friday, January 13, 2017 10:05 AM
> To: Tan, Jianfeng; Alejandro Lucero
> Cc: Gregory Etelson; dev; users@dpdk.org
> Subject: Re: [dpdk-users] [dpdk-dev] IGB_UIO: PCI Resources Management
> 
> On 1/13/2017 1:51 AM, Tan, Jianfeng wrote:
> >
> >
> >> -----Original Message-----
> >> From: users [mailto:users-bounces@dpdk.org] On Behalf Of Ferruh Yigit
> >> Sent: Thursday, January 12, 2017 8:22 PM
> >> To: Alejandro Lucero
> >> Cc: Gregory Etelson; dev; users@dpdk.org
> >> Subject: Re: [dpdk-users] [dpdk-dev] IGB_UIO: PCI Resources
> Management
> >>
> >> On 1/12/2017 12:12 PM, Alejandro Lucero wrote:
> >>>
> >>>
> >>> On Thu, Jan 12, 2017 at 11:55 AM, Ferruh Yigit <ferruh.yigit@intel.com
> >>> <mailto:ferruh.yigit@intel.com>> wrote:
> >>>
> >>>     On 12/9/2016 8:54 AM, Gregory Etelson wrote:
> >>>     > Hello,
> >>>     >
> >>>     > IGB_UIO driver does not close port PCI activities after DPDK process
> >> exits.
> >>>     > DPDK API provides rte_eth_dev_close() to manage port PCI,
> >>>     > but it can be skipped if process receives SIGKILL signal
> >>>
> >>>     I guess I understand the problem.
> >>>
> >>>
> >>> This is a known problem, but it is not just a UIO problem, and this
> >>> patch does not solve it, maybe it just solves part of it.
> >>>
> >>> In fact, a DPDK program crashing could imply the NIC DMAing after that
> >>> and after that memory was assigned to another program.
> >>
> >> Yes.
> >> Can there be a way to stop NIC DMA, (or prevent it access to mem
> >> anymore) when app crashes?
> >> I think that is what this patch is looking for.
> >
> > If I understand it correctly, you are looking for this patch?
> > http://dpdk.org/dev/patchwork/patch/17495/
> >
> 
> That is good, thanks Jianfeng, I will check it.
> 
> btw, patch's current state is rejected, which is by mistake, it seems I
> confused it with "iomem and ioport mapping" patch, sorry about it, I
> will update its status immediately.

No problem at all. This patch is rejected as it's based on "iomem and ioport mapping" patch. As "iomem and ioport mapping" patch has backward compatibility issue, we need to figure out a way to resubmit this patch without changing the original "iomem and ioport mapping" in igb_uio.

Thanks,
Jianfeng

^ permalink raw reply

* Re: [PATCH] app/testpmd: fix static build link ordering
From: Jerin Jacob @ 2017-01-13  3:21 UTC (permalink / raw)
  To: Ferruh Yigit; +Cc: Thomas Monjalon, dev, stable
In-Reply-To: <6167bd74-c515-fc12-744d-9168a633c646@intel.com>

On Thu, Jan 12, 2017 at 03:27:30PM +0000, Ferruh Yigit wrote:
> On 1/12/2017 1:58 PM, Jerin Jacob wrote:
> > On Thu, Jan 12, 2017 at 10:26:08AM +0100, Thomas Monjalon wrote:
> >> 2017-01-12 13:16, Jerin Jacob:
> >>> +ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
> >>>  _LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += -lrte_pmd_ixgbe
> >>> +endif
> >>
> >> _LDLIBS is an internal variable of rte.app.mk.
> >> Please could you check that there is no issue when using LDLIBS instead
> >> of _LDLIBS?
> > 
> > Tested it. Suggested change has issue in shared lib configuration.
> > 
> > [dpdk-master] $ git diff
> > diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
> > index 050663a..27cadd5 100644
> > --- a/app/test-pmd/Makefile
> > +++ b/app/test-pmd/Makefile
> > @@ -59,9 +59,7 @@ SRCS-y += csumonly.c
> >  SRCS-y += icmpecho.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ieee1588fwd.c
> >  
> > -ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
> > -_LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += -lrte_pmd_ixgbe
> > -endif
> > +LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += -lrte_pmd_ixgbe
> 
> It is LDLIBS instead of LDLIBS-y, following may work:

LDLIBS is not helping the situation as LDLIBS comes before the _LDLIBS-y
mk/rte.app.mk:LDLIBS += $(_LDLIBS-y) $(CPU_LDLIBS) $(EXTRA_LDLIBS)

But moving to EXTRA_LDLIBS looks OK.But it has to be under CONFIG_RTE_LIBRTE_IXGBE_PMD

Thomas, Ferruh
Let me know if you have any objection on below mentioned diff

[master] $ git diff
app/test-pmd/Makefile 
diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index 050663a..2be8c50 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -59,8 +59,8 @@ SRCS-y += csumonly.c
 SRCS-y += icmpecho.c
 SRCS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ieee1588fwd.c
 
-ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
-_LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += -lrte_pmd_ixgbe
+ifeq ($(CONFIG_RTE_LIBRTE_IXGBE_PMD),y)
+EXTRA_LDLIBS += -lrte_pmd_ixgbe
 endif
 
 CFLAGS_cmdline.o := -D_GNU_SOURCE


> 
> -_LDLIBS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += -lrte_pmd_ixgbe
> +ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
> +ifeq ($(CONFIG_RTE_LIBRTE_IXGBE_PMD),y)
> +LDLIBS += -lrte_pmd_ixgbe
> +endif
> +endif
> 
> 
> Also using EXTRA_LDLIBS instead of LDLIBS may remove the requirement of
> the SHARED_LIB check, because of where it is located, but this seems
> just coincidental.
> 
> 
> 

^ permalink raw reply related

* Re: [PATCH] tools: add tags and cscope index file generation support
From: Jerin Jacob @ 2017-01-13  2:50 UTC (permalink / raw)
  To: Ferruh Yigit; +Cc: dev, thomas.monjalon
In-Reply-To: <204efa1f-0631-ed9c-3d68-8c573edfbb76@intel.com>

On Thu, Jan 12, 2017 at 02:19:44PM +0000, Ferruh Yigit wrote:
> On 11/27/2016 12:12 AM, Jerin Jacob wrote:
> > This script generates cscope, gtags, and tags
> > index files based on EAL environment.
> > (architecture and OS(linux/bsd))
> > 
> > Selection of the architecture and OS environment
> > is based on dpdk configuration target(T=)
> > 
> > example usage:
> > make tags T=x86_64-native-linuxapp-gcc
> > make cscope T=x86_64-native-linuxapp-gcc
> > make gtags T=x86_64-native-linuxapp-gcc
> > 
> > Signed-off-by: Jerin Jacob <jerin.jacob@caviumnetworks.com>
> 
> <...>
> 
> > +
> > +if [ -n "$T" ]; then
> <...>
> > +	usage $1
> > +	exit
> 
> I would prefer fallback to RTE_TARGET parsing, or processing all files (
> cscope -Rqn ), instead of exiting here, but that is your call.

I would prefer avoid any fallback(sometimes RTE_TARGET can be "build" also) and
print the usage and exit error if the requirements are not meet.

> 
> 
> Also "tags.sh" needs to be moved into "devtools" after latest updates.

Sure. I will send v2 to address this.

> 
> Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>

Thanks for the review.

^ permalink raw reply

* Re: Does DPDK i40e driver support 'Toeplitz hash'
From: Wu, Jingjing @ 2017-01-13  2:27 UTC (permalink / raw)
  To: Yigit, Ferruh, ALeX Wang, dev@dpdk.org; +Cc: Keith Amidon, Zhang, Helin
In-Reply-To: <93668bf1-1e46-17f7-473b-5ab0ca7d9d61@intel.com>



> -----Original Message-----
> From: Yigit, Ferruh
> Sent: Thursday, January 12, 2017 7:59 PM
> To: ALeX Wang <ee07b291@gmail.com>; dev@dpdk.org
> Cc: Keith Amidon <keith@awakenetworks.com>; Wu, Jingjing
> <jingjing.wu@intel.com>; Zhang, Helin <helin.zhang@intel.com>
> Subject: Re: [dpdk-dev] Does DPDK i40e driver support 'Toeplitz hash'
> 
> On 12/12/2016 6:31 AM, ALeX Wang wrote:
> > Hi,
> >
> > We want to use 'Toeplitz hash' for RSS hash on our server equipped
> > with 'Intel X710-DA4' card.
> >
Yes, it is supported.
For the configuration, you can refer to the implementation of command set_hash_global_config in testpmd example.

> > However, seemed that we hit the exact same issue as this:
> >
> >    Why only rx queue "0" can receive network packet by i40e NIC
> >    http://dpdk.org/ml/archives/dev/2015-July/022453.html
> >
> > ...
> > We are using the v16.11 release and would like to confirm if the issue
> > above has been address.  If so, could anyone post here how to configure?
> 

Different requirement may require different configuration.
For example, you'd like to enable RSS on IP packets, you need to set

struct rte_eth_conf to be like
{
	.rxmode = {
		.mq_mode = ETH_MQ_RX_RSS,
	},
	.rx_adv_conf = {
		.rss_conf = {
			.rss_key = NULL,
			.rss_hf = ETH_RSS_IP,
		},
	},
};

This is also to be found in dpdk's examples, such like testpmd and l3fwd.

Thanks
Jingjing

> CC'ing i40e maintainers:
> Jingjing Wu <jingjing.wu@intel.com>; Helin Zhang <helin.zhang@intel.com>
> 
> >
> > Appreciate,
> > Alex Wang,
> >


^ permalink raw reply

* Re: [PATCH v2 5/7] net/virtio_user: add vhost kernel support
From: Jason Wang @ 2017-01-13  2:27 UTC (permalink / raw)
  To: Tan, Jianfeng, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
In-Reply-To: <5129c726-30fa-d4ed-a0c0-5b811772a6fb@intel.com>



On 2017年01月12日 17:40, Tan, Jianfeng wrote:
> Hi Jason,
>
>> Ok, just want to show its advantages. It can be added on top.
>>
>> And two more suggestions:
>> - better to split tap support out of vhost file
>
> Good suggestion! Will do that in next version.
>
>> - kernel support more than 8 queues on recent kernel, so there's no 
>> need to limit it to 8. When running on old kernel, TUNSETIFF will 
>> fail which can give user a hint to reduce #queues.
>>
>
> After a try, even we remove such restriction in vhost_kernel backend, 
> we are still restricted by VIRTIO_MAX_VIRTQUEUES (8) defined in virtio 
> PMD.

I think we'd better remove this limitation in the future.

Thanks

>
> Thanks for the suggestions!
>
> Jianfeng

^ permalink raw reply

* Re: [dpdk-users]  IGB_UIO: PCI Resources Management
From: Ferruh Yigit @ 2017-01-13  2:04 UTC (permalink / raw)
  To: Tan, Jianfeng, Alejandro Lucero; +Cc: Gregory Etelson, dev, users@dpdk.org
In-Reply-To: <ED26CBA2FAD1BF48A8719AEF02201E3651111AE6@SHSMSX103.ccr.corp.intel.com>

On 1/13/2017 1:51 AM, Tan, Jianfeng wrote:
> 
> 
>> -----Original Message-----
>> From: users [mailto:users-bounces@dpdk.org] On Behalf Of Ferruh Yigit
>> Sent: Thursday, January 12, 2017 8:22 PM
>> To: Alejandro Lucero
>> Cc: Gregory Etelson; dev; users@dpdk.org
>> Subject: Re: [dpdk-users] [dpdk-dev] IGB_UIO: PCI Resources Management
>>
>> On 1/12/2017 12:12 PM, Alejandro Lucero wrote:
>>>
>>>
>>> On Thu, Jan 12, 2017 at 11:55 AM, Ferruh Yigit <ferruh.yigit@intel.com
>>> <mailto:ferruh.yigit@intel.com>> wrote:
>>>
>>>     On 12/9/2016 8:54 AM, Gregory Etelson wrote:
>>>     > Hello,
>>>     >
>>>     > IGB_UIO driver does not close port PCI activities after DPDK process
>> exits.
>>>     > DPDK API provides rte_eth_dev_close() to manage port PCI,
>>>     > but it can be skipped if process receives SIGKILL signal
>>>
>>>     I guess I understand the problem.
>>>
>>>
>>> This is a known problem, but it is not just a UIO problem, and this
>>> patch does not solve it, maybe it just solves part of it.
>>>
>>> In fact, a DPDK program crashing could imply the NIC DMAing after that
>>> and after that memory was assigned to another program.
>>
>> Yes.
>> Can there be a way to stop NIC DMA, (or prevent it access to mem
>> anymore) when app crashes?
>> I think that is what this patch is looking for.
> 
> If I understand it correctly, you are looking for this patch?
> http://dpdk.org/dev/patchwork/patch/17495/
> 

That is good, thanks Jianfeng, I will check it.

btw, patch's current state is rejected, which is by mistake, it seems I
confused it with "iomem and ioport mapping" patch, sorry about it, I
will update its status immediately.

^ permalink raw reply

* Re: [dpdk-users]   IGB_UIO: PCI Resources Management
From: Tan, Jianfeng @ 2017-01-13  1:51 UTC (permalink / raw)
  To: Yigit, Ferruh, Alejandro Lucero; +Cc: Gregory Etelson, dev, users@dpdk.org
In-Reply-To: <e1507eab-1604-b300-8f86-5d5ac2c98ee5@intel.com>



> -----Original Message-----
> From: users [mailto:users-bounces@dpdk.org] On Behalf Of Ferruh Yigit
> Sent: Thursday, January 12, 2017 8:22 PM
> To: Alejandro Lucero
> Cc: Gregory Etelson; dev; users@dpdk.org
> Subject: Re: [dpdk-users] [dpdk-dev] IGB_UIO: PCI Resources Management
> 
> On 1/12/2017 12:12 PM, Alejandro Lucero wrote:
> >
> >
> > On Thu, Jan 12, 2017 at 11:55 AM, Ferruh Yigit <ferruh.yigit@intel.com
> > <mailto:ferruh.yigit@intel.com>> wrote:
> >
> >     On 12/9/2016 8:54 AM, Gregory Etelson wrote:
> >     > Hello,
> >     >
> >     > IGB_UIO driver does not close port PCI activities after DPDK process
> exits.
> >     > DPDK API provides rte_eth_dev_close() to manage port PCI,
> >     > but it can be skipped if process receives SIGKILL signal
> >
> >     I guess I understand the problem.
> >
> >
> > This is a known problem, but it is not just a UIO problem, and this
> > patch does not solve it, maybe it just solves part of it.
> >
> > In fact, a DPDK program crashing could imply the NIC DMAing after that
> > and after that memory was assigned to another program.
> 
> Yes.
> Can there be a way to stop NIC DMA, (or prevent it access to mem
> anymore) when app crashes?
> I think that is what this patch is looking for.

If I understand it correctly, you are looking for this patch?
http://dpdk.org/dev/patchwork/patch/17495/

^ permalink raw reply

* [PATCH v3 5/5] doc: add flow distributor guide
From: Pablo de Lara @ 2017-01-12 22:16 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Sameh Gobriel
In-Reply-To: <1484259360-198276-1-git-send-email-pablo.de.lara.guarch@intel.com>

Signed-off-by: Sameh Gobriel <sameh.gobriel@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS                                       |    1 +
 doc/guides/sample_app_ug/flow_distributor.rst     |  494 ++++++++
 doc/guides/sample_app_ug/img/flow_distributor.svg | 1254 +++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst                |    1 +
 4 files changed, 1750 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/flow_distributor.rst
 create mode 100644 doc/guides/sample_app_ug/img/flow_distributor.svg

diff --git a/MAINTAINERS b/MAINTAINERS
index 66e9466..0d3b247 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -535,6 +535,7 @@ F: lib/librte_efd/
 F: doc/guides/prog_guide/efd_lib.rst
 F: app/test/test_efd*
 F: examples/flow_distributor/
+F: doc/guides/sample_app_ug/flow_distributor.rst
 
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
diff --git a/doc/guides/sample_app_ug/flow_distributor.rst b/doc/guides/sample_app_ug/flow_distributor.rst
new file mode 100644
index 0000000..a8cd5f4
--- /dev/null
+++ b/doc/guides/sample_app_ug/flow_distributor.rst
@@ -0,0 +1,494 @@
+..  BSD LICENSE
+    Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Flow Distributor Sample Application
+===================================
+
+This sample application demonstrates the use of EFD library as a flow-level
+load balancer, for more information about the EFD Library please refer to the
+DPDK programmer's guide.
+
+This sample application is a variant of the
+:ref:`client-server sample application <multi_process_app>`
+where a specific target node is specified for every and each flow
+(not in a round-robin fashion as the original load balancing sample application).
+
+Overview
+--------
+
+The architecture of the EFD flow-based load balancer sample application is
+presented in the following figure.
+
+.. _figure_efd_sample_app_overview:
+
+.. figure:: img/flow_distributor.*
+
+   Using EFD as a Flow-Level Load Balancer
+
+As shown in :numref:`_figure_efd_sample_app_overview`,
+the sample application consists of a front-end node (distributor)
+using the EFD library to create a load-balancing table for flows,
+for each flow a target backend worker node is specified. The EFD table does not
+store the flow key (unlike a regular hash table), and hence, it can
+individually load-balance millions of flows (number of targets * maximum number
+of flows fit in a flow table per target) while still fitting in CPU cache.
+
+It should be noted that although they are referred to as nodes, the frontend
+distributor and worker nodes are processes running on the same platform.
+
+Front-end Distributor
+~~~~~~~~~~~~~~~~~~~~~
+
+Upon initializing, the frontend distributor node (process) creates a flow
+distributor table (based on the EFD library) which is populated with flow
+information and its intended target node.
+
+The sample application assigns a specific target node_id (process) for each of
+the IP destination addresses as follows:
+
+.. code-block:: c
+
+    node_id = i % num_nodes; /* Target node id is generated */
+    ip_dst = rte_cpu_to_be_32(i); /* Specific ip destination address is
+                                     assigned to this target node */
+
+then the pair of <key,target> is inserted into the flow distribution table.
+
+The main loop of the the distributor node receives a burst of packets, then for
+each packet, a flow key (IP destination address) is extracted. The flow
+distributor table is looked up and the target node id is returned.  Packets are
+then enqueued to the specified target node id.
+
+It should be noted that flow distributor table is not a membership test table.
+I.e. if the key has already been inserted the target node id will be correct,
+but for new keys the flow distributor table will return a value (which can be
+valid).
+
+Backend Worker Nodes
+~~~~~~~~~~~~~~~~~~~~
+
+Upon initializing, the worker node (process) creates a flow table (a regular
+hash table that stores the key default size 1M flows) which is populated with
+only the flow information that is serviced at this node. This flow key is
+essential to point out new keys that have not been inserted before.
+
+The worker node's main loop is simply receiving packets then doing a hash table
+lookup. If a match occurs then statistics are updated for flows serviced by
+this node. If no match is found in the local hash table then this indicates
+that this is a new flow, which is dropped.
+
+
+Compiling the Application
+-------------------------
+
+The sequence of steps used to build the application is:
+
+#.  Export the required environment variables:
+
+    .. code-block:: console
+
+        export RTE_SDK=/path/to/rte_sdk
+        export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+#.  Build the application executable file:
+
+    .. code-block:: console
+
+        cd ${RTE_SDK}/examples/flow_distributor/
+        make
+
+    For more details on how to build the DPDK libraries and sample
+    applications,
+    please refer to the *DPDK Getting Started Guide.*
+
+
+Running the Application
+-----------------------
+
+The application has two binaries to be run: the front-end distributor
+and the back-end node.
+
+The frontend distributor (distributor) has the following command line options::
+
+    ./distributor [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+* ``-n NUM_NODES:`` Number of back-end nodes that will be used
+* ``-f NUM_FLOWS:`` Number of flows to be added in the EFD table (1 million, by default)
+
+The back-end node (node) has the following command line options::
+
+    ./node [EAL options] -- -n NODE_ID
+
+Where,
+
+* ``-n NODE_ID:`` Node ID, which cannot be equal or higher than NUM_MODES
+
+
+First, the distributor app must be launched, with the number of nodes that will be run.
+Once it has been started, the node instances can be run, with different NODE_ID.
+These instances have to be run as secondary processes, with ``--proc-type=secondary``
+in the EAL options, which will attach to the primary process memory, and therefore,
+they can access the queues created by the primary process to distribute packets.
+
+To successfully run the application, the command line used to start the
+application has to be in sync with the traffic flows configured on the traffic
+generator side.
+
+For examples of application command lines and traffic generator flows, please
+refer to the DPDK Test Report. For more details on how to set up and run the
+sample applications provided with DPDK package, please refer to the
+:ref:`DPDK Getting Started Guide for Linux <linux_gsg>` and
+:ref:`DPDK Getting Started Guide for FreeBSD <freebsd_gsg>`.
+
+
+Explanation
+-----------
+
+As described in previous sections, there are two processes in this example.
+
+The first process, the front-end distributor, creates and populates the EFD table,
+which is used to distribute packets to nodes, which the number of flows
+specified in the command line (1 million, by default).
+
+
+.. code-block:: c
+
+    static void
+    create_flow_distributor_table(void)
+    {
+        uint8_t socket_id = rte_socket_id();
+
+        /* create table */
+        efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
+                        1 << socket_id, socket_id);
+
+        if (efd_table == NULL)
+            rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+    }
+
+    static void
+    populate_flow_distributor_table(void)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint8_t socket_id = rte_socket_id();
+        uint64_t node_id;
+
+        /* Add flows in table */
+        for (i = 0; i < num_flows; i++) {
+            node_id = i % num_nodes;
+
+            ip_dst = rte_cpu_to_be_32(i);
+            ret = rte_efd_update(efd_table, socket_id,
+                            (void *)&ip_dst, (efd_value_t)node_id);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
+                                    "flow distributor table\n", i);
+        }
+
+        printf("EFD table: Adding 0x%x keys\n", num_flows);
+    }
+
+After initialization, packets are received from the enabled ports, and the IPv4
+address from the packets is used as a key to look up in the EFD table,
+which tells the node where the packet has to be distributed.
+
+.. code-block:: c
+
+    static void
+    process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
+            uint16_t rx_count, unsigned int socket_id)
+    {
+        uint16_t i;
+        uint8_t node;
+        efd_value_t data[EFD_BURST_MAX];
+        const void *key_ptrs[EFD_BURST_MAX];
+
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[EFD_BURST_MAX];
+
+        for (i = 0; i < rx_count; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = (void *)&ipv4_dst_ip[i];
+        }
+
+        rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+                    (const void **) key_ptrs, data);
+        for (i = 0; i < rx_count; i++) {
+            node = (uint8_t) ((uintptr_t)data[i]);
+
+            if (node >= num_nodes) {
+                /*
+                 * Node is out of range, which means that
+                 * flow has not been inserted
+                 */
+                flow_dist_stats.drop++;
+                rte_pktmbuf_free(pkts[i]);
+            } else {
+                flow_dist_stats.distributed++;
+                enqueue_rx_packet(node, pkts[i]);
+            }
+        }
+
+        for (i = 0; i < num_nodes; i++)
+            flush_rx_queue(i);
+    }
+
+The burst of packets received is enqueued in temporary buffers (per node),
+and enqueued in the shared ring between the distributor and the node.
+After this, a new burst of packets is received and this process is
+repeated infinitely.
+
+.. code-block:: c
+
+    static void
+    flush_rx_queue(uint16_t node)
+    {
+        uint16_t j;
+        struct node *cl;
+
+        if (cl_rx_buf[node].count == 0)
+            return;
+
+        cl = &nodes[node];
+        if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+                cl_rx_buf[node].count) != 0){
+            for (j = 0; j < cl_rx_buf[node].count; j++)
+                rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+            cl->stats.rx_drop += cl_rx_buf[node].count;
+        } else
+            cl->stats.rx += cl_rx_buf[node].count;
+
+        cl_rx_buf[node].count = 0;
+    }
+
+The second process, the back-end node, receives the packets from the shared
+ring with the distributor and send them out, if they belong to the node.
+
+At initialization, it attaches to the distributor process memory, to have
+access to the shared ring, parameters and statistics.
+
+.. code-block:: c
+
+    rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+    if (rx_ring == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
+                "is distributor process running?\n");
+
+    mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+    if (mp == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+    mz = rte_memzone_lookup(MZ_SHARED_INFO);
+    if (mz == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+    info = mz->addr;
+    tx_stats = &(info->tx_stats[node_id]);
+    filter_stats = &(info->filter_stats[node_id]);
+
+Then, the hash table that contains the flows that will be handled
+by the node is created and populated.
+
+.. code-block:: c
+
+    static struct rte_hash *
+    create_hash_table(const struct shared_info *info)
+    {
+        uint32_t num_flows_node = info->num_flows / info->num_nodes;
+        char name[RTE_HASH_NAMESIZE];
+        struct rte_hash *h;
+
+        /* create table */
+        struct rte_hash_parameters hash_params = {
+            .entries = num_flows_node * 2, /* table load = 50% */
+            .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+            .socket_id = rte_socket_id(),
+            .hash_func_init_val = 0,
+        };
+
+        snprintf(name, sizeof(name), "hash_table_%d", node_id);
+        hash_params.name = name;
+        h = rte_hash_create(&hash_params);
+
+        if (h == NULL)
+            rte_exit(EXIT_FAILURE,
+                    "Problem creating the hash table for node %d\n",
+                    node_id);
+        return h;
+    }
+
+    static void
+    populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint32_t num_flows_node = 0;
+        uint64_t target_node;
+
+        /* Add flows in table */
+        for (i = 0; i < info->num_flows; i++) {
+            target_node = i % info->num_nodes;
+            if (target_node != node_id)
+                continue;
+
+            ip_dst = rte_cpu_to_be_32(i);
+
+            ret = rte_hash_add_key(h, (void *) &ip_dst);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u "
+                        "in hash table\n", i);
+            else
+                num_flows_node++;
+
+        }
+
+        printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+    }
+
+After initialization, packets are dequeued from the shared ring
+(from the distributor) and, like in the distributor process,
+the IPv4 address from the packets is used as a key to look up in the hash table.
+If there is a hit, packet is stored in a buffer, to be eventually transmitted
+in one of the enabled ports. If key is not there, packet is dropped, since the
+flow is not handled by the node.
+
+.. code-block:: c
+
+    static inline void
+    handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+    {
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+        const void *key_ptrs[PKT_READ_SIZE];
+        unsigned int i;
+        int32_t positions[PKT_READ_SIZE] = {0};
+
+        for (i = 0; i < num_packets; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = &ipv4_dst_ip[i];
+        }
+        /* Check if packets belongs to any flows handled by this node */
+        rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+        for (i = 0; i < num_packets; i++) {
+            if (likely(positions[i] >= 0)) {
+                filter_stats->passed++;
+                transmit_packet(bufs[i]);
+            } else {
+                filter_stats->drop++;
+                /* Drop packet, as flow is not handled by this node */
+                rte_pktmbuf_free(bufs[i]);
+            }
+        }
+    }
+
+Finally, note that both processes updates statistics, such as transmitted, received
+and dropped packets, which are shown and refreshed by the distributor app.
+
+.. code-block:: c
+
+    static void
+    do_stats_display(void)
+    {
+        unsigned int i, j;
+        const char clr[] = {27, '[', '2', 'J', '\0'};
+        const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+        uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+        uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+        /* to get TX stats, we need to do some summing calculations */
+        memset(port_tx, 0, sizeof(port_tx));
+        memset(port_tx_drop, 0, sizeof(port_tx_drop));
+        memset(node_tx, 0, sizeof(node_tx));
+        memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+        for (i = 0; i < num_nodes; i++) {
+            const struct tx_stats *tx = &info->tx_stats[i];
+
+            for (j = 0; j < info->num_ports; j++) {
+                const uint64_t tx_val = tx->tx[info->id[j]];
+                const uint64_t drop_val = tx->tx_drop[info->id[j]];
+
+                port_tx[j] += tx_val;
+                port_tx_drop[j] += drop_val;
+                node_tx[i] += tx_val;
+                node_tx_drop[i] += drop_val;
+            }
+        }
+
+        /* Clear screen and move to top left */
+        printf("%s%s", clr, topLeft);
+
+        printf("PORTS\n");
+        printf("-----\n");
+        for (i = 0; i < info->num_ports; i++)
+            printf("Port %u: '%s'\t", (unsigned int)info->id[i],
+                    get_printable_mac_addr(info->id[i]));
+        printf("\n\n");
+        for (i = 0; i < info->num_ports; i++) {
+            printf("Port %u - rx: %9"PRIu64"\t"
+                    "tx: %9"PRIu64"\n",
+                    (unsigned int)info->id[i], info->rx_stats.rx[i],
+                    port_tx[i]);
+        }
+
+        printf("\nFLOW DISTRIBUTOR\n");
+        printf("-----\n");
+        printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+                flow_dist_stats.distributed, flow_dist_stats.drop);
+
+        printf("\nNODES\n");
+        printf("-------\n");
+        for (i = 0; i < num_nodes; i++) {
+            const unsigned long long rx = nodes[i].stats.rx;
+            const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+            const struct filter_stats *filter = &info->filter_stats[i];
+
+            printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+                    "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+                    "            filter_passed: %9"PRIu64", "
+                    "filter_drop: %9"PRIu64"\n",
+                    i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+                    filter->passed, filter->drop);
+        }
+
+        printf("\n");
+    }
diff --git a/doc/guides/sample_app_ug/img/flow_distributor.svg b/doc/guides/sample_app_ug/img/flow_distributor.svg
new file mode 100644
index 0000000..9aee30b
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/flow_distributor.svg
@@ -0,0 +1,1254 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i6.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.2496in" height="5.89673in"
+		viewBox="0 0 593.971 424.565" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st5 {fill:#feffff;font-family:Calibri;font-size:0.75em}
+		.st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st7 {fill:none;stroke:#2e75b5;stroke-width:2.25}
+		.st8 {fill:#305497;stroke:#2e75b5;stroke-width:1}
+		.st9 {fill:#feffff;font-family:Calibri;font-size:0.833336em;font-weight:bold}
+		.st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2)}
+		.st11 {fill:#5b9bd5}
+		.st12 {stroke:#c7c8c8;stroke-width:0.25}
+		.st13 {fill:#acccea;stroke:#c7c8c8;stroke-width:0.25}
+		.st14 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st15 {fill:#ed7d31;stroke:#c7c8c8;stroke-width:0.25}
+		.st16 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
+		.st17 {marker-end:url(#mrkr5-212);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st18 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st19 {fill:none;stroke:#2e75b5;stroke-width:1}
+		.st20 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
+		.st21 {fill:none;stroke:none;stroke-width:0.25}
+		.st22 {font-size:1em}
+		.st23 {fill:#ffffff}
+		.st24 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st25 {marker-end:url(#mrkr5-444);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st26 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-212" class="st18" v:arrowType="5" v:arrowSize="2" v:setback="5.8" refX="-5.8" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+		<marker id="mrkr5-444" class="st26" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(319.501,-335.688)">
+			<title>Rectangle.58</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow3-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape4-7" v:mID="4" v:groupContext="shape" transform="translate(353.251,-335.688)">
+			<title>Rectangle.59</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow4-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape5-13" v:mID="5" v:groupContext="shape" transform="translate(400.501,-335.688)">
+			<title>Rectangle.60</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow5-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape6-19" v:mID="6" v:groupContext="shape" transform="translate(434.251,-335.688)">
+			<title>Rectangle.61</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow6-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(481.501,-335.688)">
+			<title>Rectangle.62</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape8-30" v:mID="8" v:groupContext="shape" transform="translate(515.251,-335.688)">
+			<title>Rectangle.63</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow8-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape9-35" v:mID="9" v:groupContext="shape" transform="translate(319.501,-313.188)">
+			<title>Rectangle.64</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow9-36" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape10-40" v:mID="10" v:groupContext="shape" transform="translate(353.251,-313.188)">
+			<title>Rectangle.65</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow10-41" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape11-45" v:mID="11" v:groupContext="shape" transform="translate(400.501,-313.188)">
+			<title>Rectangle.66</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow11-46" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape12-50" v:mID="12" v:groupContext="shape" transform="translate(434.251,-313.188)">
+			<title>Rectangle.67</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow12-51" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape13-55" v:mID="13" v:groupContext="shape" transform="translate(481.501,-313.188)">
+			<title>Rectangle.68</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow13-56" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape14-60" v:mID="14" v:groupContext="shape" transform="translate(515.251,-313.188)">
+			<title>Rectangle.69</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow14-61" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(319.501,-277.188)">
+			<title>Rectangle.70</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow15-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(353.251,-277.188)">
+			<title>Rectangle.71</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape17-77" v:mID="17" v:groupContext="shape" transform="translate(400.501,-277.188)">
+			<title>Rectangle.72</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow17-78" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape18-83" v:mID="18" v:groupContext="shape" transform="translate(434.251,-277.188)">
+			<title>Rectangle.73</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow18-84" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape19-89" v:mID="19" v:groupContext="shape" transform="translate(481.501,-277.188)">
+			<title>Rectangle.74</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow19-90" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape20-95" v:mID="20" v:groupContext="shape" transform="translate(515.251,-277.188)">
+			<title>Rectangle.75</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow20-96" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape21-101" v:mID="21" v:groupContext="shape" transform="translate(319.501,-240.687)">
+			<title>Rectangle.76</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow21-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape22-106" v:mID="22" v:groupContext="shape" transform="translate(353.251,-240.687)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow22-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape23-111" v:mID="23" v:groupContext="shape" transform="translate(400.501,-240.687)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow23-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape24-116" v:mID="24" v:groupContext="shape" transform="translate(434.251,-240.687)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow24-117" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape25-121" v:mID="25" v:groupContext="shape" transform="translate(481.501,-240.687)">
+			<title>Rectangle.80</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow25-122" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape26-127" v:mID="26" v:groupContext="shape" transform="translate(515.251,-240.687)">
+			<title>Rectangle.81</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow26-128" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape27-133" v:mID="27" v:groupContext="shape" transform="translate(317.251,-231.687)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow27-134" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape28-138" v:mID="28" v:groupContext="shape" transform="translate(328.501,-362.688)">
+			<title>Sheet.28</title>
+			<desc>Local Table for N Specific Flows Serviced at Node 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.77" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node 1</text>		</g>
+		<g id="group34-141" transform="translate(66.0294,-165.569)" v:mID="34" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Device)"/>
+				<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Load balancer)"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Load balancer</title>
+			<g id="shape35-142" v:mID="35" v:groupContext="shape" transform="translate(0,-7.33146)">
+				<title>Sheet.35</title>
+				<g id="shadow35-143" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st10"/>
+					<path d="M0 377.86 L72 377.86" class="st6"/>
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st6"/>
+				</g>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st11"/>
+				<path d="M0 377.86 L72 377.86" class="st12"/>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st12"/>
+			</g>
+			<g id="shape36-152" v:mID="36" v:groupContext="shape" transform="translate(8.03054,-12.9324)">
+				<title>Sheet.36</title>
+				<g id="shadow36-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97
+								 413.34 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83
+								 L51.34 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37
+								 L38.13 400.48 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16
+								 L46.49 392.01 ZM27.71 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42
+								 27.71 415.42 C32.75 415.42 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71
+								 400.04 C31.15 400.04 33.96 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54
+								 21.46 409.74 21.46 406.29 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56
+								 L11.6 407.56 L8.62 410.51 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z"
+							class="st2"/>
+				</g>
+				<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97 413.34
+							 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83 L51.34
+							 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37 L38.13 400.48
+							 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16 L46.49 392.01 ZM27.71
+							 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42 27.71 415.42 C32.75 415.42
+							 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71 400.04 C31.15 400.04 33.96
+							 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54 21.46 409.74 21.46 406.29
+							 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56 L11.6 407.56 L8.62 410.51
+							 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z" class="st13"/>
+			</g>
+		</g>
+		<g id="shape37-157" v:mID="37" v:groupContext="shape" transform="translate(21.0294,-45.4375)">
+			<title>Rectangle</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow37-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="336.433" width="135" height="88.1315" class="st2"/>
+			</g>
+			<rect x="0" y="336.433" width="135" height="88.1315" class="st3"/>
+		</g>
+		<g id="shape38-162" v:mID="38" v:groupContext="shape" transform="translate(34.693,-126.438)">
+			<title>Sheet.38</title>
+			<desc>EFD Table</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="49.3364" cy="415.565" width="98.68" height="18"/>
+			<rect x="0" y="406.565" width="98.6728" height="18" class="st8"/>
+			<text x="24.87" y="419.17" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>EFD Table</text>		</g>
+		<g id="shape39-165" v:mID="39" v:groupContext="shape" transform="translate(30.0294,-99.4375)">
+			<title>Rectangle.39</title>
+			<desc>Group_id</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow39-166" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="7.87" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group_id</text>		</g>
+		<g id="shape40-171" v:mID="40" v:groupContext="shape" transform="translate(93.193,-99.4375)">
+			<title>Rectangle.40</title>
+			<desc>Hash index</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow40-172" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="4.64" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash index</text>		</g>
+		<g id="shape41-177" v:mID="41" v:groupContext="shape" transform="translate(30.193,-82.4275)">
+			<title>Rectangle.41</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow41-178" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape42-182" v:mID="42" v:groupContext="shape" transform="translate(30.193,-66.8125)">
+			<title>Rectangle.42</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow42-183" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape43-187" v:mID="43" v:groupContext="shape" transform="translate(30.1112,-52.1875)">
+			<title>Rectangle.43</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow43-188" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape44-192" v:mID="44" v:groupContext="shape" transform="translate(93.0294,-81.4375)">
+			<title>Rectangle.44</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow44-193" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape45-197" v:mID="45" v:groupContext="shape" transform="translate(93.193,-66.8125)">
+			<title>Rectangle.45</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow45-198" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape46-202" v:mID="46" v:groupContext="shape" transform="translate(93.193,-52.1875)">
+			<title>Rectangle.46</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow46-203" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape47-207" v:mID="47" v:groupContext="shape" transform="translate(374.924,544.022) rotate(135)">
+			<title>Sheet.47</title>
+			<path d="M-0 417.75 A40.674 18.0151 -156.2 0 0 40.24 422.15 L40.49 421.89" class="st17"/>
+		</g>
+		<g id="shape48-213" v:mID="48" v:groupContext="shape" transform="translate(21.0294,-19)">
+			<title>Sheet.48</title>
+			<desc>Supports X*N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="19.05" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports X*N Flows</text>		</g>
+		<g id="shape49-216" v:mID="49" v:groupContext="shape" transform="translate(48.0294,-229.938)">
+			<title>Sheet.49</title>
+			<desc>Frontend Server or Load Balancer</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="400.94" width="108" height="47.25"/>
+			<rect x="0" y="377.315" width="108" height="47.25" class="st21"/>
+			<text x="14.56" y="397.34" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Frontend Server<v:newlineChar/><tspan
+						x="13.16" dy="1.2em" class="st22">or Load Balancer </tspan> </text>		</g>
+		<g id="group51-220" transform="translate(223.876,-310.938)" v:mID="51" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server</title>
+			<g id="shape52-221" v:mID="52" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.52</title>
+				<g id="shadow52-222" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape53-226" v:mID="53" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.53</title>
+				<g id="shadow53-227" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape54-231" v:mID="54" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.54</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow54-232" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape59-239" v:mID="59" v:groupContext="shape" transform="translate(277.876,-373.938)">
+			<title>Sheet.59</title>
+			<path d="M-0 424.56 A111.108 53.2538 42.31 0 1 93.83 421.21 L94.14 421.41" class="st17"/>
+		</g>
+		<g id="shape60-244" v:mID="60" v:groupContext="shape" transform="translate(205.876,-283.938)">
+			<title>Sheet.60</title>
+			<desc>Backend Server 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 1</text>		</g>
+		<g id="group61-247" transform="translate(223.876,-207.438)" v:mID="61" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.61</title>
+			<g id="shape62-248" v:mID="62" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.62</title>
+				<g id="shadow62-249" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape63-253" v:mID="63" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.63</title>
+				<g id="shadow63-254" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape64-258" v:mID="64" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.64</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow64-259" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape65-266" v:mID="65" v:groupContext="shape" transform="translate(205.876,-180.437)">
+			<title>Sheet.65</title>
+			<desc>Backend Server 2</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 2</text>		</g>
+		<g id="group66-269" transform="translate(219.029,-58.9375)" v:mID="66" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.66</title>
+			<g id="shape67-270" v:mID="67" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.67</title>
+				<g id="shadow67-271" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape68-275" v:mID="68" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.68</title>
+				<g id="shadow68-276" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape69-280" v:mID="69" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.69</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow69-281" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape70-288" v:mID="70" v:groupContext="shape" transform="translate(201.029,-26.056)">
+			<title>Sheet.70</title>
+			<desc>Backend Server X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.86" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server X</text>		</g>
+		<g id="shape71-291" v:mID="71" v:groupContext="shape" transform="translate(684.44,239.627) rotate(90)">
+			<title>Sheet.71</title>
+			<path d="M0 424.56 L45 424.56" class="st24"/>
+		</g>
+		<g id="shape72-294" v:mID="72" v:groupContext="shape" transform="translate(6.85967,-22.443) rotate(-38.1076)">
+			<title>Sheet.72</title>
+			<path d="M-0 424.56 A96.1331 44.4001 55.03 0 1 68.24 420.56 L68.51 420.79" class="st17"/>
+		</g>
+		<g id="shape73-299" v:mID="73" v:groupContext="shape" transform="translate(328.501,-135.937)">
+			<title>Rectangle.73</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow73-300" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape74-305" v:mID="74" v:groupContext="shape" transform="translate(362.251,-135.937)">
+			<title>Rectangle.74</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow74-306" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape75-311" v:mID="75" v:groupContext="shape" transform="translate(409.501,-135.937)">
+			<title>Rectangle.75</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow75-312" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape76-317" v:mID="76" v:groupContext="shape" transform="translate(443.251,-135.937)">
+			<title>Rectangle.76</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow76-318" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape77-323" v:mID="77" v:groupContext="shape" transform="translate(490.501,-135.937)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow77-324" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape78-328" v:mID="78" v:groupContext="shape" transform="translate(524.251,-135.937)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow78-329" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape79-333" v:mID="79" v:groupContext="shape" transform="translate(328.501,-113.437)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow79-334" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape80-338" v:mID="80" v:groupContext="shape" transform="translate(362.251,-113.437)">
+			<title>Rectangle.80</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow80-339" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape81-343" v:mID="81" v:groupContext="shape" transform="translate(409.501,-113.437)">
+			<title>Rectangle.81</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow81-344" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape82-348" v:mID="82" v:groupContext="shape" transform="translate(443.251,-113.437)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow82-349" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape83-353" v:mID="83" v:groupContext="shape" transform="translate(490.501,-113.437)">
+			<title>Rectangle.83</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow83-354" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape84-358" v:mID="84" v:groupContext="shape" transform="translate(524.251,-113.437)">
+			<title>Rectangle.84</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow84-359" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape85-363" v:mID="85" v:groupContext="shape" transform="translate(328.501,-77.4375)">
+			<title>Rectangle.85</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow85-364" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape86-369" v:mID="86" v:groupContext="shape" transform="translate(362.251,-77.4375)">
+			<title>Rectangle.86</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow86-370" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape87-375" v:mID="87" v:groupContext="shape" transform="translate(409.501,-77.4375)">
+			<title>Rectangle.87</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow87-376" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape88-381" v:mID="88" v:groupContext="shape" transform="translate(443.251,-77.4375)">
+			<title>Rectangle.88</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow88-382" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape89-387" v:mID="89" v:groupContext="shape" transform="translate(490.501,-77.4375)">
+			<title>Rectangle.89</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow89-388" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape90-393" v:mID="90" v:groupContext="shape" transform="translate(524.251,-77.4375)">
+			<title>Rectangle.90</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow90-394" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape91-399" v:mID="91" v:groupContext="shape" transform="translate(328.501,-40.9375)">
+			<title>Rectangle.91</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow91-400" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape92-404" v:mID="92" v:groupContext="shape" transform="translate(362.251,-40.9375)">
+			<title>Rectangle.92</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow92-405" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape93-409" v:mID="93" v:groupContext="shape" transform="translate(409.501,-40.9375)">
+			<title>Rectangle.93</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow93-410" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape94-414" v:mID="94" v:groupContext="shape" transform="translate(443.251,-40.9375)">
+			<title>Rectangle.94</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow94-415" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape95-419" v:mID="95" v:groupContext="shape" transform="translate(490.501,-40.9375)">
+			<title>Rectangle.95</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow95-420" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape96-425" v:mID="96" v:groupContext="shape" transform="translate(524.251,-40.9375)">
+			<title>Rectangle.96</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow96-426" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape97-431" v:mID="97" v:groupContext="shape" transform="translate(326.251,-31.9375)">
+			<title>Rectangle.97</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow97-432" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape98-436" v:mID="98" v:groupContext="shape" transform="translate(337.501,-162.938)">
+			<title>Sheet.98</title>
+			<desc>Local Table for N Specific Flows Serviced at Node X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.55" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node X</text>		</g>
+		<g id="shape99-439" v:mID="99" v:groupContext="shape" transform="translate(-204.342,-29.4449) rotate(-53.7462)">
+			<title>Sheet.99</title>
+			<path d="M0 424.56 L160.37 424.56" class="st25"/>
+		</g>
+		<g id="shape100-445" v:mID="100" v:groupContext="shape" transform="translate(-37.6568,-164.882) rotate(-24.444)">
+			<title>Sheet.100</title>
+			<path d="M0 424.56 L101.71 424.56" class="st25"/>
+		</g>
+		<g id="shape101-450" v:mID="101" v:groupContext="shape" transform="translate(464.049,-50.8578) rotate(50.099)">
+			<title>Sheet.101</title>
+			<path d="M0 424.56 L139.8 424.56" class="st25"/>
+		</g>
+		<g id="shape102-455" v:mID="102" v:groupContext="shape" transform="translate(372.376,-207.438)">
+			<title>Sheet.102</title>
+			<desc>Supports N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="25.15" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports N Flows</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 775e2f7..260f6a5 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -57,6 +57,7 @@ Sample Applications User Guides
     l3_forward_virtual
     link_status_intr
     load_balancer
+    flow_distributor
     multi_process
     qos_metering
     qos_scheduler
-- 
2.7.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox