DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] nfp: add support for rx interrupts
From: Alejandro Lucero @ 2016-12-19 18:05 UTC (permalink / raw)
  To: dev

This is supported with UIO and VFIO modules. With UIO, LSC interrupt
is disabled.

Signed-off-by: Alejandro Lucero <alejandro.lucero@netronome.com>
---
 doc/guides/nics/features/nfp.ini |   1 +
 drivers/net/nfp/nfp_net.c        | 112 +++++++++++++++++++++++++++++++++++++--
 drivers/net/nfp/nfp_net_ctrl.h   |   1 +
 3 files changed, 110 insertions(+), 4 deletions(-)

diff --git a/doc/guides/nics/features/nfp.ini b/doc/guides/nics/features/nfp.ini
index 476ed31..7ac0d34 100644
--- a/doc/guides/nics/features/nfp.ini
+++ b/doc/guides/nics/features/nfp.ini
@@ -6,6 +6,7 @@
 [Features]
 Link status          = Y
 Link status event    = Y
+Rx interrupt         = Y
 Queue start/stop     = Y
 MTU update           = Y
 Jumbo frame          = Y
diff --git a/drivers/net/nfp/nfp_net.c b/drivers/net/nfp/nfp_net.c
index 0e6bf4c..ace9583 100644
--- a/drivers/net/nfp/nfp_net.c
+++ b/drivers/net/nfp/nfp_net.c
@@ -626,10 +626,51 @@ static void nfp_net_read_mac(struct nfp_net_hw *hw)
 }
 
 static int
+nfp_configure_rx_interrupt(struct rte_eth_dev *dev,
+			   struct rte_intr_handle *intr_handle)
+{
+	struct nfp_net_hw *hw;
+	int i;
+
+	if (!intr_handle->intr_vec) {
+		intr_handle->intr_vec =
+			rte_zmalloc("intr_vec",
+				    dev->data->nb_rx_queues * sizeof(int), 0);
+		if (!intr_handle->intr_vec) {
+			PMD_INIT_LOG(ERR, "Failed to allocate %d rx_queues"
+				     " intr_vec\n", dev->data->nb_rx_queues);
+			return -ENOMEM;
+		}
+	}
+
+	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	if (intr_handle->type == RTE_INTR_HANDLE_UIO) {
+		PMD_INIT_LOG(INFO, "VF: enabling RX interrupt with UIO\n");
+		/* UIO just supports one queue and no LSC*/
+		nn_cfg_writeb(hw, NFP_NET_CFG_RXR_VEC(0), 0);
+	} else {
+		PMD_INIT_LOG(INFO, "VF: enabling RX interrupt with VFIO\n");
+		for (i = 0; i < dev->data->nb_rx_queues; i++)
+			/*
+			 * The first msix vector is reserved for non
+			 * efd interrupts
+			*/
+			nn_cfg_writeb(hw, NFP_NET_CFG_RXR_VEC(i), i + 1);
+	}
+
+	/* Avoiding TX interrupts */
+	hw->ctrl |= NFP_NET_CFG_CTRL_MSIX_TX_OFF;
+	return 0;
+}
+
+static int
 nfp_net_start(struct rte_eth_dev *dev)
 {
+	struct rte_intr_handle *intr_handle = &dev->pci_dev->intr_handle;
 	uint32_t new_ctrl, update = 0;
 	struct nfp_net_hw *hw;
+	uint32_t intr_vector;
 	int ret;
 
 	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
@@ -645,10 +686,39 @@ static void nfp_net_read_mac(struct nfp_net_hw *hw)
 	/* Enabling the required queues in the device */
 	nfp_net_enable_queues(dev);
 
+	/* check and configure queue intr-vector mapping */
+	if (dev->data->dev_conf.intr_conf.rxq != 0) {
+		if (intr_handle->type == RTE_INTR_HANDLE_UIO) {
+			/*
+			 * Better not to share LSC with RX interrupts.
+			 * Unregistering LSC interrupt handler
+			 */
+			rte_intr_callback_unregister(&dev->pci_dev->intr_handle,
+				nfp_net_dev_interrupt_handler, (void *)dev);
+
+			if (dev->data->nb_rx_queues > 1) {
+				PMD_INIT_LOG(ERR, "PMD rx interrupt only "
+					     "supports 1 queue with UIO");
+				return -EIO;
+			}
+		}
+		intr_vector = dev->data->nb_rx_queues;
+		if (rte_intr_efd_enable(intr_handle, intr_vector))
+			return -1;
+	}
+
+	nfp_configure_rx_interrupt(dev, intr_handle);
+
+	rte_intr_enable(intr_handle);
+
 	/* Enable device */
-	new_ctrl = hw->ctrl | NFP_NET_CFG_CTRL_ENABLE | NFP_NET_CFG_UPDATE_MSIX;
+	new_ctrl = hw->ctrl | NFP_NET_CFG_CTRL_ENABLE;
 	update = NFP_NET_CFG_UPDATE_GEN | NFP_NET_CFG_UPDATE_RING;
 
+	/* Just configuring queues interrupts when necessary */
+	if (rte_intr_dp_is_en(intr_handle))
+		update |= NFP_NET_CFG_UPDATE_MSIX;
+
 	if (hw->cap & NFP_NET_CFG_CTRL_RINGCFG)
 		new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG;
 
@@ -1139,6 +1209,41 @@ static void nfp_net_read_mac(struct nfp_net_hw *hw)
 	return count;
 }
 
+static int
+nfp_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+	struct nfp_net_hw *hw;
+	int base = 0;
+
+	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	if (dev->pci_dev->intr_handle.type != RTE_INTR_HANDLE_UIO)
+		base = 1;
+
+	/* Make sure all updates are written before un-masking */
+	rte_wmb();
+	nn_cfg_writeb(hw, NFP_NET_CFG_ICR(base + queue_id),
+		      NFP_NET_CFG_ICR_UNMASKED);
+	return 0;
+}
+
+static int
+nfp_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+	struct nfp_net_hw *hw;
+	int base = 0;
+
+	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	if (dev->pci_dev->intr_handle.type != RTE_INTR_HANDLE_UIO)
+		base = 1;
+
+	/* Make sure all updates are written before un-masking */
+	rte_wmb();
+	nn_cfg_writeb(hw, NFP_NET_CFG_ICR(base + queue_id), 0x1);
+	return 0;
+}
+
 static void
 nfp_net_dev_link_status_print(struct rte_eth_dev *dev)
 {
@@ -2296,6 +2401,8 @@ uint32_t nfp_net_txq_full(struct nfp_net_txq *txq)
 	.rx_queue_count		= nfp_net_rx_queue_count,
 	.tx_queue_setup		= nfp_net_tx_queue_setup,
 	.tx_queue_release	= nfp_net_tx_queue_release,
+	.rx_queue_intr_enable   = nfp_rx_queue_intr_enable,
+	.rx_queue_intr_disable  = nfp_rx_queue_intr_disable,
 };
 
 static int
@@ -2431,9 +2538,6 @@ uint32_t nfp_net_txq_full(struct nfp_net_txq *txq)
 				   nfp_net_dev_interrupt_handler,
 				   (void *)eth_dev);
 
-	/* enable uio intr after callback register */
-	rte_intr_enable(&pci_dev->intr_handle);
-
 	/* Telling the firmware about the LSC interrupt entry */
 	nn_cfg_writeb(hw, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX);
 
diff --git a/drivers/net/nfp/nfp_net_ctrl.h b/drivers/net/nfp/nfp_net_ctrl.h
index 426402b..2c50043 100644
--- a/drivers/net/nfp/nfp_net_ctrl.h
+++ b/drivers/net/nfp/nfp_net_ctrl.h
@@ -112,6 +112,7 @@
 #define   NFP_NET_CFG_CTRL_L2SWITCH_LOCAL (0x1 << 23) /* Switch to local */
 #define   NFP_NET_CFG_CTRL_VXLAN          (0x1 << 24) /* Enable VXLAN */
 #define   NFP_NET_CFG_CTRL_NVGRE          (0x1 << 25) /* Enable NVGRE */
+#define   NFP_NET_CFG_CTRL_MSIX_TX_OFF    (0x1 << 26) /* Disable MSIX for TX */
 #define NFP_NET_CFG_UPDATE              0x0004
 #define   NFP_NET_CFG_UPDATE_GEN          (0x1 <<  0) /* General update */
 #define   NFP_NET_CFG_UPDATE_RING         (0x1 <<  1) /* Ring config change */
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH] nfp: extend speed capabilities advertised
From: Alejandro Lucero @ 2016-12-19 18:00 UTC (permalink / raw)
  To: Ferruh Yigit; +Cc: dev
In-Reply-To: <CAD+H990MEixLR3cv6kv4wa8gsCO38D+2_FBAig4-N5UBVmWr1A@mail.gmail.com>

I forgot one thing: to update the features file with this new one.

I will wait for your feedback regarding the discussed problem for sending
another version.

Thanks

On Mon, Dec 19, 2016 at 5:59 PM, Alejandro Lucero <
alejandro.lucero@netronome.com> wrote:

>
>
> On Mon, Dec 19, 2016 at 4:43 PM, Ferruh Yigit <ferruh.yigit@intel.com>
> wrote:
>
>> On 12/19/2016 4:18 PM, Alejandro Lucero wrote:
>> > On Mon, Dec 19, 2016 at 3:05 PM, Ferruh Yigit <ferruh.yigit@intel.com>
>> > wrote:
>> >
>> >> On 12/19/2016 3:02 PM, Alejandro Lucero wrote:
>> >>>
>> >>>
>> >>> On Mon, Dec 19, 2016 at 2:36 PM, Ferruh Yigit <ferruh.yigit@intel.com
>> >>> <mailto:ferruh.yigit@intel.com>> wrote:
>> >>>
>> >>>     Hi Alejandro,
>> >>>
>> >>>
>> >>> Hi,
>> >>>
>> >>>
>> >>>     On 12/19/2016 12:05 PM, Alejandro Lucero wrote:
>> >>>     > NFP supports more speeds than just 40 and 100GB, which were
>> >>>     > what was advertised before.
>> >>>     >
>> >>>     > Signed-off-by: Alejandro Lucero <alejandro.lucero@netronome.com
>> >> <mailto:alejandro.lucero@netronome.com>>
>> >>>     > ---
>> >>>     >  drivers/net/nfp/nfp_net.c | 4 +++-
>> >>>     >  1 file changed, 3 insertions(+), 1 deletion(-)
>> >>>     >
>> >>>     > diff --git a/drivers/net/nfp/nfp_net.c
>> b/drivers/net/nfp/nfp_net.c
>> >>>     > index 27afbfd..77015c4 100644
>> >>>     > --- a/drivers/net/nfp/nfp_net.c
>> >>>     > +++ b/drivers/net/nfp/nfp_net.c
>> >>>     > @@ -1077,7 +1077,9 @@ static void nfp_net_read_mac(struct
>> >> nfp_net_hw *hw)
>> >>>     >       dev_info->reta_size = NFP_NET_CFG_RSS_ITBL_SZ;
>> >>>     >       dev_info->hash_key_size = NFP_NET_CFG_RSS_KEY_SZ;
>> >>>     >
>> >>>     > -     dev_info->speed_capa = ETH_LINK_SPEED_40G |
>> >> ETH_LINK_SPEED_100G;
>> >>>     > +     dev_info->speed_capa = ETH_SPEED_NUM_1G |
>> ETH_LINK_SPEED_10G
>> >> |
>> >>>     > +                            ETH_SPEED_NUM_25G |
>> ETH_SPEED_NUM_40G
>> >> |
>> >>>     > +                            ETH_SPEED_NUM_50G |
>> >> ETH_LINK_SPEED_100G;
>> >>>
>> >>>     Does all devices driver by this driver supports all these speeds?
>> >>>
>> >>>     I am aware of at least one exception to this, from previous patch
>> >> [1],
>> >>>     should we take that into account?
>> >>>
>> >>>
>> >>> So we have different NFP devices and different firmwares.
>> >>> NFP by design support all those speeds, but the PMD relies on the
>> >>> firmware for being able to know which is the current configured speed
>> >>> after link negotiation. PMD development was done with a specific
>> >>> firmware, and I was told to just report such speed by default. Last
>> >>> firmware versions give that speed info, but old firmware versions do
>> not.
>> >>>
>> >>> So, all devices support such a speed range, indeed PMD works with any
>> of
>> >>> them, but reported speed is always 40G with old firmware. This is a
>> >>> firmware limitation but we have to support old and new firmware.
>> >>
>> >> But this information to the application will be wrong for some (old)
>> FW.
>> >> What do you think checking the FW version here and report capability
>> >> based on what FW supports?
>> >>
>> >>
>> > The driver advertises the right speed range supported. The problem is
>> with
>> > the report about the current link speed configured.
>> > Maybe, is the right thing to do here to not report the current link
>> speed
>> > because the driver really does not know about it?
>>
>> Sorry, confused. Is it like following:
>>
>> "
>> For new FW, there is no problem, it supports the range between 1G - 50G,
>> and reports correct current speed.
>>
>> With old FW, device still can be set to speed range between 1G - 50G,
>> but it doesn't report current speed correct, and DPDK driver reports
>> back to application as device current speed is 40G, without really
>> knowing the current speed.
>> "
>>
>>
> Yes, that is. Should then I do anything else or the patch is right for you
> now?
>
>
>> Thanks,
>> ferruh
>>
>> >
>> > If you agree with this, I'm afraid the just accepted patch about the
>> link
>> > report needs to be modified.
>> >
>> >
>> >
>> >>>
>> >>>
>> >>>
>> >>>     Also other than that exception, can you please confirm all other
>> >> devices
>> >>>     support all above speeds?
>> >>>
>> >>>     [1]
>> >>>     +       if ((NFD_CFG_MAJOR_VERSION_of(hw->ver) < 4) ||
>> >>>     +           ((NFD_CFG_MINOR_VERSION_of(hw->ver) == 4) &&
>> >>>     +           (NFD_CFG_MINOR_VERSION_of(hw->ver) == 0)))
>> >>>     +               link.link_speed = ETH_SPEED_NUM_40G;
>> >>>
>> >>>
>> >>>     >  }
>> >>>     >
>> >>>     >  static const uint32_t *
>> >>>     >
>> >>>
>> >>>
>> >>
>> >>
>>
>>
>

^ permalink raw reply

* Re: [PATCH] nfp: extend speed capabilities advertised
From: Alejandro Lucero @ 2016-12-19 17:59 UTC (permalink / raw)
  To: Ferruh Yigit; +Cc: dev
In-Reply-To: <a26f9190-39ee-7f06-be07-80016f60242e@intel.com>

On Mon, Dec 19, 2016 at 4:43 PM, Ferruh Yigit <ferruh.yigit@intel.com>
wrote:

> On 12/19/2016 4:18 PM, Alejandro Lucero wrote:
> > On Mon, Dec 19, 2016 at 3:05 PM, Ferruh Yigit <ferruh.yigit@intel.com>
> > wrote:
> >
> >> On 12/19/2016 3:02 PM, Alejandro Lucero wrote:
> >>>
> >>>
> >>> On Mon, Dec 19, 2016 at 2:36 PM, Ferruh Yigit <ferruh.yigit@intel.com
> >>> <mailto:ferruh.yigit@intel.com>> wrote:
> >>>
> >>>     Hi Alejandro,
> >>>
> >>>
> >>> Hi,
> >>>
> >>>
> >>>     On 12/19/2016 12:05 PM, Alejandro Lucero wrote:
> >>>     > NFP supports more speeds than just 40 and 100GB, which were
> >>>     > what was advertised before.
> >>>     >
> >>>     > Signed-off-by: Alejandro Lucero <alejandro.lucero@netronome.com
> >> <mailto:alejandro.lucero@netronome.com>>
> >>>     > ---
> >>>     >  drivers/net/nfp/nfp_net.c | 4 +++-
> >>>     >  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>     >
> >>>     > diff --git a/drivers/net/nfp/nfp_net.c
> b/drivers/net/nfp/nfp_net.c
> >>>     > index 27afbfd..77015c4 100644
> >>>     > --- a/drivers/net/nfp/nfp_net.c
> >>>     > +++ b/drivers/net/nfp/nfp_net.c
> >>>     > @@ -1077,7 +1077,9 @@ static void nfp_net_read_mac(struct
> >> nfp_net_hw *hw)
> >>>     >       dev_info->reta_size = NFP_NET_CFG_RSS_ITBL_SZ;
> >>>     >       dev_info->hash_key_size = NFP_NET_CFG_RSS_KEY_SZ;
> >>>     >
> >>>     > -     dev_info->speed_capa = ETH_LINK_SPEED_40G |
> >> ETH_LINK_SPEED_100G;
> >>>     > +     dev_info->speed_capa = ETH_SPEED_NUM_1G |
> ETH_LINK_SPEED_10G
> >> |
> >>>     > +                            ETH_SPEED_NUM_25G |
> ETH_SPEED_NUM_40G
> >> |
> >>>     > +                            ETH_SPEED_NUM_50G |
> >> ETH_LINK_SPEED_100G;
> >>>
> >>>     Does all devices driver by this driver supports all these speeds?
> >>>
> >>>     I am aware of at least one exception to this, from previous patch
> >> [1],
> >>>     should we take that into account?
> >>>
> >>>
> >>> So we have different NFP devices and different firmwares.
> >>> NFP by design support all those speeds, but the PMD relies on the
> >>> firmware for being able to know which is the current configured speed
> >>> after link negotiation. PMD development was done with a specific
> >>> firmware, and I was told to just report such speed by default. Last
> >>> firmware versions give that speed info, but old firmware versions do
> not.
> >>>
> >>> So, all devices support such a speed range, indeed PMD works with any
> of
> >>> them, but reported speed is always 40G with old firmware. This is a
> >>> firmware limitation but we have to support old and new firmware.
> >>
> >> But this information to the application will be wrong for some (old) FW.
> >> What do you think checking the FW version here and report capability
> >> based on what FW supports?
> >>
> >>
> > The driver advertises the right speed range supported. The problem is
> with
> > the report about the current link speed configured.
> > Maybe, is the right thing to do here to not report the current link speed
> > because the driver really does not know about it?
>
> Sorry, confused. Is it like following:
>
> "
> For new FW, there is no problem, it supports the range between 1G - 50G,
> and reports correct current speed.
>
> With old FW, device still can be set to speed range between 1G - 50G,
> but it doesn't report current speed correct, and DPDK driver reports
> back to application as device current speed is 40G, without really
> knowing the current speed.
> "
>
>
Yes, that is. Should then I do anything else or the patch is right for you
now?


> Thanks,
> ferruh
>
> >
> > If you agree with this, I'm afraid the just accepted patch about the link
> > report needs to be modified.
> >
> >
> >
> >>>
> >>>
> >>>
> >>>     Also other than that exception, can you please confirm all other
> >> devices
> >>>     support all above speeds?
> >>>
> >>>     [1]
> >>>     +       if ((NFD_CFG_MAJOR_VERSION_of(hw->ver) < 4) ||
> >>>     +           ((NFD_CFG_MINOR_VERSION_of(hw->ver) == 4) &&
> >>>     +           (NFD_CFG_MINOR_VERSION_of(hw->ver) == 0)))
> >>>     +               link.link_speed = ETH_SPEED_NUM_40G;
> >>>
> >>>
> >>>     >  }
> >>>     >
> >>>     >  static const uint32_t *
> >>>     >
> >>>
> >>>
> >>
> >>
>
>

^ permalink raw reply

* Re: [PATCH] nfp: extend speed capabilities advertised
From: Alejandro Lucero @ 2016-12-19 17:58 UTC (permalink / raw)
  To: Marc; +Cc: Ferruh Yigit, dev
In-Reply-To: <CAExC=0SLV7qa5kzS6-UESSYNHq3wqQ3vZrV54btNnMi2kbXhiA@mail.gmail.com>

On Mon, Dec 19, 2016 at 4:35 PM, Marc <marcdevel@gmail.com> wrote:

>
>
> On 19 December 2016 at 17:18, Alejandro Lucero <
> alejandro.lucero@netronome.com> wrote:
>
>> On Mon, Dec 19, 2016 at 3:05 PM, Ferruh Yigit <ferruh.yigit@intel.com>
>> wrote:
>>
>> > On 12/19/2016 3:02 PM, Alejandro Lucero wrote:
>> > >
>> > >
>> > > On Mon, Dec 19, 2016 at 2:36 PM, Ferruh Yigit <ferruh.yigit@intel.com
>> > > <mailto:ferruh.yigit@intel.com>> wrote:
>> > >
>> > >     Hi Alejandro,
>> > >
>> > >
>> > > Hi,
>> > >
>> > >
>> > >     On 12/19/2016 12:05 PM, Alejandro Lucero wrote:
>> > >     > NFP supports more speeds than just 40 and 100GB, which were
>> > >     > what was advertised before.
>> > >     >
>> > >     > Signed-off-by: Alejandro Lucero <alejandro.lucero@netronome.com
>> > <mailto:alejandro.lucero@netronome.com>>
>> > >     > ---
>> > >     >  drivers/net/nfp/nfp_net.c | 4 +++-
>> > >     >  1 file changed, 3 insertions(+), 1 deletion(-)
>> > >     >
>> > >     > diff --git a/drivers/net/nfp/nfp_net.c
>> b/drivers/net/nfp/nfp_net.c
>> > >     > index 27afbfd..77015c4 100644
>> > >     > --- a/drivers/net/nfp/nfp_net.c
>> > >     > +++ b/drivers/net/nfp/nfp_net.c
>> > >     > @@ -1077,7 +1077,9 @@ static void nfp_net_read_mac(struct
>> > nfp_net_hw *hw)
>> > >     >       dev_info->reta_size = NFP_NET_CFG_RSS_ITBL_SZ;
>> > >     >       dev_info->hash_key_size = NFP_NET_CFG_RSS_KEY_SZ;
>> > >     >
>> > >     > -     dev_info->speed_capa = ETH_LINK_SPEED_40G |
>> > ETH_LINK_SPEED_100G;
>> > >     > +     dev_info->speed_capa = ETH_SPEED_NUM_1G |
>> ETH_LINK_SPEED_10G
>> > |
>> > >     > +                            ETH_SPEED_NUM_25G |
>> ETH_SPEED_NUM_40G
>> > |
>> > >     > +                            ETH_SPEED_NUM_50G |
>> > ETH_LINK_SPEED_100G;
>> > >
>> > >     Does all devices driver by this driver supports all these speeds?
>> > >
>> > >     I am aware of at least one exception to this, from previous patch
>> > [1],
>> > >     should we take that into account?
>> > >
>> > >
>> > > So we have different NFP devices and different firmwares.
>> > > NFP by design support all those speeds, but the PMD relies on the
>> > > firmware for being able to know which is the current configured speed
>> > > after link negotiation. PMD development was done with a specific
>> > > firmware, and I was told to just report such speed by default. Last
>> > > firmware versions give that speed info, but old firmware versions do
>> not.
>> > >
>> > > So, all devices support such a speed range, indeed PMD works with any
>> of
>> > > them, but reported speed is always 40G with old firmware. This is a
>> > > firmware limitation but we have to support old and new firmware.
>> >
>> > But this information to the application will be wrong for some (old) FW.
>> > What do you think checking the FW version here and report capability
>> > based on what FW supports?
>> >
>> >
>> The driver advertises the right speed range supported. The problem is with
>> the report about the current link speed configured.
>> Maybe, is the right thing to do here to not report the current link speed
>> because the driver really does not know about it?
>>
>> If you agree with this, I'm afraid the just accepted patch about the link
>> report needs to be modified.
>>
>
> Alejandro,
>
> If negociated link state has to be changed, then struct rte_eth_dev_data
> dev_link field is where to do it.
>
>
Yes. The driver is already doing that.


> As Ferruh was saying, dev_info->speed_capa contains the speed capabilties
> of the particular NIC in use, not the driver (detecting firmware version
> would be the best here).
>
>
The NIC supports all the range. The problem is the driver does not really
knows the speed with old firmware.


> marc
>
>
>>
>>
>>
>> > >
>> > >
>> > >
>> > >     Also other than that exception, can you please confirm all other
>> > devices
>> > >     support all above speeds?
>> > >
>> > >     [1]
>> > >     +       if ((NFD_CFG_MAJOR_VERSION_of(hw->ver) < 4) ||
>> > >     +           ((NFD_CFG_MINOR_VERSION_of(hw->ver) == 4) &&
>> > >     +           (NFD_CFG_MINOR_VERSION_of(hw->ver) == 0)))
>> > >     +               link.link_speed = ETH_SPEED_NUM_40G;
>> > >
>> > >
>> > >     >  }
>> > >     >
>> > >     >  static const uint32_t *
>> > >     >
>> > >
>> > >
>> >
>> >
>>
>
>

^ permalink raw reply

* Re: [PATCH v4] net/kni: add KNI PMD
From: Yong Wang @ 2016-12-19 17:52 UTC (permalink / raw)
  To: Ferruh Yigit, dev@dpdk.org
In-Reply-To: <479e706e-4d7e-53d1-e140-5c26f001c2c5@intel.com>

> -----Original Message-----
> From: Ferruh Yigit [mailto:ferruh.yigit@intel.com]
> Sent: Thursday, December 15, 2016 7:56 AM
> To: Yong Wang <yongwang@vmware.com>; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v4] net/kni: add KNI PMD
> 
> On 12/14/2016 7:25 PM, Yong Wang wrote:
> >> -----Original Message-----
> >> From: Ferruh Yigit [mailto:ferruh.yigit@intel.com]
> >> Sent: Wednesday, December 14, 2016 8:00 AM
> >> To: Yong Wang <yongwang@vmware.com>; dev@dpdk.org
> >> Subject: Re: [dpdk-dev] [PATCH v4] net/kni: add KNI PMD
> >>
> >> On 12/12/2016 9:59 PM, Yong Wang wrote:
> >>>> -----Original Message-----
> >>>> From: Ferruh Yigit [mailto:ferruh.yigit@intel.com]
> >>>> Sent: Wednesday, November 30, 2016 10:12 AM
> >>>> To: dev@dpdk.org
> >>>> Cc: Ferruh Yigit <ferruh.yigit@intel.com>; Yong Wang
> >>>> <yongwang@vmware.com>
> >>>> Subject: [PATCH v4] net/kni: add KNI PMD
> >>>>
> >>>> Add KNI PMD which wraps librte_kni for ease of use.
> >>>>
> >>>> KNI PMD can be used as any regular PMD to send / receive packets to
> the
> >>>> Linux networking stack.
> >>>>
> >>>> Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
> >>>> ---
> >>>>
> >>>> v4:
> >>>> * allow only single queue
> >>>> * use driver.name as name
> >>>>
> >>>> v3:
> >>>> * rebase on top of latest master
> >>>>
> >>>> v2:
> >>>> * updated driver name eth_kni -> net_kni
> >>>> ---
> >>>>  config/common_base                      |   1 +
> >>>>  config/common_linuxapp                  |   1 +
> >>>>  drivers/net/Makefile                    |   1 +
> >>>>  drivers/net/kni/Makefile                |  63 +++++
> >>>>  drivers/net/kni/rte_eth_kni.c           | 462
> >>>> ++++++++++++++++++++++++++++++++
> >>>>  drivers/net/kni/rte_pmd_kni_version.map |   4 +
> >>>>  mk/rte.app.mk                           |  10 +-
> >>>>  7 files changed, 537 insertions(+), 5 deletions(-)
> >>>>  create mode 100644 drivers/net/kni/Makefile
> >>>>  create mode 100644 drivers/net/kni/rte_eth_kni.c
> >>>>  create mode 100644 drivers/net/kni/rte_pmd_kni_version.map
> >>>>
> >>>> diff --git a/config/common_base b/config/common_base
> >>>> index 4bff83a..3385879 100644
> >>>> --- a/config/common_base
> >>>> +++ b/config/common_base
> >>>> @@ -543,6 +543,7 @@ CONFIG_RTE_PIPELINE_STATS_COLLECT=n
> >>>>  # Compile librte_kni
> >>>>  #
> >>>>  CONFIG_RTE_LIBRTE_KNI=n
> >>>> +CONFIG_RTE_LIBRTE_PMD_KNI=n
> >>>>  CONFIG_RTE_KNI_KMOD=n
> >>>>  CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
> >>>>  CONFIG_RTE_KNI_VHOST=n
> >>>> diff --git a/config/common_linuxapp b/config/common_linuxapp
> >>>> index 2483dfa..2ecd510 100644
> >>>> --- a/config/common_linuxapp
> >>>> +++ b/config/common_linuxapp
> >>>> @@ -39,6 +39,7 @@ CONFIG_RTE_EAL_IGB_UIO=y
> >>>>  CONFIG_RTE_EAL_VFIO=y
> >>>>  CONFIG_RTE_KNI_KMOD=y
> >>>>  CONFIG_RTE_LIBRTE_KNI=y
> >>>> +CONFIG_RTE_LIBRTE_PMD_KNI=y
> >>>>  CONFIG_RTE_LIBRTE_VHOST=y
> >>>>  CONFIG_RTE_LIBRTE_PMD_VHOST=y
> >>>>  CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y
> >>>> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> >>>> index bc93230..c4771cd 100644
> >>>> --- a/drivers/net/Makefile
> >>>> +++ b/drivers/net/Makefile
> >>>> @@ -41,6 +41,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += enic
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += ixgbe
> >>>> +DIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += kni
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += mlx4
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5
> >>>>  DIRS-$(CONFIG_RTE_LIBRTE_MPIPE_PMD) += mpipe
> >>>> diff --git a/drivers/net/kni/Makefile b/drivers/net/kni/Makefile
> >>>> new file mode 100644
> >>>> index 0000000..0b7cf91
> >>>> --- /dev/null
> >>>> +++ b/drivers/net/kni/Makefile
> >>>> @@ -0,0 +1,63 @@
> >>>> +#   BSD LICENSE
> >>>> +#
> >>>> +#   Copyright(c) 2016 Intel Corporation. All rights reserved.
> >>>> +#
> >>>> +#   Redistribution and use in source and binary forms, with or without
> >>>> +#   modification, are permitted provided that the following conditions
> >>>> +#   are met:
> >>>> +#
> >>>> +#     * Redistributions of source code must retain the above copyright
> >>>> +#       notice, this list of conditions and the following disclaimer.
> >>>> +#     * Redistributions in binary form must reproduce the above
> copyright
> >>>> +#       notice, this list of conditions and the following disclaimer in
> >>>> +#       the documentation and/or other materials provided with the
> >>>> +#       distribution.
> >>>> +#     * Neither the name of Intel Corporation nor the names of its
> >>>> +#       contributors may be used to endorse or promote products
> derived
> >>>> +#       from this software without specific prior written permission.
> >>>> +#
> >>>> +#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> >>>> CONTRIBUTORS
> >>>> +#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
> >> BUT
> >>>> NOT
> >>>> +#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> >>>> FITNESS FOR
> >>>> +#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> >>>> COPYRIGHT
> >>>> +#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> >>>> INCIDENTAL,
> >>>> +#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
> >> BUT
> >>>> NOT
> >>>> +#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> >> LOSS
> >>>> OF USE,
> >>>> +#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED
> >>>> AND ON ANY
> >>>> +#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR
> >>>> TORT
> >>>> +#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> OUT
> >> OF
> >>>> THE USE
> >>>> +#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> >>>> DAMAGE.
> >>>> +
> >>>> +include $(RTE_SDK)/mk/rte.vars.mk
> >>>> +
> >>>> +#
> >>>> +# library name
> >>>> +#
> >>>> +LIB = librte_pmd_kni.a
> >>>> +
> >>>> +CFLAGS += -O3
> >>>> +CFLAGS += $(WERROR_FLAGS)
> >>>> +LDLIBS += -lpthread
> >>>> +
> >>>> +EXPORT_MAP := rte_pmd_kni_version.map
> >>>> +
> >>>> +LIBABIVER := 1
> >>>> +
> >>>> +#
> >>>> +# all source are stored in SRCS-y
> >>>> +#
> >>>> +SRCS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += rte_eth_kni.c
> >>>> +
> >>>> +#
> >>>> +# Export include files
> >>>> +#
> >>>> +SYMLINK-y-include +=
> >>>> +
> >>>> +# this lib depends upon:
> >>>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += lib/librte_eal
> >>>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += lib/librte_ether
> >>>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += lib/librte_kni
> >>>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += lib/librte_mbuf
> >>>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_KNI) += lib/librte_mempool
> >>>> +
> >>>> +include $(RTE_SDK)/mk/rte.lib.mk
> >>>> diff --git a/drivers/net/kni/rte_eth_kni.c
> b/drivers/net/kni/rte_eth_kni.c
> >>>> new file mode 100644
> >>>> index 0000000..6c4df96
> >>>> --- /dev/null
> >>>> +++ b/drivers/net/kni/rte_eth_kni.c
> >>>> @@ -0,0 +1,462 @@
> >>>> +/*-
> >>>> + *   BSD LICENSE
> >>>> + *
> >>>> + *   Copyright(c) 2016 Intel Corporation. All rights reserved.
> >>>> + *   All rights reserved.
> >>>> + *
> >>>> + *   Redistribution and use in source and binary forms, with or without
> >>>> + *   modification, are permitted provided that the following conditions
> >>>> + *   are met:
> >>>> + *
> >>>> + *     * Redistributions of source code must retain the above copyright
> >>>> + *       notice, this list of conditions and the following disclaimer.
> >>>> + *     * Redistributions in binary form must reproduce the above
> copyright
> >>>> + *       notice, this list of conditions and the following disclaimer in
> >>>> + *       the documentation and/or other materials provided with the
> >>>> + *       distribution.
> >>>> + *     * Neither the name of Intel Corporation nor the names of its
> >>>> + *       contributors may be used to endorse or promote products
> derived
> >>>> + *       from this software without specific prior written permission.
> >>>> + *
> >>>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> >>>> CONTRIBUTORS
> >>>> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
> >> BUT
> >>>> NOT
> >>>> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
> AND
> >>>> FITNESS FOR
> >>>> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
> THE
> >>>> COPYRIGHT
> >>>> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> >>>> INCIDENTAL,
> >>>> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
> (INCLUDING,
> >> BUT
> >>>> NOT
> >>>> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> >> LOSS
> >>>> OF USE,
> >>>> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED
> >>>> AND ON ANY
> >>>> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR
> >>>> TORT
> >>>> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> OUT
> >> OF
> >>>> THE USE
> >>>> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> >>>> DAMAGE.
> >>>> + */
> >>>> +
> >>>> +#include <fcntl.h>
> >>>> +#include <pthread.h>
> >>>> +#include <unistd.h>
> >>>> +
> >>>> +#include <rte_ethdev.h>
> >>>> +#include <rte_kni.h>
> >>>> +#include <rte_malloc.h>
> >>>> +#include <rte_vdev.h>
> >>>> +
> >>>> +/* Only single queue supported */
> >>>> +#define KNI_MAX_QUEUE_PER_PORT 1
> >>>> +
> >>>> +#define MAX_PACKET_SZ 2048
> >>>> +#define MAX_KNI_PORTS 8
> >>>> +
> >>>> +struct pmd_queue_stats {
> >>>> +	uint64_t pkts;
> >>>> +	uint64_t bytes;
> >>>> +	uint64_t err_pkts;
> >>>> +};
> >>>> +
> >>>> +struct pmd_queue {
> >>>> +	struct pmd_internals *internals;
> >>>> +	struct rte_mempool *mb_pool;
> >>>> +
> >>>> +	struct pmd_queue_stats rx;
> >>>> +	struct pmd_queue_stats tx;
> >>>> +};
> >>>> +
> >>>> +struct pmd_internals {
> >>>> +	struct rte_kni *kni;
> >>>> +	int is_kni_started;
> >>>> +
> >>>> +	pthread_t thread;
> >>>> +	int stop_thread;
> >>>> +
> >>>> +	struct pmd_queue rx_queues[KNI_MAX_QUEUE_PER_PORT];
> >>>> +	struct pmd_queue tx_queues[KNI_MAX_QUEUE_PER_PORT];
> >>>> +};
> >>>> +
> >>>> +static struct ether_addr eth_addr;
> >>>> +static struct rte_eth_link pmd_link = {
> >>>> +		.link_speed = ETH_SPEED_NUM_10G,
> >>>> +		.link_duplex = ETH_LINK_FULL_DUPLEX,
> >>>> +		.link_status = 0
> >>>> +};
> >>>> +static int is_kni_initialized;
> >>>> +
> >>>> +static uint16_t
> >>>> +eth_kni_rx(void *q, struct rte_mbuf **bufs, uint16_t nb_bufs)
> >>>> +{
> >>>> +	struct pmd_queue *kni_q = q;
> >>>> +	struct rte_kni *kni = kni_q->internals->kni;
> >>>> +	uint16_t nb_pkts;
> >>>> +
> >>>> +	nb_pkts = rte_kni_rx_burst(kni, bufs, nb_bufs);
> >>>> +
> >>>> +	kni_q->rx.pkts += nb_pkts;
> >>>> +	kni_q->rx.err_pkts += nb_bufs - nb_pkts;
> >>>> +
> >>>> +	return nb_pkts;
> >>>> +}
> >>>> +
> >>>> +static uint16_t
> >>>> +eth_kni_tx(void *q, struct rte_mbuf **bufs, uint16_t nb_bufs)
> >>>> +{
> >>>> +	struct pmd_queue *kni_q = q;
> >>>> +	struct rte_kni *kni = kni_q->internals->kni;
> >>>> +	uint16_t nb_pkts;
> >>>> +
> >>>> +	nb_pkts =  rte_kni_tx_burst(kni, bufs, nb_bufs);
> >>>> +
> >>>> +	kni_q->tx.pkts += nb_pkts;
> >>>> +	kni_q->tx.err_pkts += nb_bufs - nb_pkts;
> >>>> +
> >>>> +	return nb_pkts;
> >>>> +}
> >>>> +
> >>>> +static void *
> >>>> +kni_handle_request(void *param)
> >>>> +{
> >>>> +	struct pmd_internals *internals = param;
> >>>> +#define MS 1000
> >>>> +
> >>>> +	while (!internals->stop_thread) {
> >>>> +		rte_kni_handle_request(internals->kni);
> >>>> +		usleep(500 * MS);
> >>>> +	}
> >>>> +
> >>>> +	return param;
> >>>> +}
> >>>> +
> >>>
> >>> Do we really need a thread to handle request by default? I know there
> are
> >> apps that handle request their own way and having a separate thread
> could
> >> add synchronization problems.  Can we at least add an option to disable
> this?
> >>
> >> I didn't think about there can be a use case that requires own request
> >> handling.
> >>
> >> But, kni requests should be handled to make kni interface run properly,
> >> and to handle interface "kni" handler (internals->kni) required, which
> >> this PMD doesn't expose.
> >>
> >> So, just disabling this thread won't work on its own.
> >
> > I understand that and what I am asking is a way to at least disable this
> without having to make code changes for applications that have their own
> way of handling KNI request and the callback mentioned below sounds good
> to me.  I am fine with adding this capability with this commit or in a separate
> commit after you have this commit checked in.
> 
> I don't mind adding in new version, only I am trying to understand it.
> 
> Normally what it does is calling KNI library rte_kni_handle_request()
> API periodically on KNI handler. What an app may be doing own its way,
> other than tweaking the period?

It's the context that calls into rte_kni_handle_request() that I am referring to.  For applications that already handle this in their own thread or in the pmd thread, they don't need the extra thread created here.  It's not a big deal as they can just change the behavior by modifying the source code but I think it's reasonable to opt out of this default thread without making any source code changes to kni pmd.

^ permalink raw reply

* [PATCH v3 25/25] doc: describe testpmd flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Document syntax, interaction with rte_flow and provide usage examples.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 612 +++++++++++++++++++++++
 1 file changed, 612 insertions(+)

diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index f1c269a..50cba16 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -1631,6 +1631,9 @@ Filter Functions
 
 This section details the available filter functions that are available.
 
+Note these functions interface the deprecated legacy filtering framework,
+superseded by *rte_flow*. See `Flow rules management`_.
+
 ethertype_filter
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -2041,3 +2044,612 @@ Set different GRE key length for input set::
 For example to set GRE key length for input set to 4 bytes on port 0::
 
    testpmd> global_config 0 gre-key-len 4
+
+
+.. _testpmd_rte_flow:
+
+Flow rules management
+---------------------
+
+Control of the generic flow API (*rte_flow*) is fully exposed through the
+``flow`` command (validation, creation, destruction and queries).
+
+Considering *rte_flow* overlaps with all `Filter Functions`_, using both
+features simultaneously may cause undefined side-effects and is therefore
+not recommended.
+
+``flow`` syntax
+~~~~~~~~~~~~~~~
+
+Because the ``flow`` command uses dynamic tokens to handle the large number
+of possible flow rules combinations, its behavior differs slightly from
+other commands, in particular:
+
+- Pressing *?* or the *<tab>* key displays contextual help for the current
+  token, not that of the entire command.
+
+- Optional and repeated parameters are supported (provided they are listed
+  in the contextual help).
+
+The first parameter stands for the operation mode. Possible operations and
+their general syntax are described below. They are covered in detail in the
+following sections.
+
+- Check whether a flow rule can be created::
+
+   flow validate {port_id}
+       [group {group_id}] [priority {level}] [ingress] [egress]
+       pattern {item} [/ {item} [...]] / end
+       actions {action} [/ {action} [...]] / end
+
+- Create a flow rule::
+
+   flow create {port_id}
+       [group {group_id}] [priority {level}] [ingress] [egress]
+       pattern {item} [/ {item} [...]] / end
+       actions {action} [/ {action} [...]] / end
+
+- Destroy specific flow rules::
+
+   flow destroy {port_id} rule {rule_id} [...]
+
+- Destroy all flow rules::
+
+   flow flush {port_id}
+
+- Query an existing flow rule::
+
+   flow query {port_id} {rule_id} {action}
+
+- List existing flow rules sorted by priority, filtered by group
+  identifiers::
+
+   flow list {port_id} [group {group_id}] [...]
+
+Validating flow rules
+~~~~~~~~~~~~~~~~~~~~~
+
+``flow validate`` reports whether a flow rule would be accepted by the
+underlying device in its current state but stops short of creating it. It is
+bound to ``rte_flow_validate()``::
+
+ flow validate {port_id}
+     [group {group_id}] [priority {level}] [ingress] [egress]
+     pattern {item} [/ {item} [...]] / end
+     actions {action} [/ {action} [...]] / end
+
+If successful, it will show::
+
+ Flow rule validated
+
+Otherwise it will show an error message of the form::
+
+ Caught error type [...] ([...]): [...]
+
+This command uses the same parameters as ``flow create``, their format is
+described in `Creating flow rules`_.
+
+Check whether redirecting any Ethernet packet received on port 0 to RX queue
+index 6 is supported::
+
+ testpmd> flow validate 1 ingress pattern eth / end
+     actions queue index 6 / end
+ Flow rule validated
+ testpmd>
+
+Port 0 does not support TCPv6 rules::
+
+ testpmd> flow validate 0 ingress pattern eth / ipv6 / tcp / end
+     actions drop / end
+ Caught error type 9 (specific pattern item): Invalid argument.
+ testpmd>
+
+Creating flow rules
+~~~~~~~~~~~~~~~~~~~
+
+``flow create`` validates and creates the specified flow rule. It is bound
+to ``rte_flow_create()``::
+
+ flow create {port_id}
+     [group {group_id}] [priority {level}] [ingress] [egress]
+     pattern {item} [/ {item} [...]] / end
+     actions {action} [/ {action} [...]] / end
+
+If successful, it will return a flow rule ID usable with other commands::
+
+ Flow rule #[...] created
+
+Otherwise it will show an error message of the form::
+
+ Caught error type [...] ([...]): [...]
+
+Parameters describe in the following order:
+
+- Attributes (*group*, *priority*, *ingress*, *egress* tokens).
+- A matching pattern, starting with the *pattern* token and terminated by an
+  *end* pattern item.
+- Actions, starting with the *actions* token and terminated by an *end*
+  action.
+
+These translate directly to *rte_flow* objects provided as-is to the
+underlying functions.
+
+The shortest valid definition only comprises mandatory tokens::
+
+ testpmd> flow create 0 pattern end actions end
+
+Note that PMDs may refuse rules that essentially do nothing such as this
+one.
+
+**All unspecified object values are automatically initialized to 0.**
+
+Attributes
+^^^^^^^^^^
+
+These tokens affect flow rule attributes (``struct rte_flow_attr``) and are
+specified before the ``pattern`` token.
+
+- ``group {group id}``: priority group.
+- ``priority {level}``: priority level within group.
+- ``ingress``: rule applies to ingress traffic.
+- ``egress``: rule applies to egress traffic.
+
+Each instance of an attribute specified several times overrides the previous
+value as shown below (group 4 is used)::
+
+ testpmd> flow create 0 group 42 group 24 group 4 [...]
+
+Note that once enabled, ``ingress`` and ``egress`` cannot be disabled.
+
+While not specifying a direction is an error, some rules may allow both
+simultaneously.
+
+Most rules affect RX therefore contain the ``ingress`` token::
+
+ testpmd> flow create 0 ingress pattern [...]
+
+Matching pattern
+^^^^^^^^^^^^^^^^
+
+A matching pattern starts after the ``pattern`` token. It is made of pattern
+items and is terminated by a mandatory ``end`` item.
+
+Items are named after their type (*RTE_FLOW_ITEM_TYPE_* from ``enum
+rte_flow_item_type``).
+
+The ``/`` token is used as a separator between pattern items as shown
+below::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / end [...]
+
+Note that protocol items like these must be stacked from lowest to highest
+layer to make sense. For instance, the following rule is either invalid or
+unlikely to match any packet::
+
+ testpmd> flow create 0 ingress pattern eth / udp / ipv4 / end [...]
+
+More information on these restrictions can be found in the *rte_flow*
+documentation.
+
+Several items support additional specification structures, for example
+``ipv4`` allows specifying source and destination addresses as follows::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 src is 10.1.1.1
+     dst is 10.2.0.0 / end [...]
+
+This rule matches all IPv4 traffic with the specified properties.
+
+In this example, ``src`` and ``dst`` are field names of the underlying
+``struct rte_flow_item_ipv4`` object. All item properties can be specified
+in a similar fashion.
+
+The ``is`` token means that the subsequent value must be matched exactly,
+and assigns ``spec`` and ``mask`` fields in ``struct rte_flow_item``
+accordingly. Possible assignment tokens are:
+
+- ``is``: match value perfectly (with full bit-mask).
+- ``spec``: match value according to configured bit-mask.
+- ``last``: specify upper bound to establish a range.
+- ``mask``: specify bit-mask with relevant bits set to one.
+- ``prefix``: generate bit-mask from a prefix length.
+
+These yield identical results::
+
+ ipv4 src is 10.1.1.1
+
+::
+
+ ipv4 src spec 10.1.1.1 src mask 255.255.255.255
+
+::
+
+ ipv4 src spec 10.1.1.1 src prefix 32
+
+::
+
+ ipv4 src is 10.1.1.1 src last 10.1.1.1 # range with a single value
+
+::
+
+ ipv4 src is 10.1.1.1 src last 0 # 0 disables range
+
+Inclusive ranges can be defined with ``last``::
+
+ ipv4 src is 10.1.1.1 src last 10.2.3.4 # 10.1.1.1 to 10.2.3.4
+
+Note that ``mask`` affects both ``spec`` and ``last``::
+
+ ipv4 src is 10.1.1.1 src last 10.2.3.4 src mask 255.255.0.0
+    # matches 10.1.0.0 to 10.2.255.255
+
+Properties can be modified multiple times::
+
+ ipv4 src is 10.1.1.1 src is 10.1.2.3 src is 10.2.3.4 # matches 10.2.3.4
+
+::
+
+ ipv4 src is 10.1.1.1 src prefix 24 src prefix 16 # matches 10.1.0.0/16
+
+Pattern items
+^^^^^^^^^^^^^
+
+This section lists supported pattern items and their attributes, if any.
+
+- ``end``: end list of pattern items.
+
+- ``void``: no-op pattern item.
+
+- ``invert``: perform actions when pattern does not match.
+
+- ``any``: match any protocol for the current layer.
+
+  - ``num {unsigned}``: number of layers covered.
+
+- ``pf``: match packets addressed to the physical function.
+
+- ``vf``: match packets addressed to a virtual function ID.
+
+  - ``id {unsigned}``: destination VF ID.
+
+- ``port``: device-specific physical port index to use.
+
+  - ``index {unsigned}``: physical port index.
+
+- ``raw``: match an arbitrary byte string.
+
+  - ``relative {boolean}``: look for pattern after the previous item.
+  - ``search {boolean}``: search pattern from offset (see also limit).
+  - ``offset {integer}``: absolute or relative offset for pattern.
+  - ``limit {unsigned}``: search area limit for start of pattern.
+  - ``pattern {string}``: byte string to look for.
+
+- ``eth``: match Ethernet header.
+
+  - ``dst {MAC-48}``: destination MAC.
+  - ``src {MAC-48}``: source MAC.
+  - ``type {unsigned}``: EtherType.
+
+- ``vlan``: match 802.1Q/ad VLAN tag.
+
+  - ``tpid {unsigned}``: tag protocol identifier.
+  - ``tci {unsigned}``: tag control information.
+
+- ``ipv4``: match IPv4 header.
+
+  - ``src {ipv4 address}``: source address.
+  - ``dst {ipv4 address}``: destination address.
+
+- ``ipv6``: match IPv6 header.
+
+  - ``src {ipv6 address}``: source address.
+  - ``dst {ipv6 address}``: destination address.
+
+- ``icmp``: match ICMP header.
+
+  - ``type {unsigned}``: ICMP packet type.
+  - ``code {unsigned}``: ICMP packet code.
+
+- ``udp``: match UDP header.
+
+  - ``src {unsigned}``: UDP source port.
+  - ``dst {unsigned}``: UDP destination port.
+
+- ``tcp``: match TCP header.
+
+  - ``src {unsigned}``: TCP source port.
+  - ``dst {unsigned}``: TCP destination port.
+
+- ``sctp``: match SCTP header.
+
+  - ``src {unsigned}``: SCTP source port.
+  - ``dst {unsigned}``: SCTP destination port.
+
+- ``vxlan``: match VXLAN header.
+
+  - ``vni {unsigned}``: VXLAN identifier.
+
+Actions list
+^^^^^^^^^^^^
+
+A list of actions starts after the ``actions`` token in the same fashion as
+`Matching pattern`_; actions are separated by ``/`` tokens and the list is
+terminated by a mandatory ``end`` action.
+
+Actions are named after their type (*RTE_FLOW_ACTION_TYPE_* from ``enum
+rte_flow_action_type``).
+
+Dropping all incoming UDPv4 packets can be expressed as follows::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / end
+     actions drop / end
+
+Several actions have configurable properties which must be specified when
+there is no valid default value. For example, ``queue`` requires a target
+queue index.
+
+This rule redirects incoming UDPv4 traffic to queue index 6::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / end
+     actions queue index 6 / end
+
+While this one could be rejected by PMDs (unspecified queue index)::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / end
+     actions queue / end
+
+As defined by *rte_flow*, the list is not ordered, all actions of a given
+rule are performed simultaneously. These are equivalent::
+
+ queue index 6 / void / mark id 42 / end
+
+::
+
+ void / mark id 42 / queue index 6 / end
+
+All actions in a list should have different types, otherwise only the last
+action of a given type is taken into account::
+
+ queue index 4 / queue index 5 / queue index 6 / end # will use queue 6
+
+::
+
+ drop / drop / drop / end # drop is performed only once
+
+::
+
+ mark id 42 / queue index 3 / mark id 24 / end # mark will be 24
+
+Considering they are performed simultaneously, opposite and overlapping
+actions can sometimes be combined when the end result is unambiguous::
+
+ drop / queue index 6 / end # drop has no effect
+
+::
+
+ drop / dup index 6 / end # same as above
+
+::
+
+ queue index 6 / rss queues 6 7 8 / end # queue has no effect
+
+::
+
+ drop / passthru / end # drop has no effect
+
+Note that PMDs may still refuse such combinations.
+
+Actions
+^^^^^^^
+
+This section lists supported actions and their attributes, if any.
+
+- ``end``: end list of actions.
+
+- ``void``: no-op action.
+
+- ``passthru``: let subsequent rule process matched packets.
+
+- ``mark``: attach 32 bit value to packets.
+
+  - ``id {unsigned}``: 32 bit value to return with packets.
+
+- ``flag``: flag packets.
+
+- ``queue``: assign packets to a given queue index.
+
+  - ``index {unsigned}``: queue index to use.
+
+- ``drop``: drop packets (note: passthru has priority).
+
+- ``count``: enable counters for this rule.
+
+- ``dup``: duplicate packets to a given queue index.
+
+  - ``index {unsigned}``: queue index to duplicate packets to.
+
+- ``rss``: spread packets among several queues.
+
+  - ``queues [{unsigned} [...]] end``: queue indices to use.
+
+- ``pf``: redirect packets to physical device function.
+
+- ``vf``: redirect packets to virtual device function.
+
+  - ``original {boolean}``: use original VF ID if possible.
+  - ``id {unsigned}``: VF ID to redirect packets to.
+
+Destroying flow rules
+~~~~~~~~~~~~~~~~~~~~~
+
+``flow destroy`` destroys one or more rules from their rule ID (as returned
+by ``flow create``), this command calls ``rte_flow_destroy()`` as many
+times as necessary::
+
+ flow destroy {port_id} rule {rule_id} [...]
+
+If successful, it will show::
+
+ Flow rule #[...] destroyed
+
+It does not report anything for rule IDs that do not exist. The usual error
+message is shown when a rule cannot be destroyed::
+
+ Caught error type [...] ([...]): [...]
+
+``flow flush`` destroys all rules on a device and does not take extra
+arguments. It is bound to ``rte_flow_flush()``::
+
+ flow flush {port_id}
+
+Any errors are reported as above.
+
+Creating several rules and destroying them::
+
+ testpmd> flow create 0 ingress pattern eth / ipv6 / end
+     actions queue index 2 / end
+ Flow rule #0 created
+ testpmd> flow create 0 ingress pattern eth / ipv4 / end
+     actions queue index 3 / end
+ Flow rule #1 created
+ testpmd> flow destroy 0 rule 0 rule 1
+ Flow rule #1 destroyed
+ Flow rule #0 destroyed
+ testpmd>
+
+The same result can be achieved using ``flow flush``::
+
+ testpmd> flow create 0 ingress pattern eth / ipv6 / end
+     actions queue index 2 / end
+ Flow rule #0 created
+ testpmd> flow create 0 ingress pattern eth / ipv4 / end
+     actions queue index 3 / end
+ Flow rule #1 created
+ testpmd> flow flush 0
+ testpmd>
+
+Non-existent rule IDs are ignored::
+
+ testpmd> flow create 0 ingress pattern eth / ipv6 / end
+     actions queue index 2 / end
+ Flow rule #0 created
+ testpmd> flow create 0 ingress pattern eth / ipv4 / end
+     actions queue index 3 / end
+ Flow rule #1 created
+ testpmd> flow destroy 0 rule 42 rule 10 rule 2
+ testpmd>
+ testpmd> flow destroy 0 rule 0
+ Flow rule #0 destroyed
+ testpmd>
+
+Querying flow rules
+~~~~~~~~~~~~~~~~~~~
+
+``flow query`` queries a specific action of a flow rule having that
+ability. Such actions collect information that can be reported using this
+command. It is bound to ``rte_flow_query()``::
+
+ flow query {port_id} {rule_id} {action}
+
+If successful, it will display either the retrieved data for known actions
+or the following message::
+
+ Cannot display result for action type [...] ([...])
+
+Otherwise, it will complain either that the rule does not exist or that some
+error occurred::
+
+ Flow rule #[...] not found
+
+::
+
+ Caught error type [...] ([...]): [...]
+
+Currently only the ``count`` action is supported. This action reports the
+number of packets that hit the flow rule and the total number of bytes. Its
+output has the following format::
+
+ count:
+  hits_set: [...] # whether "hits" contains a valid value
+  bytes_set: [...] # whether "bytes" contains a valid value
+  hits: [...] # number of packets
+  bytes: [...] # number of bytes
+
+Querying counters for TCPv6 packets redirected to queue 6::
+
+ testpmd> flow create 0 ingress pattern eth / ipv6 / tcp / end
+     actions queue index 6 / count / end
+ Flow rule #4 created
+ testpmd> flow query 0 4 count
+ count:
+  hits_set: 1
+  bytes_set: 0
+  hits: 386446
+  bytes: 0
+ testpmd>
+
+Listing flow rules
+~~~~~~~~~~~~~~~~~~
+
+``flow list`` lists existing flow rules sorted by priority and optionally
+filtered by group identifiers::
+
+ flow list {port_id} [group {group_id}] [...]
+
+This command only fails with the following message if the device does not
+exist::
+
+ Invalid port [...]
+
+Output consists of a header line followed by a short description of each
+flow rule, one per line. There is no output at all when no flow rules are
+configured on the device::
+
+ ID      Group   Prio    Attr    Rule
+ [...]   [...]   [...]   [...]   [...]
+
+``Attr`` column flags:
+
+- ``i`` for ``ingress``.
+- ``e`` for ``egress``.
+
+Creating several flow rules and listing them::
+
+ testpmd> flow create 0 ingress pattern eth / ipv4 / end
+     actions queue index 6 / end
+ Flow rule #0 created
+ testpmd> flow create 0 ingress pattern eth / ipv6 / end
+     actions queue index 2 / end
+ Flow rule #1 created
+ testpmd> flow create 0 priority 5 ingress pattern eth / ipv4 / udp / end
+     actions rss queues 6 7 8 end / end
+ Flow rule #2 created
+ testpmd> flow list 0
+ ID      Group   Prio    Attr    Rule
+ 0       0       0       i-      ETH IPV4 => QUEUE
+ 1       0       0       i-      ETH IPV6 => QUEUE
+ 2       0       5       i-      ETH IPV4 UDP => RSS
+ testpmd>
+
+Rules are sorted by priority (i.e. group ID first, then priority level)::
+
+ testpmd> flow list 1
+ ID      Group   Prio    Attr    Rule
+ 0       0       0       i-      ETH => COUNT
+ 6       0       500     i-      ETH IPV6 TCP => DROP COUNT
+ 5       0       1000    i-      ETH IPV6 ICMP => QUEUE
+ 1       24      0       i-      ETH IPV4 UDP => QUEUE
+ 4       24      10      i-      ETH IPV4 TCP => DROP
+ 3       24      20      i-      ETH IPV4 => DROP
+ 2       24      42      i-      ETH IPV4 UDP => QUEUE
+ 7       63      0       i-      ETH IPV6 UDP VXLAN => MARK QUEUE
+ testpmd>
+
+Output can be limited to specific groups::
+
+ testpmd> flow list 1 group 0 group 63
+ ID      Group   Prio    Attr    Rule
+ 0       0       0       i-      ETH => COUNT
+ 6       0       500     i-      ETH IPV6 TCP => DROP COUNT
+ 5       0       1000    i-      ETH IPV6 ICMP => QUEUE
+ 7       63      0       i-      ETH IPV6 UDP VXLAN => MARK QUEUE
+ testpmd>
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 24/25] app/testpmd: add queue actions to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

- QUEUE: assign packets to a given queue index.
- DUP: duplicate packets to a given queue index.
- RSS: spread packets among several queues.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 152 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 90712bf..2376b8f 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -156,8 +156,15 @@ enum index {
 	ACTION_MARK,
 	ACTION_MARK_ID,
 	ACTION_FLAG,
+	ACTION_QUEUE,
+	ACTION_QUEUE_INDEX,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_DUP_INDEX,
+	ACTION_RSS,
+	ACTION_RSS_QUEUES,
+	ACTION_RSS_QUEUE,
 	ACTION_PF,
 	ACTION_VF,
 	ACTION_VF_ORIGINAL,
@@ -171,6 +178,14 @@ enum index {
 #define ITEM_RAW_SIZE \
 	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
 
+/** Number of queue[] entries in struct rte_flow_action_rss. */
+#define ACTION_RSS_NUM 32
+
+/** Storage size for struct rte_flow_action_rss including queues. */
+#define ACTION_RSS_SIZE \
+	(offsetof(struct rte_flow_action_rss, queue) + \
+	 sizeof(*((struct rte_flow_action_rss *)0)->queue) * ACTION_RSS_NUM)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -487,8 +502,11 @@ static const enum index next_action[] = {
 	ACTION_PASSTHRU,
 	ACTION_MARK,
 	ACTION_FLAG,
+	ACTION_QUEUE,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_RSS,
 	ACTION_PF,
 	ACTION_VF,
 	ZERO,
@@ -500,6 +518,24 @@ static const enum index action_mark[] = {
 	ZERO,
 };
 
+static const enum index action_queue[] = {
+	ACTION_QUEUE_INDEX,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_dup[] = {
+	ACTION_DUP_INDEX,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_rss[] = {
+	ACTION_RSS_QUEUES,
+	ACTION_NEXT,
+	ZERO,
+};
+
 static const enum index action_vf[] = {
 	ACTION_VF_ORIGINAL,
 	ACTION_VF_ID,
@@ -517,6 +553,9 @@ static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
 static int parse_vc_conf(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_action_rss_queue(struct context *, const struct token *,
+				     const char *, unsigned int, void *,
+				     unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -566,6 +605,8 @@ static int comp_port(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_rule_id(struct context *, const struct token *,
 			unsigned int, char *, unsigned int);
+static int comp_vc_action_rss_queue(struct context *, const struct token *,
+				    unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -1163,6 +1204,21 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_QUEUE] = {
+		.name = "queue",
+		.help = "assign packets to a given queue index",
+		.priv = PRIV_ACTION(QUEUE,
+				    sizeof(struct rte_flow_action_queue)),
+		.next = NEXT(action_queue),
+		.call = parse_vc,
+	},
+	[ACTION_QUEUE_INDEX] = {
+		.name = "index",
+		.help = "queue index to use",
+		.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_queue, index)),
+		.call = parse_vc_conf,
+	},
 	[ACTION_DROP] = {
 		.name = "drop",
 		.help = "drop packets (note: passthru has priority)",
@@ -1177,6 +1233,39 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_DUP] = {
+		.name = "dup",
+		.help = "duplicate packets to a given queue index",
+		.priv = PRIV_ACTION(DUP, sizeof(struct rte_flow_action_dup)),
+		.next = NEXT(action_dup),
+		.call = parse_vc,
+	},
+	[ACTION_DUP_INDEX] = {
+		.name = "index",
+		.help = "queue index to duplicate packets to",
+		.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_dup, index)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS] = {
+		.name = "rss",
+		.help = "spread packets among several queues",
+		.priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE),
+		.next = NEXT(action_rss),
+		.call = parse_vc,
+	},
+	[ACTION_RSS_QUEUES] = {
+		.name = "queues",
+		.help = "queue indices to use",
+		.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS_QUEUE] = {
+		.name = "{queue}",
+		.help = "queue index",
+		.call = parse_vc_action_rss_queue,
+		.comp = comp_vc_action_rss_queue,
+	},
 	[ACTION_PF] = {
 		.name = "pf",
 		.help = "redirect packets to physical device function",
@@ -1556,6 +1645,51 @@ parse_vc_conf(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/**
+ * Parse queue field for RSS action.
+ *
+ * Valid tokens are queue indices and the "end" token.
+ */
+static int
+parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			  const char *str, unsigned int len,
+			  void *buf, unsigned int size)
+{
+	static const enum index next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
+	int ret;
+	int i;
+
+	(void)token;
+	(void)buf;
+	(void)size;
+	if (ctx->curr != ACTION_RSS_QUEUE)
+		return -1;
+	i = ctx->objdata >> 16;
+	if (!strncmp(str, "end", len)) {
+		ctx->objdata &= 0xffff;
+		return len;
+	}
+	if (i >= ACTION_RSS_NUM)
+		return -1;
+	if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i])))
+		return -1;
+	ret = parse_int(ctx, token, str, len, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		return -1;
+	}
+	++i;
+	ctx->objdata = i << 16 | (ctx->objdata & 0xffff);
+	/* Repeat token. */
+	if (ctx->next_num == RTE_DIM(ctx->next))
+		return -1;
+	ctx->next[ctx->next_num++] = next;
+	if (!ctx->object)
+		return len;
+	((struct rte_flow_action_rss *)ctx->object)->num = i;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -2130,6 +2264,24 @@ comp_rule_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete queue field for RSS action. */
+static int
+comp_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			 unsigned int ent, char *buf, unsigned int size)
+{
+	static const char *const str[] = { "", "end", NULL };
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; str[i] != NULL; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", str[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 23/25] app/testpmd: add various actions to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

- MARK: attach 32 bit value to packets.
- FLAG: flag packets.
- DROP: drop packets.
- COUNT: enable counters for a rule.
- PF: redirect packets to physical device function.
- VF: redirect packets to virtual device function.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 121 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index a340a75..90712bf 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -153,6 +153,15 @@ enum index {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_MARK_ID,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
 };
 
 /** Size of pattern[] field in struct rte_flow_item_raw. */
@@ -476,6 +485,25 @@ static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	ZERO,
+};
+
+static const enum index action_mark[] = {
+	ACTION_MARK_ID,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_vf[] = {
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
+	ACTION_NEXT,
 	ZERO,
 };
 
@@ -487,6 +515,8 @@ static int parse_vc(struct context *, const struct token *,
 		    void *, unsigned int);
 static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_conf(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -1112,6 +1142,70 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_MARK] = {
+		.name = "mark",
+		.help = "attach 32 bit value to packets",
+		.priv = PRIV_ACTION(MARK, sizeof(struct rte_flow_action_mark)),
+		.next = NEXT(action_mark),
+		.call = parse_vc,
+	},
+	[ACTION_MARK_ID] = {
+		.name = "id",
+		.help = "32 bit value to return with packets",
+		.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_mark, id)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_FLAG] = {
+		.name = "flag",
+		.help = "flag packets",
+		.priv = PRIV_ACTION(FLAG, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_DROP] = {
+		.name = "drop",
+		.help = "drop packets (note: passthru has priority)",
+		.priv = PRIV_ACTION(DROP, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_COUNT] = {
+		.name = "count",
+		.help = "enable counters for this rule",
+		.priv = PRIV_ACTION(COUNT, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_PF] = {
+		.name = "pf",
+		.help = "redirect packets to physical device function",
+		.priv = PRIV_ACTION(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_VF] = {
+		.name = "vf",
+		.help = "redirect packets to virtual device function",
+		.priv = PRIV_ACTION(VF, sizeof(struct rte_flow_action_vf)),
+		.next = NEXT(action_vf),
+		.call = parse_vc,
+	},
+	[ACTION_VF_ORIGINAL] = {
+		.name = "original",
+		.help = "use original VF ID if possible",
+		.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
+					   original, 1)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_VF_ID] = {
+		.name = "id",
+		.help = "VF ID to redirect packets to",
+		.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)),
+		.call = parse_vc_conf,
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -1435,6 +1529,33 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse action configuration field. */
+static int
+parse_vc_conf(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_action *action;
+
+	(void)size;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->args.vc.actions_n)
+		return -1;
+	action = &out->args.vc.actions[out->args.vc.actions_n - 1];
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data;
+	ctx->objmask = NULL;
+	/* Update configuration pointer. */
+	action->conf = ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 22/25] app/testpmd: add L4 items to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Add the ability to match a few properties of common L4[.5] protocol
headers:

- ICMP: type and code.
- UDP: source and destination ports.
- TCP: source and destination ports.
- SCTP: source and destination ports.
- VXLAN: network identifier.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 163 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index c2725a5..a340a75 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -132,6 +132,20 @@ enum index {
 	ITEM_IPV6,
 	ITEM_IPV6_SRC,
 	ITEM_IPV6_DST,
+	ITEM_ICMP,
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_UDP,
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_TCP,
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_SCTP,
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_VXLAN,
+	ITEM_VXLAN_VNI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -359,6 +373,11 @@ static const enum index next_item[] = {
 	ITEM_VLAN,
 	ITEM_IPV4,
 	ITEM_IPV6,
+	ITEM_ICMP,
+	ITEM_UDP,
+	ITEM_TCP,
+	ITEM_SCTP,
+	ITEM_VXLAN,
 	ZERO,
 };
 
@@ -419,6 +438,40 @@ static const enum index item_ipv6[] = {
 	ZERO,
 };
 
+static const enum index item_icmp[] = {
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_udp[] = {
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_tcp[] = {
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_sctp[] = {
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_vxlan[] = {
+	ITEM_VXLAN_VNI,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -930,6 +983,103 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
 					     hdr.dst_addr)),
 	},
+	[ITEM_ICMP] = {
+		.name = "icmp",
+		.help = "match ICMP header",
+		.priv = PRIV_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),
+		.next = NEXT(item_icmp),
+		.call = parse_vc,
+	},
+	[ITEM_ICMP_TYPE] = {
+		.name = "type",
+		.help = "ICMP packet type",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_type)),
+	},
+	[ITEM_ICMP_CODE] = {
+		.name = "code",
+		.help = "ICMP packet code",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_code)),
+	},
+	[ITEM_UDP] = {
+		.name = "udp",
+		.help = "match UDP header",
+		.priv = PRIV_ITEM(UDP, sizeof(struct rte_flow_item_udp)),
+		.next = NEXT(item_udp),
+		.call = parse_vc,
+	},
+	[ITEM_UDP_SRC] = {
+		.name = "src",
+		.help = "UDP source port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.src_port)),
+	},
+	[ITEM_UDP_DST] = {
+		.name = "dst",
+		.help = "UDP destination port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.dst_port)),
+	},
+	[ITEM_TCP] = {
+		.name = "tcp",
+		.help = "match TCP header",
+		.priv = PRIV_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),
+		.next = NEXT(item_tcp),
+		.call = parse_vc,
+	},
+	[ITEM_TCP_SRC] = {
+		.name = "src",
+		.help = "TCP source port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.src_port)),
+	},
+	[ITEM_TCP_DST] = {
+		.name = "dst",
+		.help = "TCP destination port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.dst_port)),
+	},
+	[ITEM_SCTP] = {
+		.name = "sctp",
+		.help = "match SCTP header",
+		.priv = PRIV_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),
+		.next = NEXT(item_sctp),
+		.call = parse_vc,
+	},
+	[ITEM_SCTP_SRC] = {
+		.name = "src",
+		.help = "SCTP source port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.src_port)),
+	},
+	[ITEM_SCTP_DST] = {
+		.name = "dst",
+		.help = "SCTP destination port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.dst_port)),
+	},
+	[ITEM_VXLAN] = {
+		.name = "vxlan",
+		.help = "match VXLAN header",
+		.priv = PRIV_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),
+		.next = NEXT(item_vxlan),
+		.call = parse_vc,
+	},
+	[ITEM_VXLAN_VNI] = {
+		.name = "vni",
+		.help = "VXLAN identifier",
+		.next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vxlan, vni)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1491,6 +1641,19 @@ parse_int(struct context *ctx, const struct token *token,
 	case sizeof(uint16_t):
 		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
 		break;
+	case sizeof(uint8_t [3]):
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+		if (!arg->hton) {
+			((uint8_t *)buf)[0] = u;
+			((uint8_t *)buf)[1] = u >> 8;
+			((uint8_t *)buf)[2] = u >> 16;
+			break;
+		}
+#endif
+		((uint8_t *)buf)[0] = u >> 16;
+		((uint8_t *)buf)[1] = u >> 8;
+		((uint8_t *)buf)[2] = u;
+		break;
 	case sizeof(uint32_t):
 		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
 		break;
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 21/25] app/testpmd: add items ipv4/ipv6 to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Add the ability to match basic fields from IPv4 and IPv6 headers (source
and destination addresses only).

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 177 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 53709fe..c2725a5 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -38,6 +38,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <string.h>
+#include <arpa/inet.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -61,6 +62,8 @@ enum index {
 	BOOLEAN,
 	STRING,
 	MAC_ADDR,
+	IPV4_ADDR,
+	IPV6_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -123,6 +126,12 @@ enum index {
 	ITEM_VLAN,
 	ITEM_VLAN_TPID,
 	ITEM_VLAN_TCI,
+	ITEM_IPV4,
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_IPV6,
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -348,6 +357,8 @@ static const enum index next_item[] = {
 	ITEM_RAW,
 	ITEM_ETH,
 	ITEM_VLAN,
+	ITEM_IPV4,
+	ITEM_IPV6,
 	ZERO,
 };
 
@@ -394,6 +405,20 @@ static const enum index item_vlan[] = {
 	ZERO,
 };
 
+static const enum index item_ipv4[] = {
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_ipv6[] = {
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -439,6 +464,12 @@ static int parse_string(struct context *, const struct token *,
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
 			  void *, unsigned int);
+static int parse_ipv4_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
+static int parse_ipv6_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -509,6 +540,20 @@ static const struct token token_list[] = {
 		.call = parse_mac_addr,
 		.comp = comp_none,
 	},
+	[IPV4_ADDR] = {
+		.name = "{IPv4 address}",
+		.type = "IPV4 ADDRESS",
+		.help = "standard IPv4 address notation",
+		.call = parse_ipv4_addr,
+		.comp = comp_none,
+	},
+	[IPV6_ADDR] = {
+		.name = "{IPv6 address}",
+		.type = "IPV6 ADDRESS",
+		.help = "standard IPv6 address notation",
+		.call = parse_ipv6_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -843,6 +888,48 @@ static const struct token token_list[] = {
 		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
 	},
+	[ITEM_IPV4] = {
+		.name = "ipv4",
+		.help = "match IPv4 header",
+		.priv = PRIV_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),
+		.next = NEXT(item_ipv4),
+		.call = parse_vc,
+	},
+	[ITEM_IPV4_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV4_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.dst_addr)),
+	},
+	[ITEM_IPV6] = {
+		.name = "ipv6",
+		.help = "match IPv6 header",
+		.priv = PRIV_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),
+		.next = NEXT(item_ipv6),
+		.call = parse_vc,
+	},
+	[ITEM_IPV6_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV6_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.dst_addr)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1514,6 +1601,96 @@ parse_mac_addr(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse an IPv4 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv4_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in_addr tmp;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET, str2, &tmp);
+	if (ret != 1) {
+		/* Attempt integer parsing. */
+		push_args(ctx, arg);
+		return parse_int(ctx, token, str, len, buf, size);
+	}
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/**
+ * Parse an IPv6 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv6_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in6_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET6, str2, &tmp);
+	if (ret != 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 20/25] app/testpmd: add items eth/vlan to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

These pattern items match basic Ethernet headers (source, destination and
type) and related 802.1Q/ad VLAN headers.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 126 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index dafb07f..53709fe 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -43,6 +43,7 @@
 #include <rte_ethdev.h>
 #include <rte_byteorder.h>
 #include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
 #include <rte_flow.h>
 
 #include "testpmd.h"
@@ -59,6 +60,7 @@ enum index {
 	PREFIX,
 	BOOLEAN,
 	STRING,
+	MAC_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -114,6 +116,13 @@ enum index {
 	ITEM_RAW_OFFSET,
 	ITEM_RAW_LIMIT,
 	ITEM_RAW_PATTERN,
+	ITEM_ETH,
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_VLAN,
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -238,6 +247,14 @@ struct token {
 		.size = (sz), \
 	})
 
+/** Same as ARGS_ENTRY() using network byte ordering. */
+#define ARGS_ENTRY_HTON(s, f) \
+	(&(const struct arg){ \
+		.hton = 1, \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -329,6 +346,8 @@ static const enum index next_item[] = {
 	ITEM_VF,
 	ITEM_PORT,
 	ITEM_RAW,
+	ITEM_ETH,
+	ITEM_VLAN,
 	ZERO,
 };
 
@@ -360,6 +379,21 @@ static const enum index item_raw[] = {
 	ZERO,
 };
 
+static const enum index item_eth[] = {
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_vlan[] = {
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -402,6 +436,9 @@ static int parse_boolean(struct context *, const struct token *,
 static int parse_string(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_mac_addr(struct context *, const struct token *,
+			  const char *, unsigned int,
+			  void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -465,6 +502,13 @@ static const struct token token_list[] = {
 		.call = parse_string,
 		.comp = comp_none,
 	},
+	[MAC_ADDR] = {
+		.name = "{MAC address}",
+		.type = "MAC-48",
+		.help = "standard MAC address notation",
+		.call = parse_mac_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -755,6 +799,50 @@ static const struct token token_list[] = {
 					    pattern,
 					    ITEM_RAW_PATTERN_SIZE)),
 	},
+	[ITEM_ETH] = {
+		.name = "eth",
+		.help = "match Ethernet header",
+		.priv = PRIV_ITEM(ETH, sizeof(struct rte_flow_item_eth)),
+		.next = NEXT(item_eth),
+		.call = parse_vc,
+	},
+	[ITEM_ETH_DST] = {
+		.name = "dst",
+		.help = "destination MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, dst)),
+	},
+	[ITEM_ETH_SRC] = {
+		.name = "src",
+		.help = "source MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, src)),
+	},
+	[ITEM_ETH_TYPE] = {
+		.name = "type",
+		.help = "EtherType",
+		.next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_eth, type)),
+	},
+	[ITEM_VLAN] = {
+		.name = "vlan",
+		.help = "match 802.1Q/ad VLAN tag",
+		.priv = PRIV_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),
+		.next = NEXT(item_vlan),
+		.call = parse_vc,
+	},
+	[ITEM_VLAN_TPID] = {
+		.name = "tpid",
+		.help = "tag protocol identifier",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tpid)),
+	},
+	[ITEM_VLAN_TCI] = {
+		.name = "tci",
+		.help = "tag control information",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1388,6 +1476,44 @@ parse_string(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a MAC address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_mac_addr(struct context *ctx, const struct token *token,
+	       const char *str, unsigned int len,
+	       void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	struct ether_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	ret = cmdline_parse_etheraddr(NULL, str, &tmp, size);
+	if (ret < 0 || (unsigned int)ret != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 19/25] app/testpmd: add item raw to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Matches arbitrary byte strings with properties:

- relative: look for pattern after the previous item.
- search: search pattern from offset (see also limit).
- offset: absolute or relative offset for pattern.
- limit: search area limit for start of pattern.
- length: pattern length.
- pattern: byte string to look for.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 208 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 208 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index ac93679..dafb07f 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -57,6 +57,8 @@ enum index {
 	INTEGER,
 	UNSIGNED,
 	PREFIX,
+	BOOLEAN,
+	STRING,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -106,6 +108,12 @@ enum index {
 	ITEM_VF_ID,
 	ITEM_PORT,
 	ITEM_PORT_INDEX,
+	ITEM_RAW,
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -115,6 +123,13 @@ enum index {
 	ACTION_PASSTHRU,
 };
 
+/** Size of pattern[] field in struct rte_flow_item_raw. */
+#define ITEM_RAW_PATTERN_SIZE 36
+
+/** Storage size for struct rte_flow_item_raw including pattern. */
+#define ITEM_RAW_SIZE \
+	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -216,6 +231,13 @@ struct token {
 		.size = sizeof(*((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() with arbitrary size. */
+#define ARGS_ENTRY_USZ(s, f, sz) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = (sz), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -306,6 +328,7 @@ static const enum index next_item[] = {
 	ITEM_PF,
 	ITEM_VF,
 	ITEM_PORT,
+	ITEM_RAW,
 	ZERO,
 };
 
@@ -327,6 +350,16 @@ static const enum index item_port[] = {
 	ZERO,
 };
 
+static const enum index item_raw[] = {
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -363,11 +396,19 @@ static int parse_int(struct context *, const struct token *,
 static int parse_prefix(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_boolean(struct context *, const struct token *,
+			 const char *, unsigned int,
+			 void *, unsigned int);
+static int parse_string(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_boolean(struct context *, const struct token *,
+			unsigned int, char *, unsigned int);
 static int comp_action(struct context *, const struct token *,
 		       unsigned int, char *, unsigned int);
 static int comp_port(struct context *, const struct token *,
@@ -410,6 +451,20 @@ static const struct token token_list[] = {
 		.call = parse_prefix,
 		.comp = comp_none,
 	},
+	[BOOLEAN] = {
+		.name = "{boolean}",
+		.type = "BOOLEAN",
+		.help = "any boolean value",
+		.call = parse_boolean,
+		.comp = comp_boolean,
+	},
+	[STRING] = {
+		.name = "{string}",
+		.type = "STRING",
+		.help = "fixed string",
+		.call = parse_string,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -654,6 +709,52 @@ static const struct token token_list[] = {
 		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
 	},
+	[ITEM_RAW] = {
+		.name = "raw",
+		.help = "match an arbitrary byte string",
+		.priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
+		.next = NEXT(item_raw),
+		.call = parse_vc,
+	},
+	[ITEM_RAW_RELATIVE] = {
+		.name = "relative",
+		.help = "look for pattern after the previous item",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+					   relative, 1)),
+	},
+	[ITEM_RAW_SEARCH] = {
+		.name = "search",
+		.help = "search pattern from offset (see also limit)",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+					   search, 1)),
+	},
+	[ITEM_RAW_OFFSET] = {
+		.name = "offset",
+		.help = "absolute or relative offset for pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, offset)),
+	},
+	[ITEM_RAW_LIMIT] = {
+		.name = "limit",
+		.help = "search area limit for start of pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)),
+	},
+	[ITEM_RAW_PATTERN] = {
+		.name = "pattern",
+		.help = "byte string to look for",
+		.next = NEXT(item_raw,
+			     NEXT_ENTRY(STRING),
+			     NEXT_ENTRY(ITEM_PARAM_IS,
+					ITEM_PARAM_SPEC,
+					ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, length),
+			     ARGS_ENTRY_USZ(struct rte_flow_item_raw,
+					    pattern,
+					    ITEM_RAW_PATTERN_SIZE)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1235,6 +1336,96 @@ parse_int(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a string.
+ *
+ * Two arguments (ctx->args) are retrieved from the stack to store data and
+ * its length (in that order).
+ */
+static int
+parse_string(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+	const struct arg *arg_len = pop_args(ctx);
+	char tmp[16]; /* Ought to be enough. */
+	int ret;
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	if (!arg_len) {
+		push_args(ctx, arg_data);
+		return -1;
+	}
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	/* Let parse_int() fill length information first. */
+	ret = snprintf(tmp, sizeof(tmp), "%u", len);
+	if (ret < 0)
+		goto error;
+	push_args(ctx, arg_len);
+	ret = parse_int(ctx, token, tmp, ret, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		goto error;
+	}
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	/* Output buffer is not necessarily NUL-terminated. */
+	memcpy(buf, str, len);
+	memset((uint8_t *)buf + len, 0x55, size - len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+	return len;
+error:
+	push_args(ctx, arg_len);
+	push_args(ctx, arg_data);
+	return -1;
+}
+
+/** Boolean values (even indices stand for false). */
+static const char *const boolean_name[] = {
+	"0", "1",
+	"false", "true",
+	"no", "yes",
+	"N", "Y",
+	NULL,
+};
+
+/**
+ * Parse a boolean value.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_boolean(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	unsigned int i;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	for (i = 0; boolean_name[i]; ++i)
+		if (!strncmp(str, boolean_name[i], len))
+			break;
+	/* Process token as integer. */
+	if (boolean_name[i])
+		str = i & 1 ? "1" : "0";
+	push_args(ctx, arg);
+	ret = parse_int(ctx, token, str, strlen(str), buf, size);
+	return ret > 0 ? (int)len : ret;
+}
+
 /** Parse port and update context. */
 static int
 parse_port(struct context *ctx, const struct token *token,
@@ -1273,6 +1464,23 @@ comp_none(struct context *ctx, const struct token *token,
 	return 0;
 }
 
+/** Complete boolean values. */
+static int
+comp_boolean(struct context *ctx, const struct token *token,
+	     unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; boolean_name[i]; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", boolean_name[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Complete action names. */
 static int
 comp_action(struct context *ctx, const struct token *token,
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 18/25] app/testpmd: add various items to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

- PF: match packets addressed to the physical function.
- VF: match packets addressed to a virtual function ID.
- PORT: device-specific physical port index to use.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 53 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 1736954..ac93679 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -101,6 +101,11 @@ enum index {
 	ITEM_INVERT,
 	ITEM_ANY,
 	ITEM_ANY_NUM,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_VF_ID,
+	ITEM_PORT,
+	ITEM_PORT_INDEX,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -298,6 +303,9 @@ static const enum index next_item[] = {
 	ITEM_VOID,
 	ITEM_INVERT,
 	ITEM_ANY,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_PORT,
 	ZERO,
 };
 
@@ -307,6 +315,18 @@ static const enum index item_any[] = {
 	ZERO,
 };
 
+static const enum index item_vf[] = {
+	ITEM_VF_ID,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_port[] = {
+	ITEM_PORT_INDEX,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -601,6 +621,39 @@ static const struct token token_list[] = {
 		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)),
 	},
+	[ITEM_PF] = {
+		.name = "pf",
+		.help = "match packets addressed to the physical function",
+		.priv = PRIV_ITEM(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+		.call = parse_vc,
+	},
+	[ITEM_VF] = {
+		.name = "vf",
+		.help = "match packets addressed to a virtual function ID",
+		.priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)),
+		.next = NEXT(item_vf),
+		.call = parse_vc,
+	},
+	[ITEM_VF_ID] = {
+		.name = "id",
+		.help = "destination VF ID",
+		.next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_vf, id)),
+	},
+	[ITEM_PORT] = {
+		.name = "port",
+		.help = "device-specific physical port index to use",
+		.priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)),
+		.next = NEXT(item_port),
+		.call = parse_vc,
+	},
+	[ITEM_PORT_INDEX] = {
+		.name = "index",
+		.help = "physical port index",
+		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 17/25] app/testpmd: add item any to flow command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

This pattern item matches any protocol in place of the current layer and
has two properties:

- min: minimum number of layers covered (0 or more).
- max: maximum number of layers covered (0 means infinity).

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 69887fc..1736954 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -99,6 +99,8 @@ enum index {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	ITEM_ANY_NUM,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -282,7 +284,6 @@ static const enum index next_list_attr[] = {
 	ZERO,
 };
 
-__rte_unused
 static const enum index item_param[] = {
 	ITEM_PARAM_IS,
 	ITEM_PARAM_SPEC,
@@ -296,6 +297,13 @@ static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	ZERO,
+};
+
+static const enum index item_any[] = {
+	ITEM_ANY_NUM,
+	ITEM_NEXT,
 	ZERO,
 };
 
@@ -580,6 +588,19 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
 		.call = parse_vc,
 	},
+	[ITEM_ANY] = {
+		.name = "any",
+		.help = "match any protocol for the current layer",
+		.priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)),
+		.next = NEXT(item_any),
+		.call = parse_vc,
+	},
+	[ITEM_ANY_NUM] = {
+		.name = "num",
+		.help = "number of layers covered",
+		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 16/25] app/testpmd: add rte_flow bit-field support
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Several rte_flow structures expose bit-fields that cannot be set in a
generic fashion at byte level. Add bit-mask support to handle them.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 59 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 07f895e..69887fc 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -136,6 +136,7 @@ struct arg {
 	uint32_t sign:1; /**< Value is signed. */
 	uint32_t offset; /**< Relative offset from ctx->object. */
 	uint32_t size; /**< Field size. */
+	const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */
 };
 
 /** Parser token definition. */
@@ -195,6 +196,13 @@ struct token {
 		.size = sizeof(((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() to target a bit-field. */
+#define ARGS_ENTRY_BF(s, f, b) \
+	(&(const struct arg){ \
+		.size = sizeof(s), \
+		.mask = (const void *)&(const s){ .f = (1 << (b)) - 1 }, \
+	})
+
 /** Static initializer for ARGS() to target a pointer. */
 #define ARGS_ENTRY_PTR(s, f) \
 	(&(const struct arg){ \
@@ -623,6 +631,34 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/** Spread value into buffer according to bit-mask. */
+static size_t
+arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg)
+{
+	uint32_t i;
+	size_t len = 0;
+
+	/* Endian conversion is not supported on bit-fields. */
+	if (!arg->mask || arg->hton)
+		return 0;
+	for (i = 0; i != arg->size; ++i) {
+		unsigned int shift = 0;
+		uint8_t *buf = (uint8_t *)dst + i;
+
+		for (shift = 0; arg->mask[i] >> shift; ++shift) {
+			if (!(arg->mask[i] & (1 << shift)))
+				continue;
+			++len;
+			if (!dst)
+				continue;
+			*buf &= ~(1 << shift);
+			*buf |= (val & 1) << shift;
+			val >>= 1;
+		}
+	}
+	return len;
+}
+
 /**
  * Parse a prefix length and generate a bit-mask.
  *
@@ -649,6 +685,23 @@ parse_prefix(struct context *ctx, const struct token *token,
 	u = strtoumax(str, &end, 0);
 	if (errno || (size_t)(end - str) != len)
 		goto error;
+	if (arg->mask) {
+		uintmax_t v = 0;
+
+		extra = arg_entry_bf_fill(NULL, 0, arg);
+		if (u > extra)
+			goto error;
+		if (!ctx->object)
+			return len;
+		extra -= u;
+		while (u--)
+			(v <<= 1, v |= 1);
+		v <<= extra;
+		if (!arg_entry_bf_fill(ctx->object, v, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	bytes = u / 8;
 	extra = u % 8;
 	size = arg->size;
@@ -1072,6 +1125,12 @@ parse_int(struct context *ctx, const struct token *token,
 		goto error;
 	if (!ctx->object)
 		return len;
+	if (arg->mask) {
+		if (!arg_entry_bf_fill(ctx->object, u, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
 objmask:
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 15/25] app/testpmd: add rte_flow item spec prefix length
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Generating bit-masks from prefix lengths is often more convenient than
providing them entirely (e.g. to define IPv4 and IPv6 subnets).

This commit adds the "prefix" operator that assigns generated bit-masks to
any pattern item specification field.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 80 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index b66fecf..07f895e 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -56,6 +56,7 @@ enum index {
 	/* Common tokens. */
 	INTEGER,
 	UNSIGNED,
+	PREFIX,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -93,6 +94,7 @@ enum index {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -278,6 +280,7 @@ static const enum index item_param[] = {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	ZERO,
 };
 
@@ -321,6 +324,9 @@ static int parse_list(struct context *, const struct token *,
 static int parse_int(struct context *, const struct token *,
 		     const char *, unsigned int,
 		     void *, unsigned int);
+static int parse_prefix(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -361,6 +367,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[PREFIX] = {
+		.name = "{prefix}",
+		.type = "PREFIX",
+		.help = "prefix length for bit-mask",
+		.call = parse_prefix,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -528,6 +541,11 @@ static const struct token token_list[] = {
 		.help = "specify bit-mask with relevant bits set to one",
 		.call = parse_vc_spec,
 	},
+	[ITEM_PARAM_PREFIX] = {
+		.name = "prefix",
+		.help = "generate bit-mask from a prefix length",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +623,62 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/**
+ * Parse a prefix length and generate a bit-mask.
+ *
+ * Last argument (ctx->args) is retrieved to determine mask size, storage
+ * location and whether the result must use network byte ordering.
+ */
+static int
+parse_prefix(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	static const uint8_t conv[] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe\xff";
+	char *end;
+	uintmax_t u;
+	unsigned int bytes;
+	unsigned int extra;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	bytes = u / 8;
+	extra = u % 8;
+	size = arg->size;
+	if (bytes > size || bytes + !!extra > size)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+	if (!arg->hton) {
+		memset((uint8_t *)buf + size - bytes, 0xff, bytes);
+		memset(buf, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[size - bytes - 1] = conv[extra];
+	} else
+#endif
+	{
+		memset(buf, 0xff, bytes);
+		memset((uint8_t *)buf + bytes, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[bytes] = conv[extra];
+	}
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -776,6 +850,12 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	case ITEM_PARAM_LAST:
 		index = 1;
 		break;
+	case ITEM_PARAM_PREFIX:
+		/* Modify next token to expect a prefix. */
+		if (ctx->next_num < 2)
+			return -1;
+		ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
+		/* Fall through. */
 	case ITEM_PARAM_MASK:
 		index = 2;
 		break;
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 14/25] app/testpmd: add rte_flow item spec handler
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Add parser code to fully set individual fields of pattern item
specification structures, using the following operators:

- fix: sets field and applies full bit-mask for perfect matching.
- spec: sets field without modifying its bit-mask.
- last: sets upper value of the spec => last range.
- mask: sets bit-mask affecting both spec and last from arbitrary value.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 111 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 8f7ec1d..b66fecf 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -89,6 +89,10 @@ enum index {
 
 	/* Validate/create pattern. */
 	PATTERN,
+	ITEM_PARAM_IS,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -121,6 +125,7 @@ struct context {
 	uint16_t port; /**< Current port ID (for completions). */
 	uint32_t objdata; /**< Object-specific data. */
 	void *object; /**< Address of current object for relative offsets. */
+	void *objmask; /**< Object a full mask must be written to. */
 };
 
 /** Token argument. */
@@ -267,6 +272,15 @@ static const enum index next_list_attr[] = {
 	ZERO,
 };
 
+__rte_unused
+static const enum index item_param[] = {
+	ITEM_PARAM_IS,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
+	ZERO,
+};
+
 static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
@@ -287,6 +301,8 @@ static int parse_init(struct context *, const struct token *,
 static int parse_vc(struct context *, const struct token *,
 		    const char *, unsigned int,
 		    void *, unsigned int);
+static int parse_vc_spec(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -492,6 +508,26 @@ static const struct token token_list[] = {
 		.next = NEXT(next_item),
 		.call = parse_vc,
 	},
+	[ITEM_PARAM_IS] = {
+		.name = "is",
+		.help = "match value perfectly (with full bit-mask)",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_SPEC] = {
+		.name = "spec",
+		.help = "match value according to configured bit-mask",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_LAST] = {
+		.name = "last",
+		.help = "specify upper bound to establish a range",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_MASK] = {
+		.name = "mask",
+		.help = "specify bit-mask with relevant bits set to one",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +641,7 @@ parse_init(struct context *ctx, const struct token *token,
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
 	ctx->objdata = 0;
 	ctx->object = out;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -632,11 +669,13 @@ parse_vc(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.vc.data = (uint8_t *)out + size;
 		return len;
 	}
 	ctx->objdata = 0;
 	ctx->object = &out->args.vc.attr;
+	ctx->objmask = NULL;
 	switch (ctx->curr) {
 	case GROUP:
 	case PRIORITY:
@@ -652,6 +691,7 @@ parse_vc(struct context *ctx, const struct token *token,
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
 		ctx->object = out->args.vc.pattern;
+		ctx->objmask = NULL;
 		return len;
 	case ACTIONS:
 		out->args.vc.actions =
@@ -660,6 +700,7 @@ parse_vc(struct context *ctx, const struct token *token,
 						out->args.vc.pattern_n),
 					       sizeof(double));
 		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
 		return len;
 	default:
 		if (!token->priv)
@@ -682,6 +723,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.pattern_n;
 		ctx->object = item;
+		ctx->objmask = NULL;
 	} else {
 		const struct parse_action_priv *priv = token->priv;
 		struct rte_flow_action *action =
@@ -698,6 +740,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.actions_n;
 		ctx->object = action;
+		ctx->objmask = NULL;
 	}
 	memset(data, 0, data_size);
 	out->args.vc.data = data;
@@ -705,6 +748,60 @@ parse_vc(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse pattern item parameter type. */
+static int
+parse_vc_spec(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_item *item;
+	uint32_t data_size;
+	int index;
+	int objmask = 0;
+
+	(void)size;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Parse parameter types. */
+	switch (ctx->curr) {
+	case ITEM_PARAM_IS:
+		index = 0;
+		objmask = 1;
+		break;
+	case ITEM_PARAM_SPEC:
+		index = 0;
+		break;
+	case ITEM_PARAM_LAST:
+		index = 1;
+		break;
+	case ITEM_PARAM_MASK:
+		index = 2;
+		break;
+	default:
+		return -1;
+	}
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->args.vc.pattern_n)
+		return -1;
+	item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+	data_size = ctx->objdata / 3; /* spec, last, mask */
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data + (data_size * index);
+	if (objmask) {
+		ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */
+		item->mask = ctx->objmask;
+	} else
+		ctx->objmask = NULL;
+	/* Update relevant item pointer. */
+	*((const void **[]){ &item->spec, &item->last, &item->mask })[index] =
+		ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -727,6 +824,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.destroy.rule =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -737,6 +835,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -762,6 +861,7 @@ parse_flush(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -788,6 +888,7 @@ parse_query(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -849,6 +950,7 @@ parse_list(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.list.group =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -859,6 +961,7 @@ parse_list(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.list.group + out->args.list.group_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -891,6 +994,7 @@ parse_int(struct context *ctx, const struct token *token,
 		return len;
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
+objmask:
 	switch (size) {
 	case sizeof(uint8_t):
 		*(uint8_t *)buf = u;
@@ -907,6 +1011,11 @@ parse_int(struct context *ctx, const struct token *token,
 	default:
 		goto error;
 	}
+	if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+		u = -1;
+		buf = (uint8_t *)ctx->objmask + arg->offset;
+		goto objmask;
+	}
 	return len;
 error:
 	push_args(ctx, arg);
@@ -927,6 +1036,7 @@ parse_port(struct context *ctx, const struct token *token,
 	else {
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		size = sizeof(*out);
 	}
 	ret = parse_int(ctx, token, str, len, out, size);
@@ -1033,6 +1143,7 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->port = 0;
 	ctx->objdata = 0;
 	ctx->object = NULL;
+	ctx->objmask = NULL;
 }
 
 /** Parse a token (cmdline API). */
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 13/25] app/testpmd: add flow query command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Syntax:

 flow query {port_id} {rule_id} {action}

Query a specific action of an existing flow rule.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c      |   3 +
 app/test-pmd/cmdline_flow.c | 121 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 23f4b48..f768b6b 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -831,6 +831,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
+			"flow query {port_id} {rule_id} {action}\n"
+			"    Query an existing flow rule.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 2fd3a5d..8f7ec1d 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -69,11 +69,15 @@ enum index {
 	CREATE,
 	DESTROY,
 	FLUSH,
+	QUERY,
 	LIST,
 
 	/* Destroy arguments. */
 	DESTROY_RULE,
 
+	/* Query arguments. */
+	QUERY_ACTION,
+
 	/* List arguments. */
 	LIST_GROUP,
 
@@ -208,6 +212,10 @@ struct buffer {
 			uint32_t rule_n;
 		} destroy; /**< Destroy arguments. */
 		struct {
+			uint32_t rule;
+			enum rte_flow_action_type action;
+		} query; /**< Query arguments. */
+		struct {
 			uint32_t *group;
 			uint32_t group_n;
 		} list; /**< List arguments. */
@@ -285,6 +293,12 @@ static int parse_destroy(struct context *, const struct token *,
 static int parse_flush(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
+static int parse_query(struct context *, const struct token *,
+		       const char *, unsigned int,
+		       void *, unsigned int);
+static int parse_action(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_list(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -296,6 +310,8 @@ static int parse_port(struct context *, const struct token *,
 		      void *, unsigned int);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_action(struct context *, const struct token *,
+		       unsigned int, char *, unsigned int);
 static int comp_port(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_rule_id(struct context *, const struct token *,
@@ -367,7 +383,8 @@ static const struct token token_list[] = {
 			      CREATE,
 			      DESTROY,
 			      FLUSH,
-			      LIST)),
+			      LIST,
+			      QUERY)),
 		.call = parse_init,
 	},
 	/* Sub-level commands. */
@@ -399,6 +416,17 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_flush,
 	},
+	[QUERY] = {
+		.name = "query",
+		.help = "query an existing flow rule",
+		.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
+			     NEXT_ENTRY(RULE_ID),
+			     NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.query.action),
+			     ARGS_ENTRY(struct buffer, args.query.rule),
+			     ARGS_ENTRY(struct buffer, port)),
+		.call = parse_query,
+	},
 	[LIST] = {
 		.name = "list",
 		.help = "list existing flow rules",
@@ -414,6 +442,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.destroy.rule)),
 		.call = parse_destroy,
 	},
+	/* Query arguments. */
+	[QUERY_ACTION] = {
+		.name = "{action}",
+		.type = "ACTION",
+		.help = "action to query, must be part of the rule",
+		.call = parse_action,
+		.comp = comp_action,
+	},
 	/* List arguments. */
 	[LIST_GROUP] = {
 		.name = "group",
@@ -730,6 +766,67 @@ parse_flush(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for query command. */
+static int
+parse_query(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != QUERY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+	}
+	return len;
+}
+
+/** Parse action names. */
+static int
+parse_action(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	const struct arg *arg = pop_args(ctx);
+	unsigned int i;
+
+	(void)size;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	/* Parse action name. */
+	for (i = 0; next_action[i]; ++i) {
+		const struct parse_action_priv *priv;
+
+		token = &token_list[next_action[i]];
+		if (strncmp(token->name, str, len))
+			continue;
+		priv = token->priv;
+		if (!priv)
+			goto error;
+		if (out)
+			memcpy((uint8_t *)ctx->object + arg->offset,
+			       &priv->type,
+			       arg->size);
+		return len;
+	}
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Parse tokens for list command. */
 static int
 parse_list(struct context *ctx, const struct token *token,
@@ -853,6 +950,24 @@ comp_none(struct context *ctx, const struct token *token,
 	return 0;
 }
 
+/** Complete action names. */
+static int
+comp_action(struct context *ctx, const struct token *token,
+	    unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; next_action[i]; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s",
+					token_list[next_action[i]].name);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Complete available ports. */
 static int
 comp_port(struct context *ctx, const struct token *token,
@@ -1155,6 +1270,10 @@ cmd_flow_parsed(const struct buffer *in)
 	case FLUSH:
 		port_flow_flush(in->port);
 		break;
+	case QUERY:
+		port_flow_query(in->port, in->args.query.rule,
+				in->args.query.action);
+		break;
 	case LIST:
 		port_flow_list(in->port, in->args.list.group_n,
 			       in->args.list.group);
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 12/25] app/testpmd: add flow validate/create commands
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Syntax:

 flow (validate|create) {port_id}
    [group {group_id}] [priority {level}] [ingress] [egress]
    pattern {item} [/ {item} [...]] / end
    actions {action} [/ {action} [...]] / end

Either check the validity of a flow rule or create it. Any number of
pattern items and actions can be provided in any order. Completion is
available for convenience.

This commit only adds support for the most basic item and action types,
namely:

- END: terminates pattern items and actions lists.
- VOID: item/action filler, no operation.
- INVERT: inverted pattern matching, process packets that do not match.
- PASSTHRU: action that leaves packets up for additional processing by
  subsequent flow rules.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c      |  14 ++
 app/test-pmd/cmdline_flow.c | 314 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 327 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 80ddda2..23f4b48 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -811,6 +811,20 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow validate {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [/ {item} [...]] / end"
+			" actions {action} [/ {action} [...]] / end\n"
+			"    Check whether a flow rule can be created.\n\n"
+
+			"flow create {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [/ {item} [...]] / end"
+			" actions {action} [/ {action} [...]] / end\n"
+			"    Create a flow rule.\n\n"
+
 			"flow destroy {port_id} rule {rule_id} [...]\n"
 			"    Destroy specific flow rules.\n\n"
 
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 786b718..2fd3a5d 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -59,11 +59,14 @@ enum index {
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
+	PRIORITY_LEVEL,
 
 	/* Top-level command. */
 	FLOW,
 
 	/* Sub-level commands. */
+	VALIDATE,
+	CREATE,
 	DESTROY,
 	FLUSH,
 	LIST,
@@ -73,6 +76,26 @@ enum index {
 
 	/* List arguments. */
 	LIST_GROUP,
+
+	/* Validate/create arguments. */
+	GROUP,
+	PRIORITY,
+	INGRESS,
+	EGRESS,
+
+	/* Validate/create pattern. */
+	PATTERN,
+	ITEM_NEXT,
+	ITEM_END,
+	ITEM_VOID,
+	ITEM_INVERT,
+
+	/* Validate/create actions. */
+	ACTIONS,
+	ACTION_NEXT,
+	ACTION_END,
+	ACTION_VOID,
+	ACTION_PASSTHRU,
 };
 
 /** Maximum number of subsequent tokens and arguments on the stack. */
@@ -92,6 +115,7 @@ struct context {
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
 	uint16_t port; /**< Current port ID (for completions). */
+	uint32_t objdata; /**< Object-specific data. */
 	void *object; /**< Address of current object for relative offsets. */
 };
 
@@ -109,6 +133,8 @@ struct token {
 	const char *type;
 	/** Help displayed during completion (defaults to token name). */
 	const char *help;
+	/** Private data used by parser functions. */
+	const void *priv;
 	/**
 	 * Lists of subsequent tokens to push on the stack. Each call to the
 	 * parser consumes the last entry of that stack.
@@ -170,6 +196,14 @@ struct buffer {
 	uint16_t port; /**< Affected port ID. */
 	union {
 		struct {
+			struct rte_flow_attr attr;
+			struct rte_flow_item *pattern;
+			struct rte_flow_action *actions;
+			uint32_t pattern_n;
+			uint32_t actions_n;
+			uint8_t *data;
+		} vc; /**< Validate/create arguments. */
+		struct {
 			uint32_t *rule;
 			uint32_t rule_n;
 		} destroy; /**< Destroy arguments. */
@@ -180,6 +214,39 @@ struct buffer {
 	} args; /**< Command arguments. */
 };
 
+/** Private data for pattern items. */
+struct parse_item_priv {
+	enum rte_flow_item_type type; /**< Item type. */
+	uint32_t size; /**< Size of item specification structure. */
+};
+
+#define PRIV_ITEM(t, s) \
+	(&(const struct parse_item_priv){ \
+		.type = RTE_FLOW_ITEM_TYPE_ ## t, \
+		.size = s, \
+	})
+
+/** Private data for actions. */
+struct parse_action_priv {
+	enum rte_flow_action_type type; /**< Action type. */
+	uint32_t size; /**< Size of action configuration structure. */
+};
+
+#define PRIV_ACTION(t, s) \
+	(&(const struct parse_action_priv){ \
+		.type = RTE_FLOW_ACTION_TYPE_ ## t, \
+		.size = s, \
+	})
+
+static const enum index next_vc_attr[] = {
+	GROUP,
+	PRIORITY,
+	INGRESS,
+	EGRESS,
+	PATTERN,
+	ZERO,
+};
+
 static const enum index next_destroy_attr[] = {
 	DESTROY_RULE,
 	END,
@@ -192,9 +259,26 @@ static const enum index next_list_attr[] = {
 	ZERO,
 };
 
+static const enum index next_item[] = {
+	ITEM_END,
+	ITEM_VOID,
+	ITEM_INVERT,
+	ZERO,
+};
+
+static const enum index next_action[] = {
+	ACTION_END,
+	ACTION_VOID,
+	ACTION_PASSTHRU,
+	ZERO,
+};
+
 static int parse_init(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_vc(struct context *, const struct token *,
+		    const char *, unsigned int,
+		    void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -266,18 +350,41 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[PRIORITY_LEVEL] = {
+		.name = "{level}",
+		.type = "PRIORITY",
+		.help = "priority level",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
 		.type = "{command} {port_id} [{arg} [...]]",
 		.help = "manage ingress/egress flow rules",
 		.next = NEXT(NEXT_ENTRY
-			     (DESTROY,
+			     (VALIDATE,
+			      CREATE,
+			      DESTROY,
 			      FLUSH,
 			      LIST)),
 		.call = parse_init,
 	},
 	/* Sub-level commands. */
+	[VALIDATE] = {
+		.name = "validate",
+		.help = "check whether a flow rule can be created",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_vc,
+	},
+	[CREATE] = {
+		.name = "create",
+		.help = "create a flow rule",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_vc,
+	},
 	[DESTROY] = {
 		.name = "destroy",
 		.help = "destroy specific flow rules",
@@ -315,6 +422,98 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
 		.call = parse_list,
 	},
+	/* Validate/create attributes. */
+	[GROUP] = {
+		.name = "group",
+		.help = "specify a group",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_attr, group)),
+		.call = parse_vc,
+	},
+	[PRIORITY] = {
+		.name = "priority",
+		.help = "specify a priority level",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_attr, priority)),
+		.call = parse_vc,
+	},
+	[INGRESS] = {
+		.name = "ingress",
+		.help = "affect rule to ingress",
+		.next = NEXT(next_vc_attr),
+		.call = parse_vc,
+	},
+	[EGRESS] = {
+		.name = "egress",
+		.help = "affect rule to egress",
+		.next = NEXT(next_vc_attr),
+		.call = parse_vc,
+	},
+	/* Validate/create pattern. */
+	[PATTERN] = {
+		.name = "pattern",
+		.help = "submit a list of pattern items",
+		.next = NEXT(next_item),
+		.call = parse_vc,
+	},
+	[ITEM_NEXT] = {
+		.name = "/",
+		.help = "specify next pattern item",
+		.next = NEXT(next_item),
+	},
+	[ITEM_END] = {
+		.name = "end",
+		.help = "end list of pattern items",
+		.priv = PRIV_ITEM(END, 0),
+		.next = NEXT(NEXT_ENTRY(ACTIONS)),
+		.call = parse_vc,
+	},
+	[ITEM_VOID] = {
+		.name = "void",
+		.help = "no-op pattern item",
+		.priv = PRIV_ITEM(VOID, 0),
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+		.call = parse_vc,
+	},
+	[ITEM_INVERT] = {
+		.name = "invert",
+		.help = "perform actions when pattern does not match",
+		.priv = PRIV_ITEM(INVERT, 0),
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+		.call = parse_vc,
+	},
+	/* Validate/create actions. */
+	[ACTIONS] = {
+		.name = "actions",
+		.help = "submit a list of associated actions",
+		.next = NEXT(next_action),
+		.call = parse_vc,
+	},
+	[ACTION_NEXT] = {
+		.name = "/",
+		.help = "specify next action",
+		.next = NEXT(next_action),
+	},
+	[ACTION_END] = {
+		.name = "end",
+		.help = "end list of actions",
+		.priv = PRIV_ACTION(END, 0),
+		.call = parse_vc,
+	},
+	[ACTION_VOID] = {
+		.name = "void",
+		.help = "no-op action",
+		.priv = PRIV_ACTION(VOID, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_PASSTHRU] = {
+		.name = "passthru",
+		.help = "let subsequent rule process matched packets",
+		.priv = PRIV_ACTION(PASSTHRU, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -368,10 +567,108 @@ parse_init(struct context *ctx, const struct token *token,
 	/* Initialize buffer. */
 	memset(out, 0x00, sizeof(*out));
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
+	ctx->objdata = 0;
 	ctx->object = out;
 	return len;
 }
 
+/** Parse tokens for validate/create commands. */
+static int
+parse_vc(struct context *ctx, const struct token *token,
+	 const char *str, unsigned int len,
+	 void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	uint8_t *data;
+	uint32_t data_size;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != VALIDATE && ctx->curr != CREATE)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		out->args.vc.data = (uint8_t *)out + size;
+		return len;
+	}
+	ctx->objdata = 0;
+	ctx->object = &out->args.vc.attr;
+	switch (ctx->curr) {
+	case GROUP:
+	case PRIORITY:
+		return len;
+	case INGRESS:
+		out->args.vc.attr.ingress = 1;
+		return len;
+	case EGRESS:
+		out->args.vc.attr.egress = 1;
+		return len;
+	case PATTERN:
+		out->args.vc.pattern =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		ctx->object = out->args.vc.pattern;
+		return len;
+	case ACTIONS:
+		out->args.vc.actions =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)
+					       (out->args.vc.pattern +
+						out->args.vc.pattern_n),
+					       sizeof(double));
+		ctx->object = out->args.vc.actions;
+		return len;
+	default:
+		if (!token->priv)
+			return -1;
+		break;
+	}
+	if (!out->args.vc.actions) {
+		const struct parse_item_priv *priv = token->priv;
+		struct rte_flow_item *item =
+			out->args.vc.pattern + out->args.vc.pattern_n;
+
+		data_size = priv->size * 3; /* spec, last, mask */
+		data = (void *)RTE_ALIGN_FLOOR((uintptr_t)
+					       (out->args.vc.data - data_size),
+					       sizeof(double));
+		if ((uint8_t *)item + sizeof(*item) > data)
+			return -1;
+		*item = (struct rte_flow_item){
+			.type = priv->type,
+		};
+		++out->args.vc.pattern_n;
+		ctx->object = item;
+	} else {
+		const struct parse_action_priv *priv = token->priv;
+		struct rte_flow_action *action =
+			out->args.vc.actions + out->args.vc.actions_n;
+
+		data_size = priv->size; /* configuration */
+		data = (void *)RTE_ALIGN_FLOOR((uintptr_t)
+					       (out->args.vc.data - data_size),
+					       sizeof(double));
+		if ((uint8_t *)action + sizeof(*action) > data)
+			return -1;
+		*action = (struct rte_flow_action){
+			.type = priv->type,
+		};
+		++out->args.vc.actions_n;
+		ctx->object = action;
+	}
+	memset(data, 0, data_size);
+	out->args.vc.data = data;
+	ctx->objdata = data_size;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -392,6 +689,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		if (sizeof(*out) > size)
 			return -1;
 		out->command = ctx->curr;
+		ctx->objdata = 0;
 		ctx->object = out;
 		out->args.destroy.rule =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
@@ -401,6 +699,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 	if (((uint8_t *)(out->args.destroy.rule + out->args.destroy.rule_n) +
 	     sizeof(*out->args.destroy.rule)) > (uint8_t *)out + size)
 		return -1;
+	ctx->objdata = 0;
 	ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
 	return len;
 }
@@ -425,6 +724,7 @@ parse_flush(struct context *ctx, const struct token *token,
 		if (sizeof(*out) > size)
 			return -1;
 		out->command = ctx->curr;
+		ctx->objdata = 0;
 		ctx->object = out;
 	}
 	return len;
@@ -450,6 +750,7 @@ parse_list(struct context *ctx, const struct token *token,
 		if (sizeof(*out) > size)
 			return -1;
 		out->command = ctx->curr;
+		ctx->objdata = 0;
 		ctx->object = out;
 		out->args.list.group =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
@@ -459,6 +760,7 @@ parse_list(struct context *ctx, const struct token *token,
 	if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
 	     sizeof(*out->args.list.group)) > (uint8_t *)out + size)
 		return -1;
+	ctx->objdata = 0;
 	ctx->object = out->args.list.group + out->args.list.group_n++;
 	return len;
 }
@@ -526,6 +828,7 @@ parse_port(struct context *ctx, const struct token *token,
 	if (buf)
 		out = buf;
 	else {
+		ctx->objdata = 0;
 		ctx->object = out;
 		size = sizeof(*out);
 	}
@@ -613,6 +916,7 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->eol = 0;
 	ctx->last = 0;
 	ctx->port = 0;
+	ctx->objdata = 0;
 	ctx->object = NULL;
 }
 
@@ -836,6 +1140,14 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
 	switch (in->command) {
+	case VALIDATE:
+		port_flow_validate(in->port, &in->args.vc.attr,
+				   in->args.vc.pattern, in->args.vc.actions);
+		break;
+	case CREATE:
+		port_flow_create(in->port, &in->args.vc.attr,
+				 in->args.vc.pattern, in->args.vc.actions);
+		break;
 	case DESTROY:
 		port_flow_destroy(in->port, in->args.destroy.rule_n,
 				  in->args.destroy.rule);
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 11/25] app/testpmd: add flow destroy command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Syntax:

 flow destroy {port_id} rule {rule_id} [...]

Destroy a given set of flow rules associated with a port.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c      |   3 ++
 app/test-pmd/cmdline_flow.c | 106 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 108 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 6e2b289..80ddda2 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow destroy {port_id} rule {rule_id} [...]\n"
+			"    Destroy specific flow rules.\n\n"
+
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 49578eb..786b718 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -56,6 +56,7 @@ enum index {
 	/* Common tokens. */
 	INTEGER,
 	UNSIGNED,
+	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
 
@@ -63,9 +64,13 @@ enum index {
 	FLOW,
 
 	/* Sub-level commands. */
+	DESTROY,
 	FLUSH,
 	LIST,
 
+	/* Destroy arguments. */
+	DESTROY_RULE,
+
 	/* List arguments. */
 	LIST_GROUP,
 };
@@ -165,12 +170,22 @@ struct buffer {
 	uint16_t port; /**< Affected port ID. */
 	union {
 		struct {
+			uint32_t *rule;
+			uint32_t rule_n;
+		} destroy; /**< Destroy arguments. */
+		struct {
 			uint32_t *group;
 			uint32_t group_n;
 		} list; /**< List arguments. */
 	} args; /**< Command arguments. */
 };
 
+static const enum index next_destroy_attr[] = {
+	DESTROY_RULE,
+	END,
+	ZERO,
+};
+
 static const enum index next_list_attr[] = {
 	LIST_GROUP,
 	END,
@@ -180,6 +195,9 @@ static const enum index next_list_attr[] = {
 static int parse_init(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_destroy(struct context *, const struct token *,
+			 const char *, unsigned int,
+			 void *, unsigned int);
 static int parse_flush(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
@@ -196,6 +214,8 @@ static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_port(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_rule_id(struct context *, const struct token *,
+			unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -225,6 +245,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[RULE_ID] = {
+		.name = "{rule id}",
+		.type = "RULE ID",
+		.help = "rule identifier",
+		.call = parse_int,
+		.comp = comp_rule_id,
+	},
 	[PORT_ID] = {
 		.name = "{port_id}",
 		.type = "PORT ID",
@@ -245,11 +272,19 @@ static const struct token token_list[] = {
 		.type = "{command} {port_id} [{arg} [...]]",
 		.help = "manage ingress/egress flow rules",
 		.next = NEXT(NEXT_ENTRY
-			     (FLUSH,
+			     (DESTROY,
+			      FLUSH,
 			      LIST)),
 		.call = parse_init,
 	},
 	/* Sub-level commands. */
+	[DESTROY] = {
+		.name = "destroy",
+		.help = "destroy specific flow rules",
+		.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_destroy,
+	},
 	[FLUSH] = {
 		.name = "flush",
 		.help = "destroy all flow rules",
@@ -264,6 +299,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_list,
 	},
+	/* Destroy arguments. */
+	[DESTROY_RULE] = {
+		.name = "rule",
+		.help = "specify a rule identifier",
+		.next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.destroy.rule)),
+		.call = parse_destroy,
+	},
 	/* List arguments. */
 	[LIST_GROUP] = {
 		.name = "group",
@@ -329,6 +372,39 @@ parse_init(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for destroy command. */
+static int
+parse_destroy(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != DESTROY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->object = out;
+		out->args.destroy.rule =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	if (((uint8_t *)(out->args.destroy.rule + out->args.destroy.rule_n) +
+	     sizeof(*out->args.destroy.rule)) > (uint8_t *)out + size)
+		return -1;
+	ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+	return len;
+}
+
 /** Parse tokens for flush command. */
 static int
 parse_flush(struct context *ctx, const struct token *token,
@@ -494,6 +570,30 @@ comp_port(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete available rule IDs. */
+static int
+comp_rule_id(struct context *ctx, const struct token *token,
+	     unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	struct rte_port *port;
+	struct port_flow *pf;
+
+	(void)token;
+	if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+	    ctx->port == (uint16_t)RTE_PORT_ALL)
+		return -1;
+	port = &ports[ctx->port];
+	for (pf = port->flow_list; pf != NULL; pf = pf->next) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", pf->id);
+		++i;
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -736,6 +836,10 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
 	switch (in->command) {
+	case DESTROY:
+		port_flow_destroy(in->port, in->args.destroy.rule_n,
+				  in->args.destroy.rule);
+		break;
 	case FLUSH:
 		port_flow_flush(in->port);
 		break;
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 10/25] app/testpmd: add flow flush command
From: Adrien Mazarguil @ 2016-12-19 17:49 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Syntax:

 flow flush {port_id}

Destroy all flow rules on a port.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c      |  3 +++
 app/test-pmd/cmdline_flow.c | 43 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 0dc6c63..6e2b289 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow flush {port_id}\n"
+			"    Destroy all flow rules.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index bd3da38..49578eb 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -63,6 +63,7 @@ enum index {
 	FLOW,
 
 	/* Sub-level commands. */
+	FLUSH,
 	LIST,
 
 	/* List arguments. */
@@ -179,6 +180,9 @@ static const enum index next_list_attr[] = {
 static int parse_init(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_flush(struct context *, const struct token *,
+		       const char *, unsigned int,
+		       void *, unsigned int);
 static int parse_list(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -240,10 +244,19 @@ static const struct token token_list[] = {
 		.name = "flow",
 		.type = "{command} {port_id} [{arg} [...]]",
 		.help = "manage ingress/egress flow rules",
-		.next = NEXT(NEXT_ENTRY(LIST)),
+		.next = NEXT(NEXT_ENTRY
+			     (FLUSH,
+			      LIST)),
 		.call = parse_init,
 	},
 	/* Sub-level commands. */
+	[FLUSH] = {
+		.name = "flush",
+		.help = "destroy all flow rules",
+		.next = NEXT(NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_flush,
+	},
 	[LIST] = {
 		.name = "list",
 		.help = "list existing flow rules",
@@ -316,6 +329,31 @@ parse_init(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for flush command. */
+static int
+parse_flush(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != FLUSH)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->object = out;
+	}
+	return len;
+}
+
 /** Parse tokens for list command. */
 static int
 parse_list(struct context *ctx, const struct token *token,
@@ -698,6 +736,9 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
 	switch (in->command) {
+	case FLUSH:
+		port_flow_flush(in->port);
+		break;
 	case LIST:
 		port_flow_list(in->port, in->args.list.group_n,
 			       in->args.list.group);
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 09/25] app/testpmd: add flow list command
From: Adrien Mazarguil @ 2016-12-19 17:48 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Syntax:

 flow list {port_id} [group {group_id}] [...]

List configured flow rules on a port. Output can optionally be limited to a
given set of group identifiers.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c      |   4 ++
 app/test-pmd/cmdline_flow.c | 141 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 145 insertions(+)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index b124412..0dc6c63 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -810,6 +810,10 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"sctp-src-port|sctp-dst-port|sctp-veri-tag|none)"
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
+
+			"flow list {port_id} [group {group_id}] [...]\n"
+			"    List existing flow rules sorted by priority,"
+			" filtered by group identifiers.\n\n"
 		);
 	}
 }
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 81281f9..bd3da38 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -56,9 +56,17 @@ enum index {
 	/* Common tokens. */
 	INTEGER,
 	UNSIGNED,
+	PORT_ID,
+	GROUP_ID,
 
 	/* Top-level command. */
 	FLOW,
+
+	/* Sub-level commands. */
+	LIST,
+
+	/* List arguments. */
+	LIST_GROUP,
 };
 
 /** Maximum number of subsequent tokens and arguments on the stack. */
@@ -77,6 +85,7 @@ struct context {
 	uint32_t reparse:1; /**< Start over from the beginning. */
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
+	uint16_t port; /**< Current port ID (for completions). */
 	void *object; /**< Address of current object for relative offsets. */
 };
 
@@ -153,16 +162,36 @@ struct token {
 struct buffer {
 	enum index command; /**< Flow command. */
 	uint16_t port; /**< Affected port ID. */
+	union {
+		struct {
+			uint32_t *group;
+			uint32_t group_n;
+		} list; /**< List arguments. */
+	} args; /**< Command arguments. */
+};
+
+static const enum index next_list_attr[] = {
+	LIST_GROUP,
+	END,
+	ZERO,
 };
 
 static int parse_init(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_list(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
 static int parse_int(struct context *, const struct token *,
 		     const char *, unsigned int,
 		     void *, unsigned int);
+static int parse_port(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_port(struct context *, const struct token *,
+		     unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -192,13 +221,44 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[PORT_ID] = {
+		.name = "{port_id}",
+		.type = "PORT ID",
+		.help = "port identifier",
+		.call = parse_port,
+		.comp = comp_port,
+	},
+	[GROUP_ID] = {
+		.name = "{group_id}",
+		.type = "GROUP ID",
+		.help = "group identifier",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
 		.type = "{command} {port_id} [{arg} [...]]",
 		.help = "manage ingress/egress flow rules",
+		.next = NEXT(NEXT_ENTRY(LIST)),
 		.call = parse_init,
 	},
+	/* Sub-level commands. */
+	[LIST] = {
+		.name = "list",
+		.help = "list existing flow rules",
+		.next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_list,
+	},
+	/* List arguments. */
+	[LIST_GROUP] = {
+		.name = "group",
+		.help = "specify a group",
+		.next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
+		.call = parse_list,
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -256,6 +316,39 @@ parse_init(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for list command. */
+static int
+parse_list(struct context *ctx, const struct token *token,
+	   const char *str, unsigned int len,
+	   void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != LIST)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->object = out;
+		out->args.list.group =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
+	     sizeof(*out->args.list.group)) > (uint8_t *)out + size)
+		return -1;
+	ctx->object = out->args.list.group + out->args.list.group_n++;
+	return len;
+}
+
 /**
  * Parse signed/unsigned integers 8 to 64-bit long.
  *
@@ -307,6 +400,29 @@ parse_int(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/** Parse port and update context. */
+static int
+parse_port(struct context *ctx, const struct token *token,
+	   const char *str, unsigned int len,
+	   void *buf, unsigned int size)
+{
+	struct buffer *out = &(struct buffer){ .port = 0 };
+	int ret;
+
+	if (buf)
+		out = buf;
+	else {
+		ctx->object = out;
+		size = sizeof(*out);
+	}
+	ret = parse_int(ctx, token, str, len, out, size);
+	if (ret >= 0)
+		ctx->port = out->port;
+	if (!buf)
+		ctx->object = NULL;
+	return ret;
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -320,6 +436,26 @@ comp_none(struct context *ctx, const struct token *token,
 	return 0;
 }
 
+/** Complete available ports. */
+static int
+comp_port(struct context *ctx, const struct token *token,
+	  unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	portid_t p;
+
+	(void)ctx;
+	(void)token;
+	FOREACH_PORT(p, ports) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", p);
+		++i;
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -338,6 +474,7 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->reparse = 0;
 	ctx->eol = 0;
 	ctx->last = 0;
+	ctx->port = 0;
 	ctx->object = NULL;
 }
 
@@ -561,6 +698,10 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
 	switch (in->command) {
+	case LIST:
+		port_flow_list(in->port, in->args.list.group_n,
+			       in->args.list.group);
+		break;
 	default:
 		break;
 	}
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 08/25] app/testpmd: add rte_flow integer support
From: Adrien Mazarguil @ 2016-12-19 17:48 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Parse all integer types and handle conversion to network byte order in a
single function.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline_flow.c | 148 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index a36da06..81281f9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -34,11 +34,14 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
 #include <ctype.h>
 #include <string.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_byteorder.h>
 #include <cmdline_parse.h>
 #include <rte_flow.h>
 
@@ -50,6 +53,10 @@ enum index {
 	ZERO = 0,
 	END,
 
+	/* Common tokens. */
+	INTEGER,
+	UNSIGNED,
+
 	/* Top-level command. */
 	FLOW,
 };
@@ -61,12 +68,24 @@ enum index {
 struct context {
 	/** Stack of subsequent token lists to process. */
 	const enum index *next[CTX_STACK_SIZE];
+	/** Arguments for stacked tokens. */
+	const void *args[CTX_STACK_SIZE];
 	enum index curr; /**< Current token index. */
 	enum index prev; /**< Index of the last token seen. */
 	int next_num; /**< Number of entries in next[]. */
+	int args_num; /**< Number of entries in args[]. */
 	uint32_t reparse:1; /**< Start over from the beginning. */
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
+	void *object; /**< Address of current object for relative offsets. */
+};
+
+/** Token argument. */
+struct arg {
+	uint32_t hton:1; /**< Use network byte ordering. */
+	uint32_t sign:1; /**< Value is signed. */
+	uint32_t offset; /**< Relative offset from ctx->object. */
+	uint32_t size; /**< Field size. */
 };
 
 /** Parser token definition. */
@@ -80,6 +99,8 @@ struct token {
 	 * parser consumes the last entry of that stack.
 	 */
 	const enum index *const *next;
+	/** Arguments stack for subsequent tokens that need them. */
+	const struct arg *const *args;
 	/**
 	 * Token-processing callback, returns -1 in case of error, the
 	 * length of the matched string otherwise. If NULL, attempts to
@@ -112,6 +133,22 @@ struct token {
 /** Static initializer for a NEXT() entry. */
 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
 
+/** Static initializer for the args field. */
+#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
+
+/** Static initializer for ARGS() to target a field. */
+#define ARGS_ENTRY(s, f) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
+/** Static initializer for ARGS() to target a pointer. */
+#define ARGS_ENTRY_PTR(s, f) \
+	(&(const struct arg){ \
+		.size = sizeof(*((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -121,6 +158,11 @@ struct buffer {
 static int parse_init(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_int(struct context *, const struct token *,
+		     const char *, unsigned int,
+		     void *, unsigned int);
+static int comp_none(struct context *, const struct token *,
+		     unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -135,6 +177,21 @@ static const struct token token_list[] = {
 		.type = "RETURN",
 		.help = "command may end here",
 	},
+	/* Common tokens. */
+	[INTEGER] = {
+		.name = "{int}",
+		.type = "INTEGER",
+		.help = "integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
+	[UNSIGNED] = {
+		.name = "{unsigned}",
+		.type = "UNSIGNED",
+		.help = "unsigned integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -144,6 +201,23 @@ static const struct token token_list[] = {
 	},
 };
 
+/** Remove and return last entry from argument stack. */
+static const struct arg *
+pop_args(struct context *ctx)
+{
+	return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
+}
+
+/** Add entry on top of the argument stack. */
+static int
+push_args(struct context *ctx, const struct arg *arg)
+{
+	if (ctx->args_num == CTX_STACK_SIZE)
+		return -1;
+	ctx->args[ctx->args_num++] = arg;
+	return 0;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -178,9 +252,74 @@ parse_init(struct context *ctx, const struct token *token,
 	/* Initialize buffer. */
 	memset(out, 0x00, sizeof(*out));
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
+	ctx->object = out;
 	return len;
 }
 
+/**
+ * Parse signed/unsigned integers 8 to 64-bit long.
+ *
+ * Last argument (ctx->args) is retrieved to determine integer type and
+ * storage location.
+ */
+static int
+parse_int(struct context *ctx, const struct token *token,
+	  const char *str, unsigned int len,
+	  void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	uintmax_t u;
+	char *end;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = arg->sign ?
+		(uintmax_t)strtoimax(str, &end, 0) :
+		strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	size = arg->size;
+	switch (size) {
+	case sizeof(uint8_t):
+		*(uint8_t *)buf = u;
+		break;
+	case sizeof(uint16_t):
+		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
+		break;
+	case sizeof(uint32_t):
+		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
+		break;
+	case sizeof(uint64_t):
+		*(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
+		break;
+	default:
+		goto error;
+	}
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/** No completion. */
+static int
+comp_none(struct context *ctx, const struct token *token,
+	  unsigned int ent, char *buf, unsigned int size)
+{
+	(void)ctx;
+	(void)token;
+	(void)ent;
+	(void)buf;
+	(void)size;
+	return 0;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -195,9 +334,11 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->curr = 0;
 	ctx->prev = 0;
 	ctx->next_num = 0;
+	ctx->args_num = 0;
 	ctx->reparse = 0;
 	ctx->eol = 0;
 	ctx->last = 0;
+	ctx->object = NULL;
 }
 
 /** Parse a token (cmdline API). */
@@ -270,6 +411,13 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
 				return -1;
 			ctx->next[ctx->next_num++] = token->next[i];
 		}
+	/* Push arguments if any. */
+	if (token->args)
+		for (i = 0; token->args[i]; ++i) {
+			if (ctx->args_num == RTE_DIM(ctx->args))
+				return -1;
+			ctx->args[ctx->args_num++] = token->args[i];
+		}
 	return len;
 }
 
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 07/25] app/testpmd: add flow command
From: Adrien Mazarguil @ 2016-12-19 17:48 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Managing generic flow API functions from command line requires the use of
dynamic tokens for convenience as flow rules are not fixed and cannot be
defined statically.

This commit adds specific flexible parser code and object for a new "flow"
command in separate file.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/Makefile       |   1 +
 app/test-pmd/cmdline.c      |   4 +
 app/test-pmd/cmdline_flow.c | 439 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 444 insertions(+)

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index 891b85a..5988c3e 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -47,6 +47,7 @@ CFLAGS += $(WERROR_FLAGS)
 SRCS-y := testpmd.c
 SRCS-y += parameters.c
 SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline.c
+SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline_flow.c
 SRCS-y += config.c
 SRCS-y += iofwd.c
 SRCS-y += macfwd.c
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 5d1c0dd..b124412 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -9567,6 +9567,9 @@ cmdline_parse_inst_t cmd_set_flow_director_flex_payload = {
 	},
 };
 
+/* Generic flow interface command. */
+extern cmdline_parse_inst_t cmd_flow;
+
 /* *** Classification Filters Control *** */
 /* *** Get symmetric hash enable per port *** */
 struct cmd_get_sym_hash_ena_per_port_result {
@@ -11605,6 +11608,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_hash_global_config,
 	(cmdline_parse_inst_t *)&cmd_set_hash_input_set,
 	(cmdline_parse_inst_t *)&cmd_set_fdir_input_set,
+	(cmdline_parse_inst_t *)&cmd_flow,
 	(cmdline_parse_inst_t *)&cmd_mcast_addr,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_all,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_specific,
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
new file mode 100644
index 0000000..a36da06
--- /dev/null
+++ b/app/test-pmd/cmdline_flow.c
@@ -0,0 +1,439 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 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 <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_ethdev.h>
+#include <cmdline_parse.h>
+#include <rte_flow.h>
+
+#include "testpmd.h"
+
+/** Parser token indices. */
+enum index {
+	/* Special tokens. */
+	ZERO = 0,
+	END,
+
+	/* Top-level command. */
+	FLOW,
+};
+
+/** Maximum number of subsequent tokens and arguments on the stack. */
+#define CTX_STACK_SIZE 16
+
+/** Parser context. */
+struct context {
+	/** Stack of subsequent token lists to process. */
+	const enum index *next[CTX_STACK_SIZE];
+	enum index curr; /**< Current token index. */
+	enum index prev; /**< Index of the last token seen. */
+	int next_num; /**< Number of entries in next[]. */
+	uint32_t reparse:1; /**< Start over from the beginning. */
+	uint32_t eol:1; /**< EOL has been detected. */
+	uint32_t last:1; /**< No more arguments. */
+};
+
+/** Parser token definition. */
+struct token {
+	/** Type displayed during completion (defaults to "TOKEN"). */
+	const char *type;
+	/** Help displayed during completion (defaults to token name). */
+	const char *help;
+	/**
+	 * Lists of subsequent tokens to push on the stack. Each call to the
+	 * parser consumes the last entry of that stack.
+	 */
+	const enum index *const *next;
+	/**
+	 * Token-processing callback, returns -1 in case of error, the
+	 * length of the matched string otherwise. If NULL, attempts to
+	 * match the token name.
+	 *
+	 * If buf is not NULL, the result should be stored in it according
+	 * to context. An error is returned if not large enough.
+	 */
+	int (*call)(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len,
+		    void *buf, unsigned int size);
+	/**
+	 * Callback that provides possible values for this token, used for
+	 * completion. Returns -1 in case of error, the number of possible
+	 * values otherwise. If NULL, the token name is used.
+	 *
+	 * If buf is not NULL, entry index ent is written to buf and the
+	 * full length of the entry is returned (same behavior as
+	 * snprintf()).
+	 */
+	int (*comp)(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size);
+	/** Mandatory token name, no default value. */
+	const char *name;
+};
+
+/** Static initializer for the next field. */
+#define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
+
+/** Static initializer for a NEXT() entry. */
+#define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
+
+/** Parser output buffer layout expected by cmd_flow_parsed(). */
+struct buffer {
+	enum index command; /**< Flow command. */
+	uint16_t port; /**< Affected port ID. */
+};
+
+static int parse_init(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
+
+/** Token definitions. */
+static const struct token token_list[] = {
+	/* Special tokens. */
+	[ZERO] = {
+		.name = "ZERO",
+		.help = "null entry, abused as the entry point",
+		.next = NEXT(NEXT_ENTRY(FLOW)),
+	},
+	[END] = {
+		.name = "",
+		.type = "RETURN",
+		.help = "command may end here",
+	},
+	/* Top-level command. */
+	[FLOW] = {
+		.name = "flow",
+		.type = "{command} {port_id} [{arg} [...]]",
+		.help = "manage ingress/egress flow rules",
+		.call = parse_init,
+	},
+};
+
+/** Default parsing function for token name matching. */
+static int
+parse_default(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	(void)ctx;
+	(void)buf;
+	(void)size;
+	if (strncmp(str, token->name, len))
+		return -1;
+	return len;
+}
+
+/** Parse flow command, initialize output buffer for subsequent tokens. */
+static int
+parse_init(struct context *ctx, const struct token *token,
+	   const char *str, unsigned int len,
+	   void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	/* Make sure buffer is large enough. */
+	if (size < sizeof(*out))
+		return -1;
+	/* Initialize buffer. */
+	memset(out, 0x00, sizeof(*out));
+	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
+	return len;
+}
+
+/** Internal context. */
+static struct context cmd_flow_context;
+
+/** Global parser instance (cmdline API). */
+cmdline_parse_inst_t cmd_flow;
+
+/** Initialize context. */
+static void
+cmd_flow_context_init(struct context *ctx)
+{
+	/* A full memset() is not necessary. */
+	ctx->curr = 0;
+	ctx->prev = 0;
+	ctx->next_num = 0;
+	ctx->reparse = 0;
+	ctx->eol = 0;
+	ctx->last = 0;
+}
+
+/** Parse a token (cmdline API). */
+static int
+cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
+	       unsigned int size)
+{
+	struct context *ctx = &cmd_flow_context;
+	const struct token *token;
+	const enum index *list;
+	int len;
+	int i;
+
+	(void)hdr;
+	/* Restart as requested. */
+	if (ctx->reparse)
+		cmd_flow_context_init(ctx);
+	token = &token_list[ctx->curr];
+	/* Check argument length. */
+	ctx->eol = 0;
+	ctx->last = 1;
+	for (len = 0; src[len]; ++len)
+		if (src[len] == '#' || isspace(src[len]))
+			break;
+	if (!len)
+		return -1;
+	/* Last argument and EOL detection. */
+	for (i = len; src[i]; ++i)
+		if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
+			break;
+		else if (!isspace(src[i])) {
+			ctx->last = 0;
+			break;
+		}
+	for (; src[i]; ++i)
+		if (src[i] == '\r' || src[i] == '\n') {
+			ctx->eol = 1;
+			break;
+		}
+	/* Initialize context if necessary. */
+	if (!ctx->next_num) {
+		if (!token->next)
+			return 0;
+		ctx->next[ctx->next_num++] = token->next[0];
+	}
+	/* Process argument through candidates. */
+	ctx->prev = ctx->curr;
+	list = ctx->next[ctx->next_num - 1];
+	for (i = 0; list[i]; ++i) {
+		const struct token *next = &token_list[list[i]];
+		int tmp;
+
+		ctx->curr = list[i];
+		if (next->call)
+			tmp = next->call(ctx, next, src, len, result, size);
+		else
+			tmp = parse_default(ctx, next, src, len, result, size);
+		if (tmp == -1 || tmp != len)
+			continue;
+		token = next;
+		break;
+	}
+	if (!list[i])
+		return -1;
+	--ctx->next_num;
+	/* Push subsequent tokens if any. */
+	if (token->next)
+		for (i = 0; token->next[i]; ++i) {
+			if (ctx->next_num == RTE_DIM(ctx->next))
+				return -1;
+			ctx->next[ctx->next_num++] = token->next[i];
+		}
+	return len;
+}
+
+/** Return number of completion entries (cmdline API). */
+static int
+cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
+{
+	struct context *ctx = &cmd_flow_context;
+	const struct token *token = &token_list[ctx->curr];
+	const enum index *list;
+	int i;
+
+	(void)hdr;
+	/* Tell cmd_flow_parse() that context must be reinitialized. */
+	ctx->reparse = 1;
+	/* Count number of tokens in current list. */
+	if (ctx->next_num)
+		list = ctx->next[ctx->next_num - 1];
+	else
+		list = token->next[0];
+	for (i = 0; list[i]; ++i)
+		;
+	if (!i)
+		return 0;
+	/*
+	 * If there is a single token, use its completion callback, otherwise
+	 * return the number of entries.
+	 */
+	token = &token_list[list[0]];
+	if (i == 1 && token->comp) {
+		/* Save index for cmd_flow_get_help(). */
+		ctx->prev = list[0];
+		return token->comp(ctx, token, 0, NULL, 0);
+	}
+	return i;
+}
+
+/** Return a completion entry (cmdline API). */
+static int
+cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
+			  char *dst, unsigned int size)
+{
+	struct context *ctx = &cmd_flow_context;
+	const struct token *token = &token_list[ctx->curr];
+	const enum index *list;
+	int i;
+
+	(void)hdr;
+	/* Tell cmd_flow_parse() that context must be reinitialized. */
+	ctx->reparse = 1;
+	/* Count number of tokens in current list. */
+	if (ctx->next_num)
+		list = ctx->next[ctx->next_num - 1];
+	else
+		list = token->next[0];
+	for (i = 0; list[i]; ++i)
+		;
+	if (!i)
+		return -1;
+	/* If there is a single token, use its completion callback. */
+	token = &token_list[list[0]];
+	if (i == 1 && token->comp) {
+		/* Save index for cmd_flow_get_help(). */
+		ctx->prev = list[0];
+		return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
+	}
+	/* Otherwise make sure the index is valid and use defaults. */
+	if (index >= i)
+		return -1;
+	token = &token_list[list[index]];
+	snprintf(dst, size, "%s", token->name);
+	/* Save index for cmd_flow_get_help(). */
+	ctx->prev = list[index];
+	return 0;
+}
+
+/** Populate help strings for current token (cmdline API). */
+static int
+cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
+{
+	struct context *ctx = &cmd_flow_context;
+	const struct token *token = &token_list[ctx->prev];
+
+	(void)hdr;
+	/* Tell cmd_flow_parse() that context must be reinitialized. */
+	ctx->reparse = 1;
+	if (!size)
+		return -1;
+	/* Set token type and update global help with details. */
+	snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
+	if (token->help)
+		cmd_flow.help_str = token->help;
+	else
+		cmd_flow.help_str = token->name;
+	return 0;
+}
+
+/** Token definition template (cmdline API). */
+static struct cmdline_token_hdr cmd_flow_token_hdr = {
+	.ops = &(struct cmdline_token_ops){
+		.parse = cmd_flow_parse,
+		.complete_get_nb = cmd_flow_complete_get_nb,
+		.complete_get_elt = cmd_flow_complete_get_elt,
+		.get_help = cmd_flow_get_help,
+	},
+	.offset = 0,
+};
+
+/** Populate the next dynamic token. */
+static void
+cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
+	     cmdline_parse_token_hdr_t *(*hdrs)[])
+{
+	struct context *ctx = &cmd_flow_context;
+
+	/* Always reinitialize context before requesting the first token. */
+	if (!(hdr - *hdrs))
+		cmd_flow_context_init(ctx);
+	/* Return NULL when no more tokens are expected. */
+	if (!ctx->next_num && ctx->curr) {
+		*hdr = NULL;
+		return;
+	}
+	/* Determine if command should end here. */
+	if (ctx->eol && ctx->last && ctx->next_num) {
+		const enum index *list = ctx->next[ctx->next_num - 1];
+		int i;
+
+		for (i = 0; list[i]; ++i) {
+			if (list[i] != END)
+				continue;
+			*hdr = NULL;
+			return;
+		}
+	}
+	*hdr = &cmd_flow_token_hdr;
+}
+
+/** Dispatch parsed buffer to function calls. */
+static void
+cmd_flow_parsed(const struct buffer *in)
+{
+	switch (in->command) {
+	default:
+		break;
+	}
+}
+
+/** Token generator and output processing callback (cmdline API). */
+static void
+cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
+{
+	if (cl == NULL)
+		cmd_flow_tok(arg0, arg2);
+	else
+		cmd_flow_parsed(arg0);
+}
+
+/** Global parser instance (cmdline API). */
+cmdline_parse_inst_t cmd_flow = {
+	.f = cmd_flow_cb,
+	.data = NULL, /**< Unused. */
+	.help_str = NULL, /**< Updated by cmd_flow_get_help(). */
+	.tokens = {
+		NULL,
+	}, /**< Tokens are returned by cmd_flow_tok(). */
+};
-- 
2.1.4

^ permalink raw reply related

* [PATCH v3 06/25] app/testpmd: implement basic support for rte_flow
From: Adrien Mazarguil @ 2016-12-19 17:48 UTC (permalink / raw)
  To: dev
In-Reply-To: <cover.1482168851.git.adrien.mazarguil@6wind.com>

Add basic management functions for the generic flow API (validate, create,
destroy, flush, query and list). Flow rule objects and properties are
arranged in lists associated with each port.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 app/test-pmd/cmdline.c     |   1 +
 app/test-pmd/config.c      | 498 ++++++++++++++++++++++++++++++++++++++++
 app/test-pmd/csumonly.c    |   1 +
 app/test-pmd/flowgen.c     |   1 +
 app/test-pmd/icmpecho.c    |   1 +
 app/test-pmd/ieee1588fwd.c |   1 +
 app/test-pmd/iofwd.c       |   1 +
 app/test-pmd/macfwd.c      |   1 +
 app/test-pmd/macswap.c     |   1 +
 app/test-pmd/parameters.c  |   1 +
 app/test-pmd/rxonly.c      |   1 +
 app/test-pmd/testpmd.c     |   6 +
 app/test-pmd/testpmd.h     |  27 +++
 app/test-pmd/txonly.c      |   1 +
 14 files changed, 542 insertions(+)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d03a592..5d1c0dd 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -75,6 +75,7 @@
 #include <rte_string_fns.h>
 #include <rte_devargs.h>
 #include <rte_eth_ctrl.h>
+#include <rte_flow.h>
 
 #include <cmdline_rdline.h>
 #include <cmdline_parse.h>
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 8cf537d..9716ce7 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -92,6 +92,8 @@
 #include <rte_ethdev.h>
 #include <rte_string_fns.h>
 #include <rte_cycles.h>
+#include <rte_flow.h>
+#include <rte_errno.h>
 
 #include "testpmd.h"
 
@@ -751,6 +753,502 @@ port_mtu_set(portid_t port_id, uint16_t mtu)
 	printf("Set MTU failed. diag=%d\n", diag);
 }
 
+/* Generic flow management functions. */
+
+/** Generate flow_item[] entry. */
+#define MK_FLOW_ITEM(t, s) \
+	[RTE_FLOW_ITEM_TYPE_ ## t] = { \
+		.name = # t, \
+		.size = s, \
+	}
+
+/** Information about known flow pattern items. */
+static const struct {
+	const char *name;
+	size_t size;
+} flow_item[] = {
+	MK_FLOW_ITEM(END, 0),
+	MK_FLOW_ITEM(VOID, 0),
+	MK_FLOW_ITEM(INVERT, 0),
+	MK_FLOW_ITEM(ANY, sizeof(struct rte_flow_item_any)),
+	MK_FLOW_ITEM(PF, 0),
+	MK_FLOW_ITEM(VF, sizeof(struct rte_flow_item_vf)),
+	MK_FLOW_ITEM(PORT, sizeof(struct rte_flow_item_port)),
+	MK_FLOW_ITEM(RAW, sizeof(struct rte_flow_item_raw)), /* +pattern[] */
+	MK_FLOW_ITEM(ETH, sizeof(struct rte_flow_item_eth)),
+	MK_FLOW_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),
+	MK_FLOW_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),
+	MK_FLOW_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),
+	MK_FLOW_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),
+	MK_FLOW_ITEM(UDP, sizeof(struct rte_flow_item_udp)),
+	MK_FLOW_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),
+	MK_FLOW_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),
+	MK_FLOW_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),
+};
+
+/** Compute storage space needed by item specification. */
+static void
+flow_item_spec_size(const struct rte_flow_item *item,
+		    size_t *size, size_t *pad)
+{
+	if (!item->spec)
+		goto empty;
+	switch (item->type) {
+		union {
+			const struct rte_flow_item_raw *raw;
+		} spec;
+
+	case RTE_FLOW_ITEM_TYPE_RAW:
+		spec.raw = item->spec;
+		*size = offsetof(struct rte_flow_item_raw, pattern) +
+			spec.raw->length * sizeof(*spec.raw->pattern);
+		break;
+	default:
+empty:
+		*size = 0;
+		break;
+	}
+	*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;
+}
+
+/** Generate flow_action[] entry. */
+#define MK_FLOW_ACTION(t, s) \
+	[RTE_FLOW_ACTION_TYPE_ ## t] = { \
+		.name = # t, \
+		.size = s, \
+	}
+
+/** Information about known flow actions. */
+static const struct {
+	const char *name;
+	size_t size;
+} flow_action[] = {
+	MK_FLOW_ACTION(END, 0),
+	MK_FLOW_ACTION(VOID, 0),
+	MK_FLOW_ACTION(PASSTHRU, 0),
+	MK_FLOW_ACTION(MARK, sizeof(struct rte_flow_action_mark)),
+	MK_FLOW_ACTION(FLAG, 0),
+	MK_FLOW_ACTION(QUEUE, sizeof(struct rte_flow_action_queue)),
+	MK_FLOW_ACTION(DROP, 0),
+	MK_FLOW_ACTION(COUNT, 0),
+	MK_FLOW_ACTION(DUP, sizeof(struct rte_flow_action_dup)),
+	MK_FLOW_ACTION(RSS, sizeof(struct rte_flow_action_rss)), /* +queue[] */
+	MK_FLOW_ACTION(PF, 0),
+	MK_FLOW_ACTION(VF, sizeof(struct rte_flow_action_vf)),
+};
+
+/** Compute storage space needed by action configuration. */
+static void
+flow_action_conf_size(const struct rte_flow_action *action,
+		      size_t *size, size_t *pad)
+{
+	if (!action->conf)
+		goto empty;
+	switch (action->type) {
+		union {
+			const struct rte_flow_action_rss *rss;
+		} conf;
+
+	case RTE_FLOW_ACTION_TYPE_RSS:
+		conf.rss = action->conf;
+		*size = offsetof(struct rte_flow_action_rss, queue) +
+			conf.rss->num * sizeof(*conf.rss->queue);
+		break;
+	default:
+empty:
+		*size = 0;
+		break;
+	}
+	*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;
+}
+
+/** Generate a port_flow entry from attributes/pattern/actions. */
+static struct port_flow *
+port_flow_new(const struct rte_flow_attr *attr,
+	      const struct rte_flow_item *pattern,
+	      const struct rte_flow_action *actions)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_action *action;
+	struct port_flow *pf = NULL;
+	size_t tmp;
+	size_t pad;
+	size_t off1 = 0;
+	size_t off2 = 0;
+	int err = ENOTSUP;
+
+store:
+	item = pattern;
+	if (pf)
+		pf->pattern = (void *)&pf->data[off1];
+	do {
+		struct rte_flow_item *dst = NULL;
+
+		if ((unsigned int)item->type > RTE_DIM(flow_item) ||
+		    !flow_item[item->type].name)
+			goto notsup;
+		if (pf)
+			dst = memcpy(pf->data + off1, item, sizeof(*item));
+		off1 += sizeof(*item);
+		flow_item_spec_size(item, &tmp, &pad);
+		if (item->spec) {
+			if (pf)
+				dst->spec = memcpy(pf->data + off2,
+						   item->spec, tmp);
+			off2 += tmp + pad;
+		}
+		if (item->last) {
+			if (pf)
+				dst->last = memcpy(pf->data + off2,
+						   item->last, tmp);
+			off2 += tmp + pad;
+		}
+		if (item->mask) {
+			if (pf)
+				dst->mask = memcpy(pf->data + off2,
+						   item->mask, tmp);
+			off2 += tmp + pad;
+		}
+		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
+	} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);
+	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
+	action = actions;
+	if (pf)
+		pf->actions = (void *)&pf->data[off1];
+	do {
+		struct rte_flow_action *dst = NULL;
+
+		if ((unsigned int)action->type > RTE_DIM(flow_action) ||
+		    !flow_action[action->type].name)
+			goto notsup;
+		if (pf)
+			dst = memcpy(pf->data + off1, action, sizeof(*action));
+		off1 += sizeof(*action);
+		flow_action_conf_size(action, &tmp, &pad);
+		if (action->conf) {
+			if (pf)
+				dst->conf = memcpy(pf->data + off2,
+						   action->conf, tmp);
+			off2 += tmp + pad;
+		}
+		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
+	} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);
+	if (pf != NULL)
+		return pf;
+	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
+	tmp = RTE_ALIGN_CEIL(offsetof(struct port_flow, data), sizeof(double));
+	pf = calloc(1, tmp + off1 + off2);
+	if (pf == NULL)
+		err = errno;
+	else {
+		*pf = (const struct port_flow){
+			.size = tmp + off1 + off2,
+			.attr = *attr,
+		};
+		tmp -= offsetof(struct port_flow, data);
+		off2 = tmp + off1;
+		off1 = tmp;
+		goto store;
+	}
+notsup:
+	rte_errno = err;
+	return NULL;
+}
+
+/** Print a message out of a flow error. */
+static int
+port_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];
+	printf("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;
+}
+
+/** Validate flow rule. */
+int
+port_flow_validate(portid_t port_id,
+		   const struct rte_flow_attr *attr,
+		   const struct rte_flow_item *pattern,
+		   const struct rte_flow_action *actions)
+{
+	struct rte_flow_error error;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x11, sizeof(error));
+	if (rte_flow_validate(port_id, attr, pattern, actions, &error))
+		return port_flow_complain(&error);
+	printf("Flow rule validated\n");
+	return 0;
+}
+
+/** Create flow rule. */
+int
+port_flow_create(portid_t port_id,
+		 const struct rte_flow_attr *attr,
+		 const struct rte_flow_item *pattern,
+		 const struct rte_flow_action *actions)
+{
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+	if (!flow)
+		return port_flow_complain(&error);
+	port = &ports[port_id];
+	if (port->flow_list) {
+		if (port->flow_list->id == UINT32_MAX) {
+			printf("Highest rule ID is already assigned, delete"
+			       " it first");
+			rte_flow_destroy(port_id, flow, NULL);
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	} else
+		id = 0;
+	pf = port_flow_new(attr, pattern, actions);
+	if (!pf) {
+		int err = rte_errno;
+
+		printf("Cannot allocate flow: %s\n", rte_strerror(err));
+		rte_flow_destroy(port_id, flow, NULL);
+		return -err;
+	}
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u created\n", pf->id);
+	return 0;
+}
+
+/** Destroy a number of flow rules. */
+int
+port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)
+{
+	struct rte_port *port;
+	struct port_flow **tmp;
+	uint32_t c = 0;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	tmp = &port->flow_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_flow *pf = *tmp;
+
+			if (rule[i] != pf->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+			if (rte_flow_destroy(port_id, pf->flow, &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			printf("Flow rule #%u destroyed\n", pf->id);
+			*tmp = pf->next;
+			free(pf);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
+/** Remove all flow rules. */
+int
+port_flow_flush(portid_t port_id)
+{
+	struct rte_flow_error error;
+	struct rte_port *port;
+	int ret = 0;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x44, sizeof(error));
+	if (rte_flow_flush(port_id, &error)) {
+		ret = port_flow_complain(&error);
+		if (port_id_is_invalid(port_id, DISABLED_WARN) ||
+		    port_id == (portid_t)RTE_PORT_ALL)
+			return ret;
+	}
+	port = &ports[port_id];
+	while (port->flow_list) {
+		struct port_flow *pf = port->flow_list->next;
+
+		free(port->flow_list);
+		port->flow_list = pf;
+	}
+	return ret;
+}
+
+/** Query a flow rule. */
+int
+port_flow_query(portid_t port_id, uint32_t rule,
+		enum rte_flow_action_type action)
+{
+	struct rte_flow_error error;
+	struct rte_port *port;
+	struct port_flow *pf;
+	const char *name;
+	union {
+		struct rte_flow_query_count count;
+	} query;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	for (pf = port->flow_list; pf; pf = pf->next)
+		if (pf->id == rule)
+			break;
+	if (!pf) {
+		printf("Flow rule #%u not found\n", rule);
+		return -ENOENT;
+	}
+	if ((unsigned int)action > RTE_DIM(flow_action) ||
+	    !flow_action[action].name)
+		name = "unknown";
+	else
+		name = flow_action[action].name;
+	switch (action) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		break;
+	default:
+		printf("Cannot query action type %d (%s)\n", action, name);
+		return -ENOTSUP;
+	}
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x55, sizeof(error));
+	memset(&query, 0, sizeof(query));
+	if (rte_flow_query(port_id, pf->flow, action, &query, &error))
+		return port_flow_complain(&error);
+	switch (action) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		printf("%s:\n"
+		       " hits_set: %u\n"
+		       " bytes_set: %u\n"
+		       " hits: %" PRIu64 "\n"
+		       " bytes: %" PRIu64 "\n",
+		       name,
+		       query.count.hits_set,
+		       query.count.bytes_set,
+		       query.count.hits,
+		       query.count.bytes);
+		break;
+	default:
+		printf("Cannot display result for action type %d (%s)\n",
+		       action, name);
+		break;
+	}
+	return 0;
+}
+
+/** List flow rules. */
+void
+port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
+{
+	struct rte_port *port;
+	struct port_flow *pf;
+	struct port_flow *list = NULL;
+	uint32_t i;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return;
+	port = &ports[port_id];
+	if (!port->flow_list)
+		return;
+	/* Sort flows by group, priority and ID. */
+	for (pf = port->flow_list; pf != NULL; pf = pf->next) {
+		struct port_flow **tmp;
+
+		if (n) {
+			/* Filter out unwanted groups. */
+			for (i = 0; i != n; ++i)
+				if (pf->attr.group == group[i])
+					break;
+			if (i == n)
+				continue;
+		}
+		tmp = &list;
+		while (*tmp &&
+		       (pf->attr.group > (*tmp)->attr.group ||
+			(pf->attr.group == (*tmp)->attr.group &&
+			 pf->attr.priority > (*tmp)->attr.priority) ||
+			(pf->attr.group == (*tmp)->attr.group &&
+			 pf->attr.priority == (*tmp)->attr.priority &&
+			 pf->id > (*tmp)->id)))
+			tmp = &(*tmp)->tmp;
+		pf->tmp = *tmp;
+		*tmp = pf;
+	}
+	printf("ID\tGroup\tPrio\tAttr\tRule\n");
+	for (pf = list; pf != NULL; pf = pf->tmp) {
+		const struct rte_flow_item *item = pf->pattern;
+		const struct rte_flow_action *action = pf->actions;
+
+		printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c\t",
+		       pf->id,
+		       pf->attr.group,
+		       pf->attr.priority,
+		       pf->attr.ingress ? 'i' : '-',
+		       pf->attr.egress ? 'e' : '-');
+		while (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			if (item->type != RTE_FLOW_ITEM_TYPE_VOID)
+				printf("%s ", flow_item[item->type].name);
+			++item;
+		}
+		printf("=>");
+		while (action->type != RTE_FLOW_ACTION_TYPE_END) {
+			if (action->type != RTE_FLOW_ACTION_TYPE_VOID)
+				printf(" %s", flow_action[action->type].name);
+			++action;
+		}
+		printf("\n");
+	}
+}
+
 /*
  * RX/TX ring descriptors display functions.
  */
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index 57e6ae2..dd67ebf 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -70,6 +70,7 @@
 #include <rte_sctp.h>
 #include <rte_prefetch.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 #include "testpmd.h"
 
 #define IP_DEFTTL  64   /* from RFC 1340. */
diff --git a/app/test-pmd/flowgen.c b/app/test-pmd/flowgen.c
index b13ff89..13b4f90 100644
--- a/app/test-pmd/flowgen.c
+++ b/app/test-pmd/flowgen.c
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/icmpecho.c b/app/test-pmd/icmpecho.c
index 6a4e750..f25a8f5 100644
--- a/app/test-pmd/icmpecho.c
+++ b/app/test-pmd/icmpecho.c
@@ -61,6 +61,7 @@
 #include <rte_ip.h>
 #include <rte_icmp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/ieee1588fwd.c b/app/test-pmd/ieee1588fwd.c
index 0d3b37a..51170ee 100644
--- a/app/test-pmd/ieee1588fwd.c
+++ b/app/test-pmd/ieee1588fwd.c
@@ -34,6 +34,7 @@
 
 #include <rte_cycles.h>
 #include <rte_ethdev.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/iofwd.c b/app/test-pmd/iofwd.c
index 26936b7..15cb4a2 100644
--- a/app/test-pmd/iofwd.c
+++ b/app/test-pmd/iofwd.c
@@ -64,6 +64,7 @@
 #include <rte_ether.h>
 #include <rte_ethdev.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/macfwd.c b/app/test-pmd/macfwd.c
index 86e01de..d361db1 100644
--- a/app/test-pmd/macfwd.c
+++ b/app/test-pmd/macfwd.c
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/macswap.c b/app/test-pmd/macswap.c
index 36e139f..f996039 100644
--- a/app/test-pmd/macswap.c
+++ b/app/test-pmd/macswap.c
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 08e5a76..28db8cd 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -76,6 +76,7 @@
 #ifdef RTE_LIBRTE_PMD_BOND
 #include <rte_eth_bond.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/rxonly.c b/app/test-pmd/rxonly.c
index fff815c..cf00576 100644
--- a/app/test-pmd/rxonly.c
+++ b/app/test-pmd/rxonly.c
@@ -67,6 +67,7 @@
 #include <rte_ip.h>
 #include <rte_udp.h>
 #include <rte_net.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index a0332c2..bfb2f8e 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -78,6 +78,7 @@
 #ifdef RTE_LIBRTE_PDUMP
 #include <rte_pdump.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
@@ -1545,6 +1546,8 @@ close_port(portid_t pid)
 			continue;
 		}
 
+		if (port->flow_list)
+			port_flow_flush(pi);
 		rte_eth_dev_close(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
@@ -1599,6 +1602,9 @@ detach_port(uint8_t port_id)
 		return;
 	}
 
+	if (ports[port_id].flow_list)
+		port_flow_flush(port_id);
+
 	if (rte_eth_dev_detach(port_id, name))
 		return;
 
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9c1e703..22ce2d6 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -144,6 +144,19 @@ struct fwd_stream {
 /** Insert double VLAN header in forward engine */
 #define TESTPMD_TX_OFFLOAD_INSERT_QINQ       0x0080
 
+/** Descriptor for a single flow. */
+struct port_flow {
+	size_t size; /**< Allocated space including data[]. */
+	struct port_flow *next; /**< Next flow in list. */
+	struct port_flow *tmp; /**< Temporary linking. */
+	uint32_t id; /**< Flow rule ID. */
+	struct rte_flow *flow; /**< Opaque flow object returned by PMD. */
+	struct rte_flow_attr attr; /**< Attributes. */
+	struct rte_flow_item *pattern; /**< Pattern. */
+	struct rte_flow_action *actions; /**< Actions. */
+	uint8_t data[]; /**< Storage for pattern/actions. */
+};
+
 /**
  * The data structure associated with each port.
  */
@@ -177,6 +190,7 @@ struct rte_port {
 	struct ether_addr       *mc_addr_pool; /**< pool of multicast addrs */
 	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 	uint8_t                 slave_flag; /**< bonding slave port */
+	struct port_flow        *flow_list; /**< Associated flows. */
 };
 
 extern portid_t __rte_unused
@@ -504,6 +518,19 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
 			    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);
 void port_reg_display(portid_t port_id, uint32_t reg_off);
 void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);
+int port_flow_validate(portid_t port_id,
+		       const struct rte_flow_attr *attr,
+		       const struct rte_flow_item *pattern,
+		       const struct rte_flow_action *actions);
+int port_flow_create(portid_t port_id,
+		     const struct rte_flow_attr *attr,
+		     const struct rte_flow_item *pattern,
+		     const struct rte_flow_action *actions);
+int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
+int port_flow_flush(portid_t port_id);
+int port_flow_query(portid_t port_id, uint32_t rule,
+		    enum rte_flow_action_type action);
+void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
 void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);
diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c
index 8513a06..e996f35 100644
--- a/app/test-pmd/txonly.c
+++ b/app/test-pmd/txonly.c
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
-- 
2.1.4

^ permalink raw reply related


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