All of lore.kernel.org
 help / color / mirror / Atom feed
From: alvinx.zhang@intel.com
To: dev@dpdk.org
Cc: haiyue.wang@intel.com, xiaolong.ye@intel.com,
	qi.z.zhang@intel.com, beilei.xing@intel.com,
	Alvin Zhang <alvinx.zhang@intel.com>
Subject: [dpdk-dev] [PATCH v1 15/15] net/igc: implement flow API
Date: Mon,  9 Mar 2020 16:24:07 +0800	[thread overview]
Message-ID: <1583742247-370386-15-git-send-email-alvinx.zhang@intel.com> (raw)
In-Reply-To: <1583742247-370386-1-git-send-email-alvinx.zhang@intel.com>

From: Alvin Zhang <alvinx.zhang@intel.com>

Below type of flows are supported:
ether-type filter,
2-tuple filter,
SYN filter,
RSS

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---
 drivers/net/igc/Makefile     |   1 +
 drivers/net/igc/igc_ethdev.c |   3 +
 drivers/net/igc/igc_ethdev.h |  27 ++
 drivers/net/igc/igc_filter.c |   7 +
 drivers/net/igc/igc_flow.c   | 894 +++++++++++++++++++++++++++++++++++++++++++
 drivers/net/igc/igc_flow.h   |  27 ++
 drivers/net/igc/igc_txrx.c   | 126 ++++++
 drivers/net/igc/igc_txrx.h   |   5 +
 drivers/net/igc/meson.build  |   3 +-
 9 files changed, 1092 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/igc/igc_flow.c
 create mode 100644 drivers/net/igc/igc_flow.h

diff --git a/drivers/net/igc/Makefile b/drivers/net/igc/Makefile
index 45b0cf7..52d3e89 100644
--- a/drivers/net/igc/Makefile
+++ b/drivers/net/igc/Makefile
@@ -69,5 +69,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_IGC_PMD) += igc_logs.c
 SRCS-$(CONFIG_RTE_LIBRTE_IGC_PMD) += igc_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_IGC_PMD) += igc_txrx.c
 SRCS-$(CONFIG_RTE_LIBRTE_IGC_PMD) += igc_filter.c
+SRCS-$(CONFIG_RTE_LIBRTE_IGC_PMD) += igc_flow.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/igc/igc_ethdev.c b/drivers/net/igc/igc_ethdev.c
index e23dc3a..5d7ef1a 100644
--- a/drivers/net/igc/igc_ethdev.c
+++ b/drivers/net/igc/igc_ethdev.c
@@ -12,6 +12,7 @@
 #include "igc_logs.h"
 #include "igc_txrx.h"
 #include "igc_filter.h"
+#include "igc_flow.h"
 
 #define IGC_INTEL_VENDOR_ID		0x8086
 
@@ -1157,6 +1158,7 @@ static int eth_igc_vlan_tpid_set(struct rte_eth_dev *dev,
 	if (!adapter->stopped)
 		eth_igc_stop(dev);
 
+	igc_flow_flush(dev, NULL);
 	igc_clear_all_filter(dev);
 
 	igc_intr_other_disable(dev);
@@ -1328,6 +1330,7 @@ static int eth_igc_vlan_tpid_set(struct rte_eth_dev *dev,
 		igc->rxq_stats_map[i] = -1;
 	}
 
+	igc_flow_init(dev);
 	igc_clear_all_filter(dev);
 	return 0;
 
diff --git a/drivers/net/igc/igc_ethdev.h b/drivers/net/igc/igc_ethdev.h
index 68237aa..46811bc 100644
--- a/drivers/net/igc/igc_ethdev.h
+++ b/drivers/net/igc/igc_ethdev.h
@@ -193,6 +193,25 @@ struct igc_syn_filter {
 		enable:1;	/* 1-enable; 0-disable */
 };
 
+/* Structure to store RTE flow RSS configure. */
+struct igc_rss_filter {
+	struct rte_flow_action_rss conf; /**< RSS parameters. */
+	uint8_t key[IGC_HKEY_MAX_INDEX * sizeof(uint32_t)]; /* Hash key. */
+	uint16_t queue[IGC_RSS_RDT_SIZD];/* Queues indices to use. */
+	uint8_t enable;	/* 1-enabled, 0-disabled */
+};
+
+/* Structure to store flow */
+struct rte_flow {
+	TAILQ_ENTRY(rte_flow) node;
+	enum rte_filter_type filter_type;
+	RTE_STD_C11
+	char filter[0];		/* filter data */
+};
+
+/* Flow list header */
+TAILQ_HEAD(igc_flow_list, rte_flow);
+
 /*
  * Structure to store private data for each driver instance (for each port).
  */
@@ -210,6 +229,8 @@ struct igc_adapter {
 	struct igc_ethertype_filter ethertype_filters[IGC_MAX_ETQF_FILTERS];
 	struct igc_2tuple_filter tuple2_filters[IGC_MAX_2TUPLE_FILTERS];
 	struct igc_syn_filter syn_filter;
+	struct igc_rss_filter rss_filter;
+	struct igc_flow_list flow_list;
 };
 
 #define IGC_DEV_PRIVATE(_dev)	((_dev)->data->dev_private)
@@ -229,6 +250,12 @@ struct igc_adapter {
 #define IGC_DEV_PRIVATE_VFTA(_dev) \
 	(&((struct igc_adapter *)(_dev)->data->dev_private)->shadow_vfta)
 
+#define IGC_DEV_PRIVATE_RSS_FILTER(_dev) \
+	(&((struct igc_adapter *)(_dev)->data->dev_private)->rss_filter)
+
+#define IGC_DEV_PRIVATE_FLOW_LIST(_dev) \
+	(&((struct igc_adapter *)(_dev)->data->dev_private)->flow_list)
+
 static inline void
 igc_read_reg_check_set_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits)
 {
diff --git a/drivers/net/igc/igc_filter.c b/drivers/net/igc/igc_filter.c
index 02f5720..d3e21cf 100644
--- a/drivers/net/igc/igc_filter.c
+++ b/drivers/net/igc/igc_filter.c
@@ -6,6 +6,7 @@
 #include "igc_logs.h"
 #include "igc_txrx.h"
 #include "igc_filter.h"
+#include "igc_flow.h"
 
 /*
  * igc_ethertype_filter_lookup - lookup ether-type filter
@@ -828,6 +829,7 @@
 	igc_clear_all_ethertype_filter(dev);
 	igc_clear_all_ntuple_filter(dev);
 	igc_clear_syn_filter(dev);
+	igc_clear_rss_filter(dev);
 }
 
 int
@@ -852,6 +854,11 @@
 	case RTE_ETH_FILTER_HASH:
 		ret = igc_hash_filter_ctrl(dev, filter_op, arg);
 		break;
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &igc_flow_ops;
+		break;
 	default:
 		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
 							filter_type);
diff --git a/drivers/net/igc/igc_flow.c b/drivers/net/igc/igc_flow.c
new file mode 100644
index 0000000..355ac7e
--- /dev/null
+++ b/drivers/net/igc/igc_flow.c
@@ -0,0 +1,894 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2020 Intel Corporation
+ */
+
+#include "rte_malloc.h"
+#include "igc_logs.h"
+#include "igc_txrx.h"
+#include "igc_filter.h"
+#include "igc_flow.h"
+
+/*
+ * All Supported Rule Type
+ *
+ * ether-type filter
+ * pattern: ETH(type)/END
+ * action: QUEUE/END
+ * attribute:
+ *
+ * n-tuple filter
+ * pattern: [ETH/]([IPv4(protocol)|IPv6(protocol)/][UDP(dst_port)|
+ *          TCP([dst_port],[flags])|SCTP(dst_port)/])END
+ * action: QUEUE/END
+ * attribute: priority(0-7)
+ *
+ * SYN filter
+ * pattern: [ETH/][IPv4|IPv6/]TCP(flags=SYN)/END
+ * action: QUEUE/END
+ * attribute: priority(0,1)
+ *
+ * RSS filter
+ * pattern:
+ * action: RSS/END
+ * attribute:
+ */
+
+/* Structure of all filters */
+struct igc_all_filter {
+	struct rte_eth_ethertype_filter ethertype;
+	struct rte_eth_ntuple_filter ntuple;
+	struct rte_eth_syn_filter syn;
+	struct igc_rss_filter rss;
+	uint32_t	mask;	/* see IGC_FILTER_MASK_* definition */
+};
+
+#define IGC_FILTER_MASK_ETHER	(1U << RTE_ETH_FILTER_ETHERTYPE)
+#define IGC_FILTER_MASK_NTUPLE	(1U << RTE_ETH_FILTER_NTUPLE)
+#define IGC_FILTER_MASK_TCP_SYN	(1U << RTE_ETH_FILTER_SYN)
+#define IGC_FILTER_MASK_RSS	(1U << RTE_ETH_FILTER_HASH)
+#define IGC_FILTER_MASK_ALL	(IGC_FILTER_MASK_ETHER |	\
+				IGC_FILTER_MASK_NTUPLE |	\
+				IGC_FILTER_MASK_TCP_SYN |	\
+				IGC_FILTER_MASK_RSS)
+
+#define IGC_SET_FILTER_MASK(_filter, _mask_bits)	\
+		((_filter)->mask &= (_mask_bits))
+
+#define IGC_IS_ALL_BITS_SET(_val)	((_val) == (typeof(_val))~0)
+#define IGC_NOT_ALL_BITS_SET(_val)	((_val) != (typeof(_val))~0)
+
+/* Parse rule attribute */
+static int
+igc_parse_attribute(const struct rte_flow_attr *attr,
+	struct igc_all_filter *filter, struct rte_flow_error *error)
+{
+	if (!attr)
+		return 0;
+
+	if (attr->group)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR_GROUP, attr,
+				"Not support");
+
+	if (attr->egress)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr,
+				"Not support");
+
+	if (attr->transfer)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER, attr,
+				"Not support");
+
+	if (!attr->ingress)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr,
+				"A rule must apply to ingress traffic");
+
+	if (attr->priority == 0)
+		return 0;
+
+	/* only n-tuple and SYN filter have priority level */
+	IGC_SET_FILTER_MASK(filter,
+		IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN);
+
+	if (IGC_IS_ALL_BITS_SET(attr->priority)) {
+		/* only SYN filter match this value */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN);
+		filter->syn.hig_pri = 1;
+		return 0;
+	}
+
+	if (attr->priority > IGC_2TUPLE_MAX_PRI)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr,
+				"Priority value is invalid.");
+
+	if (attr->priority > 1) {
+		/* only n-tuple filter match this value */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+
+		/* get priority */
+		filter->ntuple.priority = (uint16_t)attr->priority;
+		return 0;
+	}
+
+	/* get priority */
+	filter->ntuple.priority = (uint16_t)attr->priority;
+	filter->syn.hig_pri = (uint8_t)attr->priority;
+
+	return 0;
+}
+
+/* function type of parse pattern */
+typedef int (*igc_pattern_parse)(const struct rte_flow_item *,
+		struct igc_all_filter *, struct rte_flow_error *);
+
+static int igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item,
+		__rte_unused struct igc_all_filter *filter,
+		__rte_unused struct rte_flow_error *error);
+static int igc_parse_pattern_ether(const struct rte_flow_item *item,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+static int igc_parse_pattern_ip(const struct rte_flow_item *item,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+static int igc_parse_pattern_ipv6(const struct rte_flow_item *item,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+static int igc_parse_pattern_udp(const struct rte_flow_item *item,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+static int igc_parse_pattern_tcp(const struct rte_flow_item *item,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+
+static igc_pattern_parse pattern_parse_list[] = {
+		[RTE_FLOW_ITEM_TYPE_VOID] = igc_parse_pattern_void,
+		[RTE_FLOW_ITEM_TYPE_ETH] = igc_parse_pattern_ether,
+		[RTE_FLOW_ITEM_TYPE_IPV4] = igc_parse_pattern_ip,
+		[RTE_FLOW_ITEM_TYPE_IPV6] = igc_parse_pattern_ipv6,
+		[RTE_FLOW_ITEM_TYPE_UDP] = igc_parse_pattern_udp,
+		[RTE_FLOW_ITEM_TYPE_TCP] = igc_parse_pattern_tcp,
+};
+
+/* Parse rule patterns */
+static int
+igc_parse_patterns(const struct rte_flow_item patterns[],
+	struct igc_all_filter *filter, struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item = patterns;
+
+	if (item == NULL) {
+		/* only RSS filter match this pattern */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS);
+		return 0;
+	}
+
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		int ret;
+
+		if (item->type >= RTE_DIM(pattern_parse_list))
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Not been supported");
+
+		if (item->last)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM_LAST, item,
+					"Range not been supported");
+
+		/* check pattern format is valid */
+		if (!!item->spec ^ !!item->mask)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Format error");
+
+		/* get the pattern type callback */
+		igc_pattern_parse parse_func =
+				pattern_parse_list[item->type];
+		if (!parse_func)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Not been supported");
+
+		/* call the pattern type function */
+		ret = parse_func(item, filter, error);
+		if (ret)
+			return ret;
+
+		/* if no filter match the pattern */
+		if (filter->mask == 0)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Not been supported");
+	}
+
+	return 0;
+}
+
+static int igc_parse_action_queue(struct rte_eth_dev *dev,
+		const struct rte_flow_action *act,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+static int igc_parse_action_rss(struct rte_eth_dev *dev,
+		const struct rte_flow_action *act,
+		struct igc_all_filter *filter, struct rte_flow_error *error);
+
+/* Parse flow actions */
+static int
+igc_parse_actions(struct rte_eth_dev *dev,
+		const struct rte_flow_action actions[],
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_action *act = actions;
+	int ret;
+
+	if (act == NULL)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION_NUM, act,
+				"Action is needed");
+
+	for (; act->type != RTE_FLOW_ACTION_TYPE_END; act++) {
+		switch (act->type) {
+		case RTE_FLOW_ACTION_TYPE_QUEUE:
+			ret = igc_parse_action_queue(dev, act, filter, error);
+			if (ret)
+				return ret;
+			break;
+		case RTE_FLOW_ACTION_TYPE_RSS:
+			ret = igc_parse_action_rss(dev, act, filter, error);
+			if (ret)
+				return ret;
+			break;
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			break;
+		default:
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, act,
+					"Not been supported");
+		}
+
+		/* if no filter match the action */
+		if (filter->mask == 0)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, act,
+					"Not been supported");
+	}
+
+	return 0;
+}
+
+/* Parse a flow rule */
+static int
+igc_parse_flow(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item patterns[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct igc_all_filter *filter)
+{
+	int ret;
+
+	/* clear all filters */
+	memset(filter, 0, sizeof(*filter));
+
+	/* set default filter mask */
+	filter->mask = IGC_FILTER_MASK_ALL;
+
+	ret = igc_parse_attribute(attr, filter, error);
+	if (ret)
+		return ret;
+
+	ret = igc_parse_patterns(patterns, filter, error);
+	if (ret)
+		return ret;
+
+	ret = igc_parse_actions(dev, actions, filter, error);
+	if (ret)
+		return ret;
+
+	/* if no or more than one filter matched this flow */
+	if (filter->mask == 0 || (filter->mask & (filter->mask - 1)))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				"Flow can't be recognized");
+	return 0;
+}
+
+/* Parse pattern type of void */
+static int
+igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item,
+		__rte_unused struct igc_all_filter *filter,
+		__rte_unused struct rte_flow_error *error)
+{
+	return 0;
+}
+
+/* Parse pattern type of ethernet header */
+static int
+igc_parse_pattern_ether(const struct rte_flow_item *item,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_eth *spec = item->spec;
+	const struct rte_flow_item_eth *mask = item->mask;
+	struct rte_eth_ethertype_filter *ether;
+
+	if (mask == NULL) {
+		/* only n-tuple and SYN filter match the pattern */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE |
+				IGC_FILTER_MASK_TCP_SYN);
+		return 0;
+	}
+
+	/* only ether-type filter match the pattern*/
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER);
+
+	/* destination and source MAC address are not supported */
+	if (!rte_is_zero_ether_addr(&mask->src) ||
+		!rte_is_zero_ether_addr(&mask->dst))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"Only support ether-type");
+
+	/* ether-type mask bits must be all 1 */
+	if (IGC_NOT_ALL_BITS_SET(mask->type))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"Ethernet type mask bits must be all 1");
+
+	ether = &filter->ethertype;
+
+	/* get ether-type */
+	ether->ether_type = rte_be_to_cpu_16(spec->type);
+
+	/* ether-type should not be IPv4 and IPv6 */
+	if (ether->ether_type == RTE_ETHER_TYPE_IPV4 ||
+		ether->ether_type == RTE_ETHER_TYPE_IPV6)
+		return rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+			"IPv4/IPv6 not supported by ethertype filter");
+	return 0;
+}
+
+/* Parse pattern type of IP */
+static int
+igc_parse_pattern_ip(const struct rte_flow_item *item,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_ipv4 *spec = item->spec;
+	const struct rte_flow_item_ipv4 *mask = item->mask;
+
+	if (mask == NULL) {
+		/* only n-tuple and SYN filter match this pattern */
+		IGC_SET_FILTER_MASK(filter,
+			IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN);
+		return 0;
+	}
+
+	/* only n-tuple filter match this pattern */
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+
+	/* only protocol is used */
+	if (mask->hdr.version_ihl ||
+		mask->hdr.type_of_service ||
+		mask->hdr.total_length ||
+		mask->hdr.packet_id ||
+		mask->hdr.fragment_offset ||
+		mask->hdr.time_to_live ||
+		mask->hdr.hdr_checksum ||
+		mask->hdr.dst_addr ||
+		mask->hdr.src_addr)
+		return rte_flow_error_set(error,
+			EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+			"IPv4 only support protocol");
+
+	if (mask->hdr.next_proto_id == 0)
+		return 0;
+
+	if (IGC_NOT_ALL_BITS_SET(mask->hdr.next_proto_id))
+		return rte_flow_error_set(error,
+				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"IPv4 protocol mask bits must be all 0 or 1");
+
+	/* get protocol type and protocol mask */
+	filter->ntuple.proto_mask  = mask->hdr.next_proto_id;
+	filter->ntuple.proto  = spec->hdr.next_proto_id;
+	filter->ntuple.flags |= RTE_NTUPLE_FLAGS_PROTO;
+
+	return 0;
+}
+
+/*
+ * Check ipv6 address is 0
+ * Return 1 if true, 0 for false.
+ */
+static inline bool
+igc_is_zero_ipv6_addr(const void *ipv6_addr)
+{
+	const uint64_t *ddw = ipv6_addr;
+	return ddw[0] == 0 && ddw[1] == 0;
+}
+
+/* Parse pattern type of IPv6 */
+static int
+igc_parse_pattern_ipv6(const struct rte_flow_item *item,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_ipv6 *spec = item->spec;
+	const struct rte_flow_item_ipv6 *mask = item->mask;
+
+	if (mask == NULL) {
+		/* only n-tuple and syn filter match this pattern */
+		IGC_SET_FILTER_MASK(filter,
+			IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN);
+		return 0;
+	}
+
+	/* only n-tuple filter match this pattern */
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+
+	/* only protocol is used */
+	if (mask->hdr.vtc_flow ||
+		mask->hdr.payload_len ||
+		mask->hdr.hop_limits ||
+		!igc_is_zero_ipv6_addr(mask->hdr.src_addr) ||
+		!igc_is_zero_ipv6_addr(mask->hdr.dst_addr))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"IPv6 only support protocol");
+
+	if (mask->hdr.proto == 0)
+		return 0;
+
+	if (IGC_NOT_ALL_BITS_SET(mask->hdr.proto))
+		return rte_flow_error_set(error,
+				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"IPv6 protocol mask bits must be all 0 or 1");
+
+	/* get protocol type and protocol mask */
+	filter->ntuple.proto_mask  = mask->hdr.proto;
+	filter->ntuple.proto  = spec->hdr.proto;
+	filter->ntuple.flags |= RTE_NTUPLE_FLAGS_PROTO;
+
+	return 0;
+}
+
+/* Parse pattern type of UDP */
+static int
+igc_parse_pattern_udp(const struct rte_flow_item *item,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_udp *spec = item->spec;
+	const struct rte_flow_item_udp *mask = item->mask;
+
+	/* only n-tuple filter match this pattern */
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+
+	if (mask == NULL)
+		return 0;
+
+	/* only destination port is used */
+	if (mask->hdr.dgram_len || mask->hdr.dgram_cksum || mask->hdr.src_port)
+		return rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+			"UDP only support destination port");
+
+	if (mask->hdr.dst_port == 0)
+		return 0;
+
+	if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"UDP port mask bits must be all 0 or 1");
+
+	/* get destination port info. */
+	filter->ntuple.dst_port_mask = mask->hdr.dst_port;
+	filter->ntuple.dst_port = spec->hdr.dst_port;
+	filter->ntuple.flags |= RTE_NTUPLE_FLAGS_DST_PORT;
+
+	return 0;
+}
+
+/* Parse pattern type of TCP */
+static int
+igc_parse_pattern_tcp(const struct rte_flow_item *item,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_tcp *spec = item->spec;
+	const struct rte_flow_item_tcp *mask = item->mask;
+
+	if (mask == NULL) {
+		/* only n-tuple filter match this pattern */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+		return 0;
+	}
+
+	/* only n-tuple and SYN filter match this pattern */
+	IGC_SET_FILTER_MASK(filter,
+			IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN);
+
+	/* only destination port and TCP flags are used */
+	if (mask->hdr.sent_seq ||
+		mask->hdr.recv_ack ||
+		mask->hdr.data_off ||
+		mask->hdr.rx_win ||
+		mask->hdr.cksum ||
+		mask->hdr.tcp_urp ||
+		mask->hdr.src_port)
+		return rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+			"TCP only support destination port and flags");
+
+	/* if destination port is used */
+	if (mask->hdr.dst_port) {
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+		if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port))
+			return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+				"TCP port mask bits must be all 1");
+
+		/* get destination port info. */
+		filter->ntuple.dst_port = spec->hdr.dst_port;
+		filter->ntuple.dst_port_mask = mask->hdr.dst_port;
+		filter->ntuple.flags |= RTE_NTUPLE_FLAGS_DST_PORT;
+	}
+
+	/* if TCP flags are used */
+	if (mask->hdr.tcp_flags) {
+		if (IGC_IS_ALL_BITS_SET(mask->hdr.tcp_flags)) {
+			/* only n-tuple match this pattern */
+			IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+
+			/* get TCP flags */
+			filter->ntuple.tcp_flags = spec->hdr.tcp_flags;
+			filter->ntuple.flags |= RTE_NTUPLE_FLAGS_TCP_FLAG;
+		} else if (mask->hdr.tcp_flags == RTE_TCP_SYN_FLAG) {
+			/* only TCP SYN filter match this pattern */
+			IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN);
+		} else {
+			/* no filter match this pattern */
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM_MASK, item,
+					"TCP flags can't match");
+		}
+	} else {
+		/* only n-tuple match this pattern */
+		IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE);
+	}
+
+	return 0;
+}
+
+static int
+igc_parse_action_queue(struct rte_eth_dev *dev,
+		const struct rte_flow_action *act,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	uint16_t queue_idx;
+
+	if (act->conf == NULL)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"NULL pointer");
+
+	/* only ether-type, n-tuple, SYN filter match the action */
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER |
+			IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN);
+
+	/* get queue index */
+	queue_idx = ((const struct rte_flow_action_queue *)act->conf)->index;
+
+	/* check the queue index is valid */
+	if (queue_idx >= dev->data->nb_rx_queues)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"Queue id is invalid");
+
+	/* get queue info. */
+	filter->ethertype.queue = queue_idx;
+	filter->ntuple.queue = queue_idx;
+	filter->syn.queue = queue_idx;
+	return 0;
+}
+
+/* Parse action of RSS */
+static int
+igc_parse_action_rss(struct rte_eth_dev *dev,
+		const struct rte_flow_action *act,
+		struct igc_all_filter *filter,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_action_rss *rss = act->conf;
+	uint32_t i;
+
+	if (act->conf == NULL)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"NULL pointer");
+
+	/* only RSS match the action */
+	IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS);
+
+	/* RSS redirect table can't be zero and can't exceed 128 */
+	if (!rss || !rss->queue_num || rss->queue_num > IGC_RSS_RDT_SIZD)
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"No valid queues");
+
+	/* queue index can't exceed max queue index */
+	for (i = 0; i < rss->queue_num; i++) {
+		if (rss->queue[i] >= dev->data->nb_rx_queues)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+					"Queue id is invalid");
+	}
+
+	/* only default RSS hase function is supported */
+	if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"Only default RSS hash functions is supported");
+
+	if (rss->level)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"Only 0 RSS encapsulation level is supported");
+
+	/* check key length is valid */
+	if (rss->key_len && rss->key_len != sizeof(filter->rss.key))
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+				"RSS hash key must be exactly 40 bytes");
+
+	/* get RSS info. */
+	igc_rss_conf_set(&filter->rss, rss);
+	return 0;
+}
+
+/**
+ * Allocate a rte_flow from the heap
+ * Return the pointer of the flow, or NULL for failed
+ **/
+static inline struct rte_flow *
+igc_alloc_flow(const void *filter, enum rte_filter_type type, uint inbytes)
+{
+	/* allocate memory, 8 bytes boundary aligned */
+	struct rte_flow *flow = rte_malloc("igc flow filter",
+			sizeof(struct rte_flow) + inbytes, 8);
+	if (flow == NULL) {
+		PMD_DRV_LOG(ERR, "failed to allocate memory");
+		return NULL;
+	}
+
+	flow->filter_type = type;
+
+	/* copy filter data */
+	memcpy(flow->filter, filter, inbytes);
+	return flow;
+}
+
+/* Append a rte_flow to the list */
+static inline void
+igc_append_flow(struct igc_flow_list *list, struct rte_flow *flow)
+{
+	TAILQ_INSERT_TAIL(list, flow, node);
+}
+
+/**
+ * Remove the flow and free the flow buffer
+ * The caller should make sure the flow is really exist in the list
+ **/
+static inline void
+igc_remove_flow(struct igc_flow_list *list, struct rte_flow *flow)
+{
+	TAILQ_REMOVE(list, flow, node);
+	rte_free(flow);
+}
+
+/* Check whether the flow is really in the list or not */
+static inline bool
+igc_is_flow_in_list(struct igc_flow_list *list, struct rte_flow *flow)
+{
+	struct rte_flow *it;
+
+	TAILQ_FOREACH(it, list, node) {
+		if (it == flow)
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * Create a flow rule.
+ * Theoretically one rule can match more than one filters.
+ * We will let it use the filter which it hit first.
+ * So, the sequence matters.
+ **/
+static struct rte_flow *
+igc_flow_create(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item patterns[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct rte_flow *flow = NULL;
+	struct igc_all_filter filter;
+	int ret;
+
+	ret = igc_parse_flow(dev, attr, patterns, actions, error, &filter);
+	if (ret)
+		return NULL;
+	ret = -ENOMEM;
+
+	switch (filter.mask) {
+	case IGC_FILTER_MASK_ETHER:
+		flow = igc_alloc_flow(&filter.ethertype,
+				RTE_ETH_FILTER_ETHERTYPE,
+				sizeof(filter.ethertype));
+		if (flow)
+			ret = igc_add_ethertype_filter(dev, &filter.ethertype);
+		break;
+	case IGC_FILTER_MASK_NTUPLE:
+		flow = igc_alloc_flow(&filter.ntuple, RTE_ETH_FILTER_NTUPLE,
+				sizeof(filter.ntuple));
+		if (flow)
+			ret = igc_add_del_ntuple_filter(dev,
+					&filter.ntuple, true);
+		break;
+	case IGC_FILTER_MASK_TCP_SYN:
+		flow = igc_alloc_flow(&filter.syn, RTE_ETH_FILTER_SYN,
+				sizeof(filter.syn));
+		if (flow)
+			ret = igc_set_syn_filter(dev, &filter.syn);
+		break;
+	case IGC_FILTER_MASK_RSS:
+		flow = igc_alloc_flow(&filter.rss, RTE_ETH_FILTER_HASH,
+				sizeof(filter.rss));
+		if (flow) {
+			struct igc_rss_filter *rss =
+					(struct igc_rss_filter *)flow->filter;
+			rss->conf.key = rss->key;
+			rss->conf.queue = rss->queue;
+			ret = igc_add_rss_filter(dev, &filter.rss);
+		}
+		break;
+	default:
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				"Flow can't be recognized");
+		return NULL;
+	}
+
+	if (ret) {
+		/* check and free the memory */
+		if (flow)
+			rte_free(flow);
+
+		rte_flow_error_set(error, -ret,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Failed to create flow.");
+		return NULL;
+	}
+
+	/* append the flow to the tail of the list */
+	igc_append_flow(IGC_DEV_PRIVATE_FLOW_LIST(dev), flow);
+	return flow;
+}
+
+/**
+ * Check if the flow rule is supported by the device.
+ * It only checks the format. Don't guarantee the rule can be programmed into
+ * the HW. Because there can be no enough room for the rule.
+ **/
+static int
+igc_flow_validate(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item patterns[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct igc_all_filter filter;
+
+	return igc_parse_flow(dev, attr, patterns, actions, error, &filter);
+}
+
+/**
+ * Disable a valid flow, the flow must be not NULL and
+ * chained in the device flow list.
+ **/
+static int
+igc_disable_flow(struct rte_eth_dev *dev, struct rte_flow *flow)
+{
+	int ret = 0;
+
+	switch (flow->filter_type) {
+	case RTE_ETH_FILTER_ETHERTYPE:
+		ret = igc_del_ethertype_filter(dev,
+			(struct rte_eth_ethertype_filter *)&flow->filter);
+		break;
+
+	case RTE_ETH_FILTER_NTUPLE:
+		ret = igc_add_del_ntuple_filter(dev,
+				(struct rte_eth_ntuple_filter *)&flow->filter,
+				false);
+		break;
+
+	case RTE_ETH_FILTER_SYN:
+		ret = igc_del_syn_filter(dev);
+		break;
+
+	case RTE_ETH_FILTER_HASH:
+		ret = igc_del_rss_filter(dev);
+		break;
+
+	default:
+		PMD_DRV_LOG(ERR, "Filter type (%d) not supported",
+				flow->filter_type);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/* Destroy a flow rule */
+static int
+igc_flow_destroy(struct rte_eth_dev *dev,
+		struct rte_flow *flow,
+		struct rte_flow_error *error)
+{
+	struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev);
+	int ret;
+
+	if (!flow) {
+		PMD_DRV_LOG(ERR, "NULL flow!");
+		return -EINVAL;
+	}
+
+	/* check the flow is create by IGC PMD */
+	if (!igc_is_flow_in_list(list, flow)) {
+		PMD_DRV_LOG(ERR, "Flow(%p) not been found!", flow);
+		return -ENOENT;
+	}
+
+	ret = igc_disable_flow(dev, flow);
+	if (ret)
+		rte_flow_error_set(error, -ret,
+				RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failed to destroy flow");
+
+	igc_remove_flow(list, flow);
+	return ret;
+}
+
+/* Initiate device flow list header */
+void
+igc_flow_init(struct rte_eth_dev *dev)
+{
+	TAILQ_INIT(IGC_DEV_PRIVATE_FLOW_LIST(dev));
+}
+
+/* Destroy all flow in the list and free memory */
+int
+igc_flow_flush(struct rte_eth_dev *dev,
+		__rte_unused struct rte_flow_error *error)
+{
+	struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev);
+	struct rte_flow *flow;
+
+	while ((flow = TAILQ_FIRST(list)) != NULL) {
+		igc_disable_flow(dev, flow);
+		igc_remove_flow(list, flow);
+	}
+
+	return 0;
+}
+
+const struct rte_flow_ops igc_flow_ops = {
+	.validate = igc_flow_validate,
+	.create = igc_flow_create,
+	.destroy = igc_flow_destroy,
+	.flush = igc_flow_flush,
+};
diff --git a/drivers/net/igc/igc_flow.h b/drivers/net/igc/igc_flow.h
new file mode 100644
index 0000000..24aa796
--- /dev/null
+++ b/drivers/net/igc/igc_flow.h
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2020 Intel Corporation
+ */
+
+#ifndef _IGC_FLOW_H_
+#define _IGC_FLOW_H_
+
+#include <rte_flow_driver.h>
+#include "igc_ethdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const struct rte_flow_ops igc_flow_ops;
+
+void igc_flow_init(struct rte_eth_dev *dev);
+int igc_flow_flush(struct rte_eth_dev *dev,
+		__rte_unused struct rte_flow_error *error);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IGC_FLOW_H_ */
diff --git a/drivers/net/igc/igc_txrx.c b/drivers/net/igc/igc_txrx.c
index 217ecd2..3cc1b8f 100644
--- a/drivers/net/igc/igc_txrx.c
+++ b/drivers/net/igc/igc_txrx.c
@@ -991,6 +991,132 @@ int eth_igc_rx_descriptor_status(void *rx_queue, uint16_t offset)
 	igc_hw_rss_hash_set(hw, &rss_conf);
 }
 
+int
+igc_del_rss_filter(struct rte_eth_dev *dev)
+{
+	struct igc_rss_filter *rss_filter = IGC_DEV_PRIVATE_RSS_FILTER(dev);
+
+	if (rss_filter->enable) {
+		/* recover default RSS configuration */
+		igc_rss_configure(dev);
+
+		/* disable RSS logic and clear filter data */
+		igc_rss_disable(dev);
+		memset(rss_filter, 0, sizeof(*rss_filter));
+		return 0;
+	}
+	PMD_DRV_LOG(ERR, "filter not exist!");
+	return -ENOENT;
+}
+
+/* Initiate the filter structure by the structure of rte_flow_action_rss */
+void
+igc_rss_conf_set(struct igc_rss_filter *out,
+		const struct rte_flow_action_rss *rss)
+{
+	out->conf.func = rss->func;
+	out->conf.level = rss->level;
+	out->conf.types = rss->types;
+
+	if (rss->key_len == sizeof(out->key)) {
+		memcpy(out->key, rss->key, rss->key_len);
+		out->conf.key = out->key;
+		out->conf.key_len = rss->key_len;
+	} else {
+		out->conf.key = NULL;
+		out->conf.key_len = 0;
+	}
+
+	if (rss->queue_num <= IGC_RSS_RDT_SIZD) {
+		memcpy(out->queue, rss->queue,
+			sizeof(*out->queue) * rss->queue_num);
+		out->conf.queue = out->queue;
+		out->conf.queue_num = rss->queue_num;
+	} else {
+		out->conf.queue = NULL;
+		out->conf.queue_num = 0;
+	}
+}
+
+int
+igc_add_rss_filter(struct rte_eth_dev *dev, struct igc_rss_filter *rss)
+{
+	struct rte_eth_rss_conf rss_conf = {
+		.rss_key = rss->conf.key_len ?
+			(void *)(uintptr_t)rss->conf.key : NULL,
+		.rss_key_len = rss->conf.key_len,
+		.rss_hf = rss->conf.types,
+	};
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	struct igc_rss_filter *rss_filter = IGC_DEV_PRIVATE_RSS_FILTER(dev);
+	uint32_t i, j;
+
+	/* check RSS type is valid */
+	if ((rss_conf.rss_hf & IGC_RSS_OFFLOAD_ALL) == 0) {
+		PMD_DRV_LOG(ERR, "RSS type error!");
+		return -EINVAL;
+	}
+
+	/* check queue count is not zero */
+	if (!rss->conf.queue_num) {
+		PMD_DRV_LOG(ERR, "queue number should not be 0!");
+		return -EINVAL;
+	}
+
+	/* check queue id is valid */
+	for (i = 0; i < rss->conf.queue_num; i++)
+		if (rss->conf.queue[i] >= dev->data->nb_rx_queues) {
+			PMD_DRV_LOG(ERR, "queue id %u is invalid!",
+					rss->conf.queue[i]);
+			return -EINVAL;
+		}
+
+	/* only support one filter */
+	if (rss_filter->enable) {
+		PMD_DRV_LOG(ERR, "RSS filter exist!");
+		return -EEXIST;
+	}
+	rss_filter->enable = 1;
+
+	igc_rss_conf_set(rss_filter, &rss->conf);
+
+	/* Fill in redirection table. */
+	for (i = 0, j = 0; i < IGC_RSS_RDT_SIZD; i++, j++) {
+		union igc_rss_reta_reg reta;
+		uint16_t q_idx, reta_idx;
+
+		if (j == rss->conf.queue_num)
+			j = 0;
+		q_idx = rss->conf.queue[j];
+		reta_idx = i % sizeof(reta);
+		reta.bytes[reta_idx] = q_idx;
+		if (reta_idx == sizeof(reta) - 1)
+			IGC_WRITE_REG_LE_VALUE(hw,
+				IGC_RETA(i / sizeof(reta)), reta.dword);
+	}
+
+	if (rss_conf.rss_key == NULL)
+		rss_conf.rss_key = default_rss_key;
+	igc_hw_rss_hash_set(hw, &rss_conf);
+	return 0;
+}
+
+void
+igc_clear_rss_filter(struct rte_eth_dev *dev)
+{
+	struct igc_rss_filter *rss_filter = IGC_DEV_PRIVATE_RSS_FILTER(dev);
+
+	if (!rss_filter->enable)
+		return;
+
+	/* recover default RSS configuration */
+	igc_rss_configure(dev);
+
+	/* disable RSS logic and clear filter data */
+	igc_rss_disable(dev);
+	memset(rss_filter, 0, sizeof(*rss_filter));
+}
+
 static int
 igc_dev_mq_rx_configure(struct rte_eth_dev *dev)
 {
diff --git a/drivers/net/igc/igc_txrx.h b/drivers/net/igc/igc_txrx.h
index 50be783..14be64c 100644
--- a/drivers/net/igc/igc_txrx.h
+++ b/drivers/net/igc/igc_txrx.h
@@ -44,6 +44,11 @@ int eth_igc_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 void igc_set_rss_flowtype(struct igc_hw *hw, uint64_t flowtype);
 void
 igc_hw_rss_hash_set(struct igc_hw *hw, struct rte_eth_rss_conf *rss_conf);
+int igc_del_rss_filter(struct rte_eth_dev *dev);
+void igc_rss_conf_set(struct igc_rss_filter *out,
+		const struct rte_flow_action_rss *rss);
+int igc_add_rss_filter(struct rte_eth_dev *dev, struct igc_rss_filter *rss);
+void igc_clear_rss_filter(struct rte_eth_dev *dev);
 void eth_igc_rxq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
 	struct rte_eth_rxq_info *qinfo);
 void eth_igc_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
diff --git a/drivers/net/igc/meson.build b/drivers/net/igc/meson.build
index d509c0e..df58e2f 100644
--- a/drivers/net/igc/meson.build
+++ b/drivers/net/igc/meson.build
@@ -8,7 +8,8 @@ sources = files(
 	'igc_logs.c',
 	'igc_ethdev.c',
 	'igc_txrx.c',
-	'igc_filter.c'
+	'igc_filter.c',
+	'igc_flow.c'
 )
 
 includes += include_directories('base')
-- 
1.8.3.1


  parent reply	other threads:[~2020-03-09  8:31 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-09  8:23 [dpdk-dev] [PATCH v1 01/15] net/igc: add igc PMD alvinx.zhang
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 02/15] net/igc: update base share codes alvinx.zhang
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 03/15] net/igc: device initialization alvinx.zhang
2020-03-12  4:42   ` Ye Xiaolong
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 04/15] net/igc: implement device base ops alvinx.zhang
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 05/15] net/igc: support reception and transmission of packets alvinx.zhang
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 06/15] net/igc: implement status API alvinx.zhang
2020-03-09  8:23 ` [dpdk-dev] [PATCH v1 07/15] net/igc: enable Rx queue interrupts alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 08/15] net/igc: implement flow control ops alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 09/15] net/igc: implement RSS API alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 10/15] net/igc: implement feature of VLAN alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 11/15] net/igc: implement ether-type filter alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 12/15] net/igc: implement 2-tuple filter alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 13/15] net/igc: implement TCP SYN filter alvinx.zhang
2020-03-09  8:24 ` [dpdk-dev] [PATCH v1 14/15] net/igc: implement hash filter configure alvinx.zhang
2020-03-09  8:24 ` alvinx.zhang [this message]
2020-03-09  8:35 ` [dpdk-dev] [PATCH v1 01/15] net/igc: add igc PMD Ye Xiaolong
2020-03-12  3:09 ` Ye Xiaolong
2020-03-20  2:46 ` [dpdk-dev] [PATCH v2 00/14] " alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 01/14] net/igc: add " alvinx.zhang
2020-04-03 12:21     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 02/14] net/igc: support device initialization alvinx.zhang
2020-04-03 12:23     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 03/14] net/igc: implement device base ops alvinx.zhang
2020-04-03 12:24     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 04/14] net/igc: support reception and transmission of packets alvinx.zhang
2020-04-03 12:27     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 05/14] net/igc: implement status API alvinx.zhang
2020-04-03 12:24     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 06/14] net/igc: enable Rx queue interrupts alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 07/14] net/igc: implement flow control ops alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 08/14] net/igc: implement RSS API alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 09/14] net/igc: implement feature of VLAN alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 10/14] net/igc: implement ether-type filter alvinx.zhang
2020-04-03 12:26     ` Ferruh Yigit
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 11/14] net/igc: implement 2-tuple filter alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 12/14] net/igc: implement TCP SYN filter alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 13/14] net/igc: implement hash filter configure alvinx.zhang
2020-03-20  2:46   ` [dpdk-dev] [PATCH v2 14/14] net/igc: implement flow API alvinx.zhang
2020-04-03 12:26     ` Ferruh Yigit

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1583742247-370386-15-git-send-email-alvinx.zhang@intel.com \
    --to=alvinx.zhang@intel.com \
    --cc=beilei.xing@intel.com \
    --cc=dev@dpdk.org \
    --cc=haiyue.wang@intel.com \
    --cc=qi.z.zhang@intel.com \
    --cc=xiaolong.ye@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.