[dpdk-dev,v10,06/11] net/failsafe: support flow API

Message ID db76677a92c44a91f68ea3609dcda6f8490b195f.1500130635.git.gaetan.rivet@6wind.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel-compilation success Compilation OK

Commit Message

Gaëtan Rivet July 15, 2017, 5:57 p.m. UTC
  Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/nics/features/failsafe.ini   |   1 +
 drivers/net/failsafe/Makefile           |   1 +
 drivers/net/failsafe/failsafe.c         |   1 +
 drivers/net/failsafe/failsafe_eal.c     |   1 +
 drivers/net/failsafe/failsafe_ether.c   |  70 +++++++++++
 drivers/net/failsafe/failsafe_flow.c    | 215 ++++++++++++++++++++++++++++++++
 drivers/net/failsafe/failsafe_ops.c     |  29 +++++
 drivers/net/failsafe/failsafe_private.h |  18 +++
 8 files changed, 336 insertions(+)
 create mode 100644 drivers/net/failsafe/failsafe_flow.c
  

Comments

Ferruh Yigit July 17, 2017, 4:03 p.m. UTC | #1
On 7/15/2017 6:57 PM, Gaetan Rivet wrote:
> Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
> Acked-by: Olga Shern <olgas@mellanox.com>

<...>

> +
> +	flow = fs_flow_allocate(attr, patterns, actions);
> +	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
> +		flow->flows[i] = rte_flow_create(PORT_ID(sdev),
> +				attr, patterns, actions, error);

Should SUB_ID(sdev) used here? And in related functions.
flow->flows[SUB_ID(sdev)] = ...

> +		if (flow->flows[i] == NULL) {
> +			ERROR("Failed to create flow on sub_device %d",
> +				i);
> +			goto err;
> +		}
<...>
  
Gaëtan Rivet July 17, 2017, 4:19 p.m. UTC | #2
On Mon, Jul 17, 2017 at 05:03:46PM +0100, Ferruh Yigit wrote:
> On 7/15/2017 6:57 PM, Gaetan Rivet wrote:
> > Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
> > Acked-by: Olga Shern <olgas@mellanox.com>
> 
> <...>
> 
> > +
> > +	flow = fs_flow_allocate(attr, patterns, actions);
> > +	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
> > +		flow->flows[i] = rte_flow_create(PORT_ID(sdev),
> > +				attr, patterns, actions, error);
> 
> Should SUB_ID(sdev) used here? And in related functions.
> flow->flows[SUB_ID(sdev)] = ...
> 

The SUB_ID(sdev) is the index in the sub_device array allocated in
private data for the fail-safe. It is also used for the sub_rte_flow in
the rte_flow (which sports an array of rte_flow pointers for each
sub_device).

PORT_ID(sdev) is the index in the rte_eth_devices array, meaning that
the rte_flow_create function is actually called using the public API on
the port.

This allows to trigger the additional operations usually done within the
ether API (storing the configuration in the eth_dev_data structure, some
side management), and following eventual changes automatically from the
fail-safe level.

> > +		if (flow->flows[i] == NULL) {
> > +			ERROR("Failed to create flow on sub_device %d",
> > +				i);
> > +			goto err;
> > +		}
> <...>
>
  
Ferruh Yigit July 17, 2017, 4:34 p.m. UTC | #3
On 7/17/2017 5:19 PM, Gaëtan Rivet wrote:
> On Mon, Jul 17, 2017 at 05:03:46PM +0100, Ferruh Yigit wrote:
>> On 7/15/2017 6:57 PM, Gaetan Rivet wrote:
>>> Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
>>> Acked-by: Olga Shern <olgas@mellanox.com>
>>
>> <...>
>>
>>> +
>>> +	flow = fs_flow_allocate(attr, patterns, actions);
>>> +	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
>>> +		flow->flows[i] = rte_flow_create(PORT_ID(sdev),
>>> +				attr, patterns, actions, error);
>>
>> Should SUB_ID(sdev) used here? And in related functions.
>> flow->flows[SUB_ID(sdev)] = ...
>>
> 
> The SUB_ID(sdev) is the index in the sub_device array allocated in
> private data for the fail-safe. It is also used for the sub_rte_flow in
> the rte_flow (which sports an array of rte_flow pointers for each
> sub_device).

I got this part, let me ask another way, is it always guarantied, even
after some hotplug, "i" in FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)
will be same with SUB_ID(sdev) ?

> 
> PORT_ID(sdev) is the index in the rte_eth_devices array, meaning that
> the rte_flow_create function is actually called using the public API on
> the port.
> 
> This allows to trigger the additional operations usually done within the
> ether API (storing the configuration in the eth_dev_data structure, some
> side management), and following eventual changes automatically from the
> fail-safe level.
> 
>>> +		if (flow->flows[i] == NULL) {
>>> +			ERROR("Failed to create flow on sub_device %d",
>>> +				i);
>>> +			goto err;
>>> +		}
>> <...>
>>
>
  
Gaëtan Rivet July 17, 2017, 5:15 p.m. UTC | #4
On Mon, Jul 17, 2017 at 05:34:53PM +0100, Ferruh Yigit wrote:
> On 7/17/2017 5:19 PM, Gaëtan Rivet wrote:
> > On Mon, Jul 17, 2017 at 05:03:46PM +0100, Ferruh Yigit wrote:
> >> On 7/15/2017 6:57 PM, Gaetan Rivet wrote:
> >>> Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
> >>> Acked-by: Olga Shern <olgas@mellanox.com>
> >>
> >> <...>
> >>
> >>> +
> >>> +	flow = fs_flow_allocate(attr, patterns, actions);
> >>> +	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
> >>> +		flow->flows[i] = rte_flow_create(PORT_ID(sdev),
> >>> +				attr, patterns, actions, error);
> >>
> >> Should SUB_ID(sdev) used here? And in related functions.
> >> flow->flows[SUB_ID(sdev)] = ...
> >>
> > 
> > The SUB_ID(sdev) is the index in the sub_device array allocated in
> > private data for the fail-safe. It is also used for the sub_rte_flow in
> > the rte_flow (which sports an array of rte_flow pointers for each
> > sub_device).
> 
> I got this part, let me ask another way, is it always guarantied, even
> after some hotplug, "i" in FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE)
> will be same with SUB_ID(sdev) ?
> 

Ah, yes. Sub_device slots are static, allocated once at launch and
reserved for only one device corresponding to its declaration.

Thus, the PORT_ID() can change, but the SUB_ID() is always the same.

> > 
> > PORT_ID(sdev) is the index in the rte_eth_devices array, meaning that
> > the rte_flow_create function is actually called using the public API on
> > the port.
> > 
> > This allows to trigger the additional operations usually done within the
> > ether API (storing the configuration in the eth_dev_data structure, some
> > side management), and following eventual changes automatically from the
> > fail-safe level.
> > 
> >>> +		if (flow->flows[i] == NULL) {
> >>> +			ERROR("Failed to create flow on sub_device %d",
> >>> +				i);
> >>> +			goto err;
> >>> +		}
> >> <...>
> >>
> > 
>
  

Patch

diff --git a/doc/guides/nics/features/failsafe.ini b/doc/guides/nics/features/failsafe.ini
index 3c52823..9167b59 100644
--- a/doc/guides/nics/features/failsafe.ini
+++ b/doc/guides/nics/features/failsafe.ini
@@ -13,6 +13,7 @@  Allmulticast mode    = Y
 Unicast MAC filter   = Y
 Multicast MAC filter = Y
 VLAN filter          = Y
+Flow API             = Y
 Packet type parsing  = Y
 Basic stats          = Y
 Stats per queue      = Y
diff --git a/drivers/net/failsafe/Makefile b/drivers/net/failsafe/Makefile
index f963ffb..dc07264 100644
--- a/drivers/net/failsafe/Makefile
+++ b/drivers/net/failsafe/Makefile
@@ -45,6 +45,7 @@  SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_eal.c
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_rxtx.c
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_ether.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_FAILSAFE) += failsafe_flow.c
 
 # No exported include files
 
diff --git a/drivers/net/failsafe/failsafe.c b/drivers/net/failsafe/failsafe.c
index 73be3d7..5d35079 100644
--- a/drivers/net/failsafe/failsafe.c
+++ b/drivers/net/failsafe/failsafe.c
@@ -178,6 +178,7 @@  fs_eth_dev_create(struct rte_vdev_device *vdev)
 	dev->data->mac_addrs = &PRIV(dev)->mac_addrs[0];
 	dev->data->dev_link = eth_link;
 	PRIV(dev)->nb_mac_addr = 1;
+	TAILQ_INIT(&PRIV(dev)->flow_list);
 	dev->rx_pkt_burst = (eth_rx_burst_t)&failsafe_rx_burst;
 	dev->tx_pkt_burst = (eth_tx_burst_t)&failsafe_tx_burst;
 	if (params == NULL) {
diff --git a/drivers/net/failsafe/failsafe_eal.c b/drivers/net/failsafe/failsafe_eal.c
index 1bab3fc..31257b0 100644
--- a/drivers/net/failsafe/failsafe_eal.c
+++ b/drivers/net/failsafe/failsafe_eal.c
@@ -62,6 +62,7 @@  fs_bus_init(struct rte_eth_dev *dev)
 			ERROR("sub_device %d init went wrong", i);
 			return -ENODEV;
 		}
+		SUB_ID(sdev) = i;
 		sdev->dev = ETH(sdev)->device;
 		ETH(sdev)->state = RTE_ETH_DEV_DEFERRED;
 		sdev->state = DEV_PROBED;
diff --git a/drivers/net/failsafe/failsafe_ether.c b/drivers/net/failsafe/failsafe_ether.c
index 2a1535e..2958207 100644
--- a/drivers/net/failsafe/failsafe_ether.c
+++ b/drivers/net/failsafe/failsafe_ether.c
@@ -33,8 +33,46 @@ 
 
 #include <unistd.h>
 
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+
 #include "failsafe_private.h"
 
+/** Print a message out of a flow error. */
+static int
+fs_flow_complain(struct rte_flow_error *error)
+{
+	static const char *const errstrlist[] = {
+		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
+		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
+		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
+		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
+		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
+		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
+		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
+		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
+		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
+	};
+	const char *errstr;
+	char buf[32];
+	int err = rte_errno;
+
+	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
+			!errstrlist[error->type])
+		errstr = "unknown type";
+	else
+		errstr = errstrlist[error->type];
+	ERROR("Caught error type %d (%s): %s%s\n",
+		error->type, errstr,
+		error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
+				error->cause), buf) : "",
+		error->message ? error->message : "(no stated reason)");
+	return -err;
+}
+
 static int
 fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
 		struct sub_device *sdev)
@@ -42,6 +80,8 @@  fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
 	struct rte_eth_dev *edev;
 	struct rte_vlan_filter_conf *vfc1;
 	struct rte_vlan_filter_conf *vfc2;
+	struct rte_flow *flow;
+	struct rte_flow_error ferror;
 	uint32_t i;
 	int ret;
 
@@ -177,6 +217,36 @@  fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
 	} else {
 		DEBUG("VLAN filter already set");
 	}
+	/* rte_flow */
+	if (TAILQ_EMPTY(&PRIV(dev)->flow_list)) {
+		DEBUG("rte_flow already set");
+	} else {
+		DEBUG("Resetting rte_flow configuration");
+		ret = rte_flow_flush(PORT_ID(sdev), &ferror);
+		if (ret) {
+			fs_flow_complain(&ferror);
+			return ret;
+		}
+		i = 0;
+		rte_errno = 0;
+		DEBUG("Configuring rte_flow");
+		TAILQ_FOREACH(flow, &PRIV(dev)->flow_list, next) {
+			DEBUG("Creating flow #%" PRIu32, i++);
+			flow->flows[SUB_ID(sdev)] =
+				rte_flow_create(PORT_ID(sdev),
+						&flow->fd->attr,
+						flow->fd->items,
+						flow->fd->actions,
+						&ferror);
+			ret = rte_errno;
+			if (ret)
+				break;
+		}
+		if (ret) {
+			fs_flow_complain(&ferror);
+			return ret;
+		}
+	}
 	return 0;
 }
 
diff --git a/drivers/net/failsafe/failsafe_flow.c b/drivers/net/failsafe/failsafe_flow.c
new file mode 100644
index 0000000..8547e51
--- /dev/null
+++ b/drivers/net/failsafe/failsafe_flow.c
@@ -0,0 +1,215 @@ 
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2017 6WIND S.A.
+ *   Copyright 2017 Mellanox.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of 6WIND S.A. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+
+#include <rte_malloc.h>
+#include <rte_tailq.h>
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+
+#include "failsafe_private.h"
+
+static struct rte_flow *
+fs_flow_allocate(const struct rte_flow_attr *attr,
+		 const struct rte_flow_item *items,
+		 const struct rte_flow_action *actions)
+{
+	struct rte_flow *flow;
+	size_t fdsz;
+
+	fdsz = rte_flow_copy(NULL, 0, attr, items, actions);
+	flow = rte_zmalloc(NULL,
+			   sizeof(struct rte_flow) + fdsz,
+			   RTE_CACHE_LINE_SIZE);
+	if (flow == NULL) {
+		ERROR("Could not allocate new flow");
+		return NULL;
+	}
+	flow->fd = (void *)((uintptr_t)flow + sizeof(*flow));
+	if (rte_flow_copy(flow->fd, fdsz, attr, items, actions) != fdsz) {
+		ERROR("Failed to copy flow description");
+		rte_free(flow);
+		return NULL;
+	}
+	return flow;
+}
+
+static void
+fs_flow_release(struct rte_flow **flow)
+{
+	rte_free(*flow);
+	*flow = NULL;
+}
+
+static int
+fs_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 sub_device *sdev;
+	uint8_t i;
+	int ret;
+
+	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
+		DEBUG("Calling rte_flow_validate on sub_device %d", i);
+		ret = rte_flow_validate(PORT_ID(sdev),
+				attr, patterns, actions, error);
+		if (ret) {
+			ERROR("Operation rte_flow_validate failed for sub_device %d"
+			      " with error %d", i, ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static struct rte_flow *
+fs_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 sub_device *sdev;
+	struct rte_flow *flow;
+	uint8_t i;
+
+	flow = fs_flow_allocate(attr, patterns, actions);
+	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
+		flow->flows[i] = rte_flow_create(PORT_ID(sdev),
+				attr, patterns, actions, error);
+		if (flow->flows[i] == NULL) {
+			ERROR("Failed to create flow on sub_device %d",
+				i);
+			goto err;
+		}
+	}
+	TAILQ_INSERT_TAIL(&PRIV(dev)->flow_list, flow, next);
+	return flow;
+err:
+	FOREACH_SUBDEV(sdev, i, dev) {
+		if (flow->flows[i] != NULL)
+			rte_flow_destroy(PORT_ID(sdev),
+				flow->flows[i], error);
+	}
+	fs_flow_release(&flow);
+	return NULL;
+}
+
+static int
+fs_flow_destroy(struct rte_eth_dev *dev,
+		struct rte_flow *flow,
+		struct rte_flow_error *error)
+{
+	struct sub_device *sdev;
+	uint8_t i;
+	int ret;
+
+	if (flow == NULL) {
+		ERROR("Invalid flow");
+		return -EINVAL;
+	}
+	ret = 0;
+	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
+		int local_ret;
+
+		if (flow->flows[i] == NULL)
+			continue;
+		local_ret = rte_flow_destroy(PORT_ID(sdev),
+				flow->flows[i], error);
+		if (local_ret) {
+			ERROR("Failed to destroy flow on sub_device %d: %d",
+					i, local_ret);
+			if (ret == 0)
+				ret = local_ret;
+		}
+	}
+	TAILQ_REMOVE(&PRIV(dev)->flow_list, flow, next);
+	fs_flow_release(&flow);
+	return ret;
+}
+
+static int
+fs_flow_flush(struct rte_eth_dev *dev,
+	      struct rte_flow_error *error)
+{
+	struct sub_device *sdev;
+	struct rte_flow *flow;
+	void *tmp;
+	uint8_t i;
+	int ret;
+
+	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
+		DEBUG("Calling rte_flow_flush on sub_device %d", i);
+		ret = rte_flow_flush(PORT_ID(sdev), error);
+		if (ret) {
+			ERROR("Operation rte_flow_flush failed for sub_device %d"
+			      " with error %d", i, ret);
+			return ret;
+		}
+	}
+	TAILQ_FOREACH_SAFE(flow, &PRIV(dev)->flow_list, next, tmp) {
+		TAILQ_REMOVE(&PRIV(dev)->flow_list, flow, next);
+		fs_flow_release(&flow);
+	}
+	return 0;
+}
+
+static int
+fs_flow_query(struct rte_eth_dev *dev,
+	      struct rte_flow *flow,
+	      enum rte_flow_action_type type,
+	      void *arg,
+	      struct rte_flow_error *error)
+{
+	struct sub_device *sdev;
+
+	sdev = TX_SUBDEV(dev);
+	if (sdev != NULL) {
+		return rte_flow_query(PORT_ID(sdev),
+				flow->flows[SUB_ID(sdev)], type, arg, error);
+	}
+	WARN("No active sub_device to query about its flow");
+	return -1;
+}
+
+const struct rte_flow_ops fs_flow_ops = {
+	.validate = fs_flow_validate,
+	.create = fs_flow_create,
+	.destroy = fs_flow_destroy,
+	.flush = fs_flow_flush,
+	.query = fs_flow_query,
+};
diff --git a/drivers/net/failsafe/failsafe_ops.c b/drivers/net/failsafe/failsafe_ops.c
index 3112bc8..0c8aa35 100644
--- a/drivers/net/failsafe/failsafe_ops.c
+++ b/drivers/net/failsafe/failsafe_ops.c
@@ -36,6 +36,7 @@ 
 #include <rte_debug.h>
 #include <rte_ethdev.h>
 #include <rte_malloc.h>
+#include <rte_flow.h>
 
 #include "failsafe_private.h"
 
@@ -629,6 +630,33 @@  fs_mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr)
 		rte_eth_dev_default_mac_addr_set(PORT_ID(sdev), mac_addr);
 }
 
+static int
+fs_filter_ctrl(struct rte_eth_dev *dev,
+		enum rte_filter_type type,
+		enum rte_filter_op op,
+		void *arg)
+{
+	struct sub_device *sdev;
+	uint8_t i;
+	int ret;
+
+	if (type == RTE_ETH_FILTER_GENERIC &&
+	    op == RTE_ETH_FILTER_GET) {
+		*(const void **)arg = &fs_flow_ops;
+		return 0;
+	}
+	FOREACH_SUBDEV_ST(sdev, i, dev, DEV_ACTIVE) {
+		DEBUG("Calling rte_eth_dev_filter_ctrl on sub_device %d", i);
+		ret = rte_eth_dev_filter_ctrl(PORT_ID(sdev), type, op, arg);
+		if (ret) {
+			ERROR("Operation rte_eth_dev_filter_ctrl failed for sub_device %d"
+			      " with error %d", i, ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
 const struct eth_dev_ops failsafe_ops = {
 	.dev_configure = fs_dev_configure,
 	.dev_start = fs_dev_start,
@@ -656,4 +684,5 @@  const struct eth_dev_ops failsafe_ops = {
 	.mac_addr_remove = fs_mac_addr_remove,
 	.mac_addr_add = fs_mac_addr_add,
 	.mac_addr_set = fs_mac_addr_set,
+	.filter_ctrl = fs_filter_ctrl,
 };
diff --git a/drivers/net/failsafe/failsafe_private.h b/drivers/net/failsafe/failsafe_private.h
index 8b99d61..af1e380 100644
--- a/drivers/net/failsafe/failsafe_private.h
+++ b/drivers/net/failsafe/failsafe_private.h
@@ -34,6 +34,8 @@ 
 #ifndef _RTE_ETH_FAILSAFE_PRIVATE_H_
 #define _RTE_ETH_FAILSAFE_PRIVATE_H_
 
+#include <sys/queue.h>
+
 #include <rte_dev.h>
 #include <rte_ethdev.h>
 #include <rte_devargs.h>
@@ -72,6 +74,14 @@  struct txq {
 	struct rte_eth_txq_info info;
 };
 
+struct rte_flow {
+	TAILQ_ENTRY(rte_flow) next;
+	/* sub_flows */
+	struct rte_flow *flows[FAILSAFE_MAX_ETHPORTS];
+	/* flow description for synchronization */
+	struct rte_flow_desc *fd;
+};
+
 enum dev_state {
 	DEV_UNDEFINED = 0,
 	DEV_PARSED,
@@ -86,6 +96,7 @@  struct sub_device {
 	struct rte_bus *bus;
 	struct rte_device *dev;
 	struct rte_eth_dev *edev;
+	uint8_t sid;
 	/* Device state machine */
 	enum dev_state state;
 	/* Some device are defined as a command line */
@@ -104,6 +115,8 @@  struct fs_priv {
 	uint8_t subs_tail; /* first invalid */
 	uint8_t subs_tx; /* current emitting device */
 	uint8_t current_probed;
+	/* flow mapping */
+	TAILQ_HEAD(sub_flows, rte_flow) flow_list;
 	/* current number of mac_addr slots allocated. */
 	uint32_t nb_mac_addr;
 	struct ether_addr mac_addrs[FAILSAFE_MAX_ETHADDR];
@@ -153,6 +166,7 @@  int failsafe_eth_dev_state_sync(struct rte_eth_dev *dev);
 
 extern const char pmd_failsafe_driver_name[];
 extern const struct eth_dev_ops failsafe_ops;
+extern const struct rte_flow_ops fs_flow_ops;
 extern uint64_t hotplug_poll;
 extern int mac_from_arg;
 
@@ -170,6 +184,10 @@  extern int mac_from_arg;
 #define PORT_ID(sdev) \
 	(ETH(sdev)->data->port_id)
 
+/* sdev: (struct sub_device *) */
+#define SUB_ID(sdev) \
+	((sdev)->sid)
+
 /**
  * Stateful iterator construct over fail-safe sub-devices:
  * s:     (struct sub_device *), iterator