All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/6] net/enic: bring NIC interface functions up to date
@ 2017-03-30 21:28 John Daley
  2017-03-30 21:28 ` [PATCH 2/6] net/enic: flow API skeleton John Daley
                   ` (6 more replies)
  0 siblings, 7 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

Update the base functions for the Cisco VIC. These files are mostly
common with other VIC drivers so are left alone is as much as possilbe.
Includes in a new filter/action interface which is needed for Generic
Flow API PMD support. Update FDIR code to use the new interface.

Signed-off-by: John Daley <johndale@cisco.com>
---
 drivers/net/enic/base/cq_enet_desc.h |  13 +++
 drivers/net/enic/base/vnic_dev.c     | 162 ++++++++++++++++++++++++++++-------
 drivers/net/enic/base/vnic_dev.h     |   5 +-
 drivers/net/enic/base/vnic_devcmd.h  |  81 +++++++++++++++++-
 drivers/net/enic/enic_clsf.c         |  16 ++--
 5 files changed, 238 insertions(+), 39 deletions(-)

diff --git a/drivers/net/enic/base/cq_enet_desc.h b/drivers/net/enic/base/cq_enet_desc.h
index f9822a450..e8410563a 100644
--- a/drivers/net/enic/base/cq_enet_desc.h
+++ b/drivers/net/enic/base/cq_enet_desc.h
@@ -71,6 +71,19 @@ struct cq_enet_rq_desc {
 	u8 type_color;
 };
 
+/* Completion queue descriptor: Ethernet receive queue, 16B */
+struct cq_enet_rq_clsf_desc {
+	__le16 completed_index_flags;
+	__le16 q_number_rss_type_flags;
+	__le16 filter_id;
+	__le16 lif;
+	__le16 bytes_written_flags;
+	__le16 vlan;
+	__le16 checksum_fcoe;
+	u8 flags;
+	u8 type_color;
+};
+
 #define CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT          (0x1 << 12)
 #define CQ_ENET_RQ_DESC_FLAGS_FCOE                  (0x1 << 13)
 #define CQ_ENET_RQ_DESC_FLAGS_EOP                   (0x1 << 14)
diff --git a/drivers/net/enic/base/vnic_dev.c b/drivers/net/enic/base/vnic_dev.c
index 84e4840af..78bfdbe25 100644
--- a/drivers/net/enic/base/vnic_dev.c
+++ b/drivers/net/enic/base/vnic_dev.c
@@ -387,17 +387,24 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 
 static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 	enum vnic_devcmd_cmd proxy_cmd, enum vnic_devcmd_cmd cmd,
-	u64 *a0, u64 *a1, int wait)
+	u64 *args, int nargs, int wait)
 {
 	u32 status;
 	int err;
 
+	/*
+	 * Proxy command consumes 2 arguments. One for proxy index,
+	 * the other is for command to be proxied
+	 */
+	if (nargs > VNIC_DEVCMD_NARGS - 2) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	vdev->args[0] = vdev->proxy_index;
 	vdev->args[1] = cmd;
-	vdev->args[2] = *a0;
-	vdev->args[3] = *a1;
+	memcpy(&vdev->args[2], args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, proxy_cmd, wait);
 	if (err)
@@ -412,24 +419,26 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 		return err;
 	}
 
-	*a0 = vdev->args[1];
-	*a1 = vdev->args[2];
+	memcpy(args, &vdev->args[1], nargs * sizeof(args[0]));
 
 	return 0;
 }
 
 static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
-	enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+	enum vnic_devcmd_cmd cmd, u64 *args, int nargs, int wait)
 {
 	int err;
 
-	vdev->args[0] = *a0;
-	vdev->args[1] = *a1;
+	if (nargs > VNIC_DEVCMD_NARGS) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
+	memset(vdev->args, 0, sizeof(vdev->args));
+	memcpy(vdev->args, args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, cmd, wait);
 
-	*a0 = vdev->args[0];
-	*a1 = vdev->args[1];
+	memcpy(args, vdev->args, nargs * sizeof(args[0]));
 
 	return err;
 }
@@ -455,24 +464,64 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev)
 int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 	u64 *a0, u64 *a1, int wait)
 {
+	u64 args[2];
+	int err;
+
+	args[0] = *a0;
+	args[1] = *a1;
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	switch (vdev->proxy) {
 	case PROXY_BY_INDEX:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_BY_BDF:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_NONE:
+	default:
+		err = vnic_dev_cmd_no_proxy(vdev, cmd, args, 2, wait);
+		break;
+	}
+
+	if (err == 0) {
+		*a0 = args[0];
+		*a1 = args[1];
+	}
+
+	return err;
+}
+
+int vnic_dev_cmd_args(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+		      u64 *args, int nargs, int wait)
+{
+	switch (vdev->proxy) {
+	case PROXY_BY_INDEX:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_BY_BDF:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_NONE:
 	default:
-		return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
+		return vnic_dev_cmd_no_proxy(vdev, cmd, args, nargs, wait);
 	}
 }
 
+static int vnic_dev_advanced_filters_cap(struct vnic_dev *vdev, u64 *args,
+		int nargs)
+{
+	memset(args, 0, nargs * sizeof(*args));
+	args[0] = CMD_ADD_ADV_FILTER;
+	args[1] = FILTER_CAP_MODE_V1_FLAG;
+	return vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, nargs, 1000);
+}
+
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 {
-	u64 a0 = (u32)CMD_ADD_ADV_FILTER, a1 = 0;
+	u64 a0 = CMD_ADD_ADV_FILTER, a1 = 0;
 	int wait = 1000;
 	int err;
 
@@ -482,7 +531,65 @@ int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 	return (a1 >= (u32)FILTER_DPDK_1);
 }
 
-static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
+/*  Determine the "best" filtering mode VIC is capaible of. Returns one of 3
+ *  value or 0 on error:
+ *	FILTER_DPDK_1- advanced filters availabile
+ *	FILTER_USNIC_IP_FLAG - advanced filters but with the restriction that
+ *		the IP layer must explicitly specified. I.e. cannot have a UDP
+ *		filter that matches both IPv4 and IPv6.
+ *	FILTER_IPV4_5TUPLE - fallback if either of the 2 above are not availble.
+ *		all other filter types are not available.
+ *   Retrun true in filter_tags if supported
+ */
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags)
+{
+	u64 args[4];
+	int err;
+	u32 max_level = 0;
+
+	err = vnic_dev_advanced_filters_cap(vdev, args, 4);
+
+	/* determine if filter tags are available */
+	if (err)
+		*filter_tags = 0;
+	if ((args[2] == FILTER_CAP_MODE_V1) &&
+	    (args[3] & FILTER_ACTION_FILTER_ID_FLAG))
+		*filter_tags = 1;
+	else
+		*filter_tags = 0;
+
+	if (err || ((args[0] == 1) && (args[1] == 0))) {
+		/* Adv filter Command not supported or adv filters available but
+		 * not enabled. Try the normal filter capability command.
+		 */
+		args[0] = CMD_ADD_FILTER;
+		args[1] = 0;
+		err = vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, 2, 1000);
+		if (err)
+			return err;
+		max_level = args[1];
+		goto parse_max_level;
+	} else if (args[2] == FILTER_CAP_MODE_V1) {
+		/* parse filter capability mask in args[1] */
+		if (args[1] & FILTER_DPDK_1_FLAG)
+			*mode = FILTER_DPDK_1;
+		else if (args[1] & FILTER_USNIC_IP_FLAG)
+			*mode = FILTER_USNIC_IP;
+		else if (args[1] & FILTER_IPV4_5TUPLE_FLAG)
+			*mode = FILTER_IPV4_5TUPLE;
+		return 0;
+	}
+	max_level = args[1];
+parse_max_level:
+	if (max_level >= (u32)FILTER_USNIC_IP)
+		*mode = FILTER_USNIC_IP;
+	else
+		*mode = FILTER_IPV4_5TUPLE;
+	return 0;
+}
+
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
 {
 	u64 a0 = (u32)cmd, a1 = 0;
 	int wait = 1000;
@@ -1017,32 +1124,30 @@ int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
  *          In case of DEL filter, the caller passes the RQ number. Return
  *          value is irrelevant.
  * @data: filter data
+ * @action: action data
  */
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data)
+	struct filter_v2 *data, struct filter_action_v2 *action_v2)
 {
 	u64 a0, a1;
 	int wait = 1000;
 	dma_addr_t tlv_pa;
 	int ret = -EINVAL;
 	struct filter_tlv *tlv, *tlv_va;
-	struct filter_action *action;
 	u64 tlv_size;
-	u32 filter_size;
+	u32 filter_size, action_size;
 	static unsigned int unique_id;
 	char z_name[RTE_MEMZONE_NAMESIZE];
 	enum vnic_devcmd_cmd dev_cmd;
 
-
 	if (cmd == CLSF_ADD) {
-		if (data->type == FILTER_DPDK_1)
-			dev_cmd = CMD_ADD_ADV_FILTER;
-		else
-			dev_cmd = CMD_ADD_FILTER;
+		dev_cmd = (data->type >= FILTER_DPDK_1) ?
+			  CMD_ADD_ADV_FILTER : CMD_ADD_FILTER;
 
 		filter_size = vnic_filter_size(data);
-		tlv_size = filter_size +
-		    sizeof(struct filter_action) +
+		action_size = vnic_action_size(action_v2);
+
+		tlv_size = filter_size + action_size +
 		    2*sizeof(struct filter_tlv);
 		snprintf((char *)z_name, sizeof(z_name),
 			"vnic_clsf_%d", unique_id++);
@@ -1063,11 +1168,8 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
 					 filter_size);
 
 		tlv->type = CLSF_TLV_ACTION;
-		tlv->length = sizeof(struct filter_action);
-		action = (struct filter_action *)&tlv->val;
-		action->type = FILTER_ACTION_RQ_STEERING;
-		action->u.rq_idx = *entry;
-
+		tlv->length = action_size;
+		memcpy(&tlv->val, (void *)action_v2, action_size);
 		ret = vnic_dev_cmd(vdev, dev_cmd, &a0, &a1, wait);
 		*entry = (u16)a0;
 		vdev->free_consistent(vdev->priv, tlv_size, tlv_va, tlv_pa);
diff --git a/drivers/net/enic/base/vnic_dev.h b/drivers/net/enic/base/vnic_dev.h
index 06ebd4cea..9a9e6917a 100644
--- a/drivers/net/enic/base/vnic_dev.h
+++ b/drivers/net/enic/base/vnic_dev.h
@@ -135,6 +135,9 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev);
 int vnic_dev_fw_info(struct vnic_dev *vdev,
 	struct vnic_devcmd_fw_info **fw_info);
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev);
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd);
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags);
 int vnic_dev_asic_info(struct vnic_dev *vdev, u16 *asic_type, u16 *asic_rev);
 int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, size_t size,
 	void *value);
@@ -202,7 +205,7 @@ int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data);
+	struct filter_v2 *data, struct filter_action_v2 *action_v2);
 #ifdef ENIC_VXLAN
 int vnic_dev_overlay_offload_enable_disable(struct vnic_dev *vdev,
 	u8 overlay, u8 config);
diff --git a/drivers/net/enic/base/vnic_devcmd.h b/drivers/net/enic/base/vnic_devcmd.h
index 785fd6fdf..05d87b919 100644
--- a/drivers/net/enic/base/vnic_devcmd.h
+++ b/drivers/net/enic/base/vnic_devcmd.h
@@ -92,6 +92,8 @@
 #define _CMD_VTYPE(cmd)          (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
 #define _CMD_N(cmd)              (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
 enum vnic_devcmd_cmd {
 	CMD_NONE                = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
 
@@ -598,12 +600,29 @@ enum vnic_devcmd_cmd {
 	 * out: (u32) a0=filter identifier
 	 *
 	 * Capability query:
-	 * out: (u64) a0= 1 if capabliity query supported
-	 *      (u64) a1= MAX filter type supported
+	 * in:  (u64) a1= supported filter capability exchange modes
+	 * out: (u64) a0= 1 if capability query supported
+	 *      if (u64) a1 = 0: a1 = MAX filter type supported
+	 *      if (u64) a1 & FILTER_CAP_MODE_V1_FLAG:
+	 *                       a1 = bitmask of supported filters
+	 *                       a2 = FILTER_CAP_MODE_V1
+	 *                       a3 = bitmask of supported actions
 	 */
 	CMD_ADD_ADV_FILTER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 77),
 };
 
+/* Modes for exchanging advanced filter capabilities. The modes supported by
+ * the driver are passed in the CMD_ADD_ADV_FILTER capability command and the
+ * mode selected is returned.
+ *    V0: the maximum filter type supported is returned
+ *    V1: bitmasks of supported filters and actions are returned
+ */
+enum filter_cap_mode {
+	FILTER_CAP_MODE_V0 = 0,  /* Must always be 0 for legacy drivers */
+	FILTER_CAP_MODE_V1 = 1,
+};
+#define FILTER_CAP_MODE_V1_FLAG (1 << FILTER_CAP_MODE_V1)
+
 /* CMD_ENABLE2 flags */
 #define CMD_ENABLE2_STANDBY 0x0
 #define CMD_ENABLE2_ACTIVE  0x1
@@ -837,6 +856,7 @@ struct filter_generic_1 {
 /* Specifies the filter_action type. */
 enum {
 	FILTER_ACTION_RQ_STEERING = 0,
+	FILTER_ACTION_V2 = 1,
 	FILTER_ACTION_MAX
 };
 
@@ -847,6 +867,22 @@ struct filter_action {
 	} u;
 } __attribute__((packed));
 
+#define FILTER_ACTION_RQ_STEERING_FLAG	(1 << 0)
+#define FILTER_ACTION_FILTER_ID_FLAG	(1 << 1)
+#define FILTER_ACTION_V2_ALL		(FILTER_ACTION_RQ_STEERING_FLAG \
+					 | FILTER_ACTION_FILTER_ID_FLAG)
+
+/* Version 2 of filter action must be a strict extension of struct filter_action
+ * where the first fields exactly match in size and meaning.
+ */
+struct filter_action_v2 {
+	u32 type;
+	u32 rq_idx;
+	u32 flags;                     /* use FILTER_ACTION_XXX_FLAG defines */
+	u16 filter_id;
+	u_int8_t reserved[32];         /* for future expansion */
+} __attribute__((packed));
+
 /* Specifies the filter type. */
 enum filter_type {
 	FILTER_USNIC_ID = 0,
@@ -859,6 +895,21 @@ enum filter_type {
 	FILTER_MAX
 };
 
+#define FILTER_USNIC_ID_FLAG		(1 << FILTER_USNIC_ID)
+#define FILTER_IPV4_5TUPLE_FLAG		(1 << FILTER_IPV4_5TUPLE)
+#define FILTER_MAC_VLAN_FLAG		(1 << FILTER_MAC_VLAN)
+#define FILTER_VLAN_IP_3TUPLE_FLAG	(1 << FILTER_VLAN_IP_3TUPLE)
+#define FILTER_NVGRE_VMQ_FLAG		(1 << FILTER_NVGRE_VMQ)
+#define FILTER_USNIC_IP_FLAG		(1 << FILTER_USNIC_IP)
+#define FILTER_DPDK_1_FLAG		(1 << FILTER_DPDK_1)
+#define FILTER_V1_ALL			(FILTER_USNIC_ID_FLAG | \
+					FILTER_IPV4_5TUPLE_FLAG | \
+					FILTER_MAC_VLAN_FLAG | \
+					FILTER_VLAN_IP_3TUPLE_FLAG | \
+					FILTER_NVGRE_VMQ_FLAG | \
+					FILTER_USNIC_IP_FLAG | \
+					FILTER_DPDK_1_FLAG)
+
 struct filter {
 	u32 type;
 	union {
@@ -903,7 +954,7 @@ struct filter_tlv {
 /* Data for CMD_ADD_FILTER is 2 TLV and filter + action structs */
 #define FILTER_MAX_BUF_SIZE 100
 #define FILTER_V2_MAX_BUF_SIZE (sizeof(struct filter_v2) + \
-	sizeof(struct filter_action) + \
+	sizeof(struct filter_action_v2) + \
 	(2 * sizeof(struct filter_tlv)))
 
 /*
@@ -949,6 +1000,30 @@ enum {
 };
 
 /*
+ * Get the action structure size given action type. To be "future-proof,"
+ * drivers should use this instead of "sizeof (struct filter_action_v2)"
+ * when computing length for TLV.
+ */
+static inline u_int32_t
+vnic_action_size(struct filter_action_v2 *fap)
+{
+	u_int32_t size;
+
+	switch (fap->type) {
+	case FILTER_ACTION_RQ_STEERING:
+		size = sizeof(struct filter_action);
+		break;
+	case FILTER_ACTION_V2:
+		size = sizeof(struct filter_action_v2);
+		break;
+	default:
+		size = sizeof(struct filter_action);
+		break;
+	}
+	return size;
+}
+
+/*
  * Writing cmd register causes STAT_BUSY to get set in status register.
  * When cmd completes, STAT_BUSY will be cleared.
  *
diff --git a/drivers/net/enic/enic_clsf.c b/drivers/net/enic/enic_clsf.c
index 487f80449..9e94afdf9 100644
--- a/drivers/net/enic/enic_clsf.c
+++ b/drivers/net/enic/enic_clsf.c
@@ -345,7 +345,7 @@ int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 		/* Delete the filter */
 		vnic_dev_classifier(enic->vdev, CLSF_DEL,
-			&key->fltr_id, NULL);
+			&key->fltr_id, NULL, NULL);
 		rte_free(key);
 		enic->fdir.nodes[pos] = NULL;
 		enic->fdir.stats.free++;
@@ -365,8 +365,10 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	u32 flowtype_supported;
 	u16 flex_bytes;
 	u16 queue;
+	struct filter_action_v2 action;
 
 	memset(&fltr, 0, sizeof(fltr));
+	memset(&action, 0, sizeof(action));
 	flowtype_supported = enic->fdir.types_mask
 			     & (1 << params->input.flow_type);
 
@@ -439,7 +441,7 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 			 * Delete the filter and add the modified one later
 			 */
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			enic->fdir.stats.free++;
 		}
 
@@ -451,8 +453,11 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 	enic->fdir.copy_fltr_fn(&fltr, &params->input,
 				&enic->rte_dev->data->dev_conf.fdir_conf.mask);
+	action.type = FILTER_ACTION_RQ_STEERING;
+	action.rq_idx = queue;
 
-	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr)) {
+	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr,
+	    &action)) {
 		key->fltr_id = queue;
 	} else {
 		dev_err(enic, "Add classifier entry failed\n");
@@ -462,7 +467,8 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	}
 
 	if (do_free)
-		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL);
+		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL,
+				    NULL);
 	else{
 		enic->fdir.stats.free--;
 		enic->fdir.stats.add++;
@@ -488,7 +494,7 @@ void enic_clsf_destroy(struct enic *enic)
 		key = enic->fdir.nodes[index];
 		if (key) {
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			rte_free(key);
 			enic->fdir.nodes[index] = NULL;
 		}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/6] net/enic: flow API skeleton
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
@ 2017-03-30 21:28 ` John Daley
  2017-03-30 21:28 ` [PATCH 3/6] net/enic: flow API for NICs with advanced filters enabled John Daley
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

Stub callbacks for the generic flow API and a new FLOW debug define.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 config/common_base             |   1 +
 drivers/net/enic/Makefile      |   1 +
 drivers/net/enic/enic.h        |   1 +
 drivers/net/enic/enic_ethdev.c |  21 +++++-
 drivers/net/enic/enic_flow.c   | 154 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 175 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

diff --git a/config/common_base b/config/common_base
index 80006650b..d4edb2033 100644
--- a/config/common_base
+++ b/config/common_base
@@ -249,6 +249,7 @@ CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
 #
 CONFIG_RTE_LIBRTE_ENIC_PMD=y
 CONFIG_RTE_LIBRTE_ENIC_DEBUG=n
+CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW=n
 
 #
 # Compile burst-oriented Netronome NFP PMD driver
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 2c7496dc5..db48ff2da 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_clsf.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_res.c
+SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_cq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_wq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_dev.c
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index e921de405..af1098b05 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -306,4 +306,5 @@ void copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  __rte_unused struct rte_eth_fdir_masks *masks);
 void copy_fltr_v2(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
+extern const struct rte_flow_ops enic_flow_ops;
 #endif /* _ENIC_H_ */
diff --git a/drivers/net/enic/enic_ethdev.c b/drivers/net/enic/enic_ethdev.c
index 2c2e29e97..79098520d 100644
--- a/drivers/net/enic/enic_ethdev.c
+++ b/drivers/net/enic/enic_ethdev.c
@@ -115,13 +115,28 @@ enicpmd_dev_filter_ctrl(struct rte_eth_dev *dev,
 		     enum rte_filter_op filter_op,
 		     void *arg)
 {
-	int ret = -EINVAL;
+	int ret = 0;
+
+	ENICPMD_FUNC_TRACE();
 
-	if (RTE_ETH_FILTER_FDIR == filter_type)
+	if (dev == NULL)
+		return -EINVAL;
+
+	switch (filter_type) {
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &enic_flow_ops;
+		break;
+	case RTE_ETH_FILTER_FDIR:
 		ret = enicpmd_fdir_ctrl_func(dev, filter_op, arg);
-	else
+		break;
+	default:
 		dev_warning(enic, "Filter type (%d) not supported",
 			filter_type);
+		ret = -EINVAL;
+		break;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
new file mode 100644
index 000000000..69cf81643
--- /dev/null
+++ b/drivers/net/enic/enic_flow.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2008-2017 Cisco Systems, Inc.  All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
+ *
+ * Copyright (c) 2017, Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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.
+ *
+ */
+
+#include <errno.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_flow_driver.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+
+#include "enic_compat.h"
+#include "enic.h"
+#include "vnic_dev.h"
+#include "vnic_nic.h"
+
+#ifdef RTE_LIBRTE_ENIC_DEBUG_FLOW
+#define FLOW_TRACE() \
+	RTE_LOG(DEBUG, PMD, "%s()\n", __func__)
+#define FLOW_LOG(level, fmt, args...) \
+	RTE_LOG(level, PMD, fmt, ## args)
+#else
+#define FLOW_TRACE() do { } while (0)
+#define FLOW_LOG(level, fmt, args...) do { } while (0)
+#endif
+
+/*
+ * The following functions are callbacks for Generic flow API.
+ */
+
+/**
+ * Validate a flow supported by the NIC.
+ *
+ * @see rte_flow_validate()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
+		   const struct rte_flow_item pattern[],
+		   const struct rte_flow_action actions[],
+		   struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Create a flow supported by the NIC.
+ *
+ * @see rte_flow_create()
+ * @see rte_flow_ops
+ */
+static struct rte_flow *
+enic_flow_create(struct rte_eth_dev *dev,
+		 const struct rte_flow_attr *attrs,
+		 const struct rte_flow_item pattern[],
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return NULL;
+}
+
+/**
+ * Destroy a flow supported by the NIC.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  __rte_unused struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)flow;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flush all flows on the device.
+ *
+ * @see rte_flow_flush()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flow callback registration.
+ *
+ * @see rte_flow_ops
+ */
+const struct rte_flow_ops enic_flow_ops = {
+	.validate = enic_flow_validate,
+	.create = enic_flow_create,
+	.destroy = enic_flow_destroy,
+	.flush = enic_flow_flush,
+};
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 3/6] net/enic: flow API for NICs with advanced filters enabled
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
  2017-03-30 21:28 ` [PATCH 2/6] net/enic: flow API skeleton John Daley
@ 2017-03-30 21:28 ` John Daley
  2017-03-30 21:28 ` [PATCH 4/6] net/enic: flow API for NICs with advanced filters disabled John Daley
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode enabled via the UCS management interface. This enables:
Attributes: ingress
Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
Actions: queue, mark, flag and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic.h      |  14 +-
 drivers/net/enic/enic_flow.c | 924 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/enic/enic_main.c |   3 +
 drivers/net/enic/enic_res.c  |  15 +
 drivers/net/enic/enic_rxtx.c |  16 +-
 5 files changed, 953 insertions(+), 19 deletions(-)

diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index af1098b05..41dadb0ba 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -80,6 +80,8 @@
 #define PCI_DEVICE_ID_CISCO_VIC_ENET         0x0043  /* ethernet vnic */
 #define PCI_DEVICE_ID_CISCO_VIC_ENET_VF      0x0071  /* enet SRIOV VF */
 
+/* Special Filter id for non-specific packet flagging. Don't change value */
+#define ENIC_MAGIC_FILTER_ID 0xffff
 
 #define ENICPMD_FDIR_MAX           64
 
@@ -111,6 +113,12 @@ struct enic_memzone_entry {
 	LIST_ENTRY(enic_memzone_entry) entries;
 };
 
+struct rte_flow {
+	LIST_ENTRY(rte_flow) next;
+	u16 enic_filter_id;
+	struct filter_v2 enic_filter;
+};
+
 /* Per-instance private data structure */
 struct enic {
 	struct enic *next;
@@ -135,7 +143,9 @@ struct enic {
 	int link_status;
 	u8 hw_ip_checksum;
 	u16 max_mtu;
-	u16 adv_filters;
+	u8 adv_filters;
+	u32 flow_filter_mode;
+	u8 filter_tags;
 
 	unsigned int flags;
 	unsigned int priv_flags;
@@ -170,6 +180,8 @@ struct enic {
 	rte_spinlock_t memzone_list_lock;
 	rte_spinlock_t mtu_lock;
 
+	LIST_HEAD(enic_flows, rte_flow) flows;
+	rte_spinlock_t flows_lock;
 };
 
 /* Get the CQ index from a Start of Packet(SOP) RQ index */
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 69cf81643..1ea2770c4 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -55,6 +55,875 @@
 #define FLOW_LOG(level, fmt, args...) do { } while (0)
 #endif
 
+/** Info about how to copy items into enic filters. */
+struct enic_items {
+	/** Function for copying and validating an item. */
+	int (*copy_item)(const struct rte_flow_item *item,
+			 struct filter_v2 *enic_filter, u8 *inner_ofst);
+	/** List of valid previous items. */
+	const enum rte_flow_item_type * const prev_items;
+	/** True if it's OK for this item to be the first item. For some NIC
+	 * versions, it's invalid to start the stack above layer 3.
+	 */
+	const u8 valid_start_item;
+};
+
+/** Filtering capabilities for various NIC and firmware versions. */
+struct enic_filter_cap {
+	/** list of valid items and their handlers and attributes. */
+	const struct enic_items *item_info;
+};
+
+/* functions for copying flow actions into enic actions */
+typedef int (copy_action_fn)(const struct rte_flow_action actions[],
+			     struct filter_action_v2 *enic_action);
+
+/* functions for copying items into enic filters */
+typedef int(enic_copy_item_fn)(const struct rte_flow_item *item,
+			  struct filter_v2 *enic_filter, u8 *inner_ofst);
+
+/** Action capabilities for various NICs. */
+struct enic_action_cap {
+	/** list of valid actions */
+	const enum rte_flow_action_type *actions;
+	/** copy funtion for a particular NIC */
+	int (*copy_fn)(const struct rte_flow_action actions[],
+		       struct filter_action_v2 *enic_action);
+};
+
+/* Forward declarations */
+static enic_copy_item_fn enic_copy_item_eth_v2;
+static enic_copy_item_fn enic_copy_item_vlan_v2;
+static enic_copy_item_fn enic_copy_item_ipv4_v2;
+static enic_copy_item_fn enic_copy_item_ipv6_v2;
+static enic_copy_item_fn enic_copy_item_udp_v2;
+static enic_copy_item_fn enic_copy_item_tcp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v2;
+
+/** NICs with Advanced filters enabled */
+static const struct enic_items enic_items_v3[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/** Filtering capabilites indexed this NICs supported filter type. */
+static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_DPDK_1] = {
+		.item_info = enic_items_v3,
+	},
+};
+
+/** Supported actions for newer NICs */
+static const enum rte_flow_action_type enic_supported_actions_v2[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_MARK,
+	RTE_FLOW_ACTION_TYPE_FLAG,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
+/** Action capabilites indexed by NIC version information */
+static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_V2_ALL] = {
+		.actions = enic_supported_actions_v2,
+		.copy_fn = enic_copy_action_v2,
+	},
+};
+/**
+ * Copy ETH item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_eth_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	struct ether_hdr enic_spec;
+	struct ether_hdr enic_mask;
+	const struct rte_flow_item_eth *spec = item->spec;
+	const struct rte_flow_item_eth *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_eth_mask;
+
+	memcpy(enic_spec.d_addr.addr_bytes, spec->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_spec.s_addr.addr_bytes, spec->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+
+	memcpy(enic_mask.d_addr.addr_bytes, mask->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_mask.s_addr.addr_bytes, mask->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+	enic_spec.ether_type = spec->type;
+	enic_mask.ether_type = mask->type;
+
+	if (!*inner_ofst) {
+		/* outer header */
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].mask, &enic_mask,
+		       sizeof(struct ether_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].val, &enic_spec,
+		       sizeof(struct ether_hdr));
+	} else {
+		/* inner header */
+		if ((*inner_ofst + sizeof(struct ether_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		/* Offset into L5 where inner Ethernet header goes */
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       &enic_mask, sizeof(struct ether_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       &enic_spec, sizeof(struct ether_hdr));
+		*inner_ofst += sizeof(struct ether_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy VLAN item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_vlan_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vlan *spec = item->spec;
+	const struct rte_flow_item_vlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	/* Don't support filtering in tpid */
+	if (mask) {
+		if (mask->tpid != 0)
+			return ENOTSUP;
+	} else {
+		mask = &rte_flow_item_vlan_mask;
+		RTE_ASSERT(mask->tpid == 0);
+	}
+
+	if (!*inner_ofst) {
+		/* Outer header. Use the vlan mask/val fields */
+		gp->mask_vlan = mask->tci;
+		gp->val_vlan = spec->tci;
+	} else {
+		/* Inner header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct vlan_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct vlan_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct vlan_hdr));
+		*inner_ofst += sizeof(struct vlan_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv4 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv4 filtering.
+ */
+static int
+enic_copy_item_ipv4_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match IPv4 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV4;
+	gp->val_flags |= FILTER_GENERIC_1_IPV4;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+	       sizeof(struct ipv4_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+	       sizeof(struct ipv4_hdr));
+	return 0;
+}
+
+/**
+ * Copy IPv6 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv6 filtering.
+ */
+static int
+enic_copy_item_ipv6_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv6 *spec = item->spec;
+	const struct rte_flow_item_ipv6 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match IPv6 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV6;
+	gp->val_flags |= FILTER_GENERIC_1_IPV6;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv6_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+	       sizeof(struct ipv6_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+	       sizeof(struct ipv6_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner UDP filtering.
+ */
+static int
+enic_copy_item_udp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match UDP */
+	gp->mask_flags |= FILTER_GENERIC_1_UDP;
+	gp->val_flags |= FILTER_GENERIC_1_UDP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct udp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct udp_hdr));
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner TCP filtering.
+ */
+static int
+enic_copy_item_tcp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match TCP */
+	gp->mask_flags |= FILTER_GENERIC_1_TCP;
+	gp->val_flags |= FILTER_GENERIC_1_TCP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		return ENOTSUP;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct tcp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct tcp_hdr));
+	return 0;
+}
+
+/**
+ * Copy SCTP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner SCTP filtering.
+ */
+static int
+enic_copy_item_sctp_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_sctp *spec = item->spec;
+	const struct rte_flow_item_sctp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_sctp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct sctp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct sctp_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. VxLAN headers always start at the beginning of L5.
+ */
+static int
+enic_copy_item_vxlan_v2(const struct rte_flow_item *item,
+			struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vxlan *spec = item->spec;
+	const struct rte_flow_item_vxlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return EINVAL;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_vxlan_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].mask, mask,
+	       sizeof(struct vxlan_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].val, spec,
+	       sizeof(struct vxlan_hdr));
+
+	*inner_ofst = sizeof(struct vxlan_hdr);
+	return 0;
+}
+
+/**
+ * Return 1 if current item is valid on top of the previous one.
+ *
+ * @param prev_item[in]
+ *   The item before this one in the pattern or RTE_FLOW_ITEM_TYPE_END if this
+ *   is the first item.
+ * @param item_info[in]
+ *   Info about this item, like valid previous items.
+ * @param is_first[in]
+ *   True if this the first item in the pattern.
+ */
+static int
+item_stacking_valid(enum rte_flow_item_type prev_item,
+		    const struct enic_items *item_info, u8 is_first_item)
+{
+	enum rte_flow_item_type const *allowed_items = item_info->prev_items;
+
+	FLOW_TRACE();
+
+	for (; *allowed_items != RTE_FLOW_ITEM_TYPE_END; allowed_items++) {
+		if (prev_item == *allowed_items)
+			return 1;
+	}
+
+	/* This is the first item in the stack. Check if that's cool */
+	if (is_first_item && item_info->valid_start_item)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Build the intenal enic filter structure from the provided pattern. The
+ * pattern is validated as the items are copied.
+ *
+ * @param pattern[in]
+ * @param items_info[in]
+ *   Info about this NICs item support, like valid previous items.
+ * @param enic_filter[out]
+ *   NIC specfilc filters derived from the pattern.
+ * @param error[out]
+ */
+static int
+enic_copy_filter(const struct rte_flow_item pattern[],
+		 const struct enic_items *items_info,
+		 struct filter_v2 *enic_filter,
+		 struct rte_flow_error *error)
+{
+	int ret;
+	const struct rte_flow_item *item = pattern;
+	u8 inner_ofst = 0; /* If encapsulated, ofst into L5 */
+	enum rte_flow_item_type prev_item;
+	const struct enic_items *item_info;
+
+	enic_filter->type = FILTER_DPDK_1;
+	u8 is_first_item = 1;
+
+	FLOW_TRACE();
+
+	prev_item = 0;
+
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		/* Get info about how to validate and copy the item. If NULL
+		 * is returned the nic does not support the item.
+		 */
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+
+		item_info = &items_info[item->type];
+
+		/* check to see if item stacking is valid */
+		if (!item_stacking_valid(prev_item, item_info, is_first_item))
+			goto stacking_error;
+
+		ret = item_info->copy_item(item, enic_filter, &inner_ofst);
+		if (ret)
+			goto item_not_supported;
+		prev_item = item->type;
+		is_first_item = 0;
+	}
+	return 0;
+
+item_not_supported:
+	rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_ITEM,
+			   NULL, "enic type error");
+	return -rte_errno;
+
+stacking_error:
+	rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+			   item, "stacking error");
+	return -rte_errno;
+}
+/**
+ * Build the intenal version 2 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v2(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			enic_action->flags |= FILTER_ACTION_RQ_STEERING_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_MARK: {
+			const struct rte_flow_action_mark *mark =
+				(const struct rte_flow_action_mark *)
+				actions->conf;
+
+			/* ENIC_MAGIC_FILTER_ID is reserved and is the highest
+			 * in the range of allows mark ids.
+			 */
+			if (mark->id >= ENIC_MAGIC_FILTER_ID)
+				return EINVAL;
+			enic_action->filter_id = mark->id;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_FLAG: {
+			enic_action->filter_id = ENIC_MAGIC_FILTER_ID;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			continue;
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_V2;
+	return 0;
+}
+
+/** Check if the action is supported */
+static int
+enic_match_action(const struct rte_flow_action *action,
+		  const enum rte_flow_action_type *supported_actions)
+{
+	for (; *supported_actions != RTE_FLOW_ACTION_TYPE_END;
+	     supported_actions++) {
+		if (action->type == *supported_actions)
+			return 1;
+	}
+	return 0;
+}
+
+/** Get the NIC filter capabilties structure */
+static const struct enic_filter_cap *
+enic_get_filter_cap(struct enic *enic)
+{
+	/* FIXME: only support advanced filters for now */
+	if (enic->flow_filter_mode != FILTER_DPDK_1)
+		return (const struct enic_filter_cap *)NULL;
+
+	if (enic->flow_filter_mode)
+		return &enic_filter_cap[enic->flow_filter_mode];
+
+	return (const struct enic_filter_cap *)NULL;
+}
+
+/** Get the actions for this NIC version. */
+static const struct enic_action_cap *
+enic_get_action_cap(struct enic *enic)
+{
+	static const struct enic_action_cap *ea;
+
+	if (enic->filter_tags)
+		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	return ea;
+}
+/**
+ * Internal flow parse/validate function.
+ *
+ * @param dev[in]
+ *   This device pointer.
+ * @param pattern[in]
+ * @param actions[in]
+ * @param error[out]
+ * @param enic_filter[out]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[out]
+ *   Internal NIC action structure pointer.
+ */
+static int
+enic_flow_parse(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attrs,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct filter_v2 *enic_filter,
+		struct filter_action_v2 *enic_action)
+{
+	unsigned int ret = 0;
+	struct enic *enic = pmd_priv(dev);
+	const struct enic_filter_cap *enic_filter_cap;
+	const struct enic_action_cap *enic_action_cap;
+	const struct rte_flow_action *action;
+
+	FLOW_TRACE();
+
+	memset(enic_filter, 0, sizeof(*enic_filter));
+	memset(enic_action, 0, sizeof(*enic_action));
+
+	if (!pattern) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+				   NULL, "No pattern specified");
+		return -rte_errno;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				   NULL, "No action specified");
+		return -rte_errno;
+	}
+
+	if (attrs) {
+		if (attrs->group) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+					   NULL,
+					   "priority groups are not supported");
+			return -rte_errno;
+		} else if (attrs->priority) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					   NULL,
+					   "priorities are not supported");
+			return -rte_errno;
+		} else if (attrs->egress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
+					   NULL,
+					   "egress is not supported");
+			return -rte_errno;
+		} else if (!attrs->ingress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
+					   NULL,
+					   "only ingress is supported");
+			return -rte_errno;
+		}
+
+	} else {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ATTR,
+				   NULL, "No attribute specified");
+		return -rte_errno;
+	}
+
+	/* Verify Actions. */
+	enic_action_cap =  enic_get_action_cap(enic);
+	for (action = &actions[0]; action->type != RTE_FLOW_ACTION_TYPE_END;
+	     action++) {
+		if (action->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+		else if (!enic_match_action(action, enic_action_cap->actions))
+			break;
+	}
+	if (action->type != RTE_FLOW_ACTION_TYPE_END) {
+		rte_flow_error_set(error, EPERM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   action, "Invalid action.");
+		return -rte_errno;
+	}
+	ret = enic_action_cap->copy_fn(actions, enic_action);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Unsupported action.");
+		return -rte_errno;
+	}
+
+	/* Verify Flow items. If copying the filter from flow format to enic
+	 * format fails, the flow is not supported
+	 */
+	enic_filter_cap =  enic_get_filter_cap(enic);
+	if (enic_filter_cap == NULL) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Flow API not available");
+		return -rte_errno;
+	}
+	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
+				       enic_filter, error);
+	return ret;
+}
+
+/**
+ * Push filter/action to the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param enic_filter[in]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static struct rte_flow *
+enic_flow_add_filter(struct enic *enic, struct filter_v2 *enic_filter,
+		   struct filter_action_v2 *enic_action,
+		   struct rte_flow_error *error)
+{
+	struct rte_flow *flow;
+	int ret;
+	u16 entry;
+
+	FLOW_TRACE();
+
+	flow = rte_calloc(__func__, 1, sizeof(*flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "cannot allocate flow memory");
+		return NULL;
+	}
+
+	/* entry[in] is the queue id, entry[out] is the filter Id for delete */
+	entry = enic_action->rq_idx;
+	ret = vnic_dev_classifier(enic->vdev, CLSF_ADD, &entry, enic_filter,
+				  enic_action);
+	if (!ret) {
+		flow->enic_filter_id = entry;
+		flow->enic_filter = *enic_filter;
+	} else {
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier error");
+		rte_free(flow);
+		return NULL;
+	}
+	return flow;
+}
+
+/**
+ * Remove filter/action from the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param filter_id[in]
+ *   Id of NIC filter.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static int
+enic_flow_del_filter(struct enic *enic, u16 filter_id,
+		   struct rte_flow_error *error)
+{
+	int ret;
+
+	FLOW_TRACE();
+
+	ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL, NULL);
+	if (!ret)
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier failed");
+	return ret;
+}
+
 /*
  * The following functions are callbacks for Generic flow API.
  */
@@ -71,15 +940,15 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 		   const struct rte_flow_action actions[],
 		   struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	int ret;
 
 	FLOW_TRACE();
 
-	return 0;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
+			       &enic_filter, &enic_action);
+	return ret;
 }
 
 /**
@@ -95,15 +964,27 @@ enic_flow_create(struct rte_eth_dev *dev,
 		 const struct rte_flow_action actions[],
 		 struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	int ret;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
-	return NULL;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error, &enic_filter,
+			      &enic_action);
+	if (ret < 0)
+		return NULL;
+
+	rte_spinlock_lock(&enic->flows_lock);
+	flow = enic_flow_add_filter(enic, &enic_filter, &enic_action,
+				    error);
+	if (flow)
+		LIST_INSERT_HEAD(&enic->flows, flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
+
+	return flow;
 }
 
 /**
@@ -116,11 +997,14 @@ static int
 enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 		  __rte_unused struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+	enic_flow_del_filter(enic, flow->enic_filter_id, error);
+	LIST_REMOVE(flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
@@ -133,11 +1017,19 @@ enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 static int
 enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)error;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+
+	while (!LIST_EMPTY(&enic->flows)) {
+		flow = LIST_FIRST(&enic->flows);
+		enic_flow_del_filter(enic, flow->enic_filter_id, error);
+		LIST_REMOVE(flow, next);
+	}
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index 570b7b69a..4c515f601 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -1316,6 +1316,9 @@ static int enic_dev_init(struct enic *enic)
 
 	vnic_dev_set_reset_flag(enic->vdev, 0);
 
+	LIST_INIT(&enic->flows);
+	rte_spinlock_init(&enic->flows_lock);
+
 	/* set up link status checking */
 	vnic_dev_notify_set(enic->vdev, -1); /* No Intr for notify */
 
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c
index 867bd25c4..e4b80d49c 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/enic/enic_res.c
@@ -104,6 +104,21 @@ int enic_get_vnic_config(struct enic *enic)
 	dev_info(enic, "Advanced Filters %savailable\n", ((enic->adv_filters)
 		 ? "" : "not "));
 
+	err = vnic_dev_capable_filter_mode(enic->vdev, &enic->flow_filter_mode,
+					   &enic->filter_tags);
+	if (err) {
+		dev_err(enic_get_dev(enic),
+			"Error getting filter modes, %d\n", err);
+		return err;
+	}
+
+	dev_info(enic, "Flow api filter mode: %s, Filter tagging %savailable\n",
+		((enic->flow_filter_mode == FILTER_DPDK_1) ? "DPDK" :
+		((enic->flow_filter_mode == FILTER_USNIC_IP) ? "USNIC" :
+		((enic->flow_filter_mode == FILTER_IPV4_5TUPLE) ? "5TUPLE" :
+		"NONE"))),
+		((enic->filter_tags) ? "" : "not "));
+
 	c->wq_desc_count =
 		min_t(u32, ENIC_MAX_WQ_DESCS,
 		max_t(u32, ENIC_MIN_WQ_DESCS,
diff --git a/drivers/net/enic/enic_rxtx.c b/drivers/net/enic/enic_rxtx.c
index 343dabc64..5f59e3726 100644
--- a/drivers/net/enic/enic_rxtx.c
+++ b/drivers/net/enic/enic_rxtx.c
@@ -253,8 +253,20 @@ enic_cq_rx_to_pkt_flags(struct cq_desc *cqd, struct rte_mbuf *mbuf)
 	}
 	mbuf->vlan_tci = vlan_tci;
 
-	/* RSS flag */
-	if (enic_cq_rx_desc_rss_type(cqrd)) {
+	if ((cqd->type_color & CQ_DESC_TYPE_MASK) == CQ_DESC_TYPE_CLASSIFIER) {
+		struct cq_enet_rq_clsf_desc *clsf_cqd;
+		uint16_t filter_id;
+		clsf_cqd = (struct cq_enet_rq_clsf_desc *)cqd;
+		filter_id = clsf_cqd->filter_id;
+		if (filter_id) {
+			pkt_flags |= PKT_RX_FDIR;
+			if (filter_id != ENIC_MAGIC_FILTER_ID) {
+				mbuf->hash.fdir.hi = clsf_cqd->filter_id;
+				pkt_flags |= PKT_RX_FDIR_ID;
+			}
+		}
+	} else if (enic_cq_rx_desc_rss_type(cqrd)) {
+		/* RSS flag */
 		pkt_flags |= PKT_RX_RSS_HASH;
 		mbuf->hash.rss = enic_cq_rx_desc_rss_hash(cqrd);
 	}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 4/6] net/enic: flow API for NICs with advanced filters disabled
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
  2017-03-30 21:28 ` [PATCH 2/6] net/enic: flow API skeleton John Daley
  2017-03-30 21:28 ` [PATCH 3/6] net/enic: flow API for NICs with advanced filters enabled John Daley
@ 2017-03-30 21:28 ` John Daley
  2017-03-30 21:28 ` [PATCH 5/6] net/enic: flow API support for Legacy NICs John Daley
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode disabled via the UCS management interface. This allows:
Attributes: ingress
Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
Actions: queue and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

With advanced filters disabled, an IPv4 or IPv6 item must be specified
in the pattern.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 135 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 133 insertions(+), 2 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 1ea2770c4..21da80a88 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -101,8 +101,85 @@ static enic_copy_item_fn enic_copy_item_tcp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
+/**
+ * NICs have Advanced Filters capability but they are disabled. This means
+ * that layer 3 must be specified.
+ */
+static const struct enic_items enic_items_v2[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
 /** NICs with Advanced filters enabled */
 static const struct enic_items enic_items_v3[] = {
 	[RTE_FLOW_ITEM_TYPE_ETH] = {
@@ -178,11 +255,20 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_USNIC_IP] = {
+		.item_info = enic_items_v2,
+	},
 	[FILTER_DPDK_1] = {
 		.item_info = enic_items_v3,
 	},
 };
 
+/** Supported actions for older NICs */
+static const enum rte_flow_action_type enic_supported_actions_v1[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
 /** Supported actions for newer NICs */
 static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 	RTE_FLOW_ACTION_TYPE_QUEUE,
@@ -193,6 +279,10 @@ static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 
 /** Action capabilites indexed by NIC version information */
 static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_RQ_STEERING_FLAG] = {
+		.actions = enic_supported_actions_v1,
+		.copy_fn = enic_copy_action_v1,
+	},
 	[FILTER_ACTION_V2_ALL] = {
 		.actions = enic_supported_actions_v2,
 		.copy_fn = enic_copy_action_v2,
@@ -607,7 +697,6 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 	enum rte_flow_item_type prev_item;
 	const struct enic_items *item_info;
 
-	enic_filter->type = FILTER_DPDK_1;
 	u8 is_first_item = 1;
 
 	FLOW_TRACE();
@@ -645,6 +734,44 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 			   item, "stacking error");
 	return -rte_errno;
 }
+
+/**
+ * Build the intenal version 1 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v1(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		if (actions->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			break;
+		}
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_RQ_STEERING;
+	return 0;
+}
+
 /**
  * Build the intenal version 2 NIC action structure from the provided pattern.
  * The pattern is validated as the items are copied.
@@ -719,7 +846,8 @@ static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
 	/* FIXME: only support advanced filters for now */
-	if (enic->flow_filter_mode != FILTER_DPDK_1)
+	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
+	   (enic->flow_filter_mode != FILTER_USNIC_IP))
 		return (const struct enic_filter_cap *)NULL;
 
 	if (enic->flow_filter_mode)
@@ -736,6 +864,8 @@ enic_get_action_cap(struct enic *enic)
 
 	if (enic->filter_tags)
 		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	else
+		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
 /**
@@ -848,6 +978,7 @@ enic_flow_parse(struct rte_eth_dev *dev,
 			   NULL, "Flow API not available");
 		return -rte_errno;
 	}
+	enic_filter->type = enic->flow_filter_mode;
 	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
 				       enic_filter, error);
 	return ret;
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 5/6] net/enic: flow API support for Legacy NICs
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
                   ` (2 preceding siblings ...)
  2017-03-30 21:28 ` [PATCH 4/6] net/enic: flow API for NICs with advanced filters disabled John Daley
@ 2017-03-30 21:28 ` John Daley
  2017-03-30 21:28 ` [PATCH 6/6] net/enic: flow API debug John Daley
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

5-tuple exact Flow support for 1200 series adapters. This allows:
Attributes: ingress
Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
       addresses and ports and all must be specified).
Actions: queue and void
Selectors: 'is'

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 206 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 201 insertions(+), 5 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 21da80a88..2ad784056 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -92,6 +92,9 @@ struct enic_action_cap {
 };
 
 /* Forward declarations */
+static enic_copy_item_fn enic_copy_item_ipv4_v1;
+static enic_copy_item_fn enic_copy_item_udp_v1;
+static enic_copy_item_fn enic_copy_item_tcp_v1;
 static enic_copy_item_fn enic_copy_item_eth_v2;
 static enic_copy_item_fn enic_copy_item_vlan_v2;
 static enic_copy_item_fn enic_copy_item_ipv4_v2;
@@ -105,6 +108,36 @@ static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
 /**
+ * Legacy NICs or NICs with outdated firmware. Only 5-tuple perfect match
+ * is supported.
+ */
+static const struct enic_items enic_items_v1[] = {
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v1,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/**
  * NICs have Advanced Filters capability but they are disabled. This means
  * that layer 3 must be specified.
  */
@@ -255,6 +288,9 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_IPV4_5TUPLE] = {
+		.item_info = enic_items_v1,
+	},
 	[FILTER_USNIC_IP] = {
 		.item_info = enic_items_v2,
 	},
@@ -288,6 +324,171 @@ static const struct enic_action_cap enic_action_cap[] = {
 		.copy_fn = enic_copy_action_v2,
 	},
 };
+
+static int
+mask_exact_match(const u8 *supported, const u8 *supplied,
+		 unsigned int size)
+{
+	unsigned int i;
+	for (i = 0; i < size; i++) {
+		if (supported[i] != supplied[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Copy IPv4 item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_ipv4_v1(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct ipv4_hdr supported_mask = {
+		.src_addr = 0xffffffff,
+		.dst_addr = 0xffffffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	/* This is an exact match filter, both fields must be set */
+	if (!spec || !spec->hdr.src_addr || !spec->hdr.dst_addr) {
+		FLOW_LOG(ERR, "IPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "IPv4 exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_addr = spec->hdr.src_addr;
+	enic_5tup->dst_addr = spec->hdr.dst_addr;
+
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_udp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct udp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "UDP exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "UDP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_UDP;
+
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_tcp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct tcp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_tcp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "TCPIPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			     (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "TCP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_TCP;
+
+	return 0;
+}
+
 /**
  * Copy ETH item into version 2 NIC filter.
  *
@@ -845,11 +1046,6 @@ enic_match_action(const struct rte_flow_action *action,
 static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
-	/* FIXME: only support advanced filters for now */
-	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
-	   (enic->flow_filter_mode != FILTER_USNIC_IP))
-		return (const struct enic_filter_cap *)NULL;
-
 	if (enic->flow_filter_mode)
 		return &enic_filter_cap[enic->flow_filter_mode];
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 6/6] net/enic: flow API debug
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
                   ` (3 preceding siblings ...)
  2017-03-30 21:28 ` [PATCH 5/6] net/enic: flow API support for Legacy NICs John Daley
@ 2017-03-30 21:28 ` John Daley
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
  2017-03-31 10:12 ` [PATCH 1/6] net/enic: bring NIC interface functions up to date Ferruh Yigit
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-30 21:28 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, adrien.mazarguil, John Daley

Added a debug function to print enic filters and actions when
rte_validate_flow is called. Compiled in CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW
is enabled and log level is INFO.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 2ad784056..1f7256a95 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -1064,6 +1064,142 @@ enic_get_action_cap(struct enic *enic)
 		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
+
+/* Debug function to dump internal NIC action structure. */
+static void
+enic_dump_actions(const struct filter_action_v2 *ea)
+{
+	if (ea->type == FILTER_ACTION_RQ_STEERING) {
+		FLOW_LOG(INFO, "Action(V1), queue: %u\n", ea->rq_idx);
+	} else if (ea->type == FILTER_ACTION_V2) {
+		FLOW_LOG(INFO, "Actions(V2)\n");
+		if (ea->flags & FILTER_ACTION_RQ_STEERING_FLAG)
+			FLOW_LOG(INFO, "\tqueue: %u\n",
+			       enic_sop_rq_idx_to_rte_idx(ea->rq_idx));
+		if (ea->flags & FILTER_ACTION_FILTER_ID_FLAG)
+			FLOW_LOG(INFO, "\tfilter_id: %u\n", ea->filter_id);
+	}
+}
+
+/* Debug function to dump internal NIC filter structure. */
+static void
+enic_dump_filter(const struct filter_v2 *filt)
+{
+	const struct filter_generic_1 *gp;
+	int i, j, mbyte;
+	char buf[128], *bp;
+	char ip4[16], ip6[16], udp[16], tcp[16], tcpudp[16], ip4csum[16];
+	char l4csum[16], ipfrag[16];
+
+	switch (filt->type) {
+	case FILTER_IPV4_5TUPLE:
+		FLOW_LOG(INFO, "FILTER_IPV4_5TUPLE\n");
+		break;
+	case FILTER_USNIC_IP:
+	case FILTER_DPDK_1:
+		/* FIXME: this should be a loop */
+		gp = &filt->u.generic_1;
+		FLOW_LOG(INFO, "Filter: vlan: 0x%04x, mask: 0x%04x\n",
+		       gp->val_vlan, gp->mask_vlan);
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV4)
+			sprintf(ip4, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip4(y)" : "ip4(n)");
+		else
+			sprintf(ip4, "%s ", "ip4(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV6)
+			sprintf(ip6, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip6(y)" : "ip6(n)");
+		else
+			sprintf(ip6, "%s ", "ip6(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_UDP)
+			sprintf(udp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_UDP)
+				 ? "udp(y)" : "udp(n)");
+		else
+			sprintf(udp, "%s ", "udp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP)
+			sprintf(tcp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP)
+				 ? "tcp(y)" : "tcp(n)");
+		else
+			sprintf(tcp, "%s ", "tcp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+			sprintf(tcpudp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+				 ? "tcpudp(y)" : "tcpudp(n)");
+		else
+			sprintf(tcpudp, "%s ", "tcpudp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IP4SUM_OK)
+			sprintf(ip4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IP4SUM_OK)
+				 ? "ip4csum(y)" : "ip4csum(n)");
+		else
+			sprintf(ip4csum, "%s ", "ip4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_L4SUM_OK)
+			sprintf(l4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_L4SUM_OK)
+				 ? "l4csum(y)" : "l4csum(n)");
+		else
+			sprintf(l4csum, "%s ", "l4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPFRAG)
+			sprintf(ipfrag, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPFRAG)
+				 ? "ipfrag(y)" : "ipfrag(n)");
+		else
+			sprintf(ipfrag, "%s ", "ipfrag(x)");
+		FLOW_LOG(INFO, "\tFlags: %s%s%s%s%s%s%s%s\n", ip4, ip6, udp,
+			 tcp, tcpudp, ip4csum, l4csum, ipfrag);
+
+		for (i = 0; i < FILTER_GENERIC_1_NUM_LAYERS; i++) {
+			mbyte = FILTER_GENERIC_1_KEY_LEN - 1;
+			while (mbyte && !gp->layer[i].mask[mbyte])
+				mbyte--;
+			if (mbyte == 0)
+				continue;
+
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].mask[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u mask: %s\n", i + 2, buf);
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].val[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u  val: %s\n", i + 2, buf);
+		}
+		break;
+	default:
+		FLOW_LOG(INFO, "FILTER UNKNOWN\n");
+		break;
+	}
+}
+
+/* Debug function to dump internal NIC flow structures. */
+static void
+enic_dump_flow(const struct filter_action_v2 *ea, const struct filter_v2 *filt)
+{
+	enic_dump_filter(filt);
+	enic_dump_actions(ea);
+}
+
+
 /**
  * Internal flow parse/validate function.
  *
@@ -1275,6 +1411,8 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 
 	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
 			       &enic_filter, &enic_action);
+	if (!ret)
+		enic_dump_flow(&enic_action, &enic_filter);
 	return ret;
 }
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 0/7] *** flow API support for enic ***
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
                   ` (4 preceding siblings ...)
  2017-03-30 21:28 ` [PATCH 6/6] net/enic: flow API debug John Daley
@ 2017-03-31  2:06 ` John Daley
  2017-03-31  2:06   ` [PATCH v2 1/7] net/enic: bring NIC interface functions up to date John Daley
                     ` (6 more replies)
  2017-03-31 10:12 ` [PATCH 1/6] net/enic: bring NIC interface functions up to date Ferruh Yigit
  6 siblings, 7 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

*** flow API support for enic ***

Version 2 fixes some checkpatch fails and adds documentaion.
My checkpatch didn't catch typos and I missed a couple valid errors.
There are still some warnings: 1 for returning non-negative errnos from
a function which I'm OK with this because the caller uses them in the
flow error function where they are supposed to be positive. The other
warning is about double use of macro arguement in ARRAY_SIZE which is OK.

thanks,
john


John Daley (7):
  net/enic: bring NIC interface functions up to date
  net/enic: flow API skeleton
  net/enic: flow API for NICs with advanced filters enabled
  net/enic: flow API for NICs with advanced filters disabled
  net/enic: flow API for Legacy NICs
  net/enic: flow API debug
  net/enic: flow API documentation

 config/common_base                     |    1 +
 doc/guides/nics/enic.rst               |   44 +
 doc/guides/nics/features/enic.ini      |    1 +
 doc/guides/rel_notes/release_17_05.rst |    6 +
 drivers/net/enic/Makefile              |    1 +
 drivers/net/enic/base/cq_enet_desc.h   |   13 +
 drivers/net/enic/base/vnic_dev.c       |  162 +++-
 drivers/net/enic/base/vnic_dev.h       |    5 +-
 drivers/net/enic/base/vnic_devcmd.h    |   81 +-
 drivers/net/enic/enic.h                |   15 +-
 drivers/net/enic/enic_clsf.c           |   16 +-
 drivers/net/enic/enic_ethdev.c         |   21 +-
 drivers/net/enic/enic_flow.c           | 1511 ++++++++++++++++++++++++++++++++
 drivers/net/enic/enic_main.c           |    3 +
 drivers/net/enic/enic_res.c            |   15 +
 drivers/net/enic/enic_rxtx.c           |   16 +-
 16 files changed, 1866 insertions(+), 45 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

-- 
2.12.0

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v2 1/7] net/enic: bring NIC interface functions up to date
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 2/7] net/enic: flow API skeleton John Daley
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Update the base functions for the Cisco VIC. These files are mostly
common with other VIC drivers so are left alone is as much as possilbe.
Includes in a new filter/action interface which is needed for Generic
Flow API PMD support. Update FDIR code to use the new interface.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/base/cq_enet_desc.h |  13 +++
 drivers/net/enic/base/vnic_dev.c     | 162 ++++++++++++++++++++++++++++-------
 drivers/net/enic/base/vnic_dev.h     |   5 +-
 drivers/net/enic/base/vnic_devcmd.h  |  81 +++++++++++++++++-
 drivers/net/enic/enic_clsf.c         |  16 ++--
 5 files changed, 238 insertions(+), 39 deletions(-)

diff --git a/drivers/net/enic/base/cq_enet_desc.h b/drivers/net/enic/base/cq_enet_desc.h
index f9822a450..e8410563a 100644
--- a/drivers/net/enic/base/cq_enet_desc.h
+++ b/drivers/net/enic/base/cq_enet_desc.h
@@ -71,6 +71,19 @@ struct cq_enet_rq_desc {
 	u8 type_color;
 };
 
+/* Completion queue descriptor: Ethernet receive queue, 16B */
+struct cq_enet_rq_clsf_desc {
+	__le16 completed_index_flags;
+	__le16 q_number_rss_type_flags;
+	__le16 filter_id;
+	__le16 lif;
+	__le16 bytes_written_flags;
+	__le16 vlan;
+	__le16 checksum_fcoe;
+	u8 flags;
+	u8 type_color;
+};
+
 #define CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT          (0x1 << 12)
 #define CQ_ENET_RQ_DESC_FLAGS_FCOE                  (0x1 << 13)
 #define CQ_ENET_RQ_DESC_FLAGS_EOP                   (0x1 << 14)
diff --git a/drivers/net/enic/base/vnic_dev.c b/drivers/net/enic/base/vnic_dev.c
index 84e4840af..7b3aed31a 100644
--- a/drivers/net/enic/base/vnic_dev.c
+++ b/drivers/net/enic/base/vnic_dev.c
@@ -387,17 +387,24 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 
 static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 	enum vnic_devcmd_cmd proxy_cmd, enum vnic_devcmd_cmd cmd,
-	u64 *a0, u64 *a1, int wait)
+	u64 *args, int nargs, int wait)
 {
 	u32 status;
 	int err;
 
+	/*
+	 * Proxy command consumes 2 arguments. One for proxy index,
+	 * the other is for command to be proxied
+	 */
+	if (nargs > VNIC_DEVCMD_NARGS - 2) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	vdev->args[0] = vdev->proxy_index;
 	vdev->args[1] = cmd;
-	vdev->args[2] = *a0;
-	vdev->args[3] = *a1;
+	memcpy(&vdev->args[2], args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, proxy_cmd, wait);
 	if (err)
@@ -412,24 +419,26 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 		return err;
 	}
 
-	*a0 = vdev->args[1];
-	*a1 = vdev->args[2];
+	memcpy(args, &vdev->args[1], nargs * sizeof(args[0]));
 
 	return 0;
 }
 
 static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
-	enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+	enum vnic_devcmd_cmd cmd, u64 *args, int nargs, int wait)
 {
 	int err;
 
-	vdev->args[0] = *a0;
-	vdev->args[1] = *a1;
+	if (nargs > VNIC_DEVCMD_NARGS) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
+	memset(vdev->args, 0, sizeof(vdev->args));
+	memcpy(vdev->args, args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, cmd, wait);
 
-	*a0 = vdev->args[0];
-	*a1 = vdev->args[1];
+	memcpy(args, vdev->args, nargs * sizeof(args[0]));
 
 	return err;
 }
@@ -455,24 +464,64 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev)
 int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 	u64 *a0, u64 *a1, int wait)
 {
+	u64 args[2];
+	int err;
+
+	args[0] = *a0;
+	args[1] = *a1;
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	switch (vdev->proxy) {
 	case PROXY_BY_INDEX:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_BY_BDF:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_NONE:
+	default:
+		err = vnic_dev_cmd_no_proxy(vdev, cmd, args, 2, wait);
+		break;
+	}
+
+	if (err == 0) {
+		*a0 = args[0];
+		*a1 = args[1];
+	}
+
+	return err;
+}
+
+int vnic_dev_cmd_args(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+		      u64 *args, int nargs, int wait)
+{
+	switch (vdev->proxy) {
+	case PROXY_BY_INDEX:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_BY_BDF:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_NONE:
 	default:
-		return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
+		return vnic_dev_cmd_no_proxy(vdev, cmd, args, nargs, wait);
 	}
 }
 
+static int vnic_dev_advanced_filters_cap(struct vnic_dev *vdev, u64 *args,
+		int nargs)
+{
+	memset(args, 0, nargs * sizeof(*args));
+	args[0] = CMD_ADD_ADV_FILTER;
+	args[1] = FILTER_CAP_MODE_V1_FLAG;
+	return vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, nargs, 1000);
+}
+
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 {
-	u64 a0 = (u32)CMD_ADD_ADV_FILTER, a1 = 0;
+	u64 a0 = CMD_ADD_ADV_FILTER, a1 = 0;
 	int wait = 1000;
 	int err;
 
@@ -482,7 +531,65 @@ int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 	return (a1 >= (u32)FILTER_DPDK_1);
 }
 
-static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
+/*  Determine the "best" filtering mode VIC is capaible of. Returns one of 3
+ *  value or 0 on error:
+ *	FILTER_DPDK_1- advanced filters availabile
+ *	FILTER_USNIC_IP_FLAG - advanced filters but with the restriction that
+ *		the IP layer must explicitly specified. I.e. cannot have a UDP
+ *		filter that matches both IPv4 and IPv6.
+ *	FILTER_IPV4_5TUPLE - fallback if either of the 2 above aren't available.
+ *		all other filter types are not available.
+ *   Retrun true in filter_tags if supported
+ */
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags)
+{
+	u64 args[4];
+	int err;
+	u32 max_level = 0;
+
+	err = vnic_dev_advanced_filters_cap(vdev, args, 4);
+
+	/* determine if filter tags are available */
+	if (err)
+		*filter_tags = 0;
+	if ((args[2] == FILTER_CAP_MODE_V1) &&
+	    (args[3] & FILTER_ACTION_FILTER_ID_FLAG))
+		*filter_tags = 1;
+	else
+		*filter_tags = 0;
+
+	if (err || ((args[0] == 1) && (args[1] == 0))) {
+		/* Adv filter Command not supported or adv filters available but
+		 * not enabled. Try the normal filter capability command.
+		 */
+		args[0] = CMD_ADD_FILTER;
+		args[1] = 0;
+		err = vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, 2, 1000);
+		if (err)
+			return err;
+		max_level = args[1];
+		goto parse_max_level;
+	} else if (args[2] == FILTER_CAP_MODE_V1) {
+		/* parse filter capability mask in args[1] */
+		if (args[1] & FILTER_DPDK_1_FLAG)
+			*mode = FILTER_DPDK_1;
+		else if (args[1] & FILTER_USNIC_IP_FLAG)
+			*mode = FILTER_USNIC_IP;
+		else if (args[1] & FILTER_IPV4_5TUPLE_FLAG)
+			*mode = FILTER_IPV4_5TUPLE;
+		return 0;
+	}
+	max_level = args[1];
+parse_max_level:
+	if (max_level >= (u32)FILTER_USNIC_IP)
+		*mode = FILTER_USNIC_IP;
+	else
+		*mode = FILTER_IPV4_5TUPLE;
+	return 0;
+}
+
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
 {
 	u64 a0 = (u32)cmd, a1 = 0;
 	int wait = 1000;
@@ -1017,32 +1124,30 @@ int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
  *          In case of DEL filter, the caller passes the RQ number. Return
  *          value is irrelevant.
  * @data: filter data
+ * @action: action data
  */
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data)
+	struct filter_v2 *data, struct filter_action_v2 *action_v2)
 {
 	u64 a0, a1;
 	int wait = 1000;
 	dma_addr_t tlv_pa;
 	int ret = -EINVAL;
 	struct filter_tlv *tlv, *tlv_va;
-	struct filter_action *action;
 	u64 tlv_size;
-	u32 filter_size;
+	u32 filter_size, action_size;
 	static unsigned int unique_id;
 	char z_name[RTE_MEMZONE_NAMESIZE];
 	enum vnic_devcmd_cmd dev_cmd;
 
-
 	if (cmd == CLSF_ADD) {
-		if (data->type == FILTER_DPDK_1)
-			dev_cmd = CMD_ADD_ADV_FILTER;
-		else
-			dev_cmd = CMD_ADD_FILTER;
+		dev_cmd = (data->type >= FILTER_DPDK_1) ?
+			  CMD_ADD_ADV_FILTER : CMD_ADD_FILTER;
 
 		filter_size = vnic_filter_size(data);
-		tlv_size = filter_size +
-		    sizeof(struct filter_action) +
+		action_size = vnic_action_size(action_v2);
+
+		tlv_size = filter_size + action_size +
 		    2*sizeof(struct filter_tlv);
 		snprintf((char *)z_name, sizeof(z_name),
 			"vnic_clsf_%d", unique_id++);
@@ -1063,11 +1168,8 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
 					 filter_size);
 
 		tlv->type = CLSF_TLV_ACTION;
-		tlv->length = sizeof(struct filter_action);
-		action = (struct filter_action *)&tlv->val;
-		action->type = FILTER_ACTION_RQ_STEERING;
-		action->u.rq_idx = *entry;
-
+		tlv->length = action_size;
+		memcpy(&tlv->val, (void *)action_v2, action_size);
 		ret = vnic_dev_cmd(vdev, dev_cmd, &a0, &a1, wait);
 		*entry = (u16)a0;
 		vdev->free_consistent(vdev->priv, tlv_size, tlv_va, tlv_pa);
diff --git a/drivers/net/enic/base/vnic_dev.h b/drivers/net/enic/base/vnic_dev.h
index 06ebd4cea..9a9e6917a 100644
--- a/drivers/net/enic/base/vnic_dev.h
+++ b/drivers/net/enic/base/vnic_dev.h
@@ -135,6 +135,9 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev);
 int vnic_dev_fw_info(struct vnic_dev *vdev,
 	struct vnic_devcmd_fw_info **fw_info);
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev);
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd);
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags);
 int vnic_dev_asic_info(struct vnic_dev *vdev, u16 *asic_type, u16 *asic_rev);
 int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, size_t size,
 	void *value);
@@ -202,7 +205,7 @@ int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data);
+	struct filter_v2 *data, struct filter_action_v2 *action_v2);
 #ifdef ENIC_VXLAN
 int vnic_dev_overlay_offload_enable_disable(struct vnic_dev *vdev,
 	u8 overlay, u8 config);
diff --git a/drivers/net/enic/base/vnic_devcmd.h b/drivers/net/enic/base/vnic_devcmd.h
index 785fd6fdf..05d87b919 100644
--- a/drivers/net/enic/base/vnic_devcmd.h
+++ b/drivers/net/enic/base/vnic_devcmd.h
@@ -92,6 +92,8 @@
 #define _CMD_VTYPE(cmd)          (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
 #define _CMD_N(cmd)              (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
 enum vnic_devcmd_cmd {
 	CMD_NONE                = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
 
@@ -598,12 +600,29 @@ enum vnic_devcmd_cmd {
 	 * out: (u32) a0=filter identifier
 	 *
 	 * Capability query:
-	 * out: (u64) a0= 1 if capabliity query supported
-	 *      (u64) a1= MAX filter type supported
+	 * in:  (u64) a1= supported filter capability exchange modes
+	 * out: (u64) a0= 1 if capability query supported
+	 *      if (u64) a1 = 0: a1 = MAX filter type supported
+	 *      if (u64) a1 & FILTER_CAP_MODE_V1_FLAG:
+	 *                       a1 = bitmask of supported filters
+	 *                       a2 = FILTER_CAP_MODE_V1
+	 *                       a3 = bitmask of supported actions
 	 */
 	CMD_ADD_ADV_FILTER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 77),
 };
 
+/* Modes for exchanging advanced filter capabilities. The modes supported by
+ * the driver are passed in the CMD_ADD_ADV_FILTER capability command and the
+ * mode selected is returned.
+ *    V0: the maximum filter type supported is returned
+ *    V1: bitmasks of supported filters and actions are returned
+ */
+enum filter_cap_mode {
+	FILTER_CAP_MODE_V0 = 0,  /* Must always be 0 for legacy drivers */
+	FILTER_CAP_MODE_V1 = 1,
+};
+#define FILTER_CAP_MODE_V1_FLAG (1 << FILTER_CAP_MODE_V1)
+
 /* CMD_ENABLE2 flags */
 #define CMD_ENABLE2_STANDBY 0x0
 #define CMD_ENABLE2_ACTIVE  0x1
@@ -837,6 +856,7 @@ struct filter_generic_1 {
 /* Specifies the filter_action type. */
 enum {
 	FILTER_ACTION_RQ_STEERING = 0,
+	FILTER_ACTION_V2 = 1,
 	FILTER_ACTION_MAX
 };
 
@@ -847,6 +867,22 @@ struct filter_action {
 	} u;
 } __attribute__((packed));
 
+#define FILTER_ACTION_RQ_STEERING_FLAG	(1 << 0)
+#define FILTER_ACTION_FILTER_ID_FLAG	(1 << 1)
+#define FILTER_ACTION_V2_ALL		(FILTER_ACTION_RQ_STEERING_FLAG \
+					 | FILTER_ACTION_FILTER_ID_FLAG)
+
+/* Version 2 of filter action must be a strict extension of struct filter_action
+ * where the first fields exactly match in size and meaning.
+ */
+struct filter_action_v2 {
+	u32 type;
+	u32 rq_idx;
+	u32 flags;                     /* use FILTER_ACTION_XXX_FLAG defines */
+	u16 filter_id;
+	u_int8_t reserved[32];         /* for future expansion */
+} __attribute__((packed));
+
 /* Specifies the filter type. */
 enum filter_type {
 	FILTER_USNIC_ID = 0,
@@ -859,6 +895,21 @@ enum filter_type {
 	FILTER_MAX
 };
 
+#define FILTER_USNIC_ID_FLAG		(1 << FILTER_USNIC_ID)
+#define FILTER_IPV4_5TUPLE_FLAG		(1 << FILTER_IPV4_5TUPLE)
+#define FILTER_MAC_VLAN_FLAG		(1 << FILTER_MAC_VLAN)
+#define FILTER_VLAN_IP_3TUPLE_FLAG	(1 << FILTER_VLAN_IP_3TUPLE)
+#define FILTER_NVGRE_VMQ_FLAG		(1 << FILTER_NVGRE_VMQ)
+#define FILTER_USNIC_IP_FLAG		(1 << FILTER_USNIC_IP)
+#define FILTER_DPDK_1_FLAG		(1 << FILTER_DPDK_1)
+#define FILTER_V1_ALL			(FILTER_USNIC_ID_FLAG | \
+					FILTER_IPV4_5TUPLE_FLAG | \
+					FILTER_MAC_VLAN_FLAG | \
+					FILTER_VLAN_IP_3TUPLE_FLAG | \
+					FILTER_NVGRE_VMQ_FLAG | \
+					FILTER_USNIC_IP_FLAG | \
+					FILTER_DPDK_1_FLAG)
+
 struct filter {
 	u32 type;
 	union {
@@ -903,7 +954,7 @@ struct filter_tlv {
 /* Data for CMD_ADD_FILTER is 2 TLV and filter + action structs */
 #define FILTER_MAX_BUF_SIZE 100
 #define FILTER_V2_MAX_BUF_SIZE (sizeof(struct filter_v2) + \
-	sizeof(struct filter_action) + \
+	sizeof(struct filter_action_v2) + \
 	(2 * sizeof(struct filter_tlv)))
 
 /*
@@ -949,6 +1000,30 @@ enum {
 };
 
 /*
+ * Get the action structure size given action type. To be "future-proof,"
+ * drivers should use this instead of "sizeof (struct filter_action_v2)"
+ * when computing length for TLV.
+ */
+static inline u_int32_t
+vnic_action_size(struct filter_action_v2 *fap)
+{
+	u_int32_t size;
+
+	switch (fap->type) {
+	case FILTER_ACTION_RQ_STEERING:
+		size = sizeof(struct filter_action);
+		break;
+	case FILTER_ACTION_V2:
+		size = sizeof(struct filter_action_v2);
+		break;
+	default:
+		size = sizeof(struct filter_action);
+		break;
+	}
+	return size;
+}
+
+/*
  * Writing cmd register causes STAT_BUSY to get set in status register.
  * When cmd completes, STAT_BUSY will be cleared.
  *
diff --git a/drivers/net/enic/enic_clsf.c b/drivers/net/enic/enic_clsf.c
index 487f80449..9e94afdf9 100644
--- a/drivers/net/enic/enic_clsf.c
+++ b/drivers/net/enic/enic_clsf.c
@@ -345,7 +345,7 @@ int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 		/* Delete the filter */
 		vnic_dev_classifier(enic->vdev, CLSF_DEL,
-			&key->fltr_id, NULL);
+			&key->fltr_id, NULL, NULL);
 		rte_free(key);
 		enic->fdir.nodes[pos] = NULL;
 		enic->fdir.stats.free++;
@@ -365,8 +365,10 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	u32 flowtype_supported;
 	u16 flex_bytes;
 	u16 queue;
+	struct filter_action_v2 action;
 
 	memset(&fltr, 0, sizeof(fltr));
+	memset(&action, 0, sizeof(action));
 	flowtype_supported = enic->fdir.types_mask
 			     & (1 << params->input.flow_type);
 
@@ -439,7 +441,7 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 			 * Delete the filter and add the modified one later
 			 */
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			enic->fdir.stats.free++;
 		}
 
@@ -451,8 +453,11 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 	enic->fdir.copy_fltr_fn(&fltr, &params->input,
 				&enic->rte_dev->data->dev_conf.fdir_conf.mask);
+	action.type = FILTER_ACTION_RQ_STEERING;
+	action.rq_idx = queue;
 
-	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr)) {
+	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr,
+	    &action)) {
 		key->fltr_id = queue;
 	} else {
 		dev_err(enic, "Add classifier entry failed\n");
@@ -462,7 +467,8 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	}
 
 	if (do_free)
-		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL);
+		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL,
+				    NULL);
 	else{
 		enic->fdir.stats.free--;
 		enic->fdir.stats.add++;
@@ -488,7 +494,7 @@ void enic_clsf_destroy(struct enic *enic)
 		key = enic->fdir.nodes[index];
 		if (key) {
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			rte_free(key);
 			enic->fdir.nodes[index] = NULL;
 		}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 2/7] net/enic: flow API skeleton
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
  2017-03-31  2:06   ` [PATCH v2 1/7] net/enic: bring NIC interface functions up to date John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 3/7] net/enic: flow API for NICs with advanced filters enabled John Daley
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Stub callbacks for the generic flow API and a new FLOW debug define.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 config/common_base             |   1 +
 drivers/net/enic/Makefile      |   1 +
 drivers/net/enic/enic.h        |   1 +
 drivers/net/enic/enic_ethdev.c |  21 +++++-
 drivers/net/enic/enic_flow.c   | 154 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 175 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

diff --git a/config/common_base b/config/common_base
index 80006650b..d4edb2033 100644
--- a/config/common_base
+++ b/config/common_base
@@ -249,6 +249,7 @@ CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
 #
 CONFIG_RTE_LIBRTE_ENIC_PMD=y
 CONFIG_RTE_LIBRTE_ENIC_DEBUG=n
+CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW=n
 
 #
 # Compile burst-oriented Netronome NFP PMD driver
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 2c7496dc5..db48ff2da 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_clsf.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_res.c
+SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_cq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_wq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_dev.c
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index e921de405..af1098b05 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -306,4 +306,5 @@ void copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  __rte_unused struct rte_eth_fdir_masks *masks);
 void copy_fltr_v2(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
+extern const struct rte_flow_ops enic_flow_ops;
 #endif /* _ENIC_H_ */
diff --git a/drivers/net/enic/enic_ethdev.c b/drivers/net/enic/enic_ethdev.c
index 2c2e29e97..79098520d 100644
--- a/drivers/net/enic/enic_ethdev.c
+++ b/drivers/net/enic/enic_ethdev.c
@@ -115,13 +115,28 @@ enicpmd_dev_filter_ctrl(struct rte_eth_dev *dev,
 		     enum rte_filter_op filter_op,
 		     void *arg)
 {
-	int ret = -EINVAL;
+	int ret = 0;
+
+	ENICPMD_FUNC_TRACE();
 
-	if (RTE_ETH_FILTER_FDIR == filter_type)
+	if (dev == NULL)
+		return -EINVAL;
+
+	switch (filter_type) {
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &enic_flow_ops;
+		break;
+	case RTE_ETH_FILTER_FDIR:
 		ret = enicpmd_fdir_ctrl_func(dev, filter_op, arg);
-	else
+		break;
+	default:
 		dev_warning(enic, "Filter type (%d) not supported",
 			filter_type);
+		ret = -EINVAL;
+		break;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
new file mode 100644
index 000000000..d25390f8a
--- /dev/null
+++ b/drivers/net/enic/enic_flow.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2008-2017 Cisco Systems, Inc.  All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
+ *
+ * Copyright (c) 2017, Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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.
+ *
+ */
+
+#include <errno.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_flow_driver.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+
+#include "enic_compat.h"
+#include "enic.h"
+#include "vnic_dev.h"
+#include "vnic_nic.h"
+
+#ifdef RTE_LIBRTE_ENIC_DEBUG_FLOW
+#define FLOW_TRACE() \
+	RTE_LOG(DEBUG, PMD, "%s()\n", __func__)
+#define FLOW_LOG(level, fmt, args...) \
+	RTE_LOG(level, PMD, fmt, ## args)
+#else
+#define FLOW_TRACE() do { } while (0)
+#define FLOW_LOG(level, fmt, args...) do { } while (0)
+#endif
+
+/*
+ * The following functions are callbacks for Generic flow API.
+ */
+
+/**
+ * Validate a flow supported by the NIC.
+ *
+ * @see rte_flow_validate()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
+		   const struct rte_flow_item pattern[],
+		   const struct rte_flow_action actions[],
+		   struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Create a flow supported by the NIC.
+ *
+ * @see rte_flow_create()
+ * @see rte_flow_ops
+ */
+static struct rte_flow *
+enic_flow_create(struct rte_eth_dev *dev,
+		 const struct rte_flow_attr *attrs,
+		 const struct rte_flow_item pattern[],
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return NULL;
+}
+
+/**
+ * Destroy a flow supported by the NIC.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  __rte_unused struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)flow;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flush all flows on the device.
+ *
+ * @see rte_flow_flush()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flow callback registration.
+ *
+ * @see rte_flow_ops
+ */
+const struct rte_flow_ops enic_flow_ops = {
+	.validate = enic_flow_validate,
+	.create = enic_flow_create,
+	.destroy = enic_flow_destroy,
+	.flush = enic_flow_flush,
+};
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 3/7] net/enic: flow API for NICs with advanced filters enabled
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
  2017-03-31  2:06   ` [PATCH v2 1/7] net/enic: bring NIC interface functions up to date John Daley
  2017-03-31  2:06   ` [PATCH v2 2/7] net/enic: flow API skeleton John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 4/7] net/enic: flow API for NICs with advanced filters disabled John Daley
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode enabled via the UCS management interface. This enables:
Attributes: ingress
Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
Actions: queue, mark, flag and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic.h      |  14 +-
 drivers/net/enic/enic_flow.c | 924 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/enic/enic_main.c |   3 +
 drivers/net/enic/enic_res.c  |  15 +
 drivers/net/enic/enic_rxtx.c |  16 +-
 5 files changed, 953 insertions(+), 19 deletions(-)

diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index af1098b05..41dadb0ba 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -80,6 +80,8 @@
 #define PCI_DEVICE_ID_CISCO_VIC_ENET         0x0043  /* ethernet vnic */
 #define PCI_DEVICE_ID_CISCO_VIC_ENET_VF      0x0071  /* enet SRIOV VF */
 
+/* Special Filter id for non-specific packet flagging. Don't change value */
+#define ENIC_MAGIC_FILTER_ID 0xffff
 
 #define ENICPMD_FDIR_MAX           64
 
@@ -111,6 +113,12 @@ struct enic_memzone_entry {
 	LIST_ENTRY(enic_memzone_entry) entries;
 };
 
+struct rte_flow {
+	LIST_ENTRY(rte_flow) next;
+	u16 enic_filter_id;
+	struct filter_v2 enic_filter;
+};
+
 /* Per-instance private data structure */
 struct enic {
 	struct enic *next;
@@ -135,7 +143,9 @@ struct enic {
 	int link_status;
 	u8 hw_ip_checksum;
 	u16 max_mtu;
-	u16 adv_filters;
+	u8 adv_filters;
+	u32 flow_filter_mode;
+	u8 filter_tags;
 
 	unsigned int flags;
 	unsigned int priv_flags;
@@ -170,6 +180,8 @@ struct enic {
 	rte_spinlock_t memzone_list_lock;
 	rte_spinlock_t mtu_lock;
 
+	LIST_HEAD(enic_flows, rte_flow) flows;
+	rte_spinlock_t flows_lock;
 };
 
 /* Get the CQ index from a Start of Packet(SOP) RQ index */
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index d25390f8a..e4c043665 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -55,6 +55,875 @@
 #define FLOW_LOG(level, fmt, args...) do { } while (0)
 #endif
 
+/** Info about how to copy items into enic filters. */
+struct enic_items {
+	/** Function for copying and validating an item. */
+	int (*copy_item)(const struct rte_flow_item *item,
+			 struct filter_v2 *enic_filter, u8 *inner_ofst);
+	/** List of valid previous items. */
+	const enum rte_flow_item_type * const prev_items;
+	/** True if it's OK for this item to be the first item. For some NIC
+	 * versions, it's invalid to start the stack above layer 3.
+	 */
+	const u8 valid_start_item;
+};
+
+/** Filtering capabilities for various NIC and firmware versions. */
+struct enic_filter_cap {
+	/** list of valid items and their handlers and attributes. */
+	const struct enic_items *item_info;
+};
+
+/* functions for copying flow actions into enic actions */
+typedef int (copy_action_fn)(const struct rte_flow_action actions[],
+			     struct filter_action_v2 *enic_action);
+
+/* functions for copying items into enic filters */
+typedef int(enic_copy_item_fn)(const struct rte_flow_item *item,
+			  struct filter_v2 *enic_filter, u8 *inner_ofst);
+
+/** Action capabilities for various NICs. */
+struct enic_action_cap {
+	/** list of valid actions */
+	const enum rte_flow_action_type *actions;
+	/** copy function for a particular NIC */
+	int (*copy_fn)(const struct rte_flow_action actions[],
+		       struct filter_action_v2 *enic_action);
+};
+
+/* Forward declarations */
+static enic_copy_item_fn enic_copy_item_eth_v2;
+static enic_copy_item_fn enic_copy_item_vlan_v2;
+static enic_copy_item_fn enic_copy_item_ipv4_v2;
+static enic_copy_item_fn enic_copy_item_ipv6_v2;
+static enic_copy_item_fn enic_copy_item_udp_v2;
+static enic_copy_item_fn enic_copy_item_tcp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v2;
+
+/** NICs with Advanced filters enabled */
+static const struct enic_items enic_items_v3[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/** Filtering capabilites indexed this NICs supported filter type. */
+static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_DPDK_1] = {
+		.item_info = enic_items_v3,
+	},
+};
+
+/** Supported actions for newer NICs */
+static const enum rte_flow_action_type enic_supported_actions_v2[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_MARK,
+	RTE_FLOW_ACTION_TYPE_FLAG,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
+/** Action capabilites indexed by NIC version information */
+static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_V2_ALL] = {
+		.actions = enic_supported_actions_v2,
+		.copy_fn = enic_copy_action_v2,
+	},
+};
+/**
+ * Copy ETH item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_eth_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	struct ether_hdr enic_spec;
+	struct ether_hdr enic_mask;
+	const struct rte_flow_item_eth *spec = item->spec;
+	const struct rte_flow_item_eth *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_eth_mask;
+
+	memcpy(enic_spec.d_addr.addr_bytes, spec->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_spec.s_addr.addr_bytes, spec->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+
+	memcpy(enic_mask.d_addr.addr_bytes, mask->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_mask.s_addr.addr_bytes, mask->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+	enic_spec.ether_type = spec->type;
+	enic_mask.ether_type = mask->type;
+
+	if (!*inner_ofst) {
+		/* outer header */
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].mask, &enic_mask,
+		       sizeof(struct ether_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].val, &enic_spec,
+		       sizeof(struct ether_hdr));
+	} else {
+		/* inner header */
+		if ((*inner_ofst + sizeof(struct ether_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		/* Offset into L5 where inner Ethernet header goes */
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       &enic_mask, sizeof(struct ether_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       &enic_spec, sizeof(struct ether_hdr));
+		*inner_ofst += sizeof(struct ether_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy VLAN item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_vlan_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vlan *spec = item->spec;
+	const struct rte_flow_item_vlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	/* Don't support filtering in tpid */
+	if (mask) {
+		if (mask->tpid != 0)
+			return ENOTSUP;
+	} else {
+		mask = &rte_flow_item_vlan_mask;
+		RTE_ASSERT(mask->tpid == 0);
+	}
+
+	if (!*inner_ofst) {
+		/* Outer header. Use the vlan mask/val fields */
+		gp->mask_vlan = mask->tci;
+		gp->val_vlan = spec->tci;
+	} else {
+		/* Inner header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct vlan_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct vlan_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct vlan_hdr));
+		*inner_ofst += sizeof(struct vlan_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv4 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv4 filtering.
+ */
+static int
+enic_copy_item_ipv4_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match IPv4 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV4;
+	gp->val_flags |= FILTER_GENERIC_1_IPV4;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+	       sizeof(struct ipv4_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+	       sizeof(struct ipv4_hdr));
+	return 0;
+}
+
+/**
+ * Copy IPv6 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv6 filtering.
+ */
+static int
+enic_copy_item_ipv6_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv6 *spec = item->spec;
+	const struct rte_flow_item_ipv6 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match IPv6 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV6;
+	gp->val_flags |= FILTER_GENERIC_1_IPV6;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv6_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+	       sizeof(struct ipv6_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+	       sizeof(struct ipv6_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner UDP filtering.
+ */
+static int
+enic_copy_item_udp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match UDP */
+	gp->mask_flags |= FILTER_GENERIC_1_UDP;
+	gp->val_flags |= FILTER_GENERIC_1_UDP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct udp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct udp_hdr));
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner TCP filtering.
+ */
+static int
+enic_copy_item_tcp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match TCP */
+	gp->mask_flags |= FILTER_GENERIC_1_TCP;
+	gp->val_flags |= FILTER_GENERIC_1_TCP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		return ENOTSUP;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct tcp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct tcp_hdr));
+	return 0;
+}
+
+/**
+ * Copy SCTP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner SCTP filtering.
+ */
+static int
+enic_copy_item_sctp_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_sctp *spec = item->spec;
+	const struct rte_flow_item_sctp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_sctp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct sctp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct sctp_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. VxLAN headers always start at the beginning of L5.
+ */
+static int
+enic_copy_item_vxlan_v2(const struct rte_flow_item *item,
+			struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vxlan *spec = item->spec;
+	const struct rte_flow_item_vxlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return EINVAL;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_vxlan_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].mask, mask,
+	       sizeof(struct vxlan_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].val, spec,
+	       sizeof(struct vxlan_hdr));
+
+	*inner_ofst = sizeof(struct vxlan_hdr);
+	return 0;
+}
+
+/**
+ * Return 1 if current item is valid on top of the previous one.
+ *
+ * @param prev_item[in]
+ *   The item before this one in the pattern or RTE_FLOW_ITEM_TYPE_END if this
+ *   is the first item.
+ * @param item_info[in]
+ *   Info about this item, like valid previous items.
+ * @param is_first[in]
+ *   True if this the first item in the pattern.
+ */
+static int
+item_stacking_valid(enum rte_flow_item_type prev_item,
+		    const struct enic_items *item_info, u8 is_first_item)
+{
+	enum rte_flow_item_type const *allowed_items = item_info->prev_items;
+
+	FLOW_TRACE();
+
+	for (; *allowed_items != RTE_FLOW_ITEM_TYPE_END; allowed_items++) {
+		if (prev_item == *allowed_items)
+			return 1;
+	}
+
+	/* This is the first item in the stack. Check if that's cool */
+	if (is_first_item && item_info->valid_start_item)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Build the intenal enic filter structure from the provided pattern. The
+ * pattern is validated as the items are copied.
+ *
+ * @param pattern[in]
+ * @param items_info[in]
+ *   Info about this NICs item support, like valid previous items.
+ * @param enic_filter[out]
+ *   NIC specfilc filters derived from the pattern.
+ * @param error[out]
+ */
+static int
+enic_copy_filter(const struct rte_flow_item pattern[],
+		 const struct enic_items *items_info,
+		 struct filter_v2 *enic_filter,
+		 struct rte_flow_error *error)
+{
+	int ret;
+	const struct rte_flow_item *item = pattern;
+	u8 inner_ofst = 0; /* If encapsulated, ofst into L5 */
+	enum rte_flow_item_type prev_item;
+	const struct enic_items *item_info;
+
+	enic_filter->type = FILTER_DPDK_1;
+	u8 is_first_item = 1;
+
+	FLOW_TRACE();
+
+	prev_item = 0;
+
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		/* Get info about how to validate and copy the item. If NULL
+		 * is returned the nic does not support the item.
+		 */
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+
+		item_info = &items_info[item->type];
+
+		/* check to see if item stacking is valid */
+		if (!item_stacking_valid(prev_item, item_info, is_first_item))
+			goto stacking_error;
+
+		ret = item_info->copy_item(item, enic_filter, &inner_ofst);
+		if (ret)
+			goto item_not_supported;
+		prev_item = item->type;
+		is_first_item = 0;
+	}
+	return 0;
+
+item_not_supported:
+	rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_ITEM,
+			   NULL, "enic type error");
+	return -rte_errno;
+
+stacking_error:
+	rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+			   item, "stacking error");
+	return -rte_errno;
+}
+/**
+ * Build the intenal version 2 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v2(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			enic_action->flags |= FILTER_ACTION_RQ_STEERING_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_MARK: {
+			const struct rte_flow_action_mark *mark =
+				(const struct rte_flow_action_mark *)
+				actions->conf;
+
+			/* ENIC_MAGIC_FILTER_ID is reserved and is the highest
+			 * in the range of allows mark ids.
+			 */
+			if (mark->id >= ENIC_MAGIC_FILTER_ID)
+				return EINVAL;
+			enic_action->filter_id = mark->id;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_FLAG: {
+			enic_action->filter_id = ENIC_MAGIC_FILTER_ID;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			continue;
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_V2;
+	return 0;
+}
+
+/** Check if the action is supported */
+static int
+enic_match_action(const struct rte_flow_action *action,
+		  const enum rte_flow_action_type *supported_actions)
+{
+	for (; *supported_actions != RTE_FLOW_ACTION_TYPE_END;
+	     supported_actions++) {
+		if (action->type == *supported_actions)
+			return 1;
+	}
+	return 0;
+}
+
+/** Get the NIC filter capabilties structure */
+static const struct enic_filter_cap *
+enic_get_filter_cap(struct enic *enic)
+{
+	/* FIXME: only support advanced filters for now */
+	if (enic->flow_filter_mode != FILTER_DPDK_1)
+		return (const struct enic_filter_cap *)NULL;
+
+	if (enic->flow_filter_mode)
+		return &enic_filter_cap[enic->flow_filter_mode];
+
+	return (const struct enic_filter_cap *)NULL;
+}
+
+/** Get the actions for this NIC version. */
+static const struct enic_action_cap *
+enic_get_action_cap(struct enic *enic)
+{
+	static const struct enic_action_cap *ea;
+
+	if (enic->filter_tags)
+		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	return ea;
+}
+/**
+ * Internal flow parse/validate function.
+ *
+ * @param dev[in]
+ *   This device pointer.
+ * @param pattern[in]
+ * @param actions[in]
+ * @param error[out]
+ * @param enic_filter[out]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[out]
+ *   Internal NIC action structure pointer.
+ */
+static int
+enic_flow_parse(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attrs,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct filter_v2 *enic_filter,
+		struct filter_action_v2 *enic_action)
+{
+	unsigned int ret = 0;
+	struct enic *enic = pmd_priv(dev);
+	const struct enic_filter_cap *enic_filter_cap;
+	const struct enic_action_cap *enic_action_cap;
+	const struct rte_flow_action *action;
+
+	FLOW_TRACE();
+
+	memset(enic_filter, 0, sizeof(*enic_filter));
+	memset(enic_action, 0, sizeof(*enic_action));
+
+	if (!pattern) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+				   NULL, "No pattern specified");
+		return -rte_errno;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				   NULL, "No action specified");
+		return -rte_errno;
+	}
+
+	if (attrs) {
+		if (attrs->group) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+					   NULL,
+					   "priority groups are not supported");
+			return -rte_errno;
+		} else if (attrs->priority) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					   NULL,
+					   "priorities are not supported");
+			return -rte_errno;
+		} else if (attrs->egress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
+					   NULL,
+					   "egress is not supported");
+			return -rte_errno;
+		} else if (!attrs->ingress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
+					   NULL,
+					   "only ingress is supported");
+			return -rte_errno;
+		}
+
+	} else {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ATTR,
+				   NULL, "No attribute specified");
+		return -rte_errno;
+	}
+
+	/* Verify Actions. */
+	enic_action_cap =  enic_get_action_cap(enic);
+	for (action = &actions[0]; action->type != RTE_FLOW_ACTION_TYPE_END;
+	     action++) {
+		if (action->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+		else if (!enic_match_action(action, enic_action_cap->actions))
+			break;
+	}
+	if (action->type != RTE_FLOW_ACTION_TYPE_END) {
+		rte_flow_error_set(error, EPERM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   action, "Invalid action.");
+		return -rte_errno;
+	}
+	ret = enic_action_cap->copy_fn(actions, enic_action);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Unsupported action.");
+		return -rte_errno;
+	}
+
+	/* Verify Flow items. If copying the filter from flow format to enic
+	 * format fails, the flow is not supported
+	 */
+	enic_filter_cap =  enic_get_filter_cap(enic);
+	if (enic_filter_cap == NULL) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Flow API not available");
+		return -rte_errno;
+	}
+	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
+				       enic_filter, error);
+	return ret;
+}
+
+/**
+ * Push filter/action to the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param enic_filter[in]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static struct rte_flow *
+enic_flow_add_filter(struct enic *enic, struct filter_v2 *enic_filter,
+		   struct filter_action_v2 *enic_action,
+		   struct rte_flow_error *error)
+{
+	struct rte_flow *flow;
+	int ret;
+	u16 entry;
+
+	FLOW_TRACE();
+
+	flow = rte_calloc(__func__, 1, sizeof(*flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "cannot allocate flow memory");
+		return NULL;
+	}
+
+	/* entry[in] is the queue id, entry[out] is the filter Id for delete */
+	entry = enic_action->rq_idx;
+	ret = vnic_dev_classifier(enic->vdev, CLSF_ADD, &entry, enic_filter,
+				  enic_action);
+	if (!ret) {
+		flow->enic_filter_id = entry;
+		flow->enic_filter = *enic_filter;
+	} else {
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier error");
+		rte_free(flow);
+		return NULL;
+	}
+	return flow;
+}
+
+/**
+ * Remove filter/action from the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param filter_id[in]
+ *   Id of NIC filter.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static int
+enic_flow_del_filter(struct enic *enic, u16 filter_id,
+		   struct rte_flow_error *error)
+{
+	int ret;
+
+	FLOW_TRACE();
+
+	ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL, NULL);
+	if (!ret)
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier failed");
+	return ret;
+}
+
 /*
  * The following functions are callbacks for Generic flow API.
  */
@@ -71,15 +940,15 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 		   const struct rte_flow_action actions[],
 		   struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	int ret;
 
 	FLOW_TRACE();
 
-	return 0;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
+			       &enic_filter, &enic_action);
+	return ret;
 }
 
 /**
@@ -95,15 +964,27 @@ enic_flow_create(struct rte_eth_dev *dev,
 		 const struct rte_flow_action actions[],
 		 struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	int ret;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
-	return NULL;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error, &enic_filter,
+			      &enic_action);
+	if (ret < 0)
+		return NULL;
+
+	rte_spinlock_lock(&enic->flows_lock);
+	flow = enic_flow_add_filter(enic, &enic_filter, &enic_action,
+				    error);
+	if (flow)
+		LIST_INSERT_HEAD(&enic->flows, flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
+
+	return flow;
 }
 
 /**
@@ -116,11 +997,14 @@ static int
 enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 		  __rte_unused struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+	enic_flow_del_filter(enic, flow->enic_filter_id, error);
+	LIST_REMOVE(flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
@@ -133,11 +1017,19 @@ enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 static int
 enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)error;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+
+	while (!LIST_EMPTY(&enic->flows)) {
+		flow = LIST_FIRST(&enic->flows);
+		enic_flow_del_filter(enic, flow->enic_filter_id, error);
+		LIST_REMOVE(flow, next);
+	}
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index 570b7b69a..4c515f601 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -1316,6 +1316,9 @@ static int enic_dev_init(struct enic *enic)
 
 	vnic_dev_set_reset_flag(enic->vdev, 0);
 
+	LIST_INIT(&enic->flows);
+	rte_spinlock_init(&enic->flows_lock);
+
 	/* set up link status checking */
 	vnic_dev_notify_set(enic->vdev, -1); /* No Intr for notify */
 
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c
index 867bd25c4..e4b80d49c 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/enic/enic_res.c
@@ -104,6 +104,21 @@ int enic_get_vnic_config(struct enic *enic)
 	dev_info(enic, "Advanced Filters %savailable\n", ((enic->adv_filters)
 		 ? "" : "not "));
 
+	err = vnic_dev_capable_filter_mode(enic->vdev, &enic->flow_filter_mode,
+					   &enic->filter_tags);
+	if (err) {
+		dev_err(enic_get_dev(enic),
+			"Error getting filter modes, %d\n", err);
+		return err;
+	}
+
+	dev_info(enic, "Flow api filter mode: %s, Filter tagging %savailable\n",
+		((enic->flow_filter_mode == FILTER_DPDK_1) ? "DPDK" :
+		((enic->flow_filter_mode == FILTER_USNIC_IP) ? "USNIC" :
+		((enic->flow_filter_mode == FILTER_IPV4_5TUPLE) ? "5TUPLE" :
+		"NONE"))),
+		((enic->filter_tags) ? "" : "not "));
+
 	c->wq_desc_count =
 		min_t(u32, ENIC_MAX_WQ_DESCS,
 		max_t(u32, ENIC_MIN_WQ_DESCS,
diff --git a/drivers/net/enic/enic_rxtx.c b/drivers/net/enic/enic_rxtx.c
index 343dabc64..5f59e3726 100644
--- a/drivers/net/enic/enic_rxtx.c
+++ b/drivers/net/enic/enic_rxtx.c
@@ -253,8 +253,20 @@ enic_cq_rx_to_pkt_flags(struct cq_desc *cqd, struct rte_mbuf *mbuf)
 	}
 	mbuf->vlan_tci = vlan_tci;
 
-	/* RSS flag */
-	if (enic_cq_rx_desc_rss_type(cqrd)) {
+	if ((cqd->type_color & CQ_DESC_TYPE_MASK) == CQ_DESC_TYPE_CLASSIFIER) {
+		struct cq_enet_rq_clsf_desc *clsf_cqd;
+		uint16_t filter_id;
+		clsf_cqd = (struct cq_enet_rq_clsf_desc *)cqd;
+		filter_id = clsf_cqd->filter_id;
+		if (filter_id) {
+			pkt_flags |= PKT_RX_FDIR;
+			if (filter_id != ENIC_MAGIC_FILTER_ID) {
+				mbuf->hash.fdir.hi = clsf_cqd->filter_id;
+				pkt_flags |= PKT_RX_FDIR_ID;
+			}
+		}
+	} else if (enic_cq_rx_desc_rss_type(cqrd)) {
+		/* RSS flag */
 		pkt_flags |= PKT_RX_RSS_HASH;
 		mbuf->hash.rss = enic_cq_rx_desc_rss_hash(cqrd);
 	}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 4/7] net/enic: flow API for NICs with advanced filters disabled
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
                     ` (2 preceding siblings ...)
  2017-03-31  2:06   ` [PATCH v2 3/7] net/enic: flow API for NICs with advanced filters enabled John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 5/7] net/enic: flow API for Legacy NICs John Daley
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode disabled via the UCS management interface. This allows:
Attributes: ingress
Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
Actions: queue and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

With advanced filters disabled, an IPv4 or IPv6 item must be specified
in the pattern.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 135 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 133 insertions(+), 2 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index e4c043665..6ef5daccf 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -101,8 +101,85 @@ static enic_copy_item_fn enic_copy_item_tcp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
+/**
+ * NICs have Advanced Filters capability but they are disabled. This means
+ * that layer 3 must be specified.
+ */
+static const struct enic_items enic_items_v2[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
 /** NICs with Advanced filters enabled */
 static const struct enic_items enic_items_v3[] = {
 	[RTE_FLOW_ITEM_TYPE_ETH] = {
@@ -178,11 +255,20 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_USNIC_IP] = {
+		.item_info = enic_items_v2,
+	},
 	[FILTER_DPDK_1] = {
 		.item_info = enic_items_v3,
 	},
 };
 
+/** Supported actions for older NICs */
+static const enum rte_flow_action_type enic_supported_actions_v1[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
 /** Supported actions for newer NICs */
 static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 	RTE_FLOW_ACTION_TYPE_QUEUE,
@@ -193,6 +279,10 @@ static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 
 /** Action capabilites indexed by NIC version information */
 static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_RQ_STEERING_FLAG] = {
+		.actions = enic_supported_actions_v1,
+		.copy_fn = enic_copy_action_v1,
+	},
 	[FILTER_ACTION_V2_ALL] = {
 		.actions = enic_supported_actions_v2,
 		.copy_fn = enic_copy_action_v2,
@@ -607,7 +697,6 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 	enum rte_flow_item_type prev_item;
 	const struct enic_items *item_info;
 
-	enic_filter->type = FILTER_DPDK_1;
 	u8 is_first_item = 1;
 
 	FLOW_TRACE();
@@ -645,6 +734,44 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 			   item, "stacking error");
 	return -rte_errno;
 }
+
+/**
+ * Build the intenal version 1 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v1(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		if (actions->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			break;
+		}
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_RQ_STEERING;
+	return 0;
+}
+
 /**
  * Build the intenal version 2 NIC action structure from the provided pattern.
  * The pattern is validated as the items are copied.
@@ -719,7 +846,8 @@ static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
 	/* FIXME: only support advanced filters for now */
-	if (enic->flow_filter_mode != FILTER_DPDK_1)
+	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
+	   (enic->flow_filter_mode != FILTER_USNIC_IP))
 		return (const struct enic_filter_cap *)NULL;
 
 	if (enic->flow_filter_mode)
@@ -736,6 +864,8 @@ enic_get_action_cap(struct enic *enic)
 
 	if (enic->filter_tags)
 		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	else
+		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
 /**
@@ -848,6 +978,7 @@ enic_flow_parse(struct rte_eth_dev *dev,
 			   NULL, "Flow API not available");
 		return -rte_errno;
 	}
+	enic_filter->type = enic->flow_filter_mode;
 	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
 				       enic_filter, error);
 	return ret;
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 5/7] net/enic: flow API for Legacy NICs
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
                     ` (3 preceding siblings ...)
  2017-03-31  2:06   ` [PATCH v2 4/7] net/enic: flow API for NICs with advanced filters disabled John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 6/7] net/enic: flow API debug John Daley
  2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

5-tuple exact Flow support for 1200 series adapters. This allows:
Attributes: ingress
Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
       addresses and ports and all must be specified).
Actions: queue and void
Selectors: 'is'

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 206 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 201 insertions(+), 5 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 6ef5daccf..5f8f2619a 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -92,6 +92,9 @@ struct enic_action_cap {
 };
 
 /* Forward declarations */
+static enic_copy_item_fn enic_copy_item_ipv4_v1;
+static enic_copy_item_fn enic_copy_item_udp_v1;
+static enic_copy_item_fn enic_copy_item_tcp_v1;
 static enic_copy_item_fn enic_copy_item_eth_v2;
 static enic_copy_item_fn enic_copy_item_vlan_v2;
 static enic_copy_item_fn enic_copy_item_ipv4_v2;
@@ -105,6 +108,36 @@ static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
 /**
+ * Legacy NICs or NICs with outdated firmware. Only 5-tuple perfect match
+ * is supported.
+ */
+static const struct enic_items enic_items_v1[] = {
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v1,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/**
  * NICs have Advanced Filters capability but they are disabled. This means
  * that layer 3 must be specified.
  */
@@ -255,6 +288,9 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_IPV4_5TUPLE] = {
+		.item_info = enic_items_v1,
+	},
 	[FILTER_USNIC_IP] = {
 		.item_info = enic_items_v2,
 	},
@@ -288,6 +324,171 @@ static const struct enic_action_cap enic_action_cap[] = {
 		.copy_fn = enic_copy_action_v2,
 	},
 };
+
+static int
+mask_exact_match(const u8 *supported, const u8 *supplied,
+		 unsigned int size)
+{
+	unsigned int i;
+	for (i = 0; i < size; i++) {
+		if (supported[i] != supplied[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Copy IPv4 item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_ipv4_v1(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct ipv4_hdr supported_mask = {
+		.src_addr = 0xffffffff,
+		.dst_addr = 0xffffffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	/* This is an exact match filter, both fields must be set */
+	if (!spec || !spec->hdr.src_addr || !spec->hdr.dst_addr) {
+		FLOW_LOG(ERR, "IPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "IPv4 exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_addr = spec->hdr.src_addr;
+	enic_5tup->dst_addr = spec->hdr.dst_addr;
+
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_udp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct udp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "UDP exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "UDP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_UDP;
+
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_tcp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct tcp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_tcp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "TCPIPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			     (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "TCP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_TCP;
+
+	return 0;
+}
+
 /**
  * Copy ETH item into version 2 NIC filter.
  *
@@ -845,11 +1046,6 @@ enic_match_action(const struct rte_flow_action *action,
 static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
-	/* FIXME: only support advanced filters for now */
-	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
-	   (enic->flow_filter_mode != FILTER_USNIC_IP))
-		return (const struct enic_filter_cap *)NULL;
-
 	if (enic->flow_filter_mode)
 		return &enic_filter_cap[enic->flow_filter_mode];
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 6/7] net/enic: flow API debug
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
                     ` (4 preceding siblings ...)
  2017-03-31  2:06   ` [PATCH v2 5/7] net/enic: flow API for Legacy NICs John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Added a debug function to print enic filters and actions when
rte_validate_flow is called. Compiled in CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW
is enabled and log level is INFO.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 5f8f2619a..f605f67fe 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -1064,6 +1064,142 @@ enic_get_action_cap(struct enic *enic)
 		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
+
+/* Debug function to dump internal NIC action structure. */
+static void
+enic_dump_actions(const struct filter_action_v2 *ea)
+{
+	if (ea->type == FILTER_ACTION_RQ_STEERING) {
+		FLOW_LOG(INFO, "Action(V1), queue: %u\n", ea->rq_idx);
+	} else if (ea->type == FILTER_ACTION_V2) {
+		FLOW_LOG(INFO, "Actions(V2)\n");
+		if (ea->flags & FILTER_ACTION_RQ_STEERING_FLAG)
+			FLOW_LOG(INFO, "\tqueue: %u\n",
+			       enic_sop_rq_idx_to_rte_idx(ea->rq_idx));
+		if (ea->flags & FILTER_ACTION_FILTER_ID_FLAG)
+			FLOW_LOG(INFO, "\tfilter_id: %u\n", ea->filter_id);
+	}
+}
+
+/* Debug function to dump internal NIC filter structure. */
+static void
+enic_dump_filter(const struct filter_v2 *filt)
+{
+	const struct filter_generic_1 *gp;
+	int i, j, mbyte;
+	char buf[128], *bp;
+	char ip4[16], ip6[16], udp[16], tcp[16], tcpudp[16], ip4csum[16];
+	char l4csum[16], ipfrag[16];
+
+	switch (filt->type) {
+	case FILTER_IPV4_5TUPLE:
+		FLOW_LOG(INFO, "FILTER_IPV4_5TUPLE\n");
+		break;
+	case FILTER_USNIC_IP:
+	case FILTER_DPDK_1:
+		/* FIXME: this should be a loop */
+		gp = &filt->u.generic_1;
+		FLOW_LOG(INFO, "Filter: vlan: 0x%04x, mask: 0x%04x\n",
+		       gp->val_vlan, gp->mask_vlan);
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV4)
+			sprintf(ip4, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip4(y)" : "ip4(n)");
+		else
+			sprintf(ip4, "%s ", "ip4(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV6)
+			sprintf(ip6, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip6(y)" : "ip6(n)");
+		else
+			sprintf(ip6, "%s ", "ip6(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_UDP)
+			sprintf(udp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_UDP)
+				 ? "udp(y)" : "udp(n)");
+		else
+			sprintf(udp, "%s ", "udp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP)
+			sprintf(tcp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP)
+				 ? "tcp(y)" : "tcp(n)");
+		else
+			sprintf(tcp, "%s ", "tcp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+			sprintf(tcpudp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+				 ? "tcpudp(y)" : "tcpudp(n)");
+		else
+			sprintf(tcpudp, "%s ", "tcpudp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IP4SUM_OK)
+			sprintf(ip4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IP4SUM_OK)
+				 ? "ip4csum(y)" : "ip4csum(n)");
+		else
+			sprintf(ip4csum, "%s ", "ip4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_L4SUM_OK)
+			sprintf(l4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_L4SUM_OK)
+				 ? "l4csum(y)" : "l4csum(n)");
+		else
+			sprintf(l4csum, "%s ", "l4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPFRAG)
+			sprintf(ipfrag, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPFRAG)
+				 ? "ipfrag(y)" : "ipfrag(n)");
+		else
+			sprintf(ipfrag, "%s ", "ipfrag(x)");
+		FLOW_LOG(INFO, "\tFlags: %s%s%s%s%s%s%s%s\n", ip4, ip6, udp,
+			 tcp, tcpudp, ip4csum, l4csum, ipfrag);
+
+		for (i = 0; i < FILTER_GENERIC_1_NUM_LAYERS; i++) {
+			mbyte = FILTER_GENERIC_1_KEY_LEN - 1;
+			while (mbyte && !gp->layer[i].mask[mbyte])
+				mbyte--;
+			if (mbyte == 0)
+				continue;
+
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].mask[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u mask: %s\n", i + 2, buf);
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].val[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u  val: %s\n", i + 2, buf);
+		}
+		break;
+	default:
+		FLOW_LOG(INFO, "FILTER UNKNOWN\n");
+		break;
+	}
+}
+
+/* Debug function to dump internal NIC flow structures. */
+static void
+enic_dump_flow(const struct filter_action_v2 *ea, const struct filter_v2 *filt)
+{
+	enic_dump_filter(filt);
+	enic_dump_actions(ea);
+}
+
+
 /**
  * Internal flow parse/validate function.
  *
@@ -1275,6 +1411,8 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 
 	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
 			       &enic_filter, &enic_action);
+	if (!ret)
+		enic_dump_flow(&enic_action, &enic_filter);
 	return ret;
 }
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v2 7/7] net/enic: flow API documentation
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
                     ` (5 preceding siblings ...)
  2017-03-31  2:06   ` [PATCH v2 6/7] net/enic: flow API debug John Daley
@ 2017-03-31  2:06   ` John Daley
  2017-04-02 15:18     ` Mcnamara, John
                       ` (2 more replies)
  6 siblings, 3 replies; 41+ messages in thread
From: John Daley @ 2017-03-31  2:06 UTC (permalink / raw)
  To: ferruh.yigit, john.mcnamara; +Cc: dev, John Daley

Update enic NIC guide, release notes and add flow API to the
supported features list.

Signed-off-by: John Daley <johndale@cisco.com>
---
 doc/guides/nics/enic.rst               | 44 ++++++++++++++++++++++++++++++++++
 doc/guides/nics/features/enic.ini      |  1 +
 doc/guides/rel_notes/release_17_05.rst |  6 +++++
 3 files changed, 51 insertions(+)

diff --git a/doc/guides/nics/enic.rst b/doc/guides/nics/enic.rst
index c535b589c..a71add3ab 100644
--- a/doc/guides/nics/enic.rst
+++ b/doc/guides/nics/enic.rst
@@ -140,6 +140,43 @@ Masking of these feilds for partial match is also supported.
 Without advanced filter support, the flow director is limited to IPv4
 perfect filtering of the 5-tuple with no masking of fields supported.
 
+.. _enic-genic-flow-api:
+
+Generic Flow API support
+------------------------
+
+Generic Flow API is supported. The baseline support is:
+
+- **1200 series VICs**
+
+5-tuple exact Flow support for 1200 series adapters. This allows:
+
+  - Attributes: ingress
+  - Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
+    addresses and ports and all must be specified).
+  - Actions: queue and void
+  - Selectors: 'is'
+
+- **1300 series VICS with Advanced filters disabled**
+
+With advanced filters disabled, an IPv4 or IPv6 item must be specified
+in the pattern.
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
+  - Actions: queue and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+
+- **1300 series VICS with Advanced filters enabled**
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth (full hdr masking)
+  - Actions: queue, mark, flag and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+
+More features may be added in future firmware and new versions of the VIC.
+Please refer to the release notes.
+
 Limitations
 -----------
 
@@ -169,6 +206,13 @@ Limitations
 - Flow director features are not supported on generation 1 Cisco VIC adapters
   (M81KR and P81E)
 
+- The number of filters that can be specified with the Generic Flow API is
+  dependent on how many header fields are being masked. Use 'flow create' in
+  a loop to determine how many filters your VIC will support (not more than
+  1000 for 1300 series VICs. Filter are checked for matching in the order they
+  were added. Since there currently is no grouping or priority support,
+  'catch-all' filters should be added last.
+
 How to build the suite?
 -----------------------
 The build instructions for the DPDK suite should be followed. By default
diff --git a/doc/guides/nics/features/enic.ini b/doc/guides/nics/features/enic.ini
index 51b330fa8..f1a877043 100644
--- a/doc/guides/nics/features/enic.ini
+++ b/doc/guides/nics/features/enic.ini
@@ -19,6 +19,7 @@ VLAN filter          = Y
 CRC offload          = Y
 VLAN offload         = Y
 Flow director        = Y
+Flow API             = Y
 L3 checksum offload  = Y
 L4 checksum offload  = Y
 Packet type parsing  = Y
diff --git a/doc/guides/rel_notes/release_17_05.rst b/doc/guides/rel_notes/release_17_05.rst
index 26ae57f44..73b66eb17 100644
--- a/doc/guides/rel_notes/release_17_05.rst
+++ b/doc/guides/rel_notes/release_17_05.rst
@@ -98,6 +98,12 @@ New Features
   Rx queues can be armed with an interrupt which will trigger on the
   next packet arrival.
 
+* **Added Generic Flow API support to enic.**
+
+  Generic flow API support for Ethernet, VLAN, IPv4, IPv6, UDP, TCP, VxLAN and
+  inner Ethernet pattern items with QUEUE, MARK and FLAG action for ingress
+  traffic.
+
 * **Updated the sfc_efx driver.**
 
   * Generic flow API support for Ethernet, VLAN, IPv4, IPv6, UDP and TCP
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/6] net/enic: bring NIC interface functions up to date
  2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
                   ` (5 preceding siblings ...)
  2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
@ 2017-03-31 10:12 ` Ferruh Yigit
  6 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-03-31 10:12 UTC (permalink / raw)
  To: John Daley; +Cc: dev, adrien.mazarguil

On 3/30/2017 10:28 PM, John Daley wrote:
> Update the base functions for the Cisco VIC. These files are mostly
> common with other VIC drivers so are left alone is as much as possilbe.
> Includes in a new filter/action interface which is needed for Generic
> Flow API PMD support. Update FDIR code to use the new interface.
> 
> Signed-off-by: John Daley <johndale@cisco.com>

Hi John,

Patchset is a little late for this release (17.05), and will be
considered for next release (17.08).

Thanks,
ferruh

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v2 7/7] net/enic: flow API documentation
  2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
@ 2017-04-02 15:18     ` Mcnamara, John
  2017-05-12 12:11     ` Ferruh Yigit
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
  2 siblings, 0 replies; 41+ messages in thread
From: Mcnamara, John @ 2017-04-02 15:18 UTC (permalink / raw)
  To: John Daley, Yigit, Ferruh; +Cc: dev



> -----Original Message-----
> From: John Daley [mailto:johndale@cisco.com]
> Sent: Friday, March 31, 2017 3:06 AM
> To: Yigit, Ferruh <ferruh.yigit@intel.com>; Mcnamara, John
> <john.mcnamara@intel.com>
> Cc: dev@dpdk.org; John Daley <johndale@cisco.com>
> Subject: [PATCH v2 7/7] net/enic: flow API documentation
> 
> Update enic NIC guide, release notes and add flow API to the supported
> features list.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> ---
>  doc/guides/nics/enic.rst               | 44
> ++++++++++++++++++++++++++++++++++
>  doc/guides/nics/features/enic.ini      |  1 +
>  doc/guides/rel_notes/release_17_05.rst |  6 +++++
>  3 files changed, 51 insertions(+)
> 
> diff --git a/doc/guides/nics/enic.rst b/doc/guides/nics/enic.rst index
> c535b589c..a71add3ab 100644
> --- a/doc/guides/nics/enic.rst
> +++ b/doc/guides/nics/enic.rst
> @@ -140,6 +140,43 @@ Masking of these feilds for partial match is also
> supported.
>  Without advanced filter support, the flow director is limited to IPv4
> perfect filtering of the 5-tuple with no masking of fields supported.
> 
> +.. _enic-genic-flow-api:
> +
> +Generic Flow API support
> +------------------------
> +
> +Generic Flow API is supported. The baseline support is:
> +
> +- **1200 series VICs**

If you wanted you could make the different series into sub headings.
However, that is up to you. The docs are also okay as is.

Acked-by: John McNamara <john.mcnamara@intel.com>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v2 7/7] net/enic: flow API documentation
  2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
  2017-04-02 15:18     ` Mcnamara, John
@ 2017-05-12 12:11     ` Ferruh Yigit
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
  2 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-12 12:11 UTC (permalink / raw)
  To: John Daley, john.mcnamara; +Cc: dev

On 3/31/2017 3:06 AM, John Daley wrote:
> Update enic NIC guide, release notes and add flow API to the
> supported features list.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> ---
>  doc/guides/nics/enic.rst               | 44 ++++++++++++++++++++++++++++++++++
>  doc/guides/nics/features/enic.ini      |  1 +
>  doc/guides/rel_notes/release_17_05.rst |  6 +++++

Patch needs to be rebased on top of latest next-net, to switch release
file 17.08, also enic.rst merge conflicts.

Thanks,
ferruh

>  3 files changed, 51 insertions(+)
<...>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v3 0/6] enic flow api support
  2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
  2017-04-02 15:18     ` Mcnamara, John
  2017-05-12 12:11     ` Ferruh Yigit
@ 2017-05-17  3:03     ` John Daley
  2017-05-17  3:03       ` [PATCH v3 1/6] net/enic: flow API skeleton John Daley
                         ` (6 more replies)
  2 siblings, 7 replies; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

This V3 is rebased on dpdk-next-net and retargeted at 17.08.  Also,
inner ipv4, ipv6, udp, tcp support added for 1300 series VICs.

thank you,
johnd

John Daley (6):
  net/enic: flow API skeleton
  net/enic: flow API for NICs with advanced filters enabled
  net/enic: flow API for NICs with advanced filters disabled
  net/enic: flow API for Legacy NICs
  net/enic: flow API debug
  net/enic: flow API documentation

 config/common_base                     |    1 +
 doc/guides/nics/enic.rst               |   51 ++
 doc/guides/nics/features/enic.ini      |    1 +
 doc/guides/rel_notes/release_17_08.rst |    6 +
 drivers/net/enic/Makefile              |    1 +
 drivers/net/enic/enic.h                |   15 +-
 drivers/net/enic/enic_ethdev.c         |   21 +-
 drivers/net/enic/enic_flow.c           | 1547 ++++++++++++++++++++++++++++++++
 drivers/net/enic/enic_main.c           |    3 +
 drivers/net/enic/enic_res.c            |   15 +
 drivers/net/enic/enic_rxtx.c           |   16 +-
 11 files changed, 1671 insertions(+), 6 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

-- 
2.12.0

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v3 1/6] net/enic: flow API skeleton
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17 11:12         ` Ferruh Yigit
  2017-05-17  3:03       ` [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled John Daley
                         ` (5 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Stub callbacks for the generic flow API and a new FLOW debug define.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 config/common_base             |   1 +
 drivers/net/enic/Makefile      |   1 +
 drivers/net/enic/enic.h        |   1 +
 drivers/net/enic/enic_ethdev.c |  21 +++++-
 drivers/net/enic/enic_flow.c   | 154 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 175 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

diff --git a/config/common_base b/config/common_base
index 8907bea36..67ef2ece0 100644
--- a/config/common_base
+++ b/config/common_base
@@ -254,6 +254,7 @@ CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
 #
 CONFIG_RTE_LIBRTE_ENIC_PMD=y
 CONFIG_RTE_LIBRTE_ENIC_DEBUG=n
+CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW=n
 
 #
 # Compile burst-oriented Netronome NFP PMD driver
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 2c7496dc5..db48ff2da 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_clsf.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_res.c
+SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_cq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_wq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_dev.c
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index 2358a7f6f..9647ca21f 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -306,4 +306,5 @@ void copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
 void copy_fltr_v2(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
+extern const struct rte_flow_ops enic_flow_ops;
 #endif /* _ENIC_H_ */
diff --git a/drivers/net/enic/enic_ethdev.c b/drivers/net/enic/enic_ethdev.c
index 8e16a71b7..4e8a0d9e0 100644
--- a/drivers/net/enic/enic_ethdev.c
+++ b/drivers/net/enic/enic_ethdev.c
@@ -116,13 +116,28 @@ enicpmd_dev_filter_ctrl(struct rte_eth_dev *dev,
 		     enum rte_filter_op filter_op,
 		     void *arg)
 {
-	int ret = -EINVAL;
+	int ret = 0;
+
+	ENICPMD_FUNC_TRACE();
 
-	if (RTE_ETH_FILTER_FDIR == filter_type)
+	if (dev == NULL)
+		return -EINVAL;
+
+	switch (filter_type) {
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &enic_flow_ops;
+		break;
+	case RTE_ETH_FILTER_FDIR:
 		ret = enicpmd_fdir_ctrl_func(dev, filter_op, arg);
-	else
+		break;
+	default:
 		dev_warning(enic, "Filter type (%d) not supported",
 			filter_type);
+		ret = -EINVAL;
+		break;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
new file mode 100644
index 000000000..d25390f8a
--- /dev/null
+++ b/drivers/net/enic/enic_flow.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2008-2017 Cisco Systems, Inc.  All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
+ *
+ * Copyright (c) 2017, Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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.
+ *
+ */
+
+#include <errno.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_flow_driver.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+
+#include "enic_compat.h"
+#include "enic.h"
+#include "vnic_dev.h"
+#include "vnic_nic.h"
+
+#ifdef RTE_LIBRTE_ENIC_DEBUG_FLOW
+#define FLOW_TRACE() \
+	RTE_LOG(DEBUG, PMD, "%s()\n", __func__)
+#define FLOW_LOG(level, fmt, args...) \
+	RTE_LOG(level, PMD, fmt, ## args)
+#else
+#define FLOW_TRACE() do { } while (0)
+#define FLOW_LOG(level, fmt, args...) do { } while (0)
+#endif
+
+/*
+ * The following functions are callbacks for Generic flow API.
+ */
+
+/**
+ * Validate a flow supported by the NIC.
+ *
+ * @see rte_flow_validate()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
+		   const struct rte_flow_item pattern[],
+		   const struct rte_flow_action actions[],
+		   struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Create a flow supported by the NIC.
+ *
+ * @see rte_flow_create()
+ * @see rte_flow_ops
+ */
+static struct rte_flow *
+enic_flow_create(struct rte_eth_dev *dev,
+		 const struct rte_flow_attr *attrs,
+		 const struct rte_flow_item pattern[],
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return NULL;
+}
+
+/**
+ * Destroy a flow supported by the NIC.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  __rte_unused struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)flow;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flush all flows on the device.
+ *
+ * @see rte_flow_flush()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flow callback registration.
+ *
+ * @see rte_flow_ops
+ */
+const struct rte_flow_ops enic_flow_ops = {
+	.validate = enic_flow_validate,
+	.create = enic_flow_create,
+	.destroy = enic_flow_destroy,
+	.flush = enic_flow_flush,
+};
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
  2017-05-17  3:03       ` [PATCH v3 1/6] net/enic: flow API skeleton John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17 11:12         ` Ferruh Yigit
  2017-05-17  3:03       ` [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled John Daley
                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode enabled via the UCS management interface. This enables:
Attributes: ingress
Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
       ipv6, udp, tcp.
Actions: queue, mark, flag and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic.h      |  14 +-
 drivers/net/enic/enic_flow.c | 960 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/enic/enic_main.c |   3 +
 drivers/net/enic/enic_res.c  |  15 +
 drivers/net/enic/enic_rxtx.c |  16 +-
 5 files changed, 989 insertions(+), 19 deletions(-)

diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index 9647ca21f..e28f22352 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -80,6 +80,8 @@
 #define PCI_DEVICE_ID_CISCO_VIC_ENET         0x0043  /* ethernet vnic */
 #define PCI_DEVICE_ID_CISCO_VIC_ENET_VF      0x0071  /* enet SRIOV VF */
 
+/* Special Filter id for non-specific packet flagging. Don't change value */
+#define ENIC_MAGIC_FILTER_ID 0xffff
 
 #define ENICPMD_FDIR_MAX           64
 
@@ -111,6 +113,12 @@ struct enic_memzone_entry {
 	LIST_ENTRY(enic_memzone_entry) entries;
 };
 
+struct rte_flow {
+	LIST_ENTRY(rte_flow) next;
+	u16 enic_filter_id;
+	struct filter_v2 enic_filter;
+};
+
 /* Per-instance private data structure */
 struct enic {
 	struct enic *next;
@@ -135,7 +143,9 @@ struct enic {
 	int link_status;
 	u8 hw_ip_checksum;
 	u16 max_mtu;
-	u16 adv_filters;
+	u8 adv_filters;
+	u32 flow_filter_mode;
+	u8 filter_tags;
 
 	unsigned int flags;
 	unsigned int priv_flags;
@@ -170,6 +180,8 @@ struct enic {
 	rte_spinlock_t memzone_list_lock;
 	rte_spinlock_t mtu_lock;
 
+	LIST_HEAD(enic_flows, rte_flow) flows;
+	rte_spinlock_t flows_lock;
 };
 
 /* Get the CQ index from a Start of Packet(SOP) RQ index */
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index d25390f8a..2ea8c9370 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -55,6 +55,911 @@
 #define FLOW_LOG(level, fmt, args...) do { } while (0)
 #endif
 
+/** Info about how to copy items into enic filters. */
+struct enic_items {
+	/** Function for copying and validating an item. */
+	int (*copy_item)(const struct rte_flow_item *item,
+			 struct filter_v2 *enic_filter, u8 *inner_ofst);
+	/** List of valid previous items. */
+	const enum rte_flow_item_type * const prev_items;
+	/** True if it's OK for this item to be the first item. For some NIC
+	 * versions, it's invalid to start the stack above layer 3.
+	 */
+	const u8 valid_start_item;
+};
+
+/** Filtering capabilities for various NIC and firmware versions. */
+struct enic_filter_cap {
+	/** list of valid items and their handlers and attributes. */
+	const struct enic_items *item_info;
+};
+
+/* functions for copying flow actions into enic actions */
+typedef int (copy_action_fn)(const struct rte_flow_action actions[],
+			     struct filter_action_v2 *enic_action);
+
+/* functions for copying items into enic filters */
+typedef int(enic_copy_item_fn)(const struct rte_flow_item *item,
+			  struct filter_v2 *enic_filter, u8 *inner_ofst);
+
+/** Action capabilities for various NICs. */
+struct enic_action_cap {
+	/** list of valid actions */
+	const enum rte_flow_action_type *actions;
+	/** copy function for a particular NIC */
+	int (*copy_fn)(const struct rte_flow_action actions[],
+		       struct filter_action_v2 *enic_action);
+};
+
+/* Forward declarations */
+static enic_copy_item_fn enic_copy_item_eth_v2;
+static enic_copy_item_fn enic_copy_item_vlan_v2;
+static enic_copy_item_fn enic_copy_item_ipv4_v2;
+static enic_copy_item_fn enic_copy_item_ipv6_v2;
+static enic_copy_item_fn enic_copy_item_udp_v2;
+static enic_copy_item_fn enic_copy_item_tcp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v2;
+
+/** NICs with Advanced filters enabled */
+static const struct enic_items enic_items_v3[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/** Filtering capabilites indexed this NICs supported filter type. */
+static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_DPDK_1] = {
+		.item_info = enic_items_v3,
+	},
+};
+
+/** Supported actions for newer NICs */
+static const enum rte_flow_action_type enic_supported_actions_v2[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_MARK,
+	RTE_FLOW_ACTION_TYPE_FLAG,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
+/** Action capabilites indexed by NIC version information */
+static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_V2_ALL] = {
+		.actions = enic_supported_actions_v2,
+		.copy_fn = enic_copy_action_v2,
+	},
+};
+/**
+ * Copy ETH item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_eth_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	struct ether_hdr enic_spec;
+	struct ether_hdr enic_mask;
+	const struct rte_flow_item_eth *spec = item->spec;
+	const struct rte_flow_item_eth *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_eth_mask;
+
+	memcpy(enic_spec.d_addr.addr_bytes, spec->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_spec.s_addr.addr_bytes, spec->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+
+	memcpy(enic_mask.d_addr.addr_bytes, mask->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_mask.s_addr.addr_bytes, mask->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+	enic_spec.ether_type = spec->type;
+	enic_mask.ether_type = mask->type;
+
+	if (*inner_ofst == 0) {
+		/* outer header */
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].mask, &enic_mask,
+		       sizeof(struct ether_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].val, &enic_spec,
+		       sizeof(struct ether_hdr));
+	} else {
+		/* inner header */
+		if ((*inner_ofst + sizeof(struct ether_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		/* Offset into L5 where inner Ethernet header goes */
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       &enic_mask, sizeof(struct ether_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       &enic_spec, sizeof(struct ether_hdr));
+		*inner_ofst += sizeof(struct ether_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy VLAN item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_vlan_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vlan *spec = item->spec;
+	const struct rte_flow_item_vlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	/* Don't support filtering in tpid */
+	if (mask) {
+		if (mask->tpid != 0)
+			return ENOTSUP;
+	} else {
+		mask = &rte_flow_item_vlan_mask;
+		RTE_ASSERT(mask->tpid == 0);
+	}
+
+	if (*inner_ofst == 0) {
+		/* Outer header. Use the vlan mask/val fields */
+		gp->mask_vlan = mask->tci;
+		gp->val_vlan = spec->tci;
+	} else {
+		/* Inner header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct vlan_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct vlan_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct vlan_hdr));
+		*inner_ofst += sizeof(struct vlan_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv4 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv4 filtering.
+ */
+static int
+enic_copy_item_ipv4_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst == 0) {
+		/* Match IPv4 */
+		gp->mask_flags |= FILTER_GENERIC_1_IPV4;
+		gp->val_flags |= FILTER_GENERIC_1_IPV4;
+
+		/* Match all if no spec */
+		if (!spec)
+			return 0;
+
+		if (!mask)
+			mask = &rte_flow_item_ipv4_mask;
+
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+		       sizeof(struct ipv4_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+		       sizeof(struct ipv4_hdr));
+	} else {
+		/* Inner IPv4 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct ipv4_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct ipv4_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct ipv4_hdr));
+		*inner_ofst += sizeof(struct ipv4_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv6 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv6 filtering.
+ */
+static int
+enic_copy_item_ipv6_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv6 *spec = item->spec;
+	const struct rte_flow_item_ipv6 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match IPv6 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV6;
+	gp->val_flags |= FILTER_GENERIC_1_IPV6;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv6_mask;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+		       sizeof(struct ipv6_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+		       sizeof(struct ipv6_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct ipv6_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct ipv6_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct ipv6_hdr));
+		*inner_ofst += sizeof(struct ipv6_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner UDP filtering.
+ */
+static int
+enic_copy_item_udp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match UDP */
+	gp->mask_flags |= FILTER_GENERIC_1_UDP;
+	gp->val_flags |= FILTER_GENERIC_1_UDP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+		       sizeof(struct udp_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+		       sizeof(struct udp_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct udp_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct udp_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct udp_hdr));
+		*inner_ofst += sizeof(struct udp_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner TCP filtering.
+ */
+static int
+enic_copy_item_tcp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match TCP */
+	gp->mask_flags |= FILTER_GENERIC_1_TCP;
+	gp->val_flags |= FILTER_GENERIC_1_TCP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		return ENOTSUP;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+		       sizeof(struct tcp_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+		       sizeof(struct tcp_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct tcp_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct tcp_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct tcp_hdr));
+		*inner_ofst += sizeof(struct tcp_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy SCTP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner SCTP filtering.
+ */
+static int
+enic_copy_item_sctp_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_sctp *spec = item->spec;
+	const struct rte_flow_item_sctp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_sctp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct sctp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct sctp_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. VxLAN headers always start at the beginning of L5.
+ */
+static int
+enic_copy_item_vxlan_v2(const struct rte_flow_item *item,
+			struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vxlan *spec = item->spec;
+	const struct rte_flow_item_vxlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return EINVAL;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_vxlan_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].mask, mask,
+	       sizeof(struct vxlan_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].val, spec,
+	       sizeof(struct vxlan_hdr));
+
+	*inner_ofst = sizeof(struct vxlan_hdr);
+	return 0;
+}
+
+/**
+ * Return 1 if current item is valid on top of the previous one.
+ *
+ * @param prev_item[in]
+ *   The item before this one in the pattern or RTE_FLOW_ITEM_TYPE_END if this
+ *   is the first item.
+ * @param item_info[in]
+ *   Info about this item, like valid previous items.
+ * @param is_first[in]
+ *   True if this the first item in the pattern.
+ */
+static int
+item_stacking_valid(enum rte_flow_item_type prev_item,
+		    const struct enic_items *item_info, u8 is_first_item)
+{
+	enum rte_flow_item_type const *allowed_items = item_info->prev_items;
+
+	FLOW_TRACE();
+
+	for (; *allowed_items != RTE_FLOW_ITEM_TYPE_END; allowed_items++) {
+		if (prev_item == *allowed_items)
+			return 1;
+	}
+
+	/* This is the first item in the stack. Check if that's cool */
+	if (is_first_item && item_info->valid_start_item)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Build the intenal enic filter structure from the provided pattern. The
+ * pattern is validated as the items are copied.
+ *
+ * @param pattern[in]
+ * @param items_info[in]
+ *   Info about this NICs item support, like valid previous items.
+ * @param enic_filter[out]
+ *   NIC specfilc filters derived from the pattern.
+ * @param error[out]
+ */
+static int
+enic_copy_filter(const struct rte_flow_item pattern[],
+		 const struct enic_items *items_info,
+		 struct filter_v2 *enic_filter,
+		 struct rte_flow_error *error)
+{
+	int ret;
+	const struct rte_flow_item *item = pattern;
+	u8 inner_ofst = 0; /* If encapsulated, ofst into L5 */
+	enum rte_flow_item_type prev_item;
+	const struct enic_items *item_info;
+
+	enic_filter->type = FILTER_DPDK_1;
+	u8 is_first_item = 1;
+
+	FLOW_TRACE();
+
+	prev_item = 0;
+
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		/* Get info about how to validate and copy the item. If NULL
+		 * is returned the nic does not support the item.
+		 */
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+
+		item_info = &items_info[item->type];
+
+		/* check to see if item stacking is valid */
+		if (!item_stacking_valid(prev_item, item_info, is_first_item))
+			goto stacking_error;
+
+		ret = item_info->copy_item(item, enic_filter, &inner_ofst);
+		if (ret)
+			goto item_not_supported;
+		prev_item = item->type;
+		is_first_item = 0;
+	}
+	return 0;
+
+item_not_supported:
+	rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_ITEM,
+			   NULL, "enic type error");
+	return -rte_errno;
+
+stacking_error:
+	rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+			   item, "stacking error");
+	return -rte_errno;
+}
+/**
+ * Build the intenal version 2 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v2(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			enic_action->flags |= FILTER_ACTION_RQ_STEERING_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_MARK: {
+			const struct rte_flow_action_mark *mark =
+				(const struct rte_flow_action_mark *)
+				actions->conf;
+
+			/* ENIC_MAGIC_FILTER_ID is reserved and is the highest
+			 * in the range of allows mark ids.
+			 */
+			if (mark->id >= ENIC_MAGIC_FILTER_ID)
+				return EINVAL;
+			enic_action->filter_id = mark->id;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_FLAG: {
+			enic_action->filter_id = ENIC_MAGIC_FILTER_ID;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			continue;
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_V2;
+	return 0;
+}
+
+/** Check if the action is supported */
+static int
+enic_match_action(const struct rte_flow_action *action,
+		  const enum rte_flow_action_type *supported_actions)
+{
+	for (; *supported_actions != RTE_FLOW_ACTION_TYPE_END;
+	     supported_actions++) {
+		if (action->type == *supported_actions)
+			return 1;
+	}
+	return 0;
+}
+
+/** Get the NIC filter capabilties structure */
+static const struct enic_filter_cap *
+enic_get_filter_cap(struct enic *enic)
+{
+	/* FIXME: only support advanced filters for now */
+	if (enic->flow_filter_mode != FILTER_DPDK_1)
+		return (const struct enic_filter_cap *)NULL;
+
+	if (enic->flow_filter_mode)
+		return &enic_filter_cap[enic->flow_filter_mode];
+
+	return (const struct enic_filter_cap *)NULL;
+}
+
+/** Get the actions for this NIC version. */
+static const struct enic_action_cap *
+enic_get_action_cap(struct enic *enic)
+{
+	static const struct enic_action_cap *ea;
+
+	if (enic->filter_tags)
+		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	return ea;
+}
+/**
+ * Internal flow parse/validate function.
+ *
+ * @param dev[in]
+ *   This device pointer.
+ * @param pattern[in]
+ * @param actions[in]
+ * @param error[out]
+ * @param enic_filter[out]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[out]
+ *   Internal NIC action structure pointer.
+ */
+static int
+enic_flow_parse(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attrs,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct filter_v2 *enic_filter,
+		struct filter_action_v2 *enic_action)
+{
+	unsigned int ret = 0;
+	struct enic *enic = pmd_priv(dev);
+	const struct enic_filter_cap *enic_filter_cap;
+	const struct enic_action_cap *enic_action_cap;
+	const struct rte_flow_action *action;
+
+	FLOW_TRACE();
+
+	memset(enic_filter, 0, sizeof(*enic_filter));
+	memset(enic_action, 0, sizeof(*enic_action));
+
+	if (!pattern) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+				   NULL, "No pattern specified");
+		return -rte_errno;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				   NULL, "No action specified");
+		return -rte_errno;
+	}
+
+	if (attrs) {
+		if (attrs->group) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+					   NULL,
+					   "priority groups are not supported");
+			return -rte_errno;
+		} else if (attrs->priority) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					   NULL,
+					   "priorities are not supported");
+			return -rte_errno;
+		} else if (attrs->egress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
+					   NULL,
+					   "egress is not supported");
+			return -rte_errno;
+		} else if (!attrs->ingress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
+					   NULL,
+					   "only ingress is supported");
+			return -rte_errno;
+		}
+
+	} else {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ATTR,
+				   NULL, "No attribute specified");
+		return -rte_errno;
+	}
+
+	/* Verify Actions. */
+	enic_action_cap =  enic_get_action_cap(enic);
+	for (action = &actions[0]; action->type != RTE_FLOW_ACTION_TYPE_END;
+	     action++) {
+		if (action->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+		else if (!enic_match_action(action, enic_action_cap->actions))
+			break;
+	}
+	if (action->type != RTE_FLOW_ACTION_TYPE_END) {
+		rte_flow_error_set(error, EPERM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   action, "Invalid action.");
+		return -rte_errno;
+	}
+	ret = enic_action_cap->copy_fn(actions, enic_action);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Unsupported action.");
+		return -rte_errno;
+	}
+
+	/* Verify Flow items. If copying the filter from flow format to enic
+	 * format fails, the flow is not supported
+	 */
+	enic_filter_cap =  enic_get_filter_cap(enic);
+	if (enic_filter_cap == NULL) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Flow API not available");
+		return -rte_errno;
+	}
+	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
+				       enic_filter, error);
+	return ret;
+}
+
+/**
+ * Push filter/action to the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param enic_filter[in]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static struct rte_flow *
+enic_flow_add_filter(struct enic *enic, struct filter_v2 *enic_filter,
+		   struct filter_action_v2 *enic_action,
+		   struct rte_flow_error *error)
+{
+	struct rte_flow *flow;
+	int ret;
+	u16 entry;
+
+	FLOW_TRACE();
+
+	flow = rte_calloc(__func__, 1, sizeof(*flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "cannot allocate flow memory");
+		return NULL;
+	}
+
+	/* entry[in] is the queue id, entry[out] is the filter Id for delete */
+	entry = enic_action->rq_idx;
+	ret = vnic_dev_classifier(enic->vdev, CLSF_ADD, &entry, enic_filter,
+				  enic_action);
+	if (!ret) {
+		flow->enic_filter_id = entry;
+		flow->enic_filter = *enic_filter;
+	} else {
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier error");
+		rte_free(flow);
+		return NULL;
+	}
+	return flow;
+}
+
+/**
+ * Remove filter/action from the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param filter_id[in]
+ *   Id of NIC filter.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static int
+enic_flow_del_filter(struct enic *enic, u16 filter_id,
+		   struct rte_flow_error *error)
+{
+	int ret;
+
+	FLOW_TRACE();
+
+	ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL, NULL);
+	if (!ret)
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier failed");
+	return ret;
+}
+
 /*
  * The following functions are callbacks for Generic flow API.
  */
@@ -71,15 +976,15 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 		   const struct rte_flow_action actions[],
 		   struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	int ret;
 
 	FLOW_TRACE();
 
-	return 0;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
+			       &enic_filter, &enic_action);
+	return ret;
 }
 
 /**
@@ -95,15 +1000,27 @@ enic_flow_create(struct rte_eth_dev *dev,
 		 const struct rte_flow_action actions[],
 		 struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	int ret;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
-	return NULL;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error, &enic_filter,
+			      &enic_action);
+	if (ret < 0)
+		return NULL;
+
+	rte_spinlock_lock(&enic->flows_lock);
+	flow = enic_flow_add_filter(enic, &enic_filter, &enic_action,
+				    error);
+	if (flow)
+		LIST_INSERT_HEAD(&enic->flows, flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
+
+	return flow;
 }
 
 /**
@@ -116,11 +1033,14 @@ static int
 enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 		  __rte_unused struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+	enic_flow_del_filter(enic, flow->enic_filter_id, error);
+	LIST_REMOVE(flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
@@ -133,11 +1053,19 @@ enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 static int
 enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)error;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+
+	while (!LIST_EMPTY(&enic->flows)) {
+		flow = LIST_FIRST(&enic->flows);
+		enic_flow_del_filter(enic, flow->enic_filter_id, error);
+		LIST_REMOVE(flow, next);
+	}
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index d0262418d..b4c6264ab 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -1314,6 +1314,9 @@ static int enic_dev_init(struct enic *enic)
 
 	vnic_dev_set_reset_flag(enic->vdev, 0);
 
+	LIST_INIT(&enic->flows);
+	rte_spinlock_init(&enic->flows_lock);
+
 	/* set up link status checking */
 	vnic_dev_notify_set(enic->vdev, -1); /* No Intr for notify */
 
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c
index 867bd25c4..e4b80d49c 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/enic/enic_res.c
@@ -104,6 +104,21 @@ int enic_get_vnic_config(struct enic *enic)
 	dev_info(enic, "Advanced Filters %savailable\n", ((enic->adv_filters)
 		 ? "" : "not "));
 
+	err = vnic_dev_capable_filter_mode(enic->vdev, &enic->flow_filter_mode,
+					   &enic->filter_tags);
+	if (err) {
+		dev_err(enic_get_dev(enic),
+			"Error getting filter modes, %d\n", err);
+		return err;
+	}
+
+	dev_info(enic, "Flow api filter mode: %s, Filter tagging %savailable\n",
+		((enic->flow_filter_mode == FILTER_DPDK_1) ? "DPDK" :
+		((enic->flow_filter_mode == FILTER_USNIC_IP) ? "USNIC" :
+		((enic->flow_filter_mode == FILTER_IPV4_5TUPLE) ? "5TUPLE" :
+		"NONE"))),
+		((enic->filter_tags) ? "" : "not "));
+
 	c->wq_desc_count =
 		min_t(u32, ENIC_MAX_WQ_DESCS,
 		max_t(u32, ENIC_MIN_WQ_DESCS,
diff --git a/drivers/net/enic/enic_rxtx.c b/drivers/net/enic/enic_rxtx.c
index ba0cfd01a..5867acf19 100644
--- a/drivers/net/enic/enic_rxtx.c
+++ b/drivers/net/enic/enic_rxtx.c
@@ -253,8 +253,20 @@ enic_cq_rx_to_pkt_flags(struct cq_desc *cqd, struct rte_mbuf *mbuf)
 	}
 	mbuf->vlan_tci = vlan_tci;
 
-	/* RSS flag */
-	if (enic_cq_rx_desc_rss_type(cqrd)) {
+	if ((cqd->type_color & CQ_DESC_TYPE_MASK) == CQ_DESC_TYPE_CLASSIFIER) {
+		struct cq_enet_rq_clsf_desc *clsf_cqd;
+		uint16_t filter_id;
+		clsf_cqd = (struct cq_enet_rq_clsf_desc *)cqd;
+		filter_id = clsf_cqd->filter_id;
+		if (filter_id) {
+			pkt_flags |= PKT_RX_FDIR;
+			if (filter_id != ENIC_MAGIC_FILTER_ID) {
+				mbuf->hash.fdir.hi = clsf_cqd->filter_id;
+				pkt_flags |= PKT_RX_FDIR_ID;
+			}
+		}
+	} else if (enic_cq_rx_desc_rss_type(cqrd)) {
+		/* RSS flag */
 		pkt_flags |= PKT_RX_RSS_HASH;
 		mbuf->hash.rss = enic_cq_rx_desc_rss_hash(cqrd);
 	}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
  2017-05-17  3:03       ` [PATCH v3 1/6] net/enic: flow API skeleton John Daley
  2017-05-17  3:03       ` [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17 11:13         ` Ferruh Yigit
  2017-05-17  3:03       ` [PATCH v3 4/6] net/enic: flow API for Legacy NICs John Daley
                         ` (3 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode disabled via the UCS management interface. This allows:
Attributes: ingress
Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
       ipv6, udp, tcp.
Actions: queue and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

With advanced filters disabled, an IPv4 or IPv6 item must be specified
in the pattern.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 135 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 133 insertions(+), 2 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 2ea8c9370..edbde98e6 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -101,8 +101,85 @@ static enic_copy_item_fn enic_copy_item_tcp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
+/**
+ * NICs have Advanced Filters capability but they are disabled. This means
+ * that layer 3 must be specified.
+ */
+static const struct enic_items enic_items_v2[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
 /** NICs with Advanced filters enabled */
 static const struct enic_items enic_items_v3[] = {
 	[RTE_FLOW_ITEM_TYPE_ETH] = {
@@ -178,11 +255,20 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_USNIC_IP] = {
+		.item_info = enic_items_v2,
+	},
 	[FILTER_DPDK_1] = {
 		.item_info = enic_items_v3,
 	},
 };
 
+/** Supported actions for older NICs */
+static const enum rte_flow_action_type enic_supported_actions_v1[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
 /** Supported actions for newer NICs */
 static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 	RTE_FLOW_ACTION_TYPE_QUEUE,
@@ -193,6 +279,10 @@ static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 
 /** Action capabilites indexed by NIC version information */
 static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_RQ_STEERING_FLAG] = {
+		.actions = enic_supported_actions_v1,
+		.copy_fn = enic_copy_action_v1,
+	},
 	[FILTER_ACTION_V2_ALL] = {
 		.actions = enic_supported_actions_v2,
 		.copy_fn = enic_copy_action_v2,
@@ -643,7 +733,6 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 	enum rte_flow_item_type prev_item;
 	const struct enic_items *item_info;
 
-	enic_filter->type = FILTER_DPDK_1;
 	u8 is_first_item = 1;
 
 	FLOW_TRACE();
@@ -681,6 +770,44 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 			   item, "stacking error");
 	return -rte_errno;
 }
+
+/**
+ * Build the intenal version 1 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v1(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		if (actions->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			break;
+		}
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_RQ_STEERING;
+	return 0;
+}
+
 /**
  * Build the intenal version 2 NIC action structure from the provided pattern.
  * The pattern is validated as the items are copied.
@@ -755,7 +882,8 @@ static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
 	/* FIXME: only support advanced filters for now */
-	if (enic->flow_filter_mode != FILTER_DPDK_1)
+	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
+	   (enic->flow_filter_mode != FILTER_USNIC_IP))
 		return (const struct enic_filter_cap *)NULL;
 
 	if (enic->flow_filter_mode)
@@ -772,6 +900,8 @@ enic_get_action_cap(struct enic *enic)
 
 	if (enic->filter_tags)
 		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	else
+		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
 /**
@@ -884,6 +1014,7 @@ enic_flow_parse(struct rte_eth_dev *dev,
 			   NULL, "Flow API not available");
 		return -rte_errno;
 	}
+	enic_filter->type = enic->flow_filter_mode;
 	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
 				       enic_filter, error);
 	return ret;
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v3 4/6] net/enic: flow API for Legacy NICs
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
                         ` (2 preceding siblings ...)
  2017-05-17  3:03       ` [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17  3:03       ` [PATCH v3 5/6] net/enic: flow API debug John Daley
                         ` (2 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

5-tuple exact Flow support for 1200 series adapters. This allows:
Attributes: ingress
Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
       addresses and ports and all must be specified).
Actions: queue and void
Selectors: 'is'

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 206 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 201 insertions(+), 5 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index edbde98e6..6fbdcfb6c 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -92,6 +92,9 @@ struct enic_action_cap {
 };
 
 /* Forward declarations */
+static enic_copy_item_fn enic_copy_item_ipv4_v1;
+static enic_copy_item_fn enic_copy_item_udp_v1;
+static enic_copy_item_fn enic_copy_item_tcp_v1;
 static enic_copy_item_fn enic_copy_item_eth_v2;
 static enic_copy_item_fn enic_copy_item_vlan_v2;
 static enic_copy_item_fn enic_copy_item_ipv4_v2;
@@ -105,6 +108,36 @@ static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
 /**
+ * Legacy NICs or NICs with outdated firmware. Only 5-tuple perfect match
+ * is supported.
+ */
+static const struct enic_items enic_items_v1[] = {
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v1,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/**
  * NICs have Advanced Filters capability but they are disabled. This means
  * that layer 3 must be specified.
  */
@@ -255,6 +288,9 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilites indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_IPV4_5TUPLE] = {
+		.item_info = enic_items_v1,
+	},
 	[FILTER_USNIC_IP] = {
 		.item_info = enic_items_v2,
 	},
@@ -288,6 +324,171 @@ static const struct enic_action_cap enic_action_cap[] = {
 		.copy_fn = enic_copy_action_v2,
 	},
 };
+
+static int
+mask_exact_match(const u8 *supported, const u8 *supplied,
+		 unsigned int size)
+{
+	unsigned int i;
+	for (i = 0; i < size; i++) {
+		if (supported[i] != supplied[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Copy IPv4 item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_ipv4_v1(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct ipv4_hdr supported_mask = {
+		.src_addr = 0xffffffff,
+		.dst_addr = 0xffffffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	/* This is an exact match filter, both fields must be set */
+	if (!spec || !spec->hdr.src_addr || !spec->hdr.dst_addr) {
+		FLOW_LOG(ERR, "IPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "IPv4 exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_addr = spec->hdr.src_addr;
+	enic_5tup->dst_addr = spec->hdr.dst_addr;
+
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_udp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct udp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "UDP exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "UDP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_UDP;
+
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_tcp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct tcp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_tcp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "TCPIPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			     (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "TCP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_TCP;
+
+	return 0;
+}
+
 /**
  * Copy ETH item into version 2 NIC filter.
  *
@@ -881,11 +1082,6 @@ enic_match_action(const struct rte_flow_action *action,
 static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
-	/* FIXME: only support advanced filters for now */
-	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
-	   (enic->flow_filter_mode != FILTER_USNIC_IP))
-		return (const struct enic_filter_cap *)NULL;
-
 	if (enic->flow_filter_mode)
 		return &enic_filter_cap[enic->flow_filter_mode];
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v3 5/6] net/enic: flow API debug
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
                         ` (3 preceding siblings ...)
  2017-05-17  3:03       ` [PATCH v3 4/6] net/enic: flow API for Legacy NICs John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17  3:03       ` [PATCH v3 6/6] net/enic: flow API documentation John Daley
  2017-05-17 11:12       ` [PATCH v3 0/6] " Ferruh Yigit
  6 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Added a debug function to print enic filters and actions when
rte_validate_flow is called. Compiled in CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW
is enabled and log level is INFO.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 6fbdcfb6c..0b73020e8 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -1100,6 +1100,142 @@ enic_get_action_cap(struct enic *enic)
 		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
+
+/* Debug function to dump internal NIC action structure. */
+static void
+enic_dump_actions(const struct filter_action_v2 *ea)
+{
+	if (ea->type == FILTER_ACTION_RQ_STEERING) {
+		FLOW_LOG(INFO, "Action(V1), queue: %u\n", ea->rq_idx);
+	} else if (ea->type == FILTER_ACTION_V2) {
+		FLOW_LOG(INFO, "Actions(V2)\n");
+		if (ea->flags & FILTER_ACTION_RQ_STEERING_FLAG)
+			FLOW_LOG(INFO, "\tqueue: %u\n",
+			       enic_sop_rq_idx_to_rte_idx(ea->rq_idx));
+		if (ea->flags & FILTER_ACTION_FILTER_ID_FLAG)
+			FLOW_LOG(INFO, "\tfilter_id: %u\n", ea->filter_id);
+	}
+}
+
+/* Debug function to dump internal NIC filter structure. */
+static void
+enic_dump_filter(const struct filter_v2 *filt)
+{
+	const struct filter_generic_1 *gp;
+	int i, j, mbyte;
+	char buf[128], *bp;
+	char ip4[16], ip6[16], udp[16], tcp[16], tcpudp[16], ip4csum[16];
+	char l4csum[16], ipfrag[16];
+
+	switch (filt->type) {
+	case FILTER_IPV4_5TUPLE:
+		FLOW_LOG(INFO, "FILTER_IPV4_5TUPLE\n");
+		break;
+	case FILTER_USNIC_IP:
+	case FILTER_DPDK_1:
+		/* FIXME: this should be a loop */
+		gp = &filt->u.generic_1;
+		FLOW_LOG(INFO, "Filter: vlan: 0x%04x, mask: 0x%04x\n",
+		       gp->val_vlan, gp->mask_vlan);
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV4)
+			sprintf(ip4, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip4(y)" : "ip4(n)");
+		else
+			sprintf(ip4, "%s ", "ip4(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV6)
+			sprintf(ip6, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip6(y)" : "ip6(n)");
+		else
+			sprintf(ip6, "%s ", "ip6(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_UDP)
+			sprintf(udp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_UDP)
+				 ? "udp(y)" : "udp(n)");
+		else
+			sprintf(udp, "%s ", "udp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP)
+			sprintf(tcp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP)
+				 ? "tcp(y)" : "tcp(n)");
+		else
+			sprintf(tcp, "%s ", "tcp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+			sprintf(tcpudp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+				 ? "tcpudp(y)" : "tcpudp(n)");
+		else
+			sprintf(tcpudp, "%s ", "tcpudp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IP4SUM_OK)
+			sprintf(ip4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IP4SUM_OK)
+				 ? "ip4csum(y)" : "ip4csum(n)");
+		else
+			sprintf(ip4csum, "%s ", "ip4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_L4SUM_OK)
+			sprintf(l4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_L4SUM_OK)
+				 ? "l4csum(y)" : "l4csum(n)");
+		else
+			sprintf(l4csum, "%s ", "l4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPFRAG)
+			sprintf(ipfrag, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPFRAG)
+				 ? "ipfrag(y)" : "ipfrag(n)");
+		else
+			sprintf(ipfrag, "%s ", "ipfrag(x)");
+		FLOW_LOG(INFO, "\tFlags: %s%s%s%s%s%s%s%s\n", ip4, ip6, udp,
+			 tcp, tcpudp, ip4csum, l4csum, ipfrag);
+
+		for (i = 0; i < FILTER_GENERIC_1_NUM_LAYERS; i++) {
+			mbyte = FILTER_GENERIC_1_KEY_LEN - 1;
+			while (mbyte && !gp->layer[i].mask[mbyte])
+				mbyte--;
+			if (mbyte == 0)
+				continue;
+
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].mask[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u mask: %s\n", i + 2, buf);
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].val[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u  val: %s\n", i + 2, buf);
+		}
+		break;
+	default:
+		FLOW_LOG(INFO, "FILTER UNKNOWN\n");
+		break;
+	}
+}
+
+/* Debug function to dump internal NIC flow structures. */
+static void
+enic_dump_flow(const struct filter_action_v2 *ea, const struct filter_v2 *filt)
+{
+	enic_dump_filter(filt);
+	enic_dump_actions(ea);
+}
+
+
 /**
  * Internal flow parse/validate function.
  *
@@ -1311,6 +1447,8 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 
 	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
 			       &enic_filter, &enic_action);
+	if (!ret)
+		enic_dump_flow(&enic_action, &enic_filter);
 	return ret;
 }
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v3 6/6] net/enic: flow API documentation
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
                         ` (4 preceding siblings ...)
  2017-05-17  3:03       ` [PATCH v3 5/6] net/enic: flow API debug John Daley
@ 2017-05-17  3:03       ` John Daley
  2017-05-17 11:13         ` Ferruh Yigit
  2017-05-17 11:12       ` [PATCH v3 0/6] " Ferruh Yigit
  6 siblings, 1 reply; 41+ messages in thread
From: John Daley @ 2017-05-17  3:03 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Update enic NIC guide, release notes and add flow API to the
supported features list.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 doc/guides/nics/enic.rst               | 51 ++++++++++++++++++++++++++++++++++
 doc/guides/nics/features/enic.ini      |  1 +
 doc/guides/rel_notes/release_17_08.rst |  6 ++++
 3 files changed, 58 insertions(+)

diff --git a/doc/guides/nics/enic.rst b/doc/guides/nics/enic.rst
index 89a301585..dbba087ed 100644
--- a/doc/guides/nics/enic.rst
+++ b/doc/guides/nics/enic.rst
@@ -213,6 +213,45 @@ or ``vfio`` in non-IOMMU mode.
 Please see :ref:`Limitations <enic_limitations>` for limitations in
 the use of SR-IOV.
 
+.. _enic-genic-flow-api:
+
+Generic Flow API support
+------------------------
+
+Generic Flow API is supported. The baseline support is:
+
+- **1200 series VICs**
+
+5-tuple exact Flow support for 1200 series adapters. This allows:
+
+  - Attributes: ingress
+  - Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
+    addresses and ports and all must be specified).
+  - Actions: queue and void
+  - Selectors: 'is'
+
+- **1300 series VICS with Advanced filters disabled**
+
+With advanced filters disabled, an IPv4 or IPv6 item must be specified
+in the pattern.
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth, ipv4, ipv6, udp, tcp
+  - Actions: queue and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+  - In total, up to 64 bytes of mask is allowed across all haeders
+
+- **1300 series VICS with Advanced filters enabled**
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth, ipv4, ipv6, udp, tcp
+  - Actions: queue, mark, flag and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+  - In total, up to 64 bytes of mask is allowed across all haeders
+
+More features may be added in future firmware and new versions of the VIC.
+Please refer to the release notes.
+
 .. _enic_limitations:
 
 Limitations
@@ -262,6 +301,18 @@ Limitations
 
 How to build the suite
 ----------------------
+=======
+- The number of filters that can be specified with the Generic Flow API is
+  dependent on how many header fields are being masked. Use 'flow create' in
+  a loop to determine how many filters your VIC will support (not more than
+  1000 for 1300 series VICs. Filter are checked for matching in the order they
+  were added. Since there currently is no grouping or priority support,
+  'catch-all' filters should be added last.
+
+How to build the suite?
+-----------------------
+The build instructions for the DPDK suite should be followed. By default
+the ENIC PMD library will be built into the DPDK library.
 
 Refer to the document :ref:`compiling and testing a PMD for a NIC
 <pmd_build_and_test>` for details.
diff --git a/doc/guides/nics/features/enic.ini b/doc/guides/nics/features/enic.ini
index 94e7f3cba..0de3ef53c 100644
--- a/doc/guides/nics/features/enic.ini
+++ b/doc/guides/nics/features/enic.ini
@@ -20,6 +20,7 @@ VLAN filter          = Y
 CRC offload          = Y
 VLAN offload         = Y
 Flow director        = Y
+Flow API             = Y
 L3 checksum offload  = Y
 L4 checksum offload  = Y
 Packet type parsing  = Y
diff --git a/doc/guides/rel_notes/release_17_08.rst b/doc/guides/rel_notes/release_17_08.rst
index 74aae10f7..31a43223f 100644
--- a/doc/guides/rel_notes/release_17_08.rst
+++ b/doc/guides/rel_notes/release_17_08.rst
@@ -41,6 +41,12 @@ New Features
      Also, make sure to start the actual text at the margin.
      =========================================================
 
+* **Added Generic Flow API support to enic.**
+
+  Generic flow API support for Ethernet, VLAN, IPv4, IPv6, UDP, TCP, VxLAN and
+  inner Ethernet IPv4, IPv6, UDP, TCP pattern items with QUEUE, MARK and FLAG
+  action for ingress traffic.
+
 
 Resolved Issues
 ---------------
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH v3 0/6] enic flow api support
  2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
                         ` (5 preceding siblings ...)
  2017-05-17  3:03       ` [PATCH v3 6/6] net/enic: flow API documentation John Daley
@ 2017-05-17 11:12       ` Ferruh Yigit
  6 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-17 11:12 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 4:03 AM, John Daley wrote:
> This V3 is rebased on dpdk-next-net and retargeted at 17.08.  Also,
> inner ipv4, ipv6, udp, tcp support added for 1300 series VICs.
> 
> thank you,
> johnd
> 
> John Daley (6):
>   net/enic: flow API skeleton
>   net/enic: flow API for NICs with advanced filters enabled
>   net/enic: flow API for NICs with advanced filters disabled
>   net/enic: flow API for Legacy NICs
>   net/enic: flow API debug
>   net/enic: flow API documentation

Hi John,

I am getting multiple build errors from multiple reasons, I suspect
there was a git merge issue in the patchset, can you please double check
building the patchset?

Thanks,
ferruh

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v3 1/6] net/enic: flow API skeleton
  2017-05-17  3:03       ` [PATCH v3 1/6] net/enic: flow API skeleton John Daley
@ 2017-05-17 11:12         ` Ferruh Yigit
  0 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-17 11:12 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 4:03 AM, John Daley wrote:
> Stub callbacks for the generic flow API and a new FLOW debug define.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>

<...>

> diff --git a/drivers/net/enic/enic_ethdev.c b/drivers/net/enic/enic_ethdev.c
> index 8e16a71b7..4e8a0d9e0 100644
> --- a/drivers/net/enic/enic_ethdev.c
> +++ b/drivers/net/enic/enic_ethdev.c
> @@ -116,13 +116,28 @@ enicpmd_dev_filter_ctrl(struct rte_eth_dev *dev,
>  		     enum rte_filter_op filter_op,
>  		     void *arg)
>  {
> -	int ret = -EINVAL;
> +	int ret = 0;
> +
> +	ENICPMD_FUNC_TRACE();
>  
> -	if (RTE_ETH_FILTER_FDIR == filter_type)
> +	if (dev == NULL)
> +		return -EINVAL;

dev can't be NULL here if it is only called via filter_ctrl eth_dev_ops

<...>

> diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
> new file mode 100644
> index 000000000..d25390f8a
> --- /dev/null
> +++ b/drivers/net/enic/enic_flow.c
> @@ -0,0 +1,154 @@
> +/*
> + * Copyright 2008-2017 Cisco Systems, Inc.  All rights reserved.
> + * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
> + *
> + * Copyright (c) 2017, Cisco Systems, Inc.
> + * All rights reserved.

Is this file header correct, dates and "Nuova Systems" and double Cisco
copyright.

As a side note, there is also another LICENSE file under net/enic folder

<...>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled
  2017-05-17  3:03       ` [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled John Daley
@ 2017-05-17 11:12         ` Ferruh Yigit
  0 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-17 11:12 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 4:03 AM, John Daley wrote:
> Flow support for 1300 series adapters with the 'Advanced Filter'
> mode enabled via the UCS management interface. This enables:
> Attributes: ingress
> Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
>        ipv6, udp, tcp.
> Actions: queue, mark, flag and void
> Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>

<...>

> +/** Get the NIC filter capabilties structure */
> +static const struct enic_filter_cap *
> +enic_get_filter_cap(struct enic *enic)
> +{
> +	/* FIXME: only support advanced filters for now */
> +	if (enic->flow_filter_mode != FILTER_DPDK_1)
> +		return (const struct enic_filter_cap *)NULL;
> +
> +	if (enic->flow_filter_mode)
> +		return &enic_filter_cap[enic->flow_filter_mode];
> +
> +	return (const struct enic_filter_cap *)NULL;

Do we need this casting?

<...>

> diff --git a/drivers/net/enic/enic_rxtx.c b/drivers/net/enic/enic_rxtx.c
> index ba0cfd01a..5867acf19 100644
> --- a/drivers/net/enic/enic_rxtx.c
> +++ b/drivers/net/enic/enic_rxtx.c
> @@ -253,8 +253,20 @@ enic_cq_rx_to_pkt_flags(struct cq_desc *cqd, struct rte_mbuf *mbuf)
>  	}
>  	mbuf->vlan_tci = vlan_tci;
>  
> -	/* RSS flag */
> -	if (enic_cq_rx_desc_rss_type(cqrd)) {
> +	if ((cqd->type_color & CQ_DESC_TYPE_MASK) == CQ_DESC_TYPE_CLASSIFIER) {
> +		struct cq_enet_rq_clsf_desc *clsf_cqd;
> +		uint16_t filter_id;
> +		clsf_cqd = (struct cq_enet_rq_clsf_desc *)cqd;
> +		filter_id = clsf_cqd->filter_id;
> +		if (filter_id) {
> +			pkt_flags |= PKT_RX_FDIR;
> +			if (filter_id != ENIC_MAGIC_FILTER_ID) {
> +				mbuf->hash.fdir.hi = clsf_cqd->filter_id;
> +				pkt_flags |= PKT_RX_FDIR_ID;
> +			}
> +		}
> +	} else if (enic_cq_rx_desc_rss_type(cqrd)) {
> +		/* RSS flag */

Is this piece of code related to the rte_flow ?

"struct cq_enet_rq_clsf_desc" is not defined and causing build erros.

>  		pkt_flags |= PKT_RX_RSS_HASH;
>  		mbuf->hash.rss = enic_cq_rx_desc_rss_hash(cqrd);
>  	}
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled
  2017-05-17  3:03       ` [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled John Daley
@ 2017-05-17 11:13         ` Ferruh Yigit
  0 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-17 11:13 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 4:03 AM, John Daley wrote:
> Flow support for 1300 series adapters with the 'Advanced Filter'
> mode disabled via the UCS management interface. This allows:
> Attributes: ingress
> Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
>        ipv6, udp, tcp.
> Actions: queue and void
> Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
> 
> With advanced filters disabled, an IPv4 or IPv6 item must be specified
> in the pattern.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>

<...>

> @@ -193,6 +279,10 @@ static const enum rte_flow_action_type enic_supported_actions_v2[] = {
>  
>  /** Action capabilites indexed by NIC version information */
>  static const struct enic_action_cap enic_action_cap[] = {
> +	[FILTER_ACTION_RQ_STEERING_FLAG] = {

FILTER_ACTION_RQ_STEERING_FLAG doesn't defined anywhere which is causing
build error, compiler asks if you mean FILTER_ACTION_RQ_STEERING:

drivers/net/enic/enic_flow.c:318:3: error: use of undeclared identifier
'FILTER_ACTION_RQ_STEERING_FLAG'; did you mean 'FILTER_ACTION_RQ_STEERING'?

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v3 6/6] net/enic: flow API documentation
  2017-05-17  3:03       ` [PATCH v3 6/6] net/enic: flow API documentation John Daley
@ 2017-05-17 11:13         ` Ferruh Yigit
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
  0 siblings, 1 reply; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-17 11:13 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 4:03 AM, John Daley wrote:
> Update enic NIC guide, release notes and add flow API to the
> supported features list.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>

<...>

>  How to build the suite
>  ----------------------
> +=======

This part looks like git merge error?

> +- The number of filters that can be specified with the Generic Flow API is
> +  dependent on how many header fields are being masked. Use 'flow create' in
> +  a loop to determine how many filters your VIC will support (not more than
> +  1000 for 1300 series VICs. Filter are checked for matching in the order they
> +  were added. Since there currently is no grouping or priority support,
> +  'catch-all' filters should be added last.
> +
> +How to build the suite?
> +-----------------------
> +The build instructions for the DPDK suite should be followed. By default
> +the ENIC PMD library will be built into the DPDK library.

<...>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v4 0/8] enic flow api support
  2017-05-17 11:13         ` Ferruh Yigit
@ 2017-05-17 22:38           ` John Daley
  2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
                               ` (8 more replies)
  0 siblings, 9 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Sorry for the inconvenience Ferruh, hopefully this set will work better.

V4 patchset changes:
- include patch that was missing in V3 which caused compile errors
- put mark and flag support into a separate patch to make it more clear
  what the code changes in enic_rxtx.c were for
- fixed a documentation merge error
- fix copyright, remove unnecessary check for dev == NULL

thanks,
johnd

John Daley (8):
  net/enic: bring NIC interface functions up to date
  net/enic: flow API skeleton
  net/enic: flow API for NICs with advanced filters enabled
  net/enic: flow API mark and flag support
  net/enic: flow API for NICs with advanced filters disabled
  net/enic: flow API for Legacy NICs
  net/enic: flow API debug
  net/enic: flow API documentation

 config/common_base                     |    1 +
 doc/guides/nics/enic.rst               |   52 ++
 doc/guides/nics/features/enic.ini      |    1 +
 doc/guides/rel_notes/release_17_08.rst |    6 +
 drivers/net/enic/Makefile              |    1 +
 drivers/net/enic/base/cq_enet_desc.h   |   13 +
 drivers/net/enic/base/vnic_dev.c       |  162 +++-
 drivers/net/enic/base/vnic_dev.h       |    5 +-
 drivers/net/enic/base/vnic_devcmd.h    |   81 +-
 drivers/net/enic/enic.h                |   15 +-
 drivers/net/enic/enic_clsf.c           |   16 +-
 drivers/net/enic/enic_ethdev.c         |   18 +-
 drivers/net/enic/enic_flow.c           | 1544 ++++++++++++++++++++++++++++++++
 drivers/net/enic/enic_main.c           |    3 +
 drivers/net/enic/enic_res.c            |   15 +
 drivers/net/enic/enic_rxtx.c           |   16 +-
 16 files changed, 1904 insertions(+), 45 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

-- 
2.12.0

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v4 1/8] net/enic: bring NIC interface functions up to date
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-22  9:49               ` Ferruh Yigit
  2017-06-08 10:44               ` Jerin Jacob
  2017-05-17 22:38             ` [PATCH v4 2/8] net/enic: flow API skeleton John Daley
                               ` (7 subsequent siblings)
  8 siblings, 2 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Update the base functions for the Cisco VIC. These files are mostly
common with other VIC drivers so are left alone is as much as possilbe.
Includes in a new filter/action interface which is needed for Generic
Flow API PMD support. Update FDIR code to use the new interface.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---

checkpatch style warning about multiple use of 'x' in ARRAY_SIZE macro should
be waived in my opinion.

 drivers/net/enic/base/cq_enet_desc.h |  13 +++
 drivers/net/enic/base/vnic_dev.c     | 162 ++++++++++++++++++++++++++++-------
 drivers/net/enic/base/vnic_dev.h     |   5 +-
 drivers/net/enic/base/vnic_devcmd.h  |  81 +++++++++++++++++-
 drivers/net/enic/enic_clsf.c         |  16 ++--
 5 files changed, 238 insertions(+), 39 deletions(-)

diff --git a/drivers/net/enic/base/cq_enet_desc.h b/drivers/net/enic/base/cq_enet_desc.h
index f9822a450..e8410563a 100644
--- a/drivers/net/enic/base/cq_enet_desc.h
+++ b/drivers/net/enic/base/cq_enet_desc.h
@@ -71,6 +71,19 @@ struct cq_enet_rq_desc {
 	u8 type_color;
 };
 
+/* Completion queue descriptor: Ethernet receive queue, 16B */
+struct cq_enet_rq_clsf_desc {
+	__le16 completed_index_flags;
+	__le16 q_number_rss_type_flags;
+	__le16 filter_id;
+	__le16 lif;
+	__le16 bytes_written_flags;
+	__le16 vlan;
+	__le16 checksum_fcoe;
+	u8 flags;
+	u8 type_color;
+};
+
 #define CQ_ENET_RQ_DESC_FLAGS_INGRESS_PORT          (0x1 << 12)
 #define CQ_ENET_RQ_DESC_FLAGS_FCOE                  (0x1 << 13)
 #define CQ_ENET_RQ_DESC_FLAGS_EOP                   (0x1 << 14)
diff --git a/drivers/net/enic/base/vnic_dev.c b/drivers/net/enic/base/vnic_dev.c
index 84e4840af..7b3aed31a 100644
--- a/drivers/net/enic/base/vnic_dev.c
+++ b/drivers/net/enic/base/vnic_dev.c
@@ -387,17 +387,24 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 
 static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 	enum vnic_devcmd_cmd proxy_cmd, enum vnic_devcmd_cmd cmd,
-	u64 *a0, u64 *a1, int wait)
+	u64 *args, int nargs, int wait)
 {
 	u32 status;
 	int err;
 
+	/*
+	 * Proxy command consumes 2 arguments. One for proxy index,
+	 * the other is for command to be proxied
+	 */
+	if (nargs > VNIC_DEVCMD_NARGS - 2) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	vdev->args[0] = vdev->proxy_index;
 	vdev->args[1] = cmd;
-	vdev->args[2] = *a0;
-	vdev->args[3] = *a1;
+	memcpy(&vdev->args[2], args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, proxy_cmd, wait);
 	if (err)
@@ -412,24 +419,26 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 		return err;
 	}
 
-	*a0 = vdev->args[1];
-	*a1 = vdev->args[2];
+	memcpy(args, &vdev->args[1], nargs * sizeof(args[0]));
 
 	return 0;
 }
 
 static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
-	enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+	enum vnic_devcmd_cmd cmd, u64 *args, int nargs, int wait)
 {
 	int err;
 
-	vdev->args[0] = *a0;
-	vdev->args[1] = *a1;
+	if (nargs > VNIC_DEVCMD_NARGS) {
+		pr_err("number of args %d exceeds the maximum\n", nargs);
+		return -EINVAL;
+	}
+	memset(vdev->args, 0, sizeof(vdev->args));
+	memcpy(vdev->args, args, nargs * sizeof(args[0]));
 
 	err = _vnic_dev_cmd(vdev, cmd, wait);
 
-	*a0 = vdev->args[0];
-	*a1 = vdev->args[1];
+	memcpy(args, vdev->args, nargs * sizeof(args[0]));
 
 	return err;
 }
@@ -455,24 +464,64 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev)
 int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 	u64 *a0, u64 *a1, int wait)
 {
+	u64 args[2];
+	int err;
+
+	args[0] = *a0;
+	args[1] = *a1;
 	memset(vdev->args, 0, sizeof(vdev->args));
 
 	switch (vdev->proxy) {
 	case PROXY_BY_INDEX:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_BY_BDF:
+		err =  vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
+				args, ARRAY_SIZE(args), wait);
+		break;
+	case PROXY_NONE:
+	default:
+		err = vnic_dev_cmd_no_proxy(vdev, cmd, args, 2, wait);
+		break;
+	}
+
+	if (err == 0) {
+		*a0 = args[0];
+		*a1 = args[1];
+	}
+
+	return err;
+}
+
+int vnic_dev_cmd_args(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+		      u64 *args, int nargs, int wait)
+{
+	switch (vdev->proxy) {
+	case PROXY_BY_INDEX:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_BY_BDF:
 		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
-				a0, a1, wait);
+				args, nargs, wait);
 	case PROXY_NONE:
 	default:
-		return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
+		return vnic_dev_cmd_no_proxy(vdev, cmd, args, nargs, wait);
 	}
 }
 
+static int vnic_dev_advanced_filters_cap(struct vnic_dev *vdev, u64 *args,
+		int nargs)
+{
+	memset(args, 0, nargs * sizeof(*args));
+	args[0] = CMD_ADD_ADV_FILTER;
+	args[1] = FILTER_CAP_MODE_V1_FLAG;
+	return vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, nargs, 1000);
+}
+
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 {
-	u64 a0 = (u32)CMD_ADD_ADV_FILTER, a1 = 0;
+	u64 a0 = CMD_ADD_ADV_FILTER, a1 = 0;
 	int wait = 1000;
 	int err;
 
@@ -482,7 +531,65 @@ int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
 	return (a1 >= (u32)FILTER_DPDK_1);
 }
 
-static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
+/*  Determine the "best" filtering mode VIC is capaible of. Returns one of 3
+ *  value or 0 on error:
+ *	FILTER_DPDK_1- advanced filters availabile
+ *	FILTER_USNIC_IP_FLAG - advanced filters but with the restriction that
+ *		the IP layer must explicitly specified. I.e. cannot have a UDP
+ *		filter that matches both IPv4 and IPv6.
+ *	FILTER_IPV4_5TUPLE - fallback if either of the 2 above aren't available.
+ *		all other filter types are not available.
+ *   Retrun true in filter_tags if supported
+ */
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags)
+{
+	u64 args[4];
+	int err;
+	u32 max_level = 0;
+
+	err = vnic_dev_advanced_filters_cap(vdev, args, 4);
+
+	/* determine if filter tags are available */
+	if (err)
+		*filter_tags = 0;
+	if ((args[2] == FILTER_CAP_MODE_V1) &&
+	    (args[3] & FILTER_ACTION_FILTER_ID_FLAG))
+		*filter_tags = 1;
+	else
+		*filter_tags = 0;
+
+	if (err || ((args[0] == 1) && (args[1] == 0))) {
+		/* Adv filter Command not supported or adv filters available but
+		 * not enabled. Try the normal filter capability command.
+		 */
+		args[0] = CMD_ADD_FILTER;
+		args[1] = 0;
+		err = vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, 2, 1000);
+		if (err)
+			return err;
+		max_level = args[1];
+		goto parse_max_level;
+	} else if (args[2] == FILTER_CAP_MODE_V1) {
+		/* parse filter capability mask in args[1] */
+		if (args[1] & FILTER_DPDK_1_FLAG)
+			*mode = FILTER_DPDK_1;
+		else if (args[1] & FILTER_USNIC_IP_FLAG)
+			*mode = FILTER_USNIC_IP;
+		else if (args[1] & FILTER_IPV4_5TUPLE_FLAG)
+			*mode = FILTER_IPV4_5TUPLE;
+		return 0;
+	}
+	max_level = args[1];
+parse_max_level:
+	if (max_level >= (u32)FILTER_USNIC_IP)
+		*mode = FILTER_USNIC_IP;
+	else
+		*mode = FILTER_IPV4_5TUPLE;
+	return 0;
+}
+
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
 {
 	u64 a0 = (u32)cmd, a1 = 0;
 	int wait = 1000;
@@ -1017,32 +1124,30 @@ int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
  *          In case of DEL filter, the caller passes the RQ number. Return
  *          value is irrelevant.
  * @data: filter data
+ * @action: action data
  */
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data)
+	struct filter_v2 *data, struct filter_action_v2 *action_v2)
 {
 	u64 a0, a1;
 	int wait = 1000;
 	dma_addr_t tlv_pa;
 	int ret = -EINVAL;
 	struct filter_tlv *tlv, *tlv_va;
-	struct filter_action *action;
 	u64 tlv_size;
-	u32 filter_size;
+	u32 filter_size, action_size;
 	static unsigned int unique_id;
 	char z_name[RTE_MEMZONE_NAMESIZE];
 	enum vnic_devcmd_cmd dev_cmd;
 
-
 	if (cmd == CLSF_ADD) {
-		if (data->type == FILTER_DPDK_1)
-			dev_cmd = CMD_ADD_ADV_FILTER;
-		else
-			dev_cmd = CMD_ADD_FILTER;
+		dev_cmd = (data->type >= FILTER_DPDK_1) ?
+			  CMD_ADD_ADV_FILTER : CMD_ADD_FILTER;
 
 		filter_size = vnic_filter_size(data);
-		tlv_size = filter_size +
-		    sizeof(struct filter_action) +
+		action_size = vnic_action_size(action_v2);
+
+		tlv_size = filter_size + action_size +
 		    2*sizeof(struct filter_tlv);
 		snprintf((char *)z_name, sizeof(z_name),
 			"vnic_clsf_%d", unique_id++);
@@ -1063,11 +1168,8 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
 					 filter_size);
 
 		tlv->type = CLSF_TLV_ACTION;
-		tlv->length = sizeof(struct filter_action);
-		action = (struct filter_action *)&tlv->val;
-		action->type = FILTER_ACTION_RQ_STEERING;
-		action->u.rq_idx = *entry;
-
+		tlv->length = action_size;
+		memcpy(&tlv->val, (void *)action_v2, action_size);
 		ret = vnic_dev_cmd(vdev, dev_cmd, &a0, &a1, wait);
 		*entry = (u16)a0;
 		vdev->free_consistent(vdev->priv, tlv_size, tlv_va, tlv_pa);
diff --git a/drivers/net/enic/base/vnic_dev.h b/drivers/net/enic/base/vnic_dev.h
index 06ebd4cea..9a9e6917a 100644
--- a/drivers/net/enic/base/vnic_dev.h
+++ b/drivers/net/enic/base/vnic_dev.h
@@ -135,6 +135,9 @@ void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev);
 int vnic_dev_fw_info(struct vnic_dev *vdev,
 	struct vnic_devcmd_fw_info **fw_info);
 int vnic_dev_capable_adv_filters(struct vnic_dev *vdev);
+int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd);
+int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
+				 u8 *filter_tags);
 int vnic_dev_asic_info(struct vnic_dev *vdev, u16 *asic_type, u16 *asic_rev);
 int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, size_t size,
 	void *value);
@@ -202,7 +205,7 @@ int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status);
 int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
 int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
-	struct filter_v2 *data);
+	struct filter_v2 *data, struct filter_action_v2 *action_v2);
 #ifdef ENIC_VXLAN
 int vnic_dev_overlay_offload_enable_disable(struct vnic_dev *vdev,
 	u8 overlay, u8 config);
diff --git a/drivers/net/enic/base/vnic_devcmd.h b/drivers/net/enic/base/vnic_devcmd.h
index 785fd6fdf..05d87b919 100644
--- a/drivers/net/enic/base/vnic_devcmd.h
+++ b/drivers/net/enic/base/vnic_devcmd.h
@@ -92,6 +92,8 @@
 #define _CMD_VTYPE(cmd)          (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
 #define _CMD_N(cmd)              (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
 enum vnic_devcmd_cmd {
 	CMD_NONE                = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
 
@@ -598,12 +600,29 @@ enum vnic_devcmd_cmd {
 	 * out: (u32) a0=filter identifier
 	 *
 	 * Capability query:
-	 * out: (u64) a0= 1 if capabliity query supported
-	 *      (u64) a1= MAX filter type supported
+	 * in:  (u64) a1= supported filter capability exchange modes
+	 * out: (u64) a0= 1 if capability query supported
+	 *      if (u64) a1 = 0: a1 = MAX filter type supported
+	 *      if (u64) a1 & FILTER_CAP_MODE_V1_FLAG:
+	 *                       a1 = bitmask of supported filters
+	 *                       a2 = FILTER_CAP_MODE_V1
+	 *                       a3 = bitmask of supported actions
 	 */
 	CMD_ADD_ADV_FILTER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 77),
 };
 
+/* Modes for exchanging advanced filter capabilities. The modes supported by
+ * the driver are passed in the CMD_ADD_ADV_FILTER capability command and the
+ * mode selected is returned.
+ *    V0: the maximum filter type supported is returned
+ *    V1: bitmasks of supported filters and actions are returned
+ */
+enum filter_cap_mode {
+	FILTER_CAP_MODE_V0 = 0,  /* Must always be 0 for legacy drivers */
+	FILTER_CAP_MODE_V1 = 1,
+};
+#define FILTER_CAP_MODE_V1_FLAG (1 << FILTER_CAP_MODE_V1)
+
 /* CMD_ENABLE2 flags */
 #define CMD_ENABLE2_STANDBY 0x0
 #define CMD_ENABLE2_ACTIVE  0x1
@@ -837,6 +856,7 @@ struct filter_generic_1 {
 /* Specifies the filter_action type. */
 enum {
 	FILTER_ACTION_RQ_STEERING = 0,
+	FILTER_ACTION_V2 = 1,
 	FILTER_ACTION_MAX
 };
 
@@ -847,6 +867,22 @@ struct filter_action {
 	} u;
 } __attribute__((packed));
 
+#define FILTER_ACTION_RQ_STEERING_FLAG	(1 << 0)
+#define FILTER_ACTION_FILTER_ID_FLAG	(1 << 1)
+#define FILTER_ACTION_V2_ALL		(FILTER_ACTION_RQ_STEERING_FLAG \
+					 | FILTER_ACTION_FILTER_ID_FLAG)
+
+/* Version 2 of filter action must be a strict extension of struct filter_action
+ * where the first fields exactly match in size and meaning.
+ */
+struct filter_action_v2 {
+	u32 type;
+	u32 rq_idx;
+	u32 flags;                     /* use FILTER_ACTION_XXX_FLAG defines */
+	u16 filter_id;
+	u_int8_t reserved[32];         /* for future expansion */
+} __attribute__((packed));
+
 /* Specifies the filter type. */
 enum filter_type {
 	FILTER_USNIC_ID = 0,
@@ -859,6 +895,21 @@ enum filter_type {
 	FILTER_MAX
 };
 
+#define FILTER_USNIC_ID_FLAG		(1 << FILTER_USNIC_ID)
+#define FILTER_IPV4_5TUPLE_FLAG		(1 << FILTER_IPV4_5TUPLE)
+#define FILTER_MAC_VLAN_FLAG		(1 << FILTER_MAC_VLAN)
+#define FILTER_VLAN_IP_3TUPLE_FLAG	(1 << FILTER_VLAN_IP_3TUPLE)
+#define FILTER_NVGRE_VMQ_FLAG		(1 << FILTER_NVGRE_VMQ)
+#define FILTER_USNIC_IP_FLAG		(1 << FILTER_USNIC_IP)
+#define FILTER_DPDK_1_FLAG		(1 << FILTER_DPDK_1)
+#define FILTER_V1_ALL			(FILTER_USNIC_ID_FLAG | \
+					FILTER_IPV4_5TUPLE_FLAG | \
+					FILTER_MAC_VLAN_FLAG | \
+					FILTER_VLAN_IP_3TUPLE_FLAG | \
+					FILTER_NVGRE_VMQ_FLAG | \
+					FILTER_USNIC_IP_FLAG | \
+					FILTER_DPDK_1_FLAG)
+
 struct filter {
 	u32 type;
 	union {
@@ -903,7 +954,7 @@ struct filter_tlv {
 /* Data for CMD_ADD_FILTER is 2 TLV and filter + action structs */
 #define FILTER_MAX_BUF_SIZE 100
 #define FILTER_V2_MAX_BUF_SIZE (sizeof(struct filter_v2) + \
-	sizeof(struct filter_action) + \
+	sizeof(struct filter_action_v2) + \
 	(2 * sizeof(struct filter_tlv)))
 
 /*
@@ -949,6 +1000,30 @@ enum {
 };
 
 /*
+ * Get the action structure size given action type. To be "future-proof,"
+ * drivers should use this instead of "sizeof (struct filter_action_v2)"
+ * when computing length for TLV.
+ */
+static inline u_int32_t
+vnic_action_size(struct filter_action_v2 *fap)
+{
+	u_int32_t size;
+
+	switch (fap->type) {
+	case FILTER_ACTION_RQ_STEERING:
+		size = sizeof(struct filter_action);
+		break;
+	case FILTER_ACTION_V2:
+		size = sizeof(struct filter_action_v2);
+		break;
+	default:
+		size = sizeof(struct filter_action);
+		break;
+	}
+	return size;
+}
+
+/*
  * Writing cmd register causes STAT_BUSY to get set in status register.
  * When cmd completes, STAT_BUSY will be cleared.
  *
diff --git a/drivers/net/enic/enic_clsf.c b/drivers/net/enic/enic_clsf.c
index 487f80449..9e94afdf9 100644
--- a/drivers/net/enic/enic_clsf.c
+++ b/drivers/net/enic/enic_clsf.c
@@ -345,7 +345,7 @@ int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 		/* Delete the filter */
 		vnic_dev_classifier(enic->vdev, CLSF_DEL,
-			&key->fltr_id, NULL);
+			&key->fltr_id, NULL, NULL);
 		rte_free(key);
 		enic->fdir.nodes[pos] = NULL;
 		enic->fdir.stats.free++;
@@ -365,8 +365,10 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	u32 flowtype_supported;
 	u16 flex_bytes;
 	u16 queue;
+	struct filter_action_v2 action;
 
 	memset(&fltr, 0, sizeof(fltr));
+	memset(&action, 0, sizeof(action));
 	flowtype_supported = enic->fdir.types_mask
 			     & (1 << params->input.flow_type);
 
@@ -439,7 +441,7 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 			 * Delete the filter and add the modified one later
 			 */
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			enic->fdir.stats.free++;
 		}
 
@@ -451,8 +453,11 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 
 	enic->fdir.copy_fltr_fn(&fltr, &params->input,
 				&enic->rte_dev->data->dev_conf.fdir_conf.mask);
+	action.type = FILTER_ACTION_RQ_STEERING;
+	action.rq_idx = queue;
 
-	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr)) {
+	if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr,
+	    &action)) {
 		key->fltr_id = queue;
 	} else {
 		dev_err(enic, "Add classifier entry failed\n");
@@ -462,7 +467,8 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
 	}
 
 	if (do_free)
-		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL);
+		vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL,
+				    NULL);
 	else{
 		enic->fdir.stats.free--;
 		enic->fdir.stats.add++;
@@ -488,7 +494,7 @@ void enic_clsf_destroy(struct enic *enic)
 		key = enic->fdir.nodes[index];
 		if (key) {
 			vnic_dev_classifier(enic->vdev, CLSF_DEL,
-				&key->fltr_id, NULL);
+				&key->fltr_id, NULL, NULL);
 			rte_free(key);
 			enic->fdir.nodes[index] = NULL;
 		}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 2/8] net/enic: flow API skeleton
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
  2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 3/8] net/enic: flow API for NICs with advanced filters enabled John Daley
                               ` (6 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Stub callbacks for the generic flow API and a new FLOW debug define.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 config/common_base             |   1 +
 drivers/net/enic/Makefile      |   1 +
 drivers/net/enic/enic.h        |   1 +
 drivers/net/enic/enic_ethdev.c |  18 ++++-
 drivers/net/enic/enic_flow.c   | 151 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 169 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/enic/enic_flow.c

diff --git a/config/common_base b/config/common_base
index 8907bea36..67ef2ece0 100644
--- a/config/common_base
+++ b/config/common_base
@@ -254,6 +254,7 @@ CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
 #
 CONFIG_RTE_LIBRTE_ENIC_PMD=y
 CONFIG_RTE_LIBRTE_ENIC_DEBUG=n
+CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW=n
 
 #
 # Compile burst-oriented Netronome NFP PMD driver
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 2c7496dc5..db48ff2da 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_clsf.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_res.c
+SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_cq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_wq.c
 SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_dev.c
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index 2358a7f6f..9647ca21f 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -306,4 +306,5 @@ void copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
 void copy_fltr_v2(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
 		  struct rte_eth_fdir_masks *masks);
+extern const struct rte_flow_ops enic_flow_ops;
 #endif /* _ENIC_H_ */
diff --git a/drivers/net/enic/enic_ethdev.c b/drivers/net/enic/enic_ethdev.c
index 8e16a71b7..a8e167681 100644
--- a/drivers/net/enic/enic_ethdev.c
+++ b/drivers/net/enic/enic_ethdev.c
@@ -116,13 +116,25 @@ enicpmd_dev_filter_ctrl(struct rte_eth_dev *dev,
 		     enum rte_filter_op filter_op,
 		     void *arg)
 {
-	int ret = -EINVAL;
+	int ret = 0;
+
+	ENICPMD_FUNC_TRACE();
 
-	if (RTE_ETH_FILTER_FDIR == filter_type)
+	switch (filter_type) {
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &enic_flow_ops;
+		break;
+	case RTE_ETH_FILTER_FDIR:
 		ret = enicpmd_fdir_ctrl_func(dev, filter_op, arg);
-	else
+		break;
+	default:
 		dev_warning(enic, "Filter type (%d) not supported",
 			filter_type);
+		ret = -EINVAL;
+		break;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
new file mode 100644
index 000000000..a5c6ebd0a
--- /dev/null
+++ b/drivers/net/enic/enic_flow.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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.
+ *
+ */
+
+#include <errno.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_flow_driver.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+
+#include "enic_compat.h"
+#include "enic.h"
+#include "vnic_dev.h"
+#include "vnic_nic.h"
+
+#ifdef RTE_LIBRTE_ENIC_DEBUG_FLOW
+#define FLOW_TRACE() \
+	RTE_LOG(DEBUG, PMD, "%s()\n", __func__)
+#define FLOW_LOG(level, fmt, args...) \
+	RTE_LOG(level, PMD, fmt, ## args)
+#else
+#define FLOW_TRACE() do { } while (0)
+#define FLOW_LOG(level, fmt, args...) do { } while (0)
+#endif
+
+/*
+ * The following functions are callbacks for Generic flow API.
+ */
+
+/**
+ * Validate a flow supported by the NIC.
+ *
+ * @see rte_flow_validate()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
+		   const struct rte_flow_item pattern[],
+		   const struct rte_flow_action actions[],
+		   struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Create a flow supported by the NIC.
+ *
+ * @see rte_flow_create()
+ * @see rte_flow_ops
+ */
+static struct rte_flow *
+enic_flow_create(struct rte_eth_dev *dev,
+		 const struct rte_flow_attr *attrs,
+		 const struct rte_flow_item pattern[],
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)attrs;
+	(void)pattern;
+	(void)actions;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return NULL;
+}
+
+/**
+ * Destroy a flow supported by the NIC.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  __rte_unused struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)flow;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flush all flows on the device.
+ *
+ * @see rte_flow_flush()
+ * @see rte_flow_ops
+ */
+static int
+enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	(void)dev;
+	(void)error;
+
+	FLOW_TRACE();
+
+	return 0;
+}
+
+/**
+ * Flow callback registration.
+ *
+ * @see rte_flow_ops
+ */
+const struct rte_flow_ops enic_flow_ops = {
+	.validate = enic_flow_validate,
+	.create = enic_flow_create,
+	.destroy = enic_flow_destroy,
+	.flush = enic_flow_flush,
+};
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 3/8] net/enic: flow API for NICs with advanced filters enabled
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
  2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
  2017-05-17 22:38             ` [PATCH v4 2/8] net/enic: flow API skeleton John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 4/8] net/enic: flow API mark and flag support John Daley
                               ` (5 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode enabled via the UCS management interface. This enables:
Attributes: ingress
Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
       ipv6, udp, tcp.
Actions: queue, and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---

copy functions return positive errnos because they are then used in
rte_flow_error_set() which takes a positive errno. checkpatch flags
these as warnings, but in my option the implementation should be
acceptable in this case.

 drivers/net/enic/enic.h      |  14 +-
 drivers/net/enic/enic_flow.c | 960 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/enic/enic_main.c |   3 +
 drivers/net/enic/enic_res.c  |  15 +
 4 files changed, 975 insertions(+), 17 deletions(-)

diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index 9647ca21f..e28f22352 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -80,6 +80,8 @@
 #define PCI_DEVICE_ID_CISCO_VIC_ENET         0x0043  /* ethernet vnic */
 #define PCI_DEVICE_ID_CISCO_VIC_ENET_VF      0x0071  /* enet SRIOV VF */
 
+/* Special Filter id for non-specific packet flagging. Don't change value */
+#define ENIC_MAGIC_FILTER_ID 0xffff
 
 #define ENICPMD_FDIR_MAX           64
 
@@ -111,6 +113,12 @@ struct enic_memzone_entry {
 	LIST_ENTRY(enic_memzone_entry) entries;
 };
 
+struct rte_flow {
+	LIST_ENTRY(rte_flow) next;
+	u16 enic_filter_id;
+	struct filter_v2 enic_filter;
+};
+
 /* Per-instance private data structure */
 struct enic {
 	struct enic *next;
@@ -135,7 +143,9 @@ struct enic {
 	int link_status;
 	u8 hw_ip_checksum;
 	u16 max_mtu;
-	u16 adv_filters;
+	u8 adv_filters;
+	u32 flow_filter_mode;
+	u8 filter_tags;
 
 	unsigned int flags;
 	unsigned int priv_flags;
@@ -170,6 +180,8 @@ struct enic {
 	rte_spinlock_t memzone_list_lock;
 	rte_spinlock_t mtu_lock;
 
+	LIST_HEAD(enic_flows, rte_flow) flows;
+	rte_spinlock_t flows_lock;
 };
 
 /* Get the CQ index from a Start of Packet(SOP) RQ index */
diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index a5c6ebd0a..44efe4b81 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -52,6 +52,911 @@
 #define FLOW_LOG(level, fmt, args...) do { } while (0)
 #endif
 
+/** Info about how to copy items into enic filters. */
+struct enic_items {
+	/** Function for copying and validating an item. */
+	int (*copy_item)(const struct rte_flow_item *item,
+			 struct filter_v2 *enic_filter, u8 *inner_ofst);
+	/** List of valid previous items. */
+	const enum rte_flow_item_type * const prev_items;
+	/** True if it's OK for this item to be the first item. For some NIC
+	 * versions, it's invalid to start the stack above layer 3.
+	 */
+	const u8 valid_start_item;
+};
+
+/** Filtering capabilities for various NIC and firmware versions. */
+struct enic_filter_cap {
+	/** list of valid items and their handlers and attributes. */
+	const struct enic_items *item_info;
+};
+
+/* functions for copying flow actions into enic actions */
+typedef int (copy_action_fn)(const struct rte_flow_action actions[],
+			     struct filter_action_v2 *enic_action);
+
+/* functions for copying items into enic filters */
+typedef int(enic_copy_item_fn)(const struct rte_flow_item *item,
+			  struct filter_v2 *enic_filter, u8 *inner_ofst);
+
+/** Action capabilities for various NICs. */
+struct enic_action_cap {
+	/** list of valid actions */
+	const enum rte_flow_action_type *actions;
+	/** copy function for a particular NIC */
+	int (*copy_fn)(const struct rte_flow_action actions[],
+		       struct filter_action_v2 *enic_action);
+};
+
+/* Forward declarations */
+static enic_copy_item_fn enic_copy_item_eth_v2;
+static enic_copy_item_fn enic_copy_item_vlan_v2;
+static enic_copy_item_fn enic_copy_item_ipv4_v2;
+static enic_copy_item_fn enic_copy_item_ipv6_v2;
+static enic_copy_item_fn enic_copy_item_udp_v2;
+static enic_copy_item_fn enic_copy_item_tcp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_sctp_v2;
+static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v2;
+
+/** NICs with Advanced filters enabled */
+static const struct enic_items enic_items_v3[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/** Filtering capabilities indexed this NICs supported filter type. */
+static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_DPDK_1] = {
+		.item_info = enic_items_v3,
+	},
+};
+
+/** Supported actions for newer NICs */
+static const enum rte_flow_action_type enic_supported_actions_v2[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_MARK,
+	RTE_FLOW_ACTION_TYPE_FLAG,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
+/** Action capabilities indexed by NIC version information */
+static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_V2_ALL] = {
+		.actions = enic_supported_actions_v2,
+		.copy_fn = enic_copy_action_v2,
+	},
+};
+/**
+ * Copy ETH item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_eth_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	struct ether_hdr enic_spec;
+	struct ether_hdr enic_mask;
+	const struct rte_flow_item_eth *spec = item->spec;
+	const struct rte_flow_item_eth *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_eth_mask;
+
+	memcpy(enic_spec.d_addr.addr_bytes, spec->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_spec.s_addr.addr_bytes, spec->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+
+	memcpy(enic_mask.d_addr.addr_bytes, mask->dst.addr_bytes,
+	       ETHER_ADDR_LEN);
+	memcpy(enic_mask.s_addr.addr_bytes, mask->src.addr_bytes,
+	       ETHER_ADDR_LEN);
+	enic_spec.ether_type = spec->type;
+	enic_mask.ether_type = mask->type;
+
+	if (*inner_ofst == 0) {
+		/* outer header */
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].mask, &enic_mask,
+		       sizeof(struct ether_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L2].val, &enic_spec,
+		       sizeof(struct ether_hdr));
+	} else {
+		/* inner header */
+		if ((*inner_ofst + sizeof(struct ether_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		/* Offset into L5 where inner Ethernet header goes */
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       &enic_mask, sizeof(struct ether_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       &enic_spec, sizeof(struct ether_hdr));
+		*inner_ofst += sizeof(struct ether_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy VLAN item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   If zero, this is an outer header. If non-zero, this is the offset into L5
+ *   where the header begins.
+ */
+static int
+enic_copy_item_vlan_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vlan *spec = item->spec;
+	const struct rte_flow_item_vlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	/* Don't support filtering in tpid */
+	if (mask) {
+		if (mask->tpid != 0)
+			return ENOTSUP;
+	} else {
+		mask = &rte_flow_item_vlan_mask;
+		RTE_ASSERT(mask->tpid == 0);
+	}
+
+	if (*inner_ofst == 0) {
+		/* Outer header. Use the vlan mask/val fields */
+		gp->mask_vlan = mask->tci;
+		gp->val_vlan = spec->tci;
+	} else {
+		/* Inner header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct vlan_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct vlan_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct vlan_hdr));
+		*inner_ofst += sizeof(struct vlan_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv4 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv4 filtering.
+ */
+static int
+enic_copy_item_ipv4_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst == 0) {
+		/* Match IPv4 */
+		gp->mask_flags |= FILTER_GENERIC_1_IPV4;
+		gp->val_flags |= FILTER_GENERIC_1_IPV4;
+
+		/* Match all if no spec */
+		if (!spec)
+			return 0;
+
+		if (!mask)
+			mask = &rte_flow_item_ipv4_mask;
+
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+		       sizeof(struct ipv4_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+		       sizeof(struct ipv4_hdr));
+	} else {
+		/* Inner IPv4 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct ipv4_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct ipv4_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct ipv4_hdr));
+		*inner_ofst += sizeof(struct ipv4_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy IPv6 item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner IPv6 filtering.
+ */
+static int
+enic_copy_item_ipv6_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv6 *spec = item->spec;
+	const struct rte_flow_item_ipv6 *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match IPv6 */
+	gp->mask_flags |= FILTER_GENERIC_1_IPV6;
+	gp->val_flags |= FILTER_GENERIC_1_IPV6;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv6_mask;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].mask, &mask->hdr,
+		       sizeof(struct ipv6_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L3].val, &spec->hdr,
+		       sizeof(struct ipv6_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct ipv6_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct ipv6_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct ipv6_hdr));
+		*inner_ofst += sizeof(struct ipv6_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner UDP filtering.
+ */
+static int
+enic_copy_item_udp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match UDP */
+	gp->mask_flags |= FILTER_GENERIC_1_UDP;
+	gp->val_flags |= FILTER_GENERIC_1_UDP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+		       sizeof(struct udp_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+		       sizeof(struct udp_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct udp_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct udp_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct udp_hdr));
+		*inner_ofst += sizeof(struct udp_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner TCP filtering.
+ */
+static int
+enic_copy_item_tcp_v2(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	/* Match TCP */
+	gp->mask_flags |= FILTER_GENERIC_1_TCP;
+	gp->val_flags |= FILTER_GENERIC_1_TCP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		return ENOTSUP;
+
+	if (*inner_ofst == 0) {
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+		       sizeof(struct tcp_hdr));
+		memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+		       sizeof(struct tcp_hdr));
+	} else {
+		/* Inner IPv6 header. Mask/Val start at *inner_ofst into L5 */
+		if ((*inner_ofst + sizeof(struct tcp_hdr)) >
+		     FILTER_GENERIC_1_KEY_LEN)
+			return ENOTSUP;
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].mask[*inner_ofst],
+		       mask, sizeof(struct tcp_hdr));
+		memcpy(&gp->layer[FILTER_GENERIC_1_L5].val[*inner_ofst],
+		       spec, sizeof(struct tcp_hdr));
+		*inner_ofst += sizeof(struct tcp_hdr);
+	}
+	return 0;
+}
+
+/**
+ * Copy SCTP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. Don't support inner SCTP filtering.
+ */
+static int
+enic_copy_item_sctp_v2(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_sctp *spec = item->spec;
+	const struct rte_flow_item_sctp *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_sctp_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].mask, &mask->hdr,
+	       sizeof(struct sctp_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L4].val, &spec->hdr,
+	       sizeof(struct sctp_hdr));
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 2 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Must be 0. VxLAN headers always start at the beginning of L5.
+ */
+static int
+enic_copy_item_vxlan_v2(const struct rte_flow_item *item,
+			struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_vxlan *spec = item->spec;
+	const struct rte_flow_item_vxlan *mask = item->mask;
+	struct filter_generic_1 *gp = &enic_filter->u.generic_1;
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return EINVAL;
+
+	/* Match all if no spec */
+	if (!spec)
+		return 0;
+
+	if (!mask)
+		mask = &rte_flow_item_vxlan_mask;
+
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].mask, mask,
+	       sizeof(struct vxlan_hdr));
+	memcpy(gp->layer[FILTER_GENERIC_1_L5].val, spec,
+	       sizeof(struct vxlan_hdr));
+
+	*inner_ofst = sizeof(struct vxlan_hdr);
+	return 0;
+}
+
+/**
+ * Return 1 if current item is valid on top of the previous one.
+ *
+ * @param prev_item[in]
+ *   The item before this one in the pattern or RTE_FLOW_ITEM_TYPE_END if this
+ *   is the first item.
+ * @param item_info[in]
+ *   Info about this item, like valid previous items.
+ * @param is_first[in]
+ *   True if this the first item in the pattern.
+ */
+static int
+item_stacking_valid(enum rte_flow_item_type prev_item,
+		    const struct enic_items *item_info, u8 is_first_item)
+{
+	enum rte_flow_item_type const *allowed_items = item_info->prev_items;
+
+	FLOW_TRACE();
+
+	for (; *allowed_items != RTE_FLOW_ITEM_TYPE_END; allowed_items++) {
+		if (prev_item == *allowed_items)
+			return 1;
+	}
+
+	/* This is the first item in the stack. Check if that's cool */
+	if (is_first_item && item_info->valid_start_item)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Build the intenal enic filter structure from the provided pattern. The
+ * pattern is validated as the items are copied.
+ *
+ * @param pattern[in]
+ * @param items_info[in]
+ *   Info about this NICs item support, like valid previous items.
+ * @param enic_filter[out]
+ *   NIC specfilc filters derived from the pattern.
+ * @param error[out]
+ */
+static int
+enic_copy_filter(const struct rte_flow_item pattern[],
+		 const struct enic_items *items_info,
+		 struct filter_v2 *enic_filter,
+		 struct rte_flow_error *error)
+{
+	int ret;
+	const struct rte_flow_item *item = pattern;
+	u8 inner_ofst = 0; /* If encapsulated, ofst into L5 */
+	enum rte_flow_item_type prev_item;
+	const struct enic_items *item_info;
+
+	enic_filter->type = FILTER_DPDK_1;
+	u8 is_first_item = 1;
+
+	FLOW_TRACE();
+
+	prev_item = 0;
+
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		/* Get info about how to validate and copy the item. If NULL
+		 * is returned the nic does not support the item.
+		 */
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+
+		item_info = &items_info[item->type];
+
+		/* check to see if item stacking is valid */
+		if (!item_stacking_valid(prev_item, item_info, is_first_item))
+			goto stacking_error;
+
+		ret = item_info->copy_item(item, enic_filter, &inner_ofst);
+		if (ret)
+			goto item_not_supported;
+		prev_item = item->type;
+		is_first_item = 0;
+	}
+	return 0;
+
+item_not_supported:
+	rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_ITEM,
+			   NULL, "enic type error");
+	return -rte_errno;
+
+stacking_error:
+	rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+			   item, "stacking error");
+	return -rte_errno;
+}
+/**
+ * Build the intenal version 2 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v2(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			enic_action->flags |= FILTER_ACTION_RQ_STEERING_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_MARK: {
+			const struct rte_flow_action_mark *mark =
+				(const struct rte_flow_action_mark *)
+				actions->conf;
+
+			/* ENIC_MAGIC_FILTER_ID is reserved and is the highest
+			 * in the range of allows mark ids.
+			 */
+			if (mark->id >= ENIC_MAGIC_FILTER_ID)
+				return EINVAL;
+			enic_action->filter_id = mark->id;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_FLAG: {
+			enic_action->filter_id = ENIC_MAGIC_FILTER_ID;
+			enic_action->flags |= FILTER_ACTION_FILTER_ID_FLAG;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			continue;
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_V2;
+	return 0;
+}
+
+/** Check if the action is supported */
+static int
+enic_match_action(const struct rte_flow_action *action,
+		  const enum rte_flow_action_type *supported_actions)
+{
+	for (; *supported_actions != RTE_FLOW_ACTION_TYPE_END;
+	     supported_actions++) {
+		if (action->type == *supported_actions)
+			return 1;
+	}
+	return 0;
+}
+
+/** Get the NIC filter capabilties structure */
+static const struct enic_filter_cap *
+enic_get_filter_cap(struct enic *enic)
+{
+	/* FIXME: only support advanced filters for now */
+	if (enic->flow_filter_mode != FILTER_DPDK_1)
+		return (const struct enic_filter_cap *)NULL;
+
+	if (enic->flow_filter_mode)
+		return &enic_filter_cap[enic->flow_filter_mode];
+
+	return NULL;
+}
+
+/** Get the actions for this NIC version. */
+static const struct enic_action_cap *
+enic_get_action_cap(struct enic *enic)
+{
+	static const struct enic_action_cap *ea;
+
+	if (enic->filter_tags)
+		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	return ea;
+}
+/**
+ * Internal flow parse/validate function.
+ *
+ * @param dev[in]
+ *   This device pointer.
+ * @param pattern[in]
+ * @param actions[in]
+ * @param error[out]
+ * @param enic_filter[out]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[out]
+ *   Internal NIC action structure pointer.
+ */
+static int
+enic_flow_parse(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attrs,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct filter_v2 *enic_filter,
+		struct filter_action_v2 *enic_action)
+{
+	unsigned int ret = 0;
+	struct enic *enic = pmd_priv(dev);
+	const struct enic_filter_cap *enic_filter_cap;
+	const struct enic_action_cap *enic_action_cap;
+	const struct rte_flow_action *action;
+
+	FLOW_TRACE();
+
+	memset(enic_filter, 0, sizeof(*enic_filter));
+	memset(enic_action, 0, sizeof(*enic_action));
+
+	if (!pattern) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+				   NULL, "No pattern specified");
+		return -rte_errno;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				   NULL, "No action specified");
+		return -rte_errno;
+	}
+
+	if (attrs) {
+		if (attrs->group) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+					   NULL,
+					   "priority groups are not supported");
+			return -rte_errno;
+		} else if (attrs->priority) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					   NULL,
+					   "priorities are not supported");
+			return -rte_errno;
+		} else if (attrs->egress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
+					   NULL,
+					   "egress is not supported");
+			return -rte_errno;
+		} else if (!attrs->ingress) {
+			rte_flow_error_set(error, ENOTSUP,
+					   RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
+					   NULL,
+					   "only ingress is supported");
+			return -rte_errno;
+		}
+
+	} else {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ATTR,
+				   NULL, "No attribute specified");
+		return -rte_errno;
+	}
+
+	/* Verify Actions. */
+	enic_action_cap =  enic_get_action_cap(enic);
+	for (action = &actions[0]; action->type != RTE_FLOW_ACTION_TYPE_END;
+	     action++) {
+		if (action->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+		else if (!enic_match_action(action, enic_action_cap->actions))
+			break;
+	}
+	if (action->type != RTE_FLOW_ACTION_TYPE_END) {
+		rte_flow_error_set(error, EPERM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   action, "Invalid action.");
+		return -rte_errno;
+	}
+	ret = enic_action_cap->copy_fn(actions, enic_action);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Unsupported action.");
+		return -rte_errno;
+	}
+
+	/* Verify Flow items. If copying the filter from flow format to enic
+	 * format fails, the flow is not supported
+	 */
+	enic_filter_cap =  enic_get_filter_cap(enic);
+	if (enic_filter_cap == NULL) {
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+			   NULL, "Flow API not available");
+		return -rte_errno;
+	}
+	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
+				       enic_filter, error);
+	return ret;
+}
+
+/**
+ * Push filter/action to the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param enic_filter[in]
+ *   Internal NIC filter structure pointer.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static struct rte_flow *
+enic_flow_add_filter(struct enic *enic, struct filter_v2 *enic_filter,
+		   struct filter_action_v2 *enic_action,
+		   struct rte_flow_error *error)
+{
+	struct rte_flow *flow;
+	int ret;
+	u16 entry;
+
+	FLOW_TRACE();
+
+	flow = rte_calloc(__func__, 1, sizeof(*flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "cannot allocate flow memory");
+		return NULL;
+	}
+
+	/* entry[in] is the queue id, entry[out] is the filter Id for delete */
+	entry = enic_action->rq_idx;
+	ret = vnic_dev_classifier(enic->vdev, CLSF_ADD, &entry, enic_filter,
+				  enic_action);
+	if (!ret) {
+		flow->enic_filter_id = entry;
+		flow->enic_filter = *enic_filter;
+	} else {
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier error");
+		rte_free(flow);
+		return NULL;
+	}
+	return flow;
+}
+
+/**
+ * Remove filter/action from the NIC.
+ *
+ * @param enic[in]
+ *   Device structure pointer.
+ * @param filter_id[in]
+ *   Id of NIC filter.
+ * @param enic_action[in]
+ *   Internal NIC action structure pointer.
+ * @param error[out]
+ */
+static int
+enic_flow_del_filter(struct enic *enic, u16 filter_id,
+		   struct rte_flow_error *error)
+{
+	int ret;
+
+	FLOW_TRACE();
+
+	ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL, NULL);
+	if (!ret)
+		rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE,
+				   NULL, "vnic_dev_classifier failed");
+	return ret;
+}
+
 /*
  * The following functions are callbacks for Generic flow API.
  */
@@ -68,15 +973,15 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 		   const struct rte_flow_action actions[],
 		   struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	int ret;
 
 	FLOW_TRACE();
 
-	return 0;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
+			       &enic_filter, &enic_action);
+	return ret;
 }
 
 /**
@@ -92,15 +997,27 @@ enic_flow_create(struct rte_eth_dev *dev,
 		 const struct rte_flow_action actions[],
 		 struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)attrs;
-	(void)pattern;
-	(void)actions;
-	(void)error;
+	int ret;
+	struct filter_v2 enic_filter;
+	struct filter_action_v2 enic_action;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
-	return NULL;
+	ret = enic_flow_parse(dev, attrs, pattern, actions, error, &enic_filter,
+			      &enic_action);
+	if (ret < 0)
+		return NULL;
+
+	rte_spinlock_lock(&enic->flows_lock);
+	flow = enic_flow_add_filter(enic, &enic_filter, &enic_action,
+				    error);
+	if (flow)
+		LIST_INSERT_HEAD(&enic->flows, flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
+
+	return flow;
 }
 
 /**
@@ -113,11 +1030,14 @@ static int
 enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 		  __rte_unused struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+	enic_flow_del_filter(enic, flow->enic_filter_id, error);
+	LIST_REMOVE(flow, next);
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
@@ -130,11 +1050,19 @@ enic_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow,
 static int
 enic_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
-	(void)dev;
-	(void)error;
+	struct rte_flow *flow;
+	struct enic *enic = pmd_priv(dev);
 
 	FLOW_TRACE();
 
+	rte_spinlock_lock(&enic->flows_lock);
+
+	while (!LIST_EMPTY(&enic->flows)) {
+		flow = LIST_FIRST(&enic->flows);
+		enic_flow_del_filter(enic, flow->enic_filter_id, error);
+		LIST_REMOVE(flow, next);
+	}
+	rte_spinlock_unlock(&enic->flows_lock);
 	return 0;
 }
 
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index d0262418d..b4c6264ab 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -1314,6 +1314,9 @@ static int enic_dev_init(struct enic *enic)
 
 	vnic_dev_set_reset_flag(enic->vdev, 0);
 
+	LIST_INIT(&enic->flows);
+	rte_spinlock_init(&enic->flows_lock);
+
 	/* set up link status checking */
 	vnic_dev_notify_set(enic->vdev, -1); /* No Intr for notify */
 
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c
index 867bd25c4..e4b80d49c 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/enic/enic_res.c
@@ -104,6 +104,21 @@ int enic_get_vnic_config(struct enic *enic)
 	dev_info(enic, "Advanced Filters %savailable\n", ((enic->adv_filters)
 		 ? "" : "not "));
 
+	err = vnic_dev_capable_filter_mode(enic->vdev, &enic->flow_filter_mode,
+					   &enic->filter_tags);
+	if (err) {
+		dev_err(enic_get_dev(enic),
+			"Error getting filter modes, %d\n", err);
+		return err;
+	}
+
+	dev_info(enic, "Flow api filter mode: %s, Filter tagging %savailable\n",
+		((enic->flow_filter_mode == FILTER_DPDK_1) ? "DPDK" :
+		((enic->flow_filter_mode == FILTER_USNIC_IP) ? "USNIC" :
+		((enic->flow_filter_mode == FILTER_IPV4_5TUPLE) ? "5TUPLE" :
+		"NONE"))),
+		((enic->filter_tags) ? "" : "not "));
+
 	c->wq_desc_count =
 		min_t(u32, ENIC_MAX_WQ_DESCS,
 		max_t(u32, ENIC_MIN_WQ_DESCS,
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 4/8] net/enic: flow API mark and flag support
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (2 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 3/8] net/enic: flow API for NICs with advanced filters enabled John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 5/8] net/enic: flow API for NICs with advanced filters disabled John Daley
                               ` (4 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

For VICs with filter tagging, support the MARK and FLAG actions
by setting appropriate mbuf ol_flags if there is a filter match.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_rxtx.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/net/enic/enic_rxtx.c b/drivers/net/enic/enic_rxtx.c
index ba0cfd01a..5867acf19 100644
--- a/drivers/net/enic/enic_rxtx.c
+++ b/drivers/net/enic/enic_rxtx.c
@@ -253,8 +253,20 @@ enic_cq_rx_to_pkt_flags(struct cq_desc *cqd, struct rte_mbuf *mbuf)
 	}
 	mbuf->vlan_tci = vlan_tci;
 
-	/* RSS flag */
-	if (enic_cq_rx_desc_rss_type(cqrd)) {
+	if ((cqd->type_color & CQ_DESC_TYPE_MASK) == CQ_DESC_TYPE_CLASSIFIER) {
+		struct cq_enet_rq_clsf_desc *clsf_cqd;
+		uint16_t filter_id;
+		clsf_cqd = (struct cq_enet_rq_clsf_desc *)cqd;
+		filter_id = clsf_cqd->filter_id;
+		if (filter_id) {
+			pkt_flags |= PKT_RX_FDIR;
+			if (filter_id != ENIC_MAGIC_FILTER_ID) {
+				mbuf->hash.fdir.hi = clsf_cqd->filter_id;
+				pkt_flags |= PKT_RX_FDIR_ID;
+			}
+		}
+	} else if (enic_cq_rx_desc_rss_type(cqrd)) {
+		/* RSS flag */
 		pkt_flags |= PKT_RX_RSS_HASH;
 		mbuf->hash.rss = enic_cq_rx_desc_rss_hash(cqrd);
 	}
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 5/8] net/enic: flow API for NICs with advanced filters disabled
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (3 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 4/8] net/enic: flow API mark and flag support John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 6/8] net/enic: flow API for Legacy NICs John Daley
                               ` (3 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Flow support for 1300 series adapters with the 'Advanced Filter'
mode disabled via the UCS management interface. This allows:
Attributes: ingress
Items: Outer eth, ipv4, ipv6, udp, sctp, tcp, vxlan. Inner eth, ipv4,
       ipv6, udp, tcp.
Actions: queue and void
Selectors: 'is', 'spec' and 'mask'. 'last' is not supported

With advanced filters disabled, an IPv4 or IPv6 item must be specified
in the pattern.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 135 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 133 insertions(+), 2 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index 44efe4b81..a32e25ef9 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -98,8 +98,85 @@ static enic_copy_item_fn enic_copy_item_tcp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_sctp_v2;
 static enic_copy_item_fn enic_copy_item_vxlan_v2;
+static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
+/**
+ * NICs have Advanced Filters capability but they are disabled. This means
+ * that layer 3 must be specified.
+ */
+static const struct enic_items enic_items_v2[] = {
+	[RTE_FLOW_ITEM_TYPE_ETH] = {
+		.copy_item = enic_copy_item_eth_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_VXLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VLAN] = {
+		.copy_item = enic_copy_item_vlan_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_IPV6] = {
+		.copy_item = enic_copy_item_ipv6_v2,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_ETH,
+			       RTE_FLOW_ITEM_TYPE_VLAN,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_SCTP] = {
+		.copy_item = enic_copy_item_sctp_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_IPV6,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_VXLAN] = {
+		.copy_item = enic_copy_item_vxlan_v2,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_UDP,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
 /** NICs with Advanced filters enabled */
 static const struct enic_items enic_items_v3[] = {
 	[RTE_FLOW_ITEM_TYPE_ETH] = {
@@ -175,11 +252,20 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilities indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_USNIC_IP] = {
+		.item_info = enic_items_v2,
+	},
 	[FILTER_DPDK_1] = {
 		.item_info = enic_items_v3,
 	},
 };
 
+/** Supported actions for older NICs */
+static const enum rte_flow_action_type enic_supported_actions_v1[] = {
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+	RTE_FLOW_ACTION_TYPE_END,
+};
+
 /** Supported actions for newer NICs */
 static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 	RTE_FLOW_ACTION_TYPE_QUEUE,
@@ -190,6 +276,10 @@ static const enum rte_flow_action_type enic_supported_actions_v2[] = {
 
 /** Action capabilities indexed by NIC version information */
 static const struct enic_action_cap enic_action_cap[] = {
+	[FILTER_ACTION_RQ_STEERING_FLAG] = {
+		.actions = enic_supported_actions_v1,
+		.copy_fn = enic_copy_action_v1,
+	},
 	[FILTER_ACTION_V2_ALL] = {
 		.actions = enic_supported_actions_v2,
 		.copy_fn = enic_copy_action_v2,
@@ -640,7 +730,6 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 	enum rte_flow_item_type prev_item;
 	const struct enic_items *item_info;
 
-	enic_filter->type = FILTER_DPDK_1;
 	u8 is_first_item = 1;
 
 	FLOW_TRACE();
@@ -678,6 +767,44 @@ enic_copy_filter(const struct rte_flow_item pattern[],
 			   item, "stacking error");
 	return -rte_errno;
 }
+
+/**
+ * Build the intenal version 1 NIC action structure from the provided pattern.
+ * The pattern is validated as the items are copied.
+ *
+ * @param actions[in]
+ * @param enic_action[out]
+ *   NIC specfilc actions derived from the actions.
+ * @param error[out]
+ */
+static int
+enic_copy_action_v1(const struct rte_flow_action actions[],
+		    struct filter_action_v2 *enic_action)
+{
+	FLOW_TRACE();
+
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		if (actions->type == RTE_FLOW_ACTION_TYPE_VOID)
+			continue;
+
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE: {
+			const struct rte_flow_action_queue *queue =
+				(const struct rte_flow_action_queue *)
+				actions->conf;
+			enic_action->rq_idx =
+				enic_rte_rq_idx_to_sop_idx(queue->index);
+			break;
+		}
+		default:
+			RTE_ASSERT(0);
+			break;
+		}
+	}
+	enic_action->type = FILTER_ACTION_RQ_STEERING;
+	return 0;
+}
+
 /**
  * Build the intenal version 2 NIC action structure from the provided pattern.
  * The pattern is validated as the items are copied.
@@ -752,7 +879,8 @@ static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
 	/* FIXME: only support advanced filters for now */
-	if (enic->flow_filter_mode != FILTER_DPDK_1)
+	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
+	   (enic->flow_filter_mode != FILTER_USNIC_IP))
 		return (const struct enic_filter_cap *)NULL;
 
 	if (enic->flow_filter_mode)
@@ -769,6 +897,8 @@ enic_get_action_cap(struct enic *enic)
 
 	if (enic->filter_tags)
 		ea = &enic_action_cap[FILTER_ACTION_V2_ALL];
+	else
+		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
 /**
@@ -881,6 +1011,7 @@ enic_flow_parse(struct rte_eth_dev *dev,
 			   NULL, "Flow API not available");
 		return -rte_errno;
 	}
+	enic_filter->type = enic->flow_filter_mode;
 	ret = enic_copy_filter(pattern, enic_filter_cap->item_info,
 				       enic_filter, error);
 	return ret;
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 6/8] net/enic: flow API for Legacy NICs
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (4 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 5/8] net/enic: flow API for NICs with advanced filters disabled John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 7/8] net/enic: flow API debug John Daley
                               ` (2 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

5-tuple exact Flow support for 1200 series adapters. This allows:
Attributes: ingress
Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
       addresses and ports and all must be specified).
Actions: queue and void
Selectors: 'is'

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---

copy functions return positive errnos because they are then used in
rte_flow_error_set() which takes a positive errno. checkpatch flags
these as warnings, but in my option the implementation should be
acceptable in this case.

 drivers/net/enic/enic_flow.c | 206 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 201 insertions(+), 5 deletions(-)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index a32e25ef9..c20ff86b1 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -89,6 +89,9 @@ struct enic_action_cap {
 };
 
 /* Forward declarations */
+static enic_copy_item_fn enic_copy_item_ipv4_v1;
+static enic_copy_item_fn enic_copy_item_udp_v1;
+static enic_copy_item_fn enic_copy_item_tcp_v1;
 static enic_copy_item_fn enic_copy_item_eth_v2;
 static enic_copy_item_fn enic_copy_item_vlan_v2;
 static enic_copy_item_fn enic_copy_item_ipv4_v2;
@@ -102,6 +105,36 @@ static copy_action_fn enic_copy_action_v1;
 static copy_action_fn enic_copy_action_v2;
 
 /**
+ * Legacy NICs or NICs with outdated firmware. Only 5-tuple perfect match
+ * is supported.
+ */
+static const struct enic_items enic_items_v1[] = {
+	[RTE_FLOW_ITEM_TYPE_IPV4] = {
+		.copy_item = enic_copy_item_ipv4_v1,
+		.valid_start_item = 1,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_UDP] = {
+		.copy_item = enic_copy_item_udp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	[RTE_FLOW_ITEM_TYPE_TCP] = {
+		.copy_item = enic_copy_item_tcp_v1,
+		.valid_start_item = 0,
+		.prev_items = (const enum rte_flow_item_type[]) {
+			       RTE_FLOW_ITEM_TYPE_IPV4,
+			       RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+};
+
+/**
  * NICs have Advanced Filters capability but they are disabled. This means
  * that layer 3 must be specified.
  */
@@ -252,6 +285,9 @@ static const struct enic_items enic_items_v3[] = {
 
 /** Filtering capabilities indexed this NICs supported filter type. */
 static const struct enic_filter_cap enic_filter_cap[] = {
+	[FILTER_IPV4_5TUPLE] = {
+		.item_info = enic_items_v1,
+	},
 	[FILTER_USNIC_IP] = {
 		.item_info = enic_items_v2,
 	},
@@ -285,6 +321,171 @@ static const struct enic_action_cap enic_action_cap[] = {
 		.copy_fn = enic_copy_action_v2,
 	},
 };
+
+static int
+mask_exact_match(const u8 *supported, const u8 *supplied,
+		 unsigned int size)
+{
+	unsigned int i;
+	for (i = 0; i < size; i++) {
+		if (supported[i] != supplied[i])
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Copy IPv4 item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_ipv4_v1(const struct rte_flow_item *item,
+		       struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct ipv4_hdr supported_mask = {
+		.src_addr = 0xffffffff,
+		.dst_addr = 0xffffffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_ipv4_mask;
+
+	/* This is an exact match filter, both fields must be set */
+	if (!spec || !spec->hdr.src_addr || !spec->hdr.dst_addr) {
+		FLOW_LOG(ERR, "IPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "IPv4 exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_addr = spec->hdr.src_addr;
+	enic_5tup->dst_addr = spec->hdr.dst_addr;
+
+	return 0;
+}
+
+/**
+ * Copy UDP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_udp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct udp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_udp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "UDP exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			      (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "UDP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_UDP;
+
+	return 0;
+}
+
+/**
+ * Copy TCP item into version 1 NIC filter.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param enic_filter[out]
+ *   Partially filled in NIC filter structure.
+ * @param inner_ofst[in]
+ *   Should always be 0 for version 1.
+ */
+static int
+enic_copy_item_tcp_v1(const struct rte_flow_item *item,
+		      struct filter_v2 *enic_filter, u8 *inner_ofst)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+	struct filter_ipv4_5tuple *enic_5tup = &enic_filter->u.ipv4;
+	struct tcp_hdr supported_mask = {
+		.src_port = 0xffff,
+		.dst_port = 0xffff,
+	};
+
+	FLOW_TRACE();
+
+	if (*inner_ofst)
+		return ENOTSUP;
+
+	if (!mask)
+		mask = &rte_flow_item_tcp_mask;
+
+	/* This is an exact match filter, both ports must be set */
+	if (!spec || !spec->hdr.src_port || !spec->hdr.dst_port) {
+		FLOW_LOG(ERR, "TCPIPv4 exact match src/dst addr");
+		return ENOTSUP;
+	}
+
+	/* check that the suppied mask exactly matches capabilty */
+	if (!mask_exact_match((const u8 *)&supported_mask,
+			     (const u8 *)item->mask, sizeof(*mask))) {
+		FLOW_LOG(ERR, "TCP exact match mask");
+		return ENOTSUP;
+	}
+
+	enic_filter->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+	enic_5tup->src_port = spec->hdr.src_port;
+	enic_5tup->dst_port = spec->hdr.dst_port;
+	enic_5tup->protocol = PROTO_TCP;
+
+	return 0;
+}
+
 /**
  * Copy ETH item into version 2 NIC filter.
  *
@@ -878,11 +1079,6 @@ enic_match_action(const struct rte_flow_action *action,
 static const struct enic_filter_cap *
 enic_get_filter_cap(struct enic *enic)
 {
-	/* FIXME: only support advanced filters for now */
-	if ((enic->flow_filter_mode != FILTER_DPDK_1) &&
-	   (enic->flow_filter_mode != FILTER_USNIC_IP))
-		return (const struct enic_filter_cap *)NULL;
-
 	if (enic->flow_filter_mode)
 		return &enic_filter_cap[enic->flow_filter_mode];
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 7/8] net/enic: flow API debug
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (5 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 6/8] net/enic: flow API for Legacy NICs John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-17 22:38             ` [PATCH v4 8/8] net/enic: flow API documentation John Daley
  2017-05-22 10:05             ` [PATCH v4 0/8] enic flow api support Ferruh Yigit
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Added a debug function to print enic filters and actions when
rte_validate_flow is called. Compiled in CONFIG_RTE_LIBRTE_ENIC_DEBUG_FLOW
is enabled and log level is INFO.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 drivers/net/enic/enic_flow.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/drivers/net/enic/enic_flow.c b/drivers/net/enic/enic_flow.c
index c20ff86b1..a728d0777 100644
--- a/drivers/net/enic/enic_flow.c
+++ b/drivers/net/enic/enic_flow.c
@@ -1097,6 +1097,142 @@ enic_get_action_cap(struct enic *enic)
 		ea = &enic_action_cap[FILTER_ACTION_RQ_STEERING_FLAG];
 	return ea;
 }
+
+/* Debug function to dump internal NIC action structure. */
+static void
+enic_dump_actions(const struct filter_action_v2 *ea)
+{
+	if (ea->type == FILTER_ACTION_RQ_STEERING) {
+		FLOW_LOG(INFO, "Action(V1), queue: %u\n", ea->rq_idx);
+	} else if (ea->type == FILTER_ACTION_V2) {
+		FLOW_LOG(INFO, "Actions(V2)\n");
+		if (ea->flags & FILTER_ACTION_RQ_STEERING_FLAG)
+			FLOW_LOG(INFO, "\tqueue: %u\n",
+			       enic_sop_rq_idx_to_rte_idx(ea->rq_idx));
+		if (ea->flags & FILTER_ACTION_FILTER_ID_FLAG)
+			FLOW_LOG(INFO, "\tfilter_id: %u\n", ea->filter_id);
+	}
+}
+
+/* Debug function to dump internal NIC filter structure. */
+static void
+enic_dump_filter(const struct filter_v2 *filt)
+{
+	const struct filter_generic_1 *gp;
+	int i, j, mbyte;
+	char buf[128], *bp;
+	char ip4[16], ip6[16], udp[16], tcp[16], tcpudp[16], ip4csum[16];
+	char l4csum[16], ipfrag[16];
+
+	switch (filt->type) {
+	case FILTER_IPV4_5TUPLE:
+		FLOW_LOG(INFO, "FILTER_IPV4_5TUPLE\n");
+		break;
+	case FILTER_USNIC_IP:
+	case FILTER_DPDK_1:
+		/* FIXME: this should be a loop */
+		gp = &filt->u.generic_1;
+		FLOW_LOG(INFO, "Filter: vlan: 0x%04x, mask: 0x%04x\n",
+		       gp->val_vlan, gp->mask_vlan);
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV4)
+			sprintf(ip4, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip4(y)" : "ip4(n)");
+		else
+			sprintf(ip4, "%s ", "ip4(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPV6)
+			sprintf(ip6, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPV4)
+				 ? "ip6(y)" : "ip6(n)");
+		else
+			sprintf(ip6, "%s ", "ip6(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_UDP)
+			sprintf(udp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_UDP)
+				 ? "udp(y)" : "udp(n)");
+		else
+			sprintf(udp, "%s ", "udp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP)
+			sprintf(tcp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP)
+				 ? "tcp(y)" : "tcp(n)");
+		else
+			sprintf(tcp, "%s ", "tcp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+			sprintf(tcpudp, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_TCP_OR_UDP)
+				 ? "tcpudp(y)" : "tcpudp(n)");
+		else
+			sprintf(tcpudp, "%s ", "tcpudp(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IP4SUM_OK)
+			sprintf(ip4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IP4SUM_OK)
+				 ? "ip4csum(y)" : "ip4csum(n)");
+		else
+			sprintf(ip4csum, "%s ", "ip4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_L4SUM_OK)
+			sprintf(l4csum, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_L4SUM_OK)
+				 ? "l4csum(y)" : "l4csum(n)");
+		else
+			sprintf(l4csum, "%s ", "l4csum(x)");
+
+		if (gp->mask_flags & FILTER_GENERIC_1_IPFRAG)
+			sprintf(ipfrag, "%s ",
+				(gp->val_flags & FILTER_GENERIC_1_IPFRAG)
+				 ? "ipfrag(y)" : "ipfrag(n)");
+		else
+			sprintf(ipfrag, "%s ", "ipfrag(x)");
+		FLOW_LOG(INFO, "\tFlags: %s%s%s%s%s%s%s%s\n", ip4, ip6, udp,
+			 tcp, tcpudp, ip4csum, l4csum, ipfrag);
+
+		for (i = 0; i < FILTER_GENERIC_1_NUM_LAYERS; i++) {
+			mbyte = FILTER_GENERIC_1_KEY_LEN - 1;
+			while (mbyte && !gp->layer[i].mask[mbyte])
+				mbyte--;
+			if (mbyte == 0)
+				continue;
+
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].mask[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u mask: %s\n", i + 2, buf);
+			bp = buf;
+			for (j = 0; j <= mbyte; j++) {
+				sprintf(bp, "%02x",
+					gp->layer[i].val[j]);
+				bp += 2;
+			}
+			*bp = '\0';
+			FLOW_LOG(INFO, "\tL%u  val: %s\n", i + 2, buf);
+		}
+		break;
+	default:
+		FLOW_LOG(INFO, "FILTER UNKNOWN\n");
+		break;
+	}
+}
+
+/* Debug function to dump internal NIC flow structures. */
+static void
+enic_dump_flow(const struct filter_action_v2 *ea, const struct filter_v2 *filt)
+{
+	enic_dump_filter(filt);
+	enic_dump_actions(ea);
+}
+
+
 /**
  * Internal flow parse/validate function.
  *
@@ -1308,6 +1444,8 @@ enic_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attrs,
 
 	ret = enic_flow_parse(dev, attrs, pattern, actions, error,
 			       &enic_filter, &enic_action);
+	if (!ret)
+		enic_dump_flow(&enic_action, &enic_filter);
 	return ret;
 }
 
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v4 8/8] net/enic: flow API documentation
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (6 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 7/8] net/enic: flow API debug John Daley
@ 2017-05-17 22:38             ` John Daley
  2017-05-22 10:05             ` [PATCH v4 0/8] enic flow api support Ferruh Yigit
  8 siblings, 0 replies; 41+ messages in thread
From: John Daley @ 2017-05-17 22:38 UTC (permalink / raw)
  To: ferruh.yigit; +Cc: dev, John Daley

Update enic NIC guide, release notes and add flow API to the
supported features list.

Signed-off-by: John Daley <johndale@cisco.com>
Reviewed-by: Nelson Escobar <neescoba@cisco.com>
---
 doc/guides/nics/enic.rst               | 52 ++++++++++++++++++++++++++++++++++
 doc/guides/nics/features/enic.ini      |  1 +
 doc/guides/rel_notes/release_17_08.rst |  6 ++++
 3 files changed, 59 insertions(+)

diff --git a/doc/guides/nics/enic.rst b/doc/guides/nics/enic.rst
index 89a301585..cb5ae1250 100644
--- a/doc/guides/nics/enic.rst
+++ b/doc/guides/nics/enic.rst
@@ -213,6 +213,45 @@ or ``vfio`` in non-IOMMU mode.
 Please see :ref:`Limitations <enic_limitations>` for limitations in
 the use of SR-IOV.
 
+.. _enic-genic-flow-api:
+
+Generic Flow API support
+------------------------
+
+Generic Flow API is supported. The baseline support is:
+
+- **1200 series VICs**
+
+  5-tuple exact Flow support for 1200 series adapters. This allows:
+
+  - Attributes: ingress
+  - Items: ipv4, ipv6, udp, tcp (must exactly match src/dst IP
+    addresses and ports and all must be specified).
+  - Actions: queue and void
+  - Selectors: 'is'
+
+- **1300 series VICS with Advanced filters disabled**
+
+  With advanced filters disabled, an IPv4 or IPv6 item must be specified
+  in the pattern.
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth, ipv4, ipv6, udp, tcp
+  - Actions: queue and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+  - In total, up to 64 bytes of mask is allowed across all haeders
+
+- **1300 series VICS with Advanced filters enabled**
+
+  - Attributes: ingress
+  - Items: eth, ipv4, ipv6, udp, tcp, vxlan, inner eth, ipv4, ipv6, udp, tcp
+  - Actions: queue, mark, flag and void
+  - Selectors: 'is', 'spec' and 'mask'. 'last' is not supported
+  - In total, up to 64 bytes of mask is allowed across all haeders
+
+More features may be added in future firmware and new versions of the VIC.
+Please refer to the release notes.
+
 .. _enic_limitations:
 
 Limitations
@@ -260,9 +299,21 @@ Limitations
   - The number of SR-IOV devices is limited to 256. Components on target system
     might limit this number to fewer than 256.
 
+- **Flow API**
+
+  - The number of filters that can be specified with the Generic Flow API is
+    dependent on how many header fields are being masked. Use 'flow create' in
+    a loop to determine how many filters your VIC will support (not more than
+    1000 for 1300 series VICs). Filter are checked for matching in the order they
+    were added. Since there currently is no grouping or priority support,
+    'catch-all' filters should be added last.
+
 How to build the suite
 ----------------------
 
+The build instructions for the DPDK suite should be followed. By default
+the ENIC PMD library will be built into the DPDK library.
+
 Refer to the document :ref:`compiling and testing a PMD for a NIC
 <pmd_build_and_test>` for details.
 
@@ -313,6 +364,7 @@ Supported features
 - Scattered Rx
 - MTU update
 - SR-IOV on UCS managed servers connected to Fabric Interconnects.
+- Flow API
 
 Known bugs and unsupported features in this release
 ---------------------------------------------------
diff --git a/doc/guides/nics/features/enic.ini b/doc/guides/nics/features/enic.ini
index 94e7f3cba..0de3ef53c 100644
--- a/doc/guides/nics/features/enic.ini
+++ b/doc/guides/nics/features/enic.ini
@@ -20,6 +20,7 @@ VLAN filter          = Y
 CRC offload          = Y
 VLAN offload         = Y
 Flow director        = Y
+Flow API             = Y
 L3 checksum offload  = Y
 L4 checksum offload  = Y
 Packet type parsing  = Y
diff --git a/doc/guides/rel_notes/release_17_08.rst b/doc/guides/rel_notes/release_17_08.rst
index 74aae10f7..e3a920438 100644
--- a/doc/guides/rel_notes/release_17_08.rst
+++ b/doc/guides/rel_notes/release_17_08.rst
@@ -41,6 +41,12 @@ New Features
      Also, make sure to start the actual text at the margin.
      =========================================================
 
+* **Added Generic Flow API support to enic.**
+
+  Flow API support for outer Ethernet, VLAN, IPv4, IPv6, UDP, TCP, SCTP, VxLAN
+  and inner Ethernet, VLAN, IPv4, IPv6, UDP and TCP pattern items with QUEUE,
+  MARK, FLAG and VOID actions for ingress traffic.
+
 
 Resolved Issues
 ---------------
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH v4 1/8] net/enic: bring NIC interface functions up to date
  2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
@ 2017-05-22  9:49               ` Ferruh Yigit
  2017-06-08 10:44               ` Jerin Jacob
  1 sibling, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-22  9:49 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 11:38 PM, John Daley wrote:
> Update the base functions for the Cisco VIC. These files are mostly
> common with other VIC drivers so are left alone is as much as possilbe.
> Includes in a new filter/action interface which is needed for Generic
> Flow API PMD support. Update FDIR code to use the new interface.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>
> ---
> 
> checkpatch style warning about multiple use of 'x' in ARRAY_SIZE macro should
> be waived in my opinion.

It is possible to use exiting RTE_DIM macro.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v4 0/8] enic flow api support
  2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
                               ` (7 preceding siblings ...)
  2017-05-17 22:38             ` [PATCH v4 8/8] net/enic: flow API documentation John Daley
@ 2017-05-22 10:05             ` Ferruh Yigit
  8 siblings, 0 replies; 41+ messages in thread
From: Ferruh Yigit @ 2017-05-22 10:05 UTC (permalink / raw)
  To: John Daley; +Cc: dev

On 5/17/2017 11:38 PM, John Daley wrote:
> Sorry for the inconvenience Ferruh, hopefully this set will work better.
> 
> V4 patchset changes:
> - include patch that was missing in V3 which caused compile errors
> - put mark and flag support into a separate patch to make it more clear
>   what the code changes in enic_rxtx.c were for
> - fixed a documentation merge error
> - fix copyright, remove unnecessary check for dev == NULL
> 
> thanks,
> johnd
> 
> John Daley (8):
>   net/enic: bring NIC interface functions up to date
>   net/enic: flow API skeleton
>   net/enic: flow API for NICs with advanced filters enabled
>   net/enic: flow API mark and flag support
>   net/enic: flow API for NICs with advanced filters disabled
>   net/enic: flow API for Legacy NICs
>   net/enic: flow API debug
>   net/enic: flow API documentation

Series applied to dpdk-next-net/master, thanks.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH v4 1/8] net/enic: bring NIC interface functions up to date
  2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
  2017-05-22  9:49               ` Ferruh Yigit
@ 2017-06-08 10:44               ` Jerin Jacob
  1 sibling, 0 replies; 41+ messages in thread
From: Jerin Jacob @ 2017-06-08 10:44 UTC (permalink / raw)
  To: John Daley; +Cc: ferruh.yigit, dev

-----Original Message-----
> Date: Wed, 17 May 2017 15:38:04 -0700
> From: John Daley <johndale@cisco.com>
> To: ferruh.yigit@intel.com
> Cc: dev@dpdk.org, John Daley <johndale@cisco.com>
> Subject: [dpdk-dev] [PATCH v4 1/8] net/enic: bring NIC interface functions
>  up to date
> X-Mailer: git-send-email 2.12.0
> 
> Update the base functions for the Cisco VIC. These files are mostly
> common with other VIC drivers so are left alone is as much as possilbe.
> Includes in a new filter/action interface which is needed for Generic
> Flow API PMD support. Update FDIR code to use the new interface.
> 
> Signed-off-by: John Daley <johndale@cisco.com>
> Reviewed-by: Nelson Escobar <neescoba@cisco.com>

Looks like this patch got merged to next-net.
Seeing some build issue with next-net now.

== Build drivers/net/ena
== Build drivers/net/enic
  CC base/vnic_dev.o
/export/dpdk-next-net/drivers/net/enic/base/vnic_dev.c: In function
‘vnic_dev_get_mac_addr’:
/export/dpdk-next-net/drivers/net/enic/base/vnic_dev.c:470:12: error:
‘a0’ is used uninitialized in this function [-Werror=uninitialized]
  args[0] = *a0;
            ^~~
cc1: all warnings being treated as errors
make[4]: *** [/export/dpdk-next-net/mk/internal/rte.compile-pre.mk:140:

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2017-06-08 10:45 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-30 21:28 [PATCH 1/6] net/enic: bring NIC interface functions up to date John Daley
2017-03-30 21:28 ` [PATCH 2/6] net/enic: flow API skeleton John Daley
2017-03-30 21:28 ` [PATCH 3/6] net/enic: flow API for NICs with advanced filters enabled John Daley
2017-03-30 21:28 ` [PATCH 4/6] net/enic: flow API for NICs with advanced filters disabled John Daley
2017-03-30 21:28 ` [PATCH 5/6] net/enic: flow API support for Legacy NICs John Daley
2017-03-30 21:28 ` [PATCH 6/6] net/enic: flow API debug John Daley
2017-03-31  2:06 ` [PATCH v2 0/7] *** flow API support for enic *** John Daley
2017-03-31  2:06   ` [PATCH v2 1/7] net/enic: bring NIC interface functions up to date John Daley
2017-03-31  2:06   ` [PATCH v2 2/7] net/enic: flow API skeleton John Daley
2017-03-31  2:06   ` [PATCH v2 3/7] net/enic: flow API for NICs with advanced filters enabled John Daley
2017-03-31  2:06   ` [PATCH v2 4/7] net/enic: flow API for NICs with advanced filters disabled John Daley
2017-03-31  2:06   ` [PATCH v2 5/7] net/enic: flow API for Legacy NICs John Daley
2017-03-31  2:06   ` [PATCH v2 6/7] net/enic: flow API debug John Daley
2017-03-31  2:06   ` [PATCH v2 7/7] net/enic: flow API documentation John Daley
2017-04-02 15:18     ` Mcnamara, John
2017-05-12 12:11     ` Ferruh Yigit
2017-05-17  3:03     ` [PATCH v3 0/6] enic flow api support John Daley
2017-05-17  3:03       ` [PATCH v3 1/6] net/enic: flow API skeleton John Daley
2017-05-17 11:12         ` Ferruh Yigit
2017-05-17  3:03       ` [PATCH v3 2/6] net/enic: flow API for NICs with advanced filters enabled John Daley
2017-05-17 11:12         ` Ferruh Yigit
2017-05-17  3:03       ` [PATCH v3 3/6] net/enic: flow API for NICs with advanced filters disabled John Daley
2017-05-17 11:13         ` Ferruh Yigit
2017-05-17  3:03       ` [PATCH v3 4/6] net/enic: flow API for Legacy NICs John Daley
2017-05-17  3:03       ` [PATCH v3 5/6] net/enic: flow API debug John Daley
2017-05-17  3:03       ` [PATCH v3 6/6] net/enic: flow API documentation John Daley
2017-05-17 11:13         ` Ferruh Yigit
2017-05-17 22:38           ` [PATCH v4 0/8] enic flow api support John Daley
2017-05-17 22:38             ` [PATCH v4 1/8] net/enic: bring NIC interface functions up to date John Daley
2017-05-22  9:49               ` Ferruh Yigit
2017-06-08 10:44               ` Jerin Jacob
2017-05-17 22:38             ` [PATCH v4 2/8] net/enic: flow API skeleton John Daley
2017-05-17 22:38             ` [PATCH v4 3/8] net/enic: flow API for NICs with advanced filters enabled John Daley
2017-05-17 22:38             ` [PATCH v4 4/8] net/enic: flow API mark and flag support John Daley
2017-05-17 22:38             ` [PATCH v4 5/8] net/enic: flow API for NICs with advanced filters disabled John Daley
2017-05-17 22:38             ` [PATCH v4 6/8] net/enic: flow API for Legacy NICs John Daley
2017-05-17 22:38             ` [PATCH v4 7/8] net/enic: flow API debug John Daley
2017-05-17 22:38             ` [PATCH v4 8/8] net/enic: flow API documentation John Daley
2017-05-22 10:05             ` [PATCH v4 0/8] enic flow api support Ferruh Yigit
2017-05-17 11:12       ` [PATCH v3 0/6] " Ferruh Yigit
2017-03-31 10:12 ` [PATCH 1/6] net/enic: bring NIC interface functions up to date Ferruh Yigit

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.