DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Anatoly Burakov <anatoly.burakov@intel.com>
To: dev@dpdk.org, Bruce Richardson <bruce.richardson@intel.com>
Subject: [PATCH v5 21/25] net/i40e: refactor FDIR engine infrastructure
Date: Wed,  7 Oct 2026 11:50:16 +0100	[thread overview]
Message-ID: <45c0f47ecca71af5a4825ee67976fc93221a05e5.1791369981.git.anatoly.burakov@intel.com> (raw)
In-Reply-To: <cover.1791369979.git.anatoly.burakov@intel.com> <cover.1791369979.git.anatoly.burakov@intel.com>

Currently, there are multiple problems with how i40e flow directory feature
is implemented, both in terms of how it works with rte_flow, and how it
integrates with the PMD-specific packet template API.

For one, these two subsystems, while using shared infrastructure, do not
really interact or cooperate, and are built on top of special cases in FDIR
path. More specifically, the packet template code does not store its
packet in the hash map, and has a different hashing scheme, yet it still
registers itself in FDIR flow list, hash table, and hash map. This list
is then used by `dev_start` to restore FDIR filters that user has
inserted into the list. These filters, as written, cannot be reprogrammed
that way because the information a filter restore function would need is
lost on insert (the packet pointer is not added to the hash map).

Another issue is that while rte_flow FDIR code does lazy FDIR init on first
added flow, the packet template API does not, even though it too relies on
the same hardware feature, nor does it ever do teardown on last FDIR flow.

Yet another issue is how the "filter restore" code itself is implemented,
namely that currently it simply does not work. When doing filter restore,
the driver will walk every FDIR filter stored in the TAILQ, and attempt to
program it. However, inside the program function, there is a deduplication
check (to see if flow being installed is already present in the flow hash
table), which fails because the flows we are programming come from the same
list that is being checked for deduplication, which makes the entire filter
restore a no-op.

The FDIR filter programming code itself also has a number of readability
problems as well as being otherwise hard to use - SW bookkeeping,
validation, and flow programming is interspersed within the code, and it
is difficult to reason about what happens when the code is called from
this or that context.

So, this refactor does the following:

- Reorganize FDIR internals to track packet templates and rte_flow FDIR
  flows separately
- Refactor FDIR init/teardown to always happen on first/last rule, so that
  whichever API happens to call FDIR first, the state is consistent
- Rework the FDIR code to disentangle FDIR flow rule programming, Flex PIT
  checks, SW bookkeeping, etc. from each other
- Remove both the rte_flow FDIR TAILQ and the hash map (filter array)
  structure, because they are redundant (information about the flow is
  already stored in the rte_flow flow list, and hash_map structure only
  stored pointers to data we also have in that same list)
- Fix FDIR filter restore to replay all configuration correctly, as well as
  re-init the FDIR queue enablement tracking
- Rework the internal FDIR global state data structure to make a little
  more sense by grouping things that belong together into structures

Additionally, there was a delay mechanism at flow director rule program
time, as when programming a rule we might not know if it's actually
possible to install the rule, because space for the rules may come either
from our own pool, or it may come from a pool that is shared with other
VSI's. However, it only makes sense to wait on rule create (i.e. when it is
programmed into the hardware for the first time), but not when we are
replaying or removing these rules. So, adjust the waiting mechanism to only
wait on FDIR rule creation.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 drivers/net/intel/i40e/i40e_ethdev.c  |  169 +---
 drivers/net/intel/i40e/i40e_ethdev.h  |  128 ++-
 drivers/net/intel/i40e/i40e_fdir.c    | 1173 +++++++++++++++++--------
 drivers/net/intel/i40e/i40e_flow.c    |  136 +--
 drivers/net/intel/i40e/rte_pmd_i40e.c |    6 +-
 5 files changed, 966 insertions(+), 646 deletions(-)

diff --git a/drivers/net/intel/i40e/i40e_ethdev.c b/drivers/net/intel/i40e/i40e_ethdev.c
index 94bf6ee392a..dd42b61c679 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.c
+++ b/drivers/net/intel/i40e/i40e_ethdev.c
@@ -1041,129 +1041,6 @@ i40e_init_tunnel_filter_list(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int
-i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	char fdir_hash_name[RTE_HASH_NAMESIZE];
-	uint32_t alloc = hw->func_caps.fd_filters_guaranteed;
-	uint32_t best = hw->func_caps.fd_filters_best_effort;
-	enum i40e_filter_pctype pctype;
-	struct rte_bitmap *bmp = NULL;
-	uint32_t bmp_size;
-	void *mem = NULL;
-	uint32_t i = 0;
-	int ret;
-
-	struct rte_hash_parameters fdir_hash_params = {
-		.name = fdir_hash_name,
-		.entries = I40E_MAX_FDIR_FILTER_NUM,
-		.key_len = sizeof(struct i40e_fdir_input),
-		.hash_func = rte_hash_crc,
-		.hash_func_init_val = 0,
-		.socket_id = rte_socket_id(),
-	};
-
-	/* Initialize flow director filter rule list and hash */
-	TAILQ_INIT(&fdir_info->fdir_list);
-	snprintf(fdir_hash_name, RTE_HASH_NAMESIZE,
-		 "fdir_%s", dev->device->name);
-	fdir_info->hash_table = rte_hash_create(&fdir_hash_params);
-	if (!fdir_info->hash_table) {
-		PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
-		return -EINVAL;
-	}
-
-	fdir_info->hash_map = rte_zmalloc("i40e_fdir_hash_map",
-					  sizeof(struct i40e_fdir_filter *) *
-					  I40E_MAX_FDIR_FILTER_NUM,
-					  0);
-	if (!fdir_info->hash_map) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir hash map!");
-		ret = -ENOMEM;
-		goto err_fdir_hash_map_alloc;
-	}
-
-	fdir_info->fdir_filter_array = rte_zmalloc("fdir_filter",
-			sizeof(struct i40e_fdir_filter) *
-			I40E_MAX_FDIR_FILTER_NUM,
-			0);
-
-	if (!fdir_info->fdir_filter_array) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir filter array!");
-		ret = -ENOMEM;
-		goto err_fdir_filter_array_alloc;
-	}
-
-	for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-	     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++)
-		pf->fdir.flow_count[pctype] = 0;
-
-	fdir_info->fdir_space_size = alloc + best;
-	fdir_info->fdir_actual_cnt = 0;
-	fdir_info->fdir_guarantee_total_space = alloc;
-	fdir_info->fdir_guarantee_free_space =
-		fdir_info->fdir_guarantee_total_space;
-
-	PMD_DRV_LOG(INFO, "FDIR guarantee space: %u, best_effort space %u.", alloc, best);
-
-	fdir_info->fdir_flow_pool.pool =
-			rte_zmalloc("i40e_fdir_entry",
-				sizeof(struct i40e_fdir_entry) *
-				fdir_info->fdir_space_size,
-				0);
-
-	if (!fdir_info->fdir_flow_pool.pool) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for bitmap flow!");
-		ret = -ENOMEM;
-		goto err_fdir_bitmap_flow_alloc;
-	}
-
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		fdir_info->fdir_flow_pool.pool[i].idx = i;
-
-	bmp_size =
-		rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
-	mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
-	if (mem == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_mem_alloc;
-	}
-	bmp = rte_bitmap_init(fdir_info->fdir_space_size, mem, bmp_size);
-	if (bmp == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to initialization fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_bmp_alloc;
-	}
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		rte_bitmap_set(bmp, i);
-
-	fdir_info->fdir_flow_pool.bitmap = bmp;
-
-	return 0;
-
-err_fdir_bmp_alloc:
-	rte_free(mem);
-err_fdir_mem_alloc:
-	rte_free(fdir_info->fdir_flow_pool.pool);
-err_fdir_bitmap_flow_alloc:
-	rte_free(fdir_info->fdir_filter_array);
-err_fdir_filter_array_alloc:
-	rte_free(fdir_info->hash_map);
-err_fdir_hash_map_alloc:
-	rte_hash_free(fdir_info->hash_table);
-
-	return ret;
-}
-
 static void
 i40e_init_customized_info(struct i40e_pf *pf)
 {
@@ -1782,9 +1659,9 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	ret = i40e_init_tunnel_filter_list(dev);
 	if (ret < 0)
 		goto err_init_tunnel_filter_list;
-	ret = i40e_init_fdir_filter_list(dev);
+	ret = i40e_fdir_flow_store_init(dev);
 	if (ret < 0)
-		goto err_init_fdir_filter_list;
+		goto err_init_fdir_flow_store;
 
 	/* initialize flow engine configuration */
 	ret = ci_flow_engine_conf_init(&pf->flow_engine_conf,
@@ -1801,12 +1678,8 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return 0;
 
 err_flow_engine_conf_init:
-	rte_free(pf->fdir.fdir_flow_pool.bitmap);
-	rte_free(pf->fdir.fdir_flow_pool.pool);
-	rte_free(pf->fdir.fdir_filter_array);
-	rte_free(pf->fdir.hash_map);
-	rte_hash_free(pf->fdir.hash_table);
-err_init_fdir_filter_list:
+	i40e_fdir_flow_store_free(&pf->fdir);
+err_init_fdir_flow_store:
 	rte_hash_free(pf->tunnel.hash_table);
 	rte_free(pf->tunnel.hash_map);
 err_init_tunnel_filter_list:
@@ -1849,32 +1722,13 @@ i40e_rm_tunnel_filter_list(struct i40e_pf *pf)
 	}
 }
 
-static void
-i40e_rm_fdir_filter_list(struct i40e_pf *pf)
-{
-	struct i40e_fdir_filter *p_fdir;
-	struct i40e_fdir_info *fdir_info;
-
-	fdir_info = &pf->fdir;
-
-	/* Remove all flow director rules */
-	while ((p_fdir = TAILQ_FIRST(&fdir_info->fdir_list)))
-		TAILQ_REMOVE(&fdir_info->fdir_list, p_fdir, rules);
-}
-
 static void
 i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 {
-	struct i40e_fdir_info *fdir_info;
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
 
-	fdir_info = &pf->fdir;
-
-	/* flow director memory cleanup */
-	rte_free(fdir_info->hash_map);
-	rte_hash_free(fdir_info->hash_table);
-	rte_free(fdir_info->fdir_flow_pool.bitmap);
-	rte_free(fdir_info->fdir_flow_pool.pool);
-	rte_free(fdir_info->fdir_filter_array);
+	i40e_fdir_flow_store_free(fdir_info);
+	i40e_fdir_tmpl_store_free(fdir_info);
 }
 
 void i40e_flex_payload_reg_set_default(struct i40e_hw *hw)
@@ -2659,6 +2513,7 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	/* free the flows and reset flow config */
 	ci_flow_cleanup(&pf->flow_engine_conf);
 	ci_flow_engine_conf_reset(&pf->flow_engine_conf);
+	i40e_fdir_teardown(pf);
 
 	i40e_dev_free_queues(dev);
 
@@ -2730,7 +2585,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	} while (retries++ < 5);
 
 	i40e_rm_tunnel_filter_list(pf);
-	i40e_rm_fdir_filter_list(pf);
 
 	/* Remove all flows */
 	while ((p_flow = TAILQ_FIRST(&pf->flow_list))) {
@@ -9758,7 +9612,12 @@ i40e_filter_input_set_init(struct i40e_pf *pf)
 		/* store the default input set */
 		if (!pf->support_multi_driver)
 			pf->hash_input_set[pctype] = input_set;
-		pf->fdir.input_set[pctype] = input_set;
+		{
+			struct i40e_fdir_pctype_state *state =
+				&pf->fdir.flows.pctype[pctype];
+
+			state->input_set = input_set;
+		}
 	}
 }
 
diff --git a/drivers/net/intel/i40e/i40e_ethdev.h b/drivers/net/intel/i40e/i40e_ethdev.h
index f674f7995d8..47fb879efb6 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.h
+++ b/drivers/net/intel/i40e/i40e_ethdev.h
@@ -693,7 +693,7 @@ struct i40e_fdir_action {
 /* A structure used to define the flow director filter entry by filter_ctrl API
  * It supports RTE_ETH_FILTER_FDIR data representation.
  */
-struct i40e_fdir_filter_conf {
+struct i40e_fdir_filter {
 	uint32_t soft_id;
 	/* ID, an unique value is required when deal with FDIR entry */
 	struct i40e_fdir_input input;    /* Input set */
@@ -713,9 +713,26 @@ struct i40e_fdir_flex_mask {
 #define I40E_FILTER_PCTYPE_MAX     64
 #define I40E_MAX_FDIR_FILTER_NUM   (1024 * 8)
 
-struct i40e_fdir_filter {
-	TAILQ_ENTRY(i40e_fdir_filter) rules;
-	struct i40e_fdir_filter_conf fdir;
+/*
+ * A filter added through the PMD packet template API. It owns the raw packet,
+ * and fdir.input.flow.raw_flow.packet points at that copy, so the filter stays
+ * self-contained and can be reprogrammed at any time.
+ */
+struct i40e_fdir_tmpl_filter {
+	TAILQ_ENTRY(i40e_fdir_tmpl_filter) rules;
+	struct i40e_fdir_filter fdir;
+	uint8_t *packet;
+};
+
+/*
+ * Packet templates are keyed on their contents, which are too big for a hash
+ * key, so the key borrows the packet and a custom comparison walks it. The
+ * hash signature is computed by the caller and passed to the _with_hash() API.
+ */
+struct i40e_fdir_tmpl_key {
+	uint16_t pctype;
+	uint32_t length;
+	const uint8_t *packet;
 };
 
 /* fdir memory pool entry */
@@ -735,7 +752,46 @@ struct i40e_fdir_flow_pool {
 #define FLOW_TO_FLOW_BITMAP(f) \
 	container_of((f), struct i40e_fdir_entry, flow)
 
-TAILQ_HEAD(i40e_fdir_filter_list, i40e_fdir_filter);
+TAILQ_HEAD(i40e_fdir_tmpl_list, i40e_fdir_tmpl_filter);
+
+/* tracking for rte_flow-backed filters */
+struct i40e_fdir_pctype_state {
+	/* input set bits for this pctype */
+	uint64_t input_set;
+	uint32_t flow_count;
+	struct i40e_fdir_flex_mask flex_mask;
+	bool flex_mask_flag;
+};
+
+struct i40e_fdir_layer_state {
+	/*
+	 * The rule for extracting a byte stream as flexible payload. Each layer
+	 * can have up to three elements, and all filters sharing the same layer
+	 * reuse the same programmed layout.
+	 */
+	struct i40e_fdir_flex_pit flex_set[I40E_MAX_FLXPLD_FIED];
+	bool flex_pit_flag;
+	uint32_t flex_flow_count;
+};
+
+struct i40e_fdir_flow_store {
+	struct rte_hash *hash_table;
+	/* the pre-allocated pool of the rte_flow */
+	struct i40e_fdir_flow_pool flow_pool;
+
+	struct i40e_fdir_pctype_state pctype[I40E_FILTER_PCTYPE_MAX];
+	struct i40e_fdir_layer_state layer[I40E_MAX_FLXPLD_LAYER];
+};
+
+/* tracking for packet template filters; allocated on first use */
+struct i40e_fdir_tmpl_store {
+	struct rte_hash *hash_table;
+	/* filters indexed by their hash table slot */
+	struct i40e_fdir_tmpl_filter *filter_array;
+	/* these filters have no other owner, so they are enumerated here */
+	struct i40e_fdir_tmpl_list list;
+};
+
 /*
  *  A structure used to define fields of a FDIR related info.
  */
@@ -752,20 +808,8 @@ struct i40e_fdir_info {
 	 */
 	int txq_available_buf_count;
 
-	/* input set bits for each pctype */
-	uint64_t input_set[I40E_FILTER_PCTYPE_MAX];
-	/*
-	 * the rule how bytes stream is extracted as flexible payload
-	 * for each payload layer, the setting can up to three elements
-	 */
-	struct i40e_fdir_flex_pit flex_set[I40E_MAX_FLXPLD_LAYER * I40E_MAX_FLXPLD_FIED];
-	struct i40e_fdir_flex_mask flex_mask[I40E_FILTER_PCTYPE_MAX];
-
-	struct i40e_fdir_filter_list fdir_list;
-	struct i40e_fdir_filter **hash_map;
-	struct rte_hash *hash_table;
-	/* An array to store the inserted rules input */
-	struct i40e_fdir_filter *fdir_filter_array;
+	struct i40e_fdir_flow_store flows;
+	struct i40e_fdir_tmpl_store tmpls;
 
 	/*
 	 * Priority ordering at filter invalidation(destroying a flow) between
@@ -784,22 +828,10 @@ struct i40e_fdir_info {
 	 * shared space
 	 */
 	uint32_t fdir_space_size;
-	/* the actual number of the fdir rules in hardware, initialized as 0 */
+	/* number of filters in hardware, across both stores */
 	uint32_t fdir_actual_cnt;
-	/* the free guaranteed space of the fdir */
-	uint32_t fdir_guarantee_free_space;
 	/* the fdir total guaranteed space */
 	uint32_t fdir_guarantee_total_space;
-	/* the pre-allocated pool of the rte_flow */
-	struct i40e_fdir_flow_pool fdir_flow_pool;
-
-	/* Mark if flex pit and mask is set */
-	bool flex_pit_flag[I40E_MAX_FLXPLD_LAYER];
-	bool flex_mask_flag[I40E_FILTER_PCTYPE_MAX];
-
-	uint32_t flow_count[I40E_FILTER_PCTYPE_MAX];
-
-	uint32_t flex_flow_count[I40E_MAX_FLXPLD_LAYER];
 };
 
 /* Ethertype filter number HW supports */
@@ -1303,7 +1335,7 @@ extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
 	union {
-		struct i40e_fdir_filter_conf fdir_filter;
+		struct i40e_fdir_filter fdir_filter;
 		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
 		struct i40e_rte_flow_rss_conf rss_conf;
 	};
@@ -1353,8 +1385,9 @@ const struct rte_memzone *i40e_memzone_reserve(const char *name,
 					uint32_t len,
 					int socket_id);
 int i40e_fdir_configure(struct rte_eth_dev *dev);
-void i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on);
+void i40e_fdir_rx_proc_sync(struct rte_eth_dev *dev);
 void i40e_fdir_teardown(struct i40e_pf *pf);
+int i40e_fdir_engine_init(struct rte_eth_dev *dev);
 enum i40e_filter_pctype
 	i40e_flowtype_to_pctype(const struct i40e_adapter *adapter,
 				uint16_t flow_type);
@@ -1384,8 +1417,6 @@ int i40e_rx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,
 			   struct rte_eth_burst_mode *mode);
 int i40e_tx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,
 			   struct rte_eth_burst_mode *mode);
-int i40e_sw_fdir_filter_del(struct i40e_pf *pf,
-			    struct i40e_fdir_input *input);
 struct i40e_tunnel_filter *
 i40e_sw_tunnel_filter_lookup(struct i40e_tunnel_rule *tunnel_rule,
 			     const struct i40e_tunnel_filter_input *input);
@@ -1399,9 +1430,28 @@ struct rte_flow *
 i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info);
 void i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
 		struct rte_flow *flow);
-int i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
-			      const struct i40e_fdir_filter_conf *filter,
-			      bool add);
+int i40e_fdir_tmpl_add(struct rte_eth_dev *dev,
+				 const struct i40e_fdir_filter *filter);
+int i40e_fdir_tmpl_del(struct rte_eth_dev *dev,
+				 const struct i40e_fdir_filter *filter);
+struct i40e_fdir_filter *
+i40e_fdir_filter_lookup(struct i40e_fdir_info *fdir_info,
+			const struct i40e_fdir_input *input);
+int i40e_fdir_filter_validate(struct rte_eth_dev *dev,
+			      const struct i40e_fdir_filter *filter);
+int i40e_fdir_filter_register(struct rte_eth_dev *dev,
+			      const struct i40e_fdir_filter *filter,
+			      struct i40e_fdir_filter **node);
+int i40e_fdir_filter_unregister(struct rte_eth_dev *dev,
+				struct i40e_fdir_filter *node);
+int i40e_fdir_filter_program(struct rte_eth_dev *dev,
+			     const struct i40e_fdir_filter *filter,
+			     bool add, bool wait_status);
+bool i40e_fdir_filter_needs_status_wait(const struct i40e_pf *pf,
+					uint32_t filter_count);
+void i40e_fdir_tmpl_store_free(struct i40e_fdir_info *fdir_info);
+int i40e_fdir_flow_store_init(struct rte_eth_dev *dev);
+void i40e_fdir_flow_store_free(struct i40e_fdir_info *fdir_info);
 int i40e_dev_tunnel_filter_set(struct i40e_pf *pf,
 			       struct rte_eth_tunnel_filter_conf *tunnel_filter,
 			       uint8_t add);
diff --git a/drivers/net/intel/i40e/i40e_fdir.c b/drivers/net/intel/i40e/i40e_fdir.c
index 8a233f8a97a..051a5cdc5cf 100644
--- a/drivers/net/intel/i40e/i40e_fdir.c
+++ b/drivers/net/intel/i40e/i40e_fdir.c
@@ -86,17 +86,10 @@
 	(1ULL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1ULL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-static int i40e_fdir_filter_convert(const struct i40e_fdir_filter_conf *input,
-			 struct i40e_fdir_filter *filter);
-static struct i40e_fdir_filter *
-i40e_sw_fdir_filter_lookup(struct i40e_fdir_info *fdir_info,
-			const struct i40e_fdir_input *input);
-static int i40e_sw_fdir_filter_insert(struct i40e_pf *pf,
-				   struct i40e_fdir_filter *filter);
 static int
 i40e_flow_fdir_filter_programming(struct i40e_pf *pf,
 				  enum i40e_filter_pctype pctype,
-				  const struct i40e_fdir_filter_conf *filter,
+				  const struct i40e_fdir_filter *filter,
 				  bool add, bool wait_status);
 
 static int
@@ -211,21 +204,21 @@ i40e_fdir_setup(struct i40e_pf *pf)
 	err = i40e_switch_tx_queue(hw, vsi->base_queue, TRUE);
 	if (err) {
 		PMD_DRV_LOG(ERR, "Failed to do fdir TX switch on.");
-		goto fail_mem;
+		goto fail_tx_queue;
 	}
 
 	/* Init the rx queue in hardware */
 	err = i40e_fdir_rx_queue_init(pf->fdir.rxq);
 	if (err) {
 		PMD_DRV_LOG(ERR, "Failed to do FDIR RX initialization.");
-		goto fail_mem;
+		goto fail_tx_queue;
 	}
 
 	/* switch on rx queue */
 	err = i40e_switch_rx_queue(hw, vsi->base_queue, TRUE);
 	if (err) {
 		PMD_DRV_LOG(ERR, "Failed to do FDIR RX switch on.");
-		goto fail_mem;
+		goto fail_rx_queue;
 	}
 
 	/* enable FDIR MSIX interrupt */
@@ -244,7 +237,7 @@ i40e_fdir_setup(struct i40e_pf *pf)
 		PMD_DRV_LOG(ERR, "Cannot init memzone for "
 				 "flow director program packet.");
 		err = I40E_ERR_NO_MEMORY;
-		goto fail_mem;
+		goto fail_intr;
 	}
 
 	for (i = 0; i < I40E_FDIR_PRG_PKT_CNT; i++) {
@@ -255,14 +248,20 @@ i40e_fdir_setup(struct i40e_pf *pf)
 	}
 
 	pf->fdir.match_counter_index = I40E_COUNTER_INDEX_FDIR(hw->pf_id);
-	pf->fdir.fdir_actual_cnt = 0;
-	pf->fdir.fdir_guarantee_free_space =
-		pf->fdir.fdir_guarantee_total_space;
 
 	PMD_DRV_LOG(INFO, "FDIR setup successfully, with programming queue %u.",
 		    vsi->base_queue);
 	return I40E_SUCCESS;
 
+fail_intr:
+	i40e_vsi_queues_unbind_intr(vsi);
+	i40e_vsi_disable_queues_intr(vsi);
+fail_rx_queue:
+	if (i40e_switch_rx_queue(hw, vsi->base_queue, FALSE) != I40E_SUCCESS)
+		PMD_DRV_LOG(DEBUG, "Failed to do FDIR RX switch off");
+fail_tx_queue:
+	if (i40e_switch_tx_queue(hw, vsi->base_queue, FALSE) != I40E_SUCCESS)
+		PMD_DRV_LOG(DEBUG, "Failed to do FDIR TX switch off");
 fail_mem:
 	i40e_rx_queue_release(pf->fdir.rxq);
 	pf->fdir.rxq = NULL;
@@ -344,46 +343,52 @@ i40e_init_flx_pld(struct i40e_pf *pf)
 	 * of payload as flexible payload.
 	 */
 	for (i = I40E_FLXPLD_L2_IDX; i < I40E_MAX_FLXPLD_LAYER; i++) {
+		struct i40e_fdir_layer_state *layer = &pf->fdir.flows.layer[i];
+		struct i40e_fdir_flex_pit *flex_set = &layer->flex_set[0];
 		index = i * I40E_MAX_FLXPLD_FIED;
-		pf->fdir.flex_set[index].src_offset = 0;
-		pf->fdir.flex_set[index].size = I40E_FDIR_MAX_FLEXWORD_NUM;
-		pf->fdir.flex_set[index].dst_offset = 0;
+		flex_set->src_offset = 0;
+		flex_set->size = I40E_FDIR_MAX_FLEXWORD_NUM;
+		flex_set->dst_offset = 0;
 		I40E_WRITE_REG(hw, I40E_PRTQF_FLX_PIT(index), 0x0000C900);
 		I40E_WRITE_REG(hw,
 			I40E_PRTQF_FLX_PIT(index + 1), 0x0000FC29);/*non-used*/
 		I40E_WRITE_REG(hw,
 			I40E_PRTQF_FLX_PIT(index + 2), 0x0000FC2A);/*non-used*/
-		pf->fdir.flex_pit_flag[i] = 0;
+		layer->flex_pit_flag = false;
 	}
 
 	/* initialize the masks */
 	for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
 	     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++) {
+		struct i40e_fdir_pctype_state *state = &pf->fdir.flows.pctype[pctype];
 		flow_type = i40e_pctype_to_flowtype(pf->adapter, pctype);
 
 		if (flow_type == RTE_ETH_FLOW_UNKNOWN)
 			continue;
-		pf->fdir.flex_mask[pctype].word_mask = 0;
+		state->flex_mask.word_mask = 0;
 		i40e_write_rx_ctl(hw, I40E_PRTQF_FD_FLXINSET(pctype), 0);
 		for (i = 0; i < I40E_FDIR_BITMASK_NUM_WORD; i++) {
-			pf->fdir.flex_mask[pctype].bitmask[i].offset = 0;
-			pf->fdir.flex_mask[pctype].bitmask[i].mask = 0;
+			state->flex_mask.bitmask[i].offset = 0;
+			state->flex_mask.bitmask[i].mask = 0;
 			i40e_write_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, i), 0);
 		}
 	}
 }
 
 /*
- * Enable/disable flow director RX processing in vector routines.
+ * Match flow director RX processing to whether any filter is registered.
  */
 void
-i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on)
+i40e_fdir_rx_proc_sync(struct rte_eth_dev *dev)
 {
-	int32_t i;
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	bool on = pf->fdir.fdir_actual_cnt > 0;
+	uint16_t i;
 
 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
 		struct ci_rx_queue *rxq = dev->data->rx_queues[i];
-		if (!rxq)
+
+		if (rxq == NULL)
 			continue;
 		rxq->fdir_enabled = on;
 	}
@@ -421,12 +426,37 @@ i40e_fdir_configure(struct rte_eth_dev *dev)
 
 	i40e_init_flx_pld(pf); /* set flex config to default value */
 
-	/* Enable FDIR processing in RX routines */
-	i40e_fdir_rx_proc_enable(dev, 1);
-
 	return ret;
 }
 
+/*
+ * Bring up the flow director engine on first use.
+ */
+int
+i40e_fdir_engine_init(struct rte_eth_dev *dev)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	int ret;
+
+	if (pf->fdir.fdir_vsi != NULL)
+		return 0;
+
+	ret = i40e_fdir_setup(pf);
+	if (ret != I40E_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to setup fdir.");
+		return -ENOTSUP;
+	}
+
+	ret = i40e_fdir_configure(dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to configure fdir.");
+		i40e_fdir_teardown(pf);
+		return ret;
+	}
+
+	return 0;
+}
+
 
 static struct i40e_customized_pctype *
 i40e_flow_fdir_find_customized_pctype(struct i40e_pf *pf, uint8_t pctype)
@@ -642,7 +672,7 @@ i40e_flow_fdir_construct_pkt(struct i40e_pf *pf,
 	struct rte_ipv6_hdr *esp_ipv6;
 
 	uint8_t size, dst = 0;
-	uint8_t i, pit_idx, set_idx = I40E_FLXPLD_L4_IDX; /* use l4 by default*/
+	uint8_t i, set_idx = I40E_FLXPLD_L4_IDX; /* use l4 by default*/
 	int len;
 	uint8_t pctype = fdir_input->pctype;
 	struct i40e_customized_pctype *cus_pctype;
@@ -894,13 +924,15 @@ i40e_flow_fdir_construct_pkt(struct i40e_pf *pf,
 
 	/* fill the flexbytes to payload */
 	for (i = 0; i < I40E_MAX_FLXPLD_FIED; i++) {
-		pit_idx = set_idx * I40E_MAX_FLXPLD_FIED + i;
-		size = pf->fdir.flex_set[pit_idx].size;
+		const struct i40e_fdir_flex_pit *pit;
+		struct i40e_fdir_layer_state *layer = &pf->fdir.flows.layer[set_idx];
+
+		pit = &layer->flex_set[i];
+		size = pit->size;
 		if (size == 0)
 			continue;
-		dst = pf->fdir.flex_set[pit_idx].dst_offset * sizeof(uint16_t);
-		ptr = payload +
-		      pf->fdir.flex_set[pit_idx].src_offset * sizeof(uint16_t);
+		dst = pit->dst_offset * sizeof(uint16_t);
+		ptr = payload + pit->src_offset * sizeof(uint16_t);
 		(void)memcpy(ptr,
 				 &fdir_input->flow_ext.flexbytes[dst],
 				 size * sizeof(uint16_t));
@@ -997,56 +1029,23 @@ i40e_fdir_programming_status_cleanup(struct ci_rx_queue *rxq)
 		PMD_DRV_LOG(INFO, "error report captured.");
 }
 
+/* Add a flow director filter into the SW hash table */
 static int
-i40e_fdir_filter_convert(const struct i40e_fdir_filter_conf *input,
-			 struct i40e_fdir_filter *filter)
+i40e_fdir_filter_hash_add(struct i40e_pf *pf,
+			  const struct i40e_fdir_filter *filter,
+			  struct i40e_fdir_filter **node)
 {
-	memcpy(&filter->fdir, input, sizeof(struct i40e_fdir_filter_conf));
-	if (input->input.flow_ext.pkt_template) {
-		filter->fdir.input.flow.raw_flow.packet = NULL;
-		filter->fdir.input.flow.raw_flow.length =
-			rte_hash_crc(input->input.flow.raw_flow.packet,
-				     input->input.flow.raw_flow.length,
-				     input->input.flow.raw_flow.pctype);
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	int ret;
+
+	ret = rte_hash_lookup(flows->hash_table, &filter->input);
+	if (ret >= 0) {
+		PMD_DRV_LOG(ERR, "Failed to add fdir filter to hash table %d!",
+			    ret);
+		return -EEXIST;
 	}
-	return 0;
-}
 
-/* Check if there exists the flow director filter */
-static struct i40e_fdir_filter *
-i40e_sw_fdir_filter_lookup(struct i40e_fdir_info *fdir_info,
-			const struct i40e_fdir_input *input)
-{
-	int ret;
-
-	if (input->flow_ext.pkt_template)
-		ret = rte_hash_lookup_with_hash(fdir_info->hash_table,
-						(const void *)input,
-						input->flow.raw_flow.length);
-	else
-		ret = rte_hash_lookup(fdir_info->hash_table,
-				      (const void *)input);
-	if (ret < 0)
-		return NULL;
-
-	return fdir_info->hash_map[ret];
-}
-
-/* Add a flow director filter into the SW list */
-static int
-i40e_sw_fdir_filter_insert(struct i40e_pf *pf, struct i40e_fdir_filter *filter)
-{
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	struct i40e_fdir_filter *hash_filter;
-	int ret;
-
-	if (filter->fdir.input.flow_ext.pkt_template)
-		ret = rte_hash_add_key_with_hash(fdir_info->hash_table,
-				 &filter->fdir.input,
-				 filter->fdir.input.flow.raw_flow.length);
-	else
-		ret = rte_hash_add_key(fdir_info->hash_table,
-				       &filter->fdir.input);
+	ret = rte_hash_add_key(flows->hash_table, &filter->input);
 	if (ret < 0) {
 		PMD_DRV_LOG(ERR,
 			    "Failed to insert fdir filter to hash table %d!",
@@ -1054,48 +1053,247 @@ i40e_sw_fdir_filter_insert(struct i40e_pf *pf, struct i40e_fdir_filter *filter)
 		return ret;
 	}
 
-	if (fdir_info->hash_map[ret])
-		return -1;
-
-	hash_filter = &fdir_info->fdir_filter_array[ret];
-	memcpy(hash_filter, filter, sizeof(*filter));
-	fdir_info->hash_map[ret] = hash_filter;
-	TAILQ_INSERT_TAIL(&fdir_info->fdir_list, hash_filter, rules);
+	**node = *filter;
 
 	return 0;
 }
 
-/* Delete a flow director filter from the SW list */
-int
-i40e_sw_fdir_filter_del(struct i40e_pf *pf, struct i40e_fdir_input *input)
+/* Delete a flow director filter from the SW hash table */
+static int
+i40e_fdir_filter_hash_del(struct i40e_pf *pf,
+			  const struct i40e_fdir_filter *node)
 {
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	struct i40e_fdir_filter *filter;
 	int ret;
 
-	if (input->flow_ext.pkt_template)
-		ret = rte_hash_del_key_with_hash(fdir_info->hash_table,
-						 input,
-						 input->flow.raw_flow.length);
-	else
-		ret = rte_hash_del_key(fdir_info->hash_table, input);
+	ret = rte_hash_del_key(pf->fdir.flows.hash_table, &node->input);
 	if (ret < 0) {
 		PMD_DRV_LOG(ERR,
-			    "Failed to delete fdir filter to hash table %d!",
+			    "Failed to delete fdir filter from hash table %d!",
 			    ret);
 		return ret;
 	}
-	filter = fdir_info->hash_map[ret];
-	fdir_info->hash_map[ret] = NULL;
 
-	TAILQ_REMOVE(&fdir_info->fdir_list, filter, rules);
+	return 0;
+}
+
+int
+i40e_fdir_flow_store_init(struct rte_eth_dev *dev)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
+	struct i40e_fdir_flow_store *flows = &fdir_info->flows;
+	char fdir_hash_name[RTE_HASH_NAMESIZE];
+	uint32_t alloc = hw->func_caps.fd_filters_guaranteed;
+	uint32_t best = hw->func_caps.fd_filters_best_effort;
+	struct rte_bitmap *bmp = NULL;
+	uint32_t bmp_size;
+	void *mem = NULL;
+	uint32_t i = 0;
+	int ret;
+
+	struct rte_hash_parameters fdir_hash_params = {
+		.name = fdir_hash_name,
+		.entries = I40E_MAX_FDIR_FILTER_NUM,
+		.key_len = sizeof(struct i40e_fdir_input),
+		.hash_func = rte_hash_crc,
+		.hash_func_init_val = 0,
+		.socket_id = rte_socket_id(),
+	};
+
+	snprintf(fdir_hash_name, RTE_HASH_NAMESIZE,
+		 "fdir_%s", dev->device->name);
+	flows->hash_table = rte_hash_create(&fdir_hash_params);
+	if (!flows->hash_table) {
+		PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
+		return -EINVAL;
+	}
+
+	fdir_info->fdir_space_size = alloc + best;
+	fdir_info->fdir_actual_cnt = 0;
+	fdir_info->fdir_guarantee_total_space = alloc;
+
+	PMD_DRV_LOG(INFO, "FDIR guarantee space: %u, best_effort space %u.", alloc, best);
+
+	flows->flow_pool.pool =
+			rte_zmalloc("i40e_fdir_entry",
+				sizeof(struct i40e_fdir_entry) *
+				fdir_info->fdir_space_size,
+				0);
+
+	if (!flows->flow_pool.pool) {
+		PMD_INIT_LOG(ERR,
+			     "Failed to allocate memory for bitmap flow!");
+		ret = -ENOMEM;
+		goto err_fdir_bitmap_flow_alloc;
+	}
+
+	for (i = 0; i < fdir_info->fdir_space_size; i++)
+		flows->flow_pool.pool[i].idx = i;
+
+	bmp_size =
+		rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
+	mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
+	if (mem == NULL) {
+		PMD_INIT_LOG(ERR,
+			     "Failed to allocate memory for fdir bitmap!");
+		ret = -ENOMEM;
+		goto err_fdir_mem_alloc;
+	}
+	bmp = rte_bitmap_init(fdir_info->fdir_space_size, mem, bmp_size);
+	if (bmp == NULL) {
+		PMD_INIT_LOG(ERR,
+			     "Failed to initialization fdir bitmap!");
+		ret = -ENOMEM;
+		goto err_fdir_bmp_alloc;
+	}
+	for (i = 0; i < fdir_info->fdir_space_size; i++)
+		rte_bitmap_set(bmp, i);
+
+	flows->flow_pool.bitmap = bmp;
 
 	return 0;
+
+err_fdir_bmp_alloc:
+	rte_free(mem);
+err_fdir_mem_alloc:
+	rte_free(flows->flow_pool.pool);
+err_fdir_bitmap_flow_alloc:
+	rte_hash_free(flows->hash_table);
+
+	return ret;
+}
+
+void
+i40e_fdir_flow_store_free(struct i40e_fdir_info *fdir_info)
+{
+	struct i40e_fdir_flow_store *flows = &fdir_info->flows;
+
+	rte_free(flows->flow_pool.bitmap);
+	rte_free(flows->flow_pool.pool);
+	rte_hash_free(flows->hash_table);
+}
+
+static uint32_t
+i40e_fdir_tmpl_sig(const struct i40e_raw_flow *raw)
+{
+	return rte_hash_crc(raw->packet, raw->length, raw->pctype);
+}
+
+static int
+i40e_fdir_tmpl_cmp(const void *key1, const void *key2,
+		   size_t key_len __rte_unused)
+{
+	const struct i40e_fdir_tmpl_key *k1 = key1;
+	const struct i40e_fdir_tmpl_key *k2 = key2;
+
+	if (k1->pctype != k2->pctype || k1->length != k2->length)
+		return 1;
+
+	return memcmp(k1->packet, k2->packet, k1->length);
+}
+
+static void
+i40e_fdir_tmpl_key_fill(struct i40e_fdir_tmpl_key *key,
+			const struct i40e_raw_flow *raw,
+			const uint8_t *packet)
+{
+	memset(key, 0, sizeof(*key));
+	key->pctype = raw->pctype;
+	key->length = raw->length;
+	key->packet = packet;
+}
+
+/* The template store is only worth its memory once the API is actually used */
+static int
+i40e_fdir_tmpl_store_init(struct rte_eth_dev *dev)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_fdir_tmpl_store *tmpls = &pf->fdir.tmpls;
+	char name[RTE_HASH_NAMESIZE];
+	struct rte_hash_parameters params = {
+		.name = name,
+		.entries = I40E_MAX_FDIR_FILTER_NUM,
+		.key_len = sizeof(struct i40e_fdir_tmpl_key),
+		.hash_func = rte_hash_crc,
+		.hash_func_init_val = 0,
+		.socket_id = rte_socket_id(),
+	};
+
+	if (tmpls->hash_table != NULL)
+		return 0;
+
+	/* the packet template store is created on first use */
+	TAILQ_INIT(&tmpls->list);
+
+	snprintf(name, sizeof(name), "i40e_fdir_tmpl_%s", dev->data->name);
+	tmpls->hash_table = rte_hash_create(&params);
+	if (tmpls->hash_table == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create fdir template hash table.");
+		return -ENOMEM;
+	}
+	/* the key only borrows the packet, so contents drive the comparison */
+	rte_hash_set_cmp_func(tmpls->hash_table, i40e_fdir_tmpl_cmp);
+
+	tmpls->filter_array = rte_zmalloc("fdir_tmpl_filter",
+			sizeof(*tmpls->filter_array) * I40E_MAX_FDIR_FILTER_NUM,
+			0);
+	if (tmpls->filter_array == NULL) {
+		PMD_DRV_LOG(ERR,
+			     "Failed to allocate fdir template filter array.");
+		rte_hash_free(tmpls->hash_table);
+		tmpls->hash_table = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+void
+i40e_fdir_tmpl_store_free(struct i40e_fdir_info *fdir_info)
+{
+	struct i40e_fdir_tmpl_store *tmpls = &fdir_info->tmpls;
+	struct i40e_fdir_tmpl_filter *tmpl, *tmp;
+	uint32_t i;
+
+	/* rules in this list point into filter array so no free() needed */
+	RTE_TAILQ_FOREACH_SAFE(tmpl, &tmpls->list, rules, tmp)
+		TAILQ_REMOVE(&tmpls->list, tmpl, rules);
+
+	if (tmpls->filter_array != NULL) {
+		for (i = 0; i < I40E_MAX_FDIR_FILTER_NUM; i++)
+			rte_free(tmpls->filter_array[i].packet);
+		rte_free(tmpls->filter_array);
+		tmpls->filter_array = NULL;
+	}
+	rte_hash_free(tmpls->hash_table);
+	tmpls->hash_table = NULL;
+}
+
+static struct i40e_fdir_tmpl_filter *
+i40e_fdir_tmpl_lookup(struct i40e_fdir_info *fdir_info,
+		      const struct i40e_raw_flow *raw)
+{
+	struct i40e_fdir_tmpl_key key;
+	int ret;
+
+	if (fdir_info->tmpls.hash_table == NULL)
+		return NULL;
+
+	i40e_fdir_tmpl_key_fill(&key, raw, raw->packet);
+
+	ret = rte_hash_lookup_with_hash(fdir_info->tmpls.hash_table, &key,
+					i40e_fdir_tmpl_sig(raw));
+	if (ret < 0)
+		return NULL;
+
+	return &fdir_info->tmpls.filter_array[ret];
 }
 
 struct rte_flow *
 i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info)
 {
+	struct i40e_fdir_flow_pool *pool = &fdir_info->flows.flow_pool;
 	struct rte_flow *flow = NULL;
 	uint64_t slab = 0;
 	uint32_t pos = 0;
@@ -1108,8 +1306,7 @@ i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info)
 		return NULL;
 	}
 
-	ret = rte_bitmap_scan(fdir_info->fdir_flow_pool.bitmap, &pos,
-			&slab);
+	ret = rte_bitmap_scan(pool->bitmap, &pos, &slab);
 
 	/* normally this won't happen as the fdir_actual_cnt should be
 	 * same with the number of the set bits in fdir_flow_pool,
@@ -1122,8 +1319,8 @@ i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info)
 
 	i = rte_bsf64(slab);
 	pos += i;
-	rte_bitmap_clear(fdir_info->fdir_flow_pool.bitmap, pos);
-	flow = &fdir_info->fdir_flow_pool.pool[pos].flow;
+	rte_bitmap_clear(pool->bitmap, pos);
+	flow = &pool->pool[pos].flow;
 
 	memset(flow, 0, sizeof(struct rte_flow));
 
@@ -1137,48 +1334,35 @@ i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
 	struct i40e_fdir_entry *f;
 
 	f = FLOW_TO_FLOW_BITMAP(flow);
-	rte_bitmap_set(fdir_info->fdir_flow_pool.bitmap, f->idx);
+	rte_bitmap_set(fdir_info->flows.flow_pool.bitmap, f->idx);
 }
 
 static int
-i40e_flow_store_flex_pit(struct i40e_pf *pf,
-			 struct i40e_fdir_flex_pit *flex_pit,
+i40e_fdir_check_flex_pit(struct i40e_pf *pf,
+			 const struct i40e_fdir_flex_pit *flex_pit,
 			 enum i40e_flxpld_layer_idx layer_idx,
 			 uint8_t raw_id)
 {
-	uint8_t field_idx;
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_layer_state *layer = &flows->layer[layer_idx];
 
-	field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + raw_id;
-	/* Check if the configuration is conflicted */
-	if (pf->fdir.flex_pit_flag[layer_idx] &&
-	    (pf->fdir.flex_set[field_idx].src_offset != flex_pit->src_offset ||
-	     pf->fdir.flex_set[field_idx].size != flex_pit->size ||
-	     pf->fdir.flex_set[field_idx].dst_offset != flex_pit->dst_offset))
+	if (!layer->flex_pit_flag)
+		return 0;
+
+	if (memcmp(&layer->flex_set[raw_id], flex_pit, sizeof(*flex_pit)) != 0)
 		return -1;
 
-	/* Check if the configuration exists. */
-	if (pf->fdir.flex_pit_flag[layer_idx] &&
-	    (pf->fdir.flex_set[field_idx].src_offset == flex_pit->src_offset &&
-	     pf->fdir.flex_set[field_idx].size == flex_pit->size &&
-	     pf->fdir.flex_set[field_idx].dst_offset == flex_pit->dst_offset))
-		return 1;
-
-	pf->fdir.flex_set[field_idx].src_offset =
-		flex_pit->src_offset;
-	pf->fdir.flex_set[field_idx].size =
-		flex_pit->size;
-	pf->fdir.flex_set[field_idx].dst_offset =
-		flex_pit->dst_offset;
-
-	return 0;
+	return 1;
 }
 
 static void
-i40e_flow_set_fdir_flex_pit(struct i40e_pf *pf,
-			    enum i40e_flxpld_layer_idx layer_idx,
-			    uint8_t raw_id)
+i40e_fdir_flex_pit_program(struct i40e_pf *pf,
+			   const struct i40e_fdir_filter *filter)
 {
+	enum i40e_flxpld_layer_idx layer_idx = filter->input.flow_ext.layer_idx;
+	uint8_t raw_id = filter->input.flow_ext.raw_id;
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	const struct i40e_fdir_flex_pit *pit;
 	uint32_t flx_pit, flx_ort;
 	uint16_t min_next_off = 0;
 	uint8_t field_idx;
@@ -1189,18 +1373,19 @@ i40e_flow_set_fdir_flex_pit(struct i40e_pf *pf,
 			  (raw_id << I40E_GLQF_ORT_FIELD_CNT_SHIFT) |
 			  (layer_idx * I40E_MAX_FLXPLD_FIED);
 		I40E_WRITE_GLB_REG(hw, I40E_GLQF_ORT(33 + layer_idx), flx_ort);
+	} else {
+		I40E_WRITE_GLB_REG(hw, I40E_GLQF_ORT(33 + layer_idx), 0);
 	}
 
 	/* Set flex pit */
 	for (i = 0; i < raw_id; i++) {
 		field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
-		flx_pit = MK_FLX_PIT(pf->fdir.flex_set[field_idx].src_offset,
-				     pf->fdir.flex_set[field_idx].size,
-				     pf->fdir.flex_set[field_idx].dst_offset);
+		pit = &filter->input.flow_ext.flex_pit[field_idx];
+		flx_pit = MK_FLX_PIT(pit->src_offset, pit->size,
+				     pit->dst_offset);
 
 		I40E_WRITE_REG(hw, I40E_PRTQF_FLX_PIT(field_idx), flx_pit);
-		min_next_off = pf->fdir.flex_set[field_idx].src_offset +
-			pf->fdir.flex_set[field_idx].size;
+		min_next_off = pit->src_offset + pit->size;
 	}
 
 	for (; i < I40E_MAX_FLXPLD_FIED; i++) {
@@ -1213,104 +1398,189 @@ i40e_flow_set_fdir_flex_pit(struct i40e_pf *pf,
 	}
 }
 
+/* Translate the byte-granular mask supplied by the caller into register form */
 static int
-i40e_flow_store_flex_mask(struct i40e_pf *pf,
-			  enum i40e_filter_pctype pctype,
-			  uint8_t *mask)
+i40e_fdir_flex_mask_convert(const uint8_t *mask,
+			    struct i40e_fdir_flex_mask *flex_mask)
 {
-	struct i40e_fdir_flex_mask flex_mask;
 	uint8_t nb_bitmask = 0;
 	uint16_t mask_tmp;
 	uint8_t i;
 
-	memset(&flex_mask, 0, sizeof(struct i40e_fdir_flex_mask));
+	memset(flex_mask, 0, sizeof(*flex_mask));
 	for (i = 0; i < I40E_FDIR_MAX_FLEX_LEN; i += sizeof(uint16_t)) {
 		mask_tmp = I40E_WORD(mask[i], mask[i + 1]);
 		if (mask_tmp) {
-			flex_mask.word_mask |=
+			flex_mask->word_mask |=
 				I40E_FLEX_WORD_MASK(i / sizeof(uint16_t));
 			if (mask_tmp != UINT16_MAX) {
 				if (nb_bitmask >= I40E_FDIR_BITMASK_NUM_WORD)
 					return -1;
-				flex_mask.bitmask[nb_bitmask].mask = ~mask_tmp;
-				flex_mask.bitmask[nb_bitmask].offset =
+				flex_mask->bitmask[nb_bitmask].mask = ~mask_tmp;
+				flex_mask->bitmask[nb_bitmask].offset =
 					i / sizeof(uint16_t);
 				nb_bitmask++;
 			}
 		}
 	}
-	flex_mask.nb_bitmask = nb_bitmask;
+	flex_mask->nb_bitmask = nb_bitmask;
 
-	if (pf->fdir.flex_mask_flag[pctype] &&
-	    (memcmp(&flex_mask, &pf->fdir.flex_mask[pctype],
-		    sizeof(struct i40e_fdir_flex_mask))))
+	return 0;
+}
+
+static int
+i40e_fdir_check_flex_mask(struct i40e_pf *pf,
+			  enum i40e_filter_pctype pctype,
+			  const uint8_t *mask,
+			  struct i40e_fdir_flex_mask *flex_mask)
+{
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_pctype_state *state = &flows->pctype[pctype];
+
+	if (i40e_fdir_flex_mask_convert(mask, flex_mask) < 0)
+		return -1;
+
+	if (!state->flex_mask_flag)
+		return 0;
+
+	if (memcmp(flex_mask, &state->flex_mask, sizeof(*flex_mask)) != 0)
 		return -2;
-	else if (pf->fdir.flex_mask_flag[pctype] &&
-		 !(memcmp(&flex_mask, &pf->fdir.flex_mask[pctype],
-			  sizeof(struct i40e_fdir_flex_mask))))
-		return 1;
 
-	pf->fdir.flex_mask[pctype] = flex_mask;
 	return 0;
 }
 
-static void
-i40e_flow_set_fdir_flex_msk(struct i40e_pf *pf,
-			    enum i40e_filter_pctype pctype)
+static int
+i40e_fdir_flex_msk_program(struct i40e_pf *pf,
+			   enum i40e_filter_pctype pctype,
+			   const struct i40e_fdir_filter *filter)
 {
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_fdir_flex_mask *flex_mask;
+	struct i40e_fdir_flex_mask flex_mask;
 	uint32_t flxinset, fd_mask;
 	uint8_t i;
 
-	/* Set flex mask */
-	flex_mask = &pf->fdir.flex_mask[pctype];
-	flxinset = (flex_mask->word_mask <<
+	if (i40e_fdir_flex_mask_convert(filter->input.flow_ext.flex_mask,
+					&flex_mask) < 0)
+		return -EINVAL;
+
+	flxinset = (flex_mask.word_mask <<
 		    I40E_PRTQF_FD_FLXINSET_INSET_SHIFT) &
 		I40E_PRTQF_FD_FLXINSET_INSET_MASK;
 	i40e_write_rx_ctl(hw, I40E_PRTQF_FD_FLXINSET(pctype), flxinset);
 
-	for (i = 0; i < flex_mask->nb_bitmask; i++) {
-		fd_mask = (flex_mask->bitmask[i].mask <<
+	for (i = 0; i < flex_mask.nb_bitmask; i++) {
+		fd_mask = (flex_mask.bitmask[i].mask <<
 			   I40E_PRTQF_FD_MSK_MASK_SHIFT) &
 			   I40E_PRTQF_FD_MSK_MASK_MASK;
-		fd_mask |= ((flex_mask->bitmask[i].offset +
+		fd_mask |= ((flex_mask.bitmask[i].offset +
 			     I40E_FLX_OFFSET_IN_FIELD_VECTOR) <<
 			    I40E_PRTQF_FD_MSK_OFFSET_SHIFT) &
 				I40E_PRTQF_FD_MSK_OFFSET_MASK;
 		i40e_write_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, i), fd_mask);
 	}
 
-	pf->fdir.flex_mask_flag[pctype] = 1;
+	/* zero out remaining fields */
+	for (; i < I40E_FDIR_BITMASK_NUM_WORD; i++)
+		i40e_write_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, i), 0);
+
+	return 0;
+}
+
+static int
+i40e_fdir_flex_check(struct i40e_pf *pf,
+		     const struct i40e_fdir_filter *filter,
+		     enum i40e_filter_pctype pctype,
+		     struct i40e_fdir_flex_mask *flex_mask)
+{
+	enum i40e_flxpld_layer_idx layer_idx = filter->input.flow_ext.layer_idx;
+	int ret;
+	int i;
+
+	for (i = 0; i < filter->input.flow_ext.raw_id; i++) {
+		uint8_t field_idx;
+
+		field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
+		ret = i40e_fdir_check_flex_pit(pf,
+				&filter->input.flow_ext.flex_pit[field_idx],
+				layer_idx, i);
+		if (ret < 0) {
+			PMD_DRV_LOG(ERR,
+				    "Conflict with the first flexible rule.");
+			return -EINVAL;
+		}
+	}
+
+	ret = i40e_fdir_check_flex_mask(pf, pctype,
+					filter->input.flow_ext.flex_mask,
+					flex_mask);
+	if (ret == -1) {
+		PMD_DRV_LOG(ERR, "Exceed maximal number of bitmasks");
+		return -EINVAL;
+	} else if (ret == -2) {
+		PMD_DRV_LOG(ERR, "Conflict with the first flexible rule");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void
+i40e_fdir_flex_store(struct i40e_pf *pf,
+		      const struct i40e_fdir_filter *filter,
+		      enum i40e_filter_pctype pctype,
+		      const struct i40e_fdir_flex_mask *flex_mask)
+{
+	enum i40e_flxpld_layer_idx layer_idx = filter->input.flow_ext.layer_idx;
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_pctype_state *state = &flows->pctype[pctype];
+	struct i40e_fdir_layer_state *layer = &flows->layer[layer_idx];
+	int i;
+
+	for (i = 0; i < filter->input.flow_ext.raw_id; i++) {
+		uint8_t field_idx;
+
+		field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
+		layer->flex_set[i] = filter->input.flow_ext.flex_pit[field_idx];
+	}
+
+	state->flex_mask = *flex_mask;
 }
 
+/* Validate an input set against what earlier filters on this pctype established */
 static int
-i40e_flow_set_fdir_inset(struct i40e_pf *pf,
-			 enum i40e_filter_pctype pctype,
-			 uint64_t input_set)
+i40e_fdir_inset_check(struct i40e_pf *pf,
+		      enum i40e_filter_pctype pctype,
+		      uint64_t input_set)
 {
-	uint32_t mask_reg[I40E_INSET_MASK_NUM_REG] = {0};
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	uint64_t inset_reg = 0;
-	int i, num;
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_pctype_state *state = &flows->pctype[pctype];
 
-	/* Check if the input set is valid */
 	if (i40e_validate_input_set(pctype, RTE_ETH_FILTER_FDIR,
 				    input_set) != 0) {
 		PMD_DRV_LOG(ERR, "Invalid input set");
 		return -EINVAL;
 	}
 
-	/* Check if the configuration is conflicted */
-	if (pf->fdir.flow_count[pctype] &&
-	    memcmp(&pf->fdir.input_set[pctype], &input_set, sizeof(uint64_t))) {
+	if (state->flow_count == 0)
+		return 0;
+
+	if (state->input_set != input_set) {
 		PMD_DRV_LOG(ERR, "Conflict with the first rule's input set.");
 		return -EINVAL;
 	}
 
-	if (pf->fdir.flow_count[pctype] &&
-	    !memcmp(&pf->fdir.input_set[pctype], &input_set, sizeof(uint64_t)))
-		return 0;
+	return 0;
+}
+
+static int
+i40e_fdir_inset_program(struct i40e_pf *pf,
+			enum i40e_filter_pctype pctype,
+			uint64_t input_set)
+{
+	uint32_t mask_reg[I40E_INSET_MASK_NUM_REG] = {0};
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	uint64_t inset_reg = 0;
+	int i, num;
 
 	num = i40e_generate_inset_mask_reg(hw, input_set, mask_reg,
 						 I40E_INSET_MASK_NUM_REG);
@@ -1360,7 +1630,6 @@ i40e_flow_set_fdir_inset(struct i40e_pf *pf,
 
 	I40E_WRITE_FLUSH(hw);
 
-	pf->fdir.input_set[pctype] = input_set;
 	return 0;
 }
 
@@ -1403,166 +1672,294 @@ i40e_find_available_buffer(struct rte_eth_dev *dev)
 	return (unsigned char *)fdir_info->prg_pkt[txq->tx_tail >> 1];
 }
 
+static enum i40e_filter_pctype
+i40e_fdir_filter_pctype(const struct i40e_fdir_filter *filter)
+{
+	if (filter->input.flow_ext.pkt_template)
+		return filter->input.flow.raw_flow.pctype;
+
+	return filter->input.pctype;
+}
+
+bool
+i40e_fdir_filter_needs_status_wait(const struct i40e_pf *pf,
+				   uint32_t filter_count)
+{
+	if (pf->fdir.fdir_invalprio != 1)
+		return true;
+
+	return filter_count >= pf->fdir.fdir_guarantee_total_space;
+}
+
 /**
- * i40e_flow_add_del_fdir_filter - add or remove a flow director filter.
- * @pf: board private structure
- * @filter: fdir filter entry
- * @add: 0 - delete, 1 - add
+ * i40e_fdir_filter_validate - check whether a filter can be accepted at all.
+ *
+ * Only inspects the filter itself; conflicts against already registered
+ * filters are detected by i40e_fdir_filter_register().
  */
 int
-i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
-			      const struct i40e_fdir_filter_conf *filter,
-			      bool add)
+i40e_fdir_filter_validate(struct rte_eth_dev *dev,
+			  const struct i40e_fdir_filter *filter)
 {
-	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	enum i40e_flxpld_layer_idx layer_idx = I40E_FLXPLD_L2_IDX;
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	uint8_t flex_mask[I40E_FDIR_MAX_FLEX_LEN];
-	struct i40e_fdir_filter check_filter; /* Check if the filter exists */
-	struct i40e_fdir_flex_pit flex_pit;
-	enum i40e_filter_pctype pctype;
-	struct i40e_fdir_filter *node;
-	unsigned char *pkt = NULL;
-	bool cfg_flex_pit = true;
-	bool wait_status = true;
-	uint8_t field_idx;
-	int ret = 0;
-	int i;
-
-	if (pf->fdir.fdir_vsi == NULL) {
-		PMD_DRV_LOG(ERR, "FDIR is not enabled");
-		return -ENOTSUP;
-	}
 
 	if (filter->action.rx_queue >= pf->dev_data->nb_rx_queues) {
 		PMD_DRV_LOG(ERR, "Invalid queue ID");
 		return -EINVAL;
 	}
+
 	if (filter->input.flow_ext.is_vf &&
 	    filter->input.flow_ext.dst_id >= pf->vf_num) {
 		PMD_DRV_LOG(ERR, "Invalid VF ID");
 		return -EINVAL;
 	}
-	if (filter->input.flow_ext.pkt_template) {
-		if (filter->input.flow.raw_flow.length > I40E_FDIR_PKT_LEN ||
-		    !filter->input.flow.raw_flow.packet) {
-			PMD_DRV_LOG(ERR, "Invalid raw packet template"
-				" flow filter parameters!");
-			return -EINVAL;
-		}
-		pctype = filter->input.flow.raw_flow.pctype;
-	} else {
-		pctype = filter->input.pctype;
-	}
-
-	/* Check if there is the filter in SW list */
-	memset(&check_filter, 0, sizeof(check_filter));
-	i40e_fdir_filter_convert(filter, &check_filter);
+
+	if (filter->input.flow_ext.pkt_template &&
+	    (filter->input.flow.raw_flow.length > I40E_FDIR_PKT_LEN ||
+	     filter->input.flow.raw_flow.packet == NULL)) {
+		PMD_DRV_LOG(ERR,
+			    "Invalid raw packet template flow filter parameters!");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * Records the filter and everything derived from it.
+ */
+int
+i40e_fdir_filter_register(struct rte_eth_dev *dev,
+			  const struct i40e_fdir_filter *filter,
+			  struct i40e_fdir_filter **node)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	enum i40e_filter_pctype pctype = i40e_fdir_filter_pctype(filter);
+	uint64_t input_set = filter->input.flow_ext.input_set;
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_pctype_state *state = &flows->pctype[pctype];
+	struct i40e_fdir_layer_state *layer = NULL;
+	struct i40e_fdir_flex_mask flex_mask;
+	bool common_pctype;
+	int ret;
+
+	/* check input set if the packet type is common */
+	common_pctype = !filter->input.flow_ext.customized_pctype;
+
+	if (common_pctype) {
+		ret = i40e_fdir_inset_check(pf, pctype, input_set);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* check if flex flow configuration is valid */
+	if (filter->input.flow_ext.is_flex_flow) {
+		ret = i40e_fdir_flex_check(pf, filter, pctype, &flex_mask);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* register the flow with the hash table */
+	ret = i40e_fdir_filter_hash_add(pf, filter, node);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Conflict with existing flow director rules!");
+		return ret;
+	}
+
+	/* save additional configuration */
+	if (common_pctype)
+		state->input_set = input_set;
+
+	if (filter->input.flow_ext.is_flex_flow) {
+		i40e_fdir_flex_store(pf, filter, pctype, &flex_mask);
+		layer = &flows->layer[filter->input.flow_ext.layer_idx];
+		layer->flex_flow_count++;
+		layer->flex_pit_flag = true;
+		state->flex_mask_flag = true;
+	}
+
+	state->flow_count++;
+	pf->fdir.fdir_actual_cnt++;
+
+	i40e_fdir_rx_proc_sync(dev);
+
+	return 0;
+}
+
+/**
+ * i40e_fdir_filter_unregister - drop software ownership of a filter.
+ */
+int
+i40e_fdir_filter_unregister(struct rte_eth_dev *dev,
+			    struct i40e_fdir_filter *node)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	enum i40e_filter_pctype pctype = i40e_fdir_filter_pctype(node);
+	enum i40e_flxpld_layer_idx layer_idx = node->input.flow_ext.layer_idx;
+	bool is_flex_flow = node->input.flow_ext.is_flex_flow;
+	struct i40e_fdir_flow_store *flows = &pf->fdir.flows;
+	struct i40e_fdir_pctype_state *state = &flows->pctype[pctype];
+	struct i40e_fdir_layer_state *layer = &flows->layer[layer_idx];
+	int ret;
+
+	ret = i40e_fdir_filter_hash_del(pf, node);
+	if (ret < 0)
+		return ret;
+
+	if (is_flex_flow && --layer->flex_flow_count == 0)
+		layer->flex_pit_flag = false;
+
+	if (--state->flow_count == 0)
+		state->flex_mask_flag = false;
+
+	pf->fdir.fdir_actual_cnt--;
+
+	i40e_fdir_rx_proc_sync(dev);
+	if (pf->fdir.fdir_actual_cnt == 0)
+		i40e_fdir_teardown(pf);
+
+	return 0;
+}
+
+/* Take software ownership of a packet template filter, owning its packet */
+static int
+i40e_fdir_tmpl_register(struct rte_eth_dev *dev,
+			const struct i40e_fdir_filter *filter,
+			struct i40e_fdir_tmpl_filter **node)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	const struct i40e_raw_flow *raw = &filter->input.flow.raw_flow;
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
+	struct i40e_fdir_tmpl_filter *tmpl;
+	struct i40e_fdir_tmpl_key key;
+	uint8_t *packet;
+	int ret;
+
+	ret = i40e_fdir_tmpl_store_init(dev);
+	if (ret < 0)
+		return ret;
+
+	if (i40e_fdir_tmpl_lookup(fdir_info, raw) != NULL) {
+		PMD_DRV_LOG(ERR, "Conflict with existing flow director rules!");
+		return -EEXIST;
+	}
+
+	packet = rte_malloc("fdir_tmpl_packet", raw->length, 0);
+	if (packet == NULL)
+		return -ENOMEM;
+	memcpy(packet, raw->packet, raw->length);
+
+	/* the stored key borrows the packet, so it must point at our copy */
+	i40e_fdir_tmpl_key_fill(&key, raw, packet);
+
+	ret = rte_hash_add_key_with_hash(fdir_info->tmpls.hash_table, &key,
+					 i40e_fdir_tmpl_sig(raw));
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR,
+			    "Failed to insert fdir template to hash table %d!",
+			    ret);
+		rte_free(packet);
+		return ret;
+	}
+
+	tmpl = &fdir_info->tmpls.filter_array[ret];
+	tmpl->fdir = *filter;
+	tmpl->packet = packet;
+	tmpl->fdir.input.flow.raw_flow.packet = packet;
+
+	fdir_info->fdir_actual_cnt++;
+
+	i40e_fdir_rx_proc_sync(dev);
+
+	*node = tmpl;
+
+	return 0;
+}
+
+static int
+i40e_fdir_tmpl_unregister(struct rte_eth_dev *dev,
+			  struct i40e_fdir_tmpl_filter *node)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	const struct i40e_raw_flow *raw = &node->fdir.input.flow.raw_flow;
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
+	struct i40e_fdir_tmpl_key key;
+	int ret;
+
+	i40e_fdir_tmpl_key_fill(&key, raw, raw->packet);
+
+	ret = rte_hash_del_key_with_hash(fdir_info->tmpls.hash_table, &key,
+					 i40e_fdir_tmpl_sig(raw));
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR,
+			    "Failed to delete fdir template from hash table %d!",
+			    ret);
+		return ret;
+	}
+
+	rte_free(node->packet);
+	node->packet = NULL;
+
+	fdir_info->fdir_actual_cnt--;
+
+	i40e_fdir_rx_proc_sync(dev);
+	if (fdir_info->fdir_actual_cnt == 0)
+		i40e_fdir_teardown(pf);
+
+	return 0;
+}
+
+/**
+ * i40e_fdir_filter_program - apply a filter to the hardware.
+ *
+ * Derives everything it needs from the filter itself and touches no software
+ * bookkeeping, so it may be called repeatedly to reapply an already registered
+ * filter. The caller is responsible for having validated and registered it.
+ */
+int
+i40e_fdir_filter_program(struct rte_eth_dev *dev,
+			 const struct i40e_fdir_filter *filter,
+			 bool add, bool wait_status)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	enum i40e_filter_pctype pctype = i40e_fdir_filter_pctype(filter);
+	unsigned char *pkt;
+	int ret;
+
+	if (pf->fdir.fdir_vsi == NULL) {
+		PMD_DRV_LOG(ERR, "FDIR is not enabled");
+		return -ENOTSUP;
+	}
 
 	if (add) {
-		/* configure the input set for common PCTYPEs*/
 		if (!filter->input.flow_ext.customized_pctype &&
 		    !filter->input.flow_ext.pkt_template) {
-			ret = i40e_flow_set_fdir_inset(pf, pctype,
+			ret = i40e_fdir_inset_program(pf, pctype,
 					filter->input.flow_ext.input_set);
 			if (ret < 0)
 				return ret;
 		}
 
 		if (filter->input.flow_ext.is_flex_flow) {
-			for (i = 0; i < filter->input.flow_ext.raw_id; i++) {
-				layer_idx = filter->input.flow_ext.layer_idx;
-				field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
-				flex_pit = filter->input.flow_ext.flex_pit[field_idx];
-
-				/* Store flex pit to SW */
-				ret = i40e_flow_store_flex_pit(pf, &flex_pit,
-							       layer_idx, i);
-				if (ret < 0) {
-					PMD_DRV_LOG(ERR, "Conflict with the"
-						    " first flexible rule.");
-					return -EINVAL;
-				} else if (ret > 0) {
-					cfg_flex_pit = false;
-				}
-			}
-
-			/* Store flex mask to SW */
-			for (i = 0; i < I40E_FDIR_MAX_FLEX_LEN; i++)
-				flex_mask[i] =
-					filter->input.flow_ext.flex_mask[i];
-
-			/* Validate the flex mask before writing any hardware
-			 * register. i40e_flow_set_fdir_flex_pit() below programs
-			 * the global GLQF_ORT register, which is shared by all
-			 * PFs on the NIC, so it must not be touched for a rule
-			 * that is going to be rejected.
-			 */
-			ret = i40e_flow_store_flex_mask(pf, pctype, flex_mask);
-			if (ret == -1) {
-				PMD_DRV_LOG(ERR, "Exceed maximal"
-					    " number of bitmasks");
-				return -EINVAL;
-			} else if (ret == -2) {
-				PMD_DRV_LOG(ERR, "Conflict with the"
-					    " first flexible rule");
-				return -EINVAL;
-			}
-
-			if (cfg_flex_pit)
-				i40e_flow_set_fdir_flex_pit(pf, layer_idx,
-						filter->input.flow_ext.raw_id);
-
-			if (ret == 0)
-				i40e_flow_set_fdir_flex_msk(pf, pctype);
+			i40e_fdir_flex_pit_program(pf, filter);
+			ret = i40e_fdir_flex_msk_program(pf, pctype, filter);
+			if (ret < 0)
+				return ret;
 		}
-
-		ret = i40e_sw_fdir_filter_insert(pf, &check_filter);
-		if (ret < 0) {
-			PMD_DRV_LOG(ERR,
-				    "Conflict with existing flow director rules!");
-			return -EINVAL;
-		}
-
-		if (fdir_info->fdir_invalprio == 1 &&
-				fdir_info->fdir_guarantee_free_space > 0)
-			wait_status = false;
-	} else {
-		if (filter->input.flow_ext.is_flex_flow)
-			layer_idx = filter->input.flow_ext.layer_idx;
-
-		node = i40e_sw_fdir_filter_lookup(fdir_info,
-				&check_filter.fdir.input);
-		if (!node) {
-			PMD_DRV_LOG(ERR,
-				    "There's no corresponding flow director filter!");
-			return -EINVAL;
-		}
-
-		ret = i40e_sw_fdir_filter_del(pf, &node->fdir.input);
-		if (ret < 0) {
-			PMD_DRV_LOG(ERR,
-					"Error deleting fdir rule from hash table!");
-			return -EINVAL;
-		}
-
-		pf->fdir.flex_mask_flag[pctype] = 0;
-
-		if (fdir_info->fdir_invalprio == 1)
-			wait_status = false;
 	}
 
-	/* find a buffer to store the pkt */
 	pkt = i40e_find_available_buffer(dev);
-	if (pkt == NULL)
-		goto error_op;
+	if (pkt == NULL) {
+		PMD_DRV_LOG(ERR, "No buffer available to program fdir filter.");
+		return -ENOSPC;
+	}
 
 	memset(pkt, 0, I40E_FDIR_PKT_LEN);
 	ret = i40e_flow_fdir_construct_pkt(pf, &filter->input, pkt);
 	if (ret < 0) {
 		PMD_DRV_LOG(ERR, "construct packet for fdir fails.");
-		goto error_op;
+		return ret;
 	}
 
 	if (hw->mac.type == I40E_MAC_X722) {
@@ -1576,45 +1973,87 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
 	if (ret < 0) {
 		PMD_DRV_LOG(ERR, "fdir programming fails for PCTYPE(%u).",
 			    pctype);
-		goto error_op;
+		return ret;
 	}
 
-	if (filter->input.flow_ext.is_flex_flow) {
-		if (add) {
-			fdir_info->flex_flow_count[layer_idx]++;
-			pf->fdir.flex_pit_flag[layer_idx] = 1;
-		} else {
-			fdir_info->flex_flow_count[layer_idx]--;
-			if (!fdir_info->flex_flow_count[layer_idx])
-				pf->fdir.flex_pit_flag[layer_idx] = 0;
-		}
+	return 0;
+}
+
+/**
+ * i40e_fdir_tmpl_add - add a packet template filter.
+ * @dev: ethernet device
+ * @filter: fdir filter entry
+ *
+ * Entry point for the PMD packet template API, whose filters have no rte_flow
+ * handle and so are tracked entirely here.
+ */
+int
+i40e_fdir_tmpl_add(struct rte_eth_dev *dev,
+			   const struct i40e_fdir_filter *filter)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_fdir_tmpl_filter *node;
+	int ret;
+
+	ret = i40e_fdir_filter_validate(dev, filter);
+	if (ret < 0)
+		return ret;
+
+	/* register updates count, so store it */
+	uint32_t cnt = pf->fdir.fdir_actual_cnt;
+
+	ret = i40e_fdir_tmpl_register(dev, filter, &node);
+	if (ret < 0)
+		return ret;
+
+	ret = i40e_fdir_engine_init(dev);
+	if (ret < 0) {
+		i40e_fdir_tmpl_unregister(dev, node);
+		return ret;
+	}
+
+	ret = i40e_fdir_filter_program(dev, &node->fdir, true,
+			i40e_fdir_filter_needs_status_wait(pf, cnt));
+	if (ret < 0) {
+		i40e_fdir_tmpl_unregister(dev, node);
+		return ret;
 	}
 
-	if (add) {
-		fdir_info->flow_count[pctype]++;
-		fdir_info->fdir_actual_cnt++;
-		if (fdir_info->fdir_invalprio == 1 &&
-				fdir_info->fdir_guarantee_free_space > 0)
-			fdir_info->fdir_guarantee_free_space--;
-	} else {
-		fdir_info->flow_count[pctype]--;
-		fdir_info->fdir_actual_cnt--;
-		if (fdir_info->fdir_invalprio == 1 &&
-				fdir_info->fdir_guarantee_free_space <
-				fdir_info->fdir_guarantee_total_space)
-			fdir_info->fdir_guarantee_free_space++;
+	TAILQ_INSERT_TAIL(&pf->fdir.tmpls.list, node, rules);
+
+	return 0;
+}
+
+int
+i40e_fdir_tmpl_del(struct rte_eth_dev *dev,
+			   const struct i40e_fdir_filter *filter)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_fdir_tmpl_filter *node;
+	int ret;
+
+	ret = i40e_fdir_filter_validate(dev, filter);
+	if (ret < 0)
+		return ret;
+
+	node = i40e_fdir_tmpl_lookup(&pf->fdir, &filter->input.flow.raw_flow);
+	if (node == NULL) {
+		PMD_DRV_LOG(ERR,
+			    "There's no corresponding flow director filter!");
+		return -EINVAL;
 	}
 
-	return ret;
+	ret = i40e_fdir_filter_program(dev, &node->fdir, false, false);
+	if (ret < 0)
+		return ret;
+
+	ret = i40e_fdir_tmpl_unregister(dev, node);
+	if (ret < 0)
+		return ret;
 
-error_op:
-	/* roll back */
-	if (add)
-		i40e_sw_fdir_filter_del(pf, &check_filter.fdir.input);
-	else
-		i40e_sw_fdir_filter_insert(pf, &check_filter);
+	TAILQ_REMOVE(&pf->fdir.tmpls.list, node, rules);
 
-	return ret;
+	return 0;
 }
 
 /*
@@ -1629,7 +2068,7 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
 static int
 i40e_flow_fdir_filter_programming(struct i40e_pf *pf,
 				  enum i40e_filter_pctype pctype,
-				  const struct i40e_fdir_filter_conf *filter,
+				  const struct i40e_fdir_filter *filter,
 				  bool add, bool wait_status)
 {
 	struct ci_tx_queue *txq = pf->fdir.txq;
@@ -1813,6 +2252,9 @@ i40e_fdir_info_get_flex_set(struct i40e_pf *pf,
 	for (layer_idx = I40E_FLXPLD_L2_IDX;
 	     layer_idx <= I40E_FLXPLD_L4_IDX;
 	     layer_idx++) {
+		struct i40e_fdir_layer_state *layer =
+			&pf->fdir.flows.layer[layer_idx];
+
 		if (layer_idx == I40E_FLXPLD_L2_IDX)
 			ptr->type = RTE_ETH_L2_PAYLOAD;
 		else if (layer_idx == I40E_FLXPLD_L3_IDX)
@@ -1821,8 +2263,7 @@ i40e_fdir_info_get_flex_set(struct i40e_pf *pf,
 			ptr->type = RTE_ETH_L4_PAYLOAD;
 
 		for (i = 0; i < I40E_MAX_FLXPLD_FIED; i++) {
-			flex_pit = &pf->fdir.flex_set[layer_idx *
-				I40E_MAX_FLXPLD_FIED + i];
+			flex_pit = &layer->flex_set[i];
 			if (flex_pit->size == 0)
 				continue;
 			src = flex_pit->src_offset * sizeof(uint16_t);
@@ -1850,7 +2291,9 @@ i40e_fdir_info_get_flex_mask(struct i40e_pf *pf,
 	for (i = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
 	     i <= I40E_FILTER_PCTYPE_L2_PAYLOAD;
 	     i++) {
-		mask =  &pf->fdir.flex_mask[i];
+		struct i40e_fdir_pctype_state *state = &pf->fdir.flows.pctype[i];
+
+		mask = &state->flex_mask;
 		flow_type = i40e_pctype_to_flowtype(pf->adapter,
 						    (enum i40e_filter_pctype)i);
 		if (flow_type == RTE_ETH_FLOW_UNKNOWN)
@@ -1941,29 +2384,35 @@ i40e_fdir_stats_get(struct rte_eth_dev *dev, struct rte_eth_fdir_stats *stat)
 			    I40E_PFQF_FDSTAT_BEST_CNT_SHIFT);
 }
 
-/* Restore flow director filter */
 void
 i40e_fdir_filter_restore(struct i40e_pf *pf)
 {
 	struct rte_eth_dev *dev = I40E_VSI_TO_ETH_DEV(pf->main_vsi);
-	struct i40e_fdir_filter_list *fdir_list = &pf->fdir.fdir_list;
-	struct i40e_fdir_filter *f;
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	uint32_t fdstat;
-	uint32_t guarant_cnt;  /**< Number of filters in guaranteed spaces. */
-	uint32_t best_cnt;     /**< Number of filters in best effort spaces. */
+	struct i40e_fdir_tmpl_filter *tmpl;
+	struct rte_flow *flow;
+	int ret;
 
-	TAILQ_FOREACH(f, fdir_list, rules)
-		i40e_flow_add_del_fdir_filter(dev, &f->fdir, TRUE);
+	i40e_fdir_rx_proc_sync(dev);
 
-	fdstat = I40E_READ_REG(hw, I40E_PFQF_FDSTAT);
-	guarant_cnt =
-		(uint32_t)((fdstat & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK) >>
-			   I40E_PFQF_FDSTAT_GUARANT_CNT_SHIFT);
-	best_cnt =
-		(uint32_t)((fdstat & I40E_PFQF_FDSTAT_BEST_CNT_MASK) >>
-			   I40E_PFQF_FDSTAT_BEST_CNT_SHIFT);
+	if (pf->fdir.fdir_vsi == NULL)
+		return;
 
-	PMD_DRV_LOG(INFO, "FDIR: Guarant count: %d,  Best count: %d",
-		    guarant_cnt, best_cnt);
+	TAILQ_FOREACH(flow, &pf->flow_list, node) {
+		struct i40e_fdir_filter *node = flow->rule;
+
+		if (flow->filter_type != RTE_ETH_FILTER_FDIR)
+			continue;
+
+		ret = i40e_fdir_filter_program(dev, node, true, false);
+		if (ret < 0)
+			PMD_DRV_LOG(ERR,
+				    "Failed to restore flow director filter: %d", ret);
+	}
+
+	TAILQ_FOREACH(tmpl, &pf->fdir.tmpls.list, rules) {
+		ret = i40e_fdir_filter_program(dev, &tmpl->fdir, true, false);
+		if (ret < 0)
+			PMD_DRV_LOG(ERR,
+				    "Failed to restore flow director template: %d", ret);
+	}
 }
diff --git a/drivers/net/intel/i40e/i40e_flow.c b/drivers/net/intel/i40e/i40e_flow.c
index 8f09dfeb11d..f3e6c923950 100644
--- a/drivers/net/intel/i40e/i40e_flow.c
+++ b/drivers/net/intel/i40e/i40e_flow.c
@@ -67,11 +67,11 @@ static int i40e_flow_dev_dump(struct rte_eth_dev *dev,
 static int i40e_flow_parse_fdir_pattern(struct rte_eth_dev *dev,
 					const struct rte_flow_item *pattern,
 					struct rte_flow_error *error,
-					struct i40e_fdir_filter_conf *filter);
+					struct i40e_fdir_filter *filter);
 static int i40e_flow_parse_fdir_action(struct rte_eth_dev *dev,
 				       const struct rte_flow_action *actions,
 				       struct rte_flow_error *error,
-				       struct i40e_fdir_filter_conf *filter);
+				       struct i40e_fdir_filter *filter);
 static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
@@ -1375,7 +1375,7 @@ i40e_flow_check_raw_item(const struct rte_flow_item *item,
 static uint8_t
 i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 				enum rte_flow_item_type item_type,
-				struct i40e_fdir_filter_conf *filter)
+				struct i40e_fdir_filter *filter)
 {
 	struct i40e_customized_pctype *cus_pctype = NULL;
 
@@ -1440,7 +1440,7 @@ i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 }
 
 static void
-i40e_flow_set_filter_spi(struct i40e_fdir_filter_conf *filter,
+i40e_flow_set_filter_spi(struct i40e_fdir_filter *filter,
 	const struct rte_flow_item_esp *esp_spec)
 {
 	if (filter->input.flow_ext.oip_type ==
@@ -1478,7 +1478,7 @@ static int
 i40e_flow_parse_fdir_pattern(struct rte_eth_dev *dev,
 			     const struct rte_flow_item *pattern,
 			     struct rte_flow_error *error,
-			     struct i40e_fdir_filter_conf *filter)
+			     struct i40e_fdir_filter *filter)
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	const struct rte_flow_item *item = pattern;
@@ -2310,7 +2310,7 @@ static int
 i40e_flow_parse_fdir_action(struct rte_eth_dev *dev,
 			    const struct rte_flow_action *actions,
 			    struct rte_flow_error *error,
-			    struct i40e_fdir_filter_conf *filter)
+			    struct i40e_fdir_filter *filter)
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct ci_flow_actions parsed_actions = {0};
@@ -2430,7 +2430,7 @@ i40e_flow_parse_fdir_filter(struct rte_eth_dev *dev,
 			    struct rte_flow_error *error,
 			    struct i40e_filter_ctx *filter)
 {
-	struct i40e_fdir_filter_conf *fdir_filter = &filter->fdir_filter;
+	struct i40e_fdir_filter *fdir_filter = &filter->fdir_filter;
 	int ret;
 
 	ret = i40e_flow_parse_fdir_pattern(dev, pattern, error, fdir_filter);
@@ -3756,28 +3756,6 @@ i40e_flow_create(struct rte_eth_dev *dev,
 		return NULL;
 
 	if (filter_ctx.type == RTE_ETH_FILTER_FDIR) {
-		/* if this is the first time we're creating an fdir flow */
-		if (pf->fdir.fdir_vsi == NULL) {
-			ret = i40e_fdir_setup(pf);
-			if (ret != I40E_SUCCESS) {
-				rte_flow_error_set(error, ENOTSUP,
-						RTE_FLOW_ERROR_TYPE_HANDLE,
-						NULL, "Failed to setup fdir.");
-				return NULL;
-			}
-			ret = i40e_fdir_configure(dev);
-			if (ret < 0) {
-				rte_flow_error_set(error, ENOTSUP,
-						RTE_FLOW_ERROR_TYPE_HANDLE,
-						NULL, "Failed to configure fdir.");
-				i40e_fdir_teardown(pf);
-				return NULL;
-			}
-		}
-		/* If create the first fdir rule, enable fdir check for rx queues */
-		if (TAILQ_EMPTY(&pf->fdir.fdir_list))
-			i40e_fdir_rx_proc_enable(dev, 1);
-
 		flow = i40e_fdir_entry_pool_get(fdir_info);
 		if (flow == NULL) {
 			rte_flow_error_set(error, ENOBUFS,
@@ -3797,13 +3775,31 @@ i40e_flow_create(struct rte_eth_dev *dev,
 	}
 
 	switch (filter_ctx.type) {
-	case RTE_ETH_FILTER_FDIR:
-		ret = i40e_flow_add_del_fdir_filter(dev, &filter_ctx.fdir_filter, 1);
+	case RTE_ETH_FILTER_FDIR: {
+		struct i40e_fdir_filter *node;
+
+		ret = i40e_fdir_filter_validate(dev, &filter_ctx.fdir_filter);
 		if (ret)
 			goto free_flow;
-		flow->rule = TAILQ_LAST(&pf->fdir.fdir_list,
-					i40e_fdir_filter_list);
+		ret = i40e_fdir_filter_register(dev, &filter_ctx.fdir_filter,
+						&node);
+		if (ret)
+			goto free_flow;
+		ret = i40e_fdir_engine_init(dev);
+		if (ret) {
+			i40e_fdir_filter_unregister(dev, node);
+			goto free_flow;
+		}
+		ret = i40e_fdir_filter_program(dev, node, 1,
+				i40e_fdir_filter_needs_status_wait(pf,
+					fdir_info->fdir_actual_cnt - 1));
+		if (ret) {
+			i40e_fdir_filter_unregister(dev, node);
+			goto free_flow;
+		}
+		flow->rule = node;
 		break;
+	}
 	case RTE_ETH_FILTER_TUNNEL:
 		ret = i40e_dev_consistent_tunnel_filter_set(pf,
 				&filter_ctx.consistent_tunnel_filter, 1);
@@ -3860,16 +3856,15 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 		ret = i40e_flow_destroy_tunnel_filter(pf,
 			      (struct i40e_tunnel_filter *)flow->rule);
 		break;
-	case RTE_ETH_FILTER_FDIR:
-		ret = i40e_flow_add_del_fdir_filter(dev,
-				&((struct i40e_fdir_filter *)flow->rule)->fdir,
-				0);
+	case RTE_ETH_FILTER_FDIR: {
+		struct i40e_fdir_filter *node = flow->rule;
 
-		/* If the last flow is destroyed, disable fdir. */
-		if (!ret && TAILQ_EMPTY(&pf->fdir.fdir_list)) {
-			i40e_fdir_rx_proc_enable(dev, 0);
-		}
+		ret = i40e_fdir_filter_program(dev, node, 0, false);
+		if (ret)
+			break;
+		ret = i40e_fdir_filter_unregister(dev, node);
 		break;
+	}
 	case RTE_ETH_FILTER_HASH:
 		ret = i40e_hash_filter_destroy(pf, flow->rule);
 		break;
@@ -3994,59 +3989,24 @@ i40e_flow_flush_fdir_filter(struct i40e_pf *pf)
 {
 	struct rte_eth_dev *dev = &rte_eth_devices[pf->dev_data->port_id];
 	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	struct i40e_fdir_filter *fdir_filter;
-	enum i40e_filter_pctype pctype;
 	struct rte_flow *flow;
 	void *temp;
 	int ret;
-	uint32_t i = 0;
 
-	ret = i40e_fdir_flush(dev);
-	if (!ret) {
-		/* Delete FDIR filters in FDIR list. */
-		while ((fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list))) {
-			ret = i40e_sw_fdir_filter_del(pf,
-						      &fdir_filter->fdir.input);
-			if (ret < 0)
-				return ret;
-		}
-
-		/* Delete FDIR flows in flow list. */
-		RTE_TAILQ_FOREACH_SAFE(flow, &pf->flow_list, node, temp) {
-			if (flow->filter_type == RTE_ETH_FILTER_FDIR) {
-				TAILQ_REMOVE(&pf->flow_list, flow, node);
-			}
-		}
-
-		/* reset bitmap */
-		rte_bitmap_reset(fdir_info->fdir_flow_pool.bitmap);
-		for (i = 0; i < fdir_info->fdir_space_size; i++) {
-			fdir_info->fdir_flow_pool.pool[i].idx = i;
-			rte_bitmap_set(fdir_info->fdir_flow_pool.bitmap, i);
-		}
-
-		fdir_info->fdir_actual_cnt = 0;
-		fdir_info->fdir_guarantee_free_space =
-			fdir_info->fdir_guarantee_total_space;
-		memset(fdir_info->fdir_filter_array,
-			0,
-			sizeof(struct i40e_fdir_filter) *
-			I40E_MAX_FDIR_FILTER_NUM);
-
-		for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-		     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++) {
-			pf->fdir.flow_count[pctype] = 0;
-			pf->fdir.flex_mask_flag[pctype] = 0;
-		}
-
-		for (i = 0; i < I40E_MAX_FLXPLD_LAYER; i++)
-			pf->fdir.flex_pit_flag[i] = 0;
-
-		/* Disable FDIR processing as all FDIR rules are now flushed */
-		i40e_fdir_rx_proc_enable(dev, 0);
+	RTE_TAILQ_FOREACH_SAFE(flow, &pf->flow_list, node, temp) {
+		if (flow->filter_type != RTE_ETH_FILTER_FDIR)
+			continue;
+		ret = i40e_fdir_filter_program(dev, flow->rule, false, false);
+		if (ret < 0)
+			return ret;
+		ret = i40e_fdir_filter_unregister(dev, flow->rule);
+		if (ret < 0)
+			return ret;
+		TAILQ_REMOVE(&pf->flow_list, flow, node);
+		i40e_fdir_entry_pool_put(fdir_info, flow);
 	}
 
-	return ret;
+	return 0;
 }
 
 /* Flush all tunnel filters */
diff --git a/drivers/net/intel/i40e/rte_pmd_i40e.c b/drivers/net/intel/i40e/rte_pmd_i40e.c
index 2e7943ef8b3..0aff505b643 100644
--- a/drivers/net/intel/i40e/rte_pmd_i40e.c
+++ b/drivers/net/intel/i40e/rte_pmd_i40e.c
@@ -3044,7 +3044,7 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 			uint8_t add)
 {
 	struct rte_eth_dev *dev = &rte_eth_devices[port];
-	struct i40e_fdir_filter_conf filter_conf;
+	struct i40e_fdir_filter filter_conf;
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
 
@@ -3068,7 +3068,9 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 		(enum i40e_fdir_status)conf->action.report_status;
 	filter_conf.action.flex_off = conf->action.flex_off;
 
-	return i40e_flow_add_del_fdir_filter(dev, &filter_conf, add);
+	if (add)
+		return i40e_fdir_tmpl_add(dev, &filter_conf);
+	return i40e_fdir_tmpl_del(dev, &filter_conf);
 }
 
 RTE_EXPORT_SYMBOL(rte_pmd_i40e_inset_get)
-- 
2.52.0


  parent reply	other threads:[~2026-10-07 10:52 UTC|newest]

Thread overview: 160+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20 14:00 [PATCH v1 00/21] Building a better rte_flow parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 01/21] ethdev: add flow graph API Anatoly Burakov
2026-08-25 13:55   ` Thomas Monjalon
2026-08-26  8:58     ` Burakov, Anatoly
2026-08-26  9:21       ` Thomas Monjalon
2026-08-26  9:26         ` Bruce Richardson
2026-08-26 10:20         ` Burakov, Anatoly
2026-08-26 14:46           ` Thomas Monjalon
2026-08-27  7:58             ` Burakov, Anatoly
2026-08-27  8:02               ` Thomas Monjalon
2026-08-20 14:00 ` [PATCH v1 02/21] net/intel/common: add flow engines infrastructure Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 03/21] net/intel/common: add utility functions Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 04/21] net/ixgbe: add support for common flow parsing Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 05/21] net/ixgbe: reimplement ethertype parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 06/21] net/ixgbe: reimplement syn parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 07/21] net/ixgbe: reimplement L2 tunnel parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 08/21] net/ixgbe: reimplement ntuple parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 09/21] net/ixgbe: reimplement security parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 10/21] net/ixgbe: reimplement FDIR parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 11/21] net/ixgbe: reimplement hash parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 12/21] net/i40e: add support for common flow parsing Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 13/21] net/i40e: reimplement ethertype parser Anatoly Burakov
2026-08-20 14:00 ` [PATCH v1 14/21] net/i40e: reimplement FDIR parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 15/21] net/i40e: reimplement tunnel QinQ parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 16/21] net/i40e: reimplement VXLAN parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 17/21] net/i40e: reimplement NVGRE parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 18/21] net/i40e: reimplement MPLS parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 19/21] net/i40e: reimplement gtp parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 20/21] net/i40e: reimplement L4 cloud parser Anatoly Burakov
2026-08-20 14:01 ` [PATCH v1 21/21] net/i40e: reimplement hash parser Anatoly Burakov
2026-08-21 15:27 ` [PATCH v1 00/21] Building a better rte_flow parser Stephen Hemminger
2026-09-08 15:20 ` [PATCH v2 00/19] " Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 01/19] ethdev: add flow graph API Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 02/19] net/intel/common: add flow engines infrastructure Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 03/19] net/intel/common: add utility functions Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 04/19] net/ixgbe: add support for common flow parsing Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 05/19] net/ixgbe: reimplement ethertype parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 06/19] net/ixgbe: reimplement syn parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 07/19] net/ixgbe: reimplement L2 tunnel parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 08/19] net/ixgbe: reimplement ntuple parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 09/19] net/ixgbe: reimplement security parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 10/19] net/ixgbe: reimplement FDIR parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 11/19] net/ixgbe: reimplement hash parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 12/19] net/ixgbe: advertise flow keep capability Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 13/19] net/i40e: add support for common flow parsing Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 14/19] net/i40e: reimplement ethertype parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 15/19] net/i40e: refactor FDIR engine infrastructure Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 16/19] net/i40e: reimplement FDIR parser Anatoly Burakov
2026-09-08 15:20   ` [PATCH v2 17/19] net/i40e: reimplement tunnel parsers Anatoly Burakov
2026-09-08 15:21   ` [PATCH v2 18/19] net/i40e: reimplement hash parser Anatoly Burakov
2026-09-08 15:21   ` [PATCH v2 19/19] net/i40e: advertise flow keep capability Anatoly Burakov
2026-09-09  9:08   ` [PATCH v2 00/19] Building a better rte_flow parser Burakov, Anatoly
2026-09-16 12:18 ` [PATCH v3 " Anatoly Burakov
2026-09-16 12:18   ` [PATCH v3 01/19] ethdev: add flow graph API Anatoly Burakov
2026-09-17  0:21     ` Stephen Hemminger
2026-10-02 11:00       ` Burakov, Anatoly
2026-09-19 16:09     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 02/19] net/intel/common: add flow engines infrastructure Anatoly Burakov
2026-09-17  0:26     ` Stephen Hemminger
2026-09-18  9:17       ` Burakov, Anatoly
2026-09-19 16:09     ` Medvedkin, Vladimir
2026-10-02 11:29       ` Burakov, Anatoly
2026-10-02 13:13       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 03/19] net/intel/common: add utility functions Anatoly Burakov
2026-09-17  0:30     ` Stephen Hemminger
2026-09-18  9:20       ` Burakov, Anatoly
2026-09-19 16:09     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 04/19] net/ixgbe: add support for common flow parsing Anatoly Burakov
2026-09-19 16:09     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 05/19] net/ixgbe: reimplement ethertype parser Anatoly Burakov
2026-09-19 16:10     ` Medvedkin, Vladimir
2026-10-02 14:45       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 06/19] net/ixgbe: reimplement syn parser Anatoly Burakov
2026-09-19 16:10     ` Medvedkin, Vladimir
2026-10-05  8:35       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 07/19] net/ixgbe: reimplement L2 tunnel parser Anatoly Burakov
2026-09-19 16:10     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 08/19] net/ixgbe: reimplement ntuple parser Anatoly Burakov
2026-09-19 16:11     ` Medvedkin, Vladimir
2026-10-05  9:45       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 09/19] net/ixgbe: reimplement security parser Anatoly Burakov
2026-09-19 16:11     ` Medvedkin, Vladimir
2026-10-05 12:16       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 10/19] net/ixgbe: reimplement FDIR parser Anatoly Burakov
2026-09-19 16:11     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 11/19] net/ixgbe: reimplement hash parser Anatoly Burakov
2026-09-19 16:11     ` Medvedkin, Vladimir
2026-10-05 12:35       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 12/19] net/ixgbe: advertise flow keep capability Anatoly Burakov
2026-09-19 16:11     ` Medvedkin, Vladimir
2026-10-05 12:54       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 13/19] net/i40e: add support for common flow parsing Anatoly Burakov
2026-09-19 16:13     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 14/19] net/i40e: reimplement ethertype parser Anatoly Burakov
2026-09-19 16:13     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 15/19] net/i40e: refactor FDIR engine infrastructure Anatoly Burakov
2026-09-19 16:13     ` Medvedkin, Vladimir
2026-10-05 14:46       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 16/19] net/i40e: reimplement FDIR parser Anatoly Burakov
2026-09-19 16:13     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 17/19] net/i40e: reimplement tunnel parsers Anatoly Burakov
2026-09-19 16:14     ` Medvedkin, Vladimir
2026-10-05 15:45       ` Burakov, Anatoly
2026-09-16 12:18   ` [PATCH v3 18/19] net/i40e: reimplement hash parser Anatoly Burakov
2026-09-19 16:14     ` Medvedkin, Vladimir
2026-09-16 12:18   ` [PATCH v3 19/19] net/i40e: advertise flow keep capability Anatoly Burakov
2026-09-19 16:16     ` Medvedkin, Vladimir
2026-09-17  0:19   ` [PATCH v3 00/19] Building a better rte_flow parser Stephen Hemminger
2026-10-05 16:37 ` [PATCH v4 00/24] " Anatoly Burakov
2026-10-05 16:37   ` [PATCH v4 01/24] ethdev: add default mask query to flow Anatoly Burakov
2026-10-05 16:37   ` [PATCH v4 02/24] ethdev: add flow graph API Anatoly Burakov
2026-10-05 16:37   ` [PATCH v4 03/24] net/intel/common: add flow engines infrastructure Anatoly Burakov
2026-10-05 16:37   ` [PATCH v4 04/24] net/intel/common: add utility functions Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 05/24] net/ixgbe: add support for common flow parsing Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 06/24] net/ixgbe: make ethertype filter table dynamic Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 07/24] net/ixgbe: reimplement ethertype parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 08/24] net/ixgbe: fix syn filter priority Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 09/24] net/ixgbe: reimplement syn parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 10/24] net/ixgbe: reimplement L2 tunnel parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 11/24] net/ixgbe: fix ntuple filter priority Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 12/24] net/ixgbe: reimplement ntuple parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 13/24] net/ixgbe: reimplement security parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 14/24] net/ixgbe: reimplement FDIR parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 15/24] net/ixgbe: don't embed RSS conf in filter structs Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 16/24] net/ixgbe: reimplement hash parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 17/24] net/ixgbe: advertise flow keep capability Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 18/24] net/i40e: add support for common flow parsing Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 19/24] net/i40e: reimplement ethertype parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 20/24] net/i40e: refactor FDIR engine infrastructure Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 21/24] net/i40e: reimplement FDIR parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 22/24] net/i40e: reimplement tunnel parsers Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 23/24] net/i40e: reimplement hash parser Anatoly Burakov
2026-10-05 16:38   ` [PATCH v4 24/24] net/i40e: advertise flow keep capability Anatoly Burakov
2026-10-07 10:49 ` [PATCH v5 00/25] Building a better rte_flow parser Anatoly Burakov
2026-10-07 10:49   ` [PATCH v5 01/25] ethdev: add default mask query to flow Anatoly Burakov
2026-10-07 14:40     ` Thomas Monjalon
2026-10-07 10:49   ` [PATCH v5 02/25] ethdev: add flow graph API Anatoly Burakov
2026-10-07 10:49   ` [PATCH v5 03/25] net/intel/common: add flow engines infrastructure Anatoly Burakov
2026-10-07 10:49   ` [PATCH v5 04/25] net/intel/common: add utility functions Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 05/25] net/ixgbe: add support for common flow parsing Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 06/25] net/ixgbe: make ethertype filter table dynamic Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 07/25] net/ixgbe: reimplement ethertype parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 08/25] net/ixgbe: fix syn filter priority Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 09/25] net/ixgbe: reimplement syn parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 10/25] net/ixgbe: reimplement L2 tunnel parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 11/25] net/ixgbe: fix ntuple filter priority Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 12/25] net/ixgbe: fix protocol-only ntuple parsing Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 13/25] net/ixgbe: reimplement ntuple parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 14/25] net/ixgbe: reimplement security parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 15/25] net/ixgbe: reimplement FDIR parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 16/25] net/ixgbe: don't embed RSS conf in filter structs Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 17/25] net/ixgbe: reimplement hash parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 18/25] net/ixgbe: advertise flow keep capability Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 19/25] net/i40e: add support for common flow parsing Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 20/25] net/i40e: reimplement ethertype parser Anatoly Burakov
2026-10-07 10:50   ` Anatoly Burakov [this message]
2026-10-07 10:50   ` [PATCH v5 22/25] net/i40e: reimplement FDIR parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 23/25] net/i40e: reimplement tunnel parsers Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 24/25] net/i40e: reimplement hash parser Anatoly Burakov
2026-10-07 10:50   ` [PATCH v5 25/25] net/i40e: advertise flow keep capability Anatoly Burakov

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=45c0f47ecca71af5a4825ee67976fc93221a05e5.1791369981.git.anatoly.burakov@intel.com \
    --to=anatoly.burakov@intel.com \
    --cc=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox