DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 1/3] mbuf: embedding timestamp into the packet
From: Oleg Kuporosov @ 2016-10-19 17:08 UTC (permalink / raw)
  To: Olivier Matz, dev@dpdk.org
In-Reply-To: <efb55b81-339b-6706-12ef-d67bae6e6c5f@6wind.com>

Hello Oliver

Great thanks for your review and con

> > - include uint64_t timestamp field into rte_mbuf with minimal impact to
> >   throughput/latency. Keep it just simple uint64_t in ns (more than 580
> >   years) would be enough for immediate needs while using full
> >   struct timespec with twice bigger size would have much stronger
> >   performance impact as missed cacheline0.
> >
> > - it is possible as there is 6-bytes gap in 1st cacheline (fast path)
> >   and moving uint16_t vlan_tci_outer field to 2nd cacheline.
> >
> > - such move will only impact for pretty rare usable VLAN RX stripping
> >   mode for outer TCI (it used only for one NIC i40e from the whole set and
> >   allows to keep minimal performance impact for RX/TX timestamps.
> 
> This argument is difficult to accept. One can say you are adding a field for a
> pretty rare case used by only one NIC :)

It  may be looks so, but in fact not only for one NIC. Absence of timestamp there 
Required from developers implement its support in out of DPDK data path with
Local DPDK patching which also may lead some penalty in accuracy.
Good example here is open source implementation of tracing library - 
https://github.com/wanduow/libtrace/tree/libtrace4

These folks patched DPDK to have timestamp for every packet (Intel DPDK Patches folder)
And used it in out of band to store and later processing (lib/format_dpdk.c).
That was actually the starting point of my investigations.
Such approach is working for 1GBb case but not for 10-100 cases.

> 
> Honestly, I'm not able to judge whether timestamp is more important than
> vlan_tci_outer. As room is tight in the first cache line, your patch submission
> is the occasion to raise the question: how to decide what should be in the
> first part of the mbuf? There are also some other candidates for moving: m-
> >seqn is only used in librte_reorder and it is not set in the RX part of a driver.

Agree, it is difficult to decide, my thoughts were:
- there is hole (6 bytes) which wasn't marked as reserved for any planned extensions;
-vlan_tci_outer is being used by only one NIC (i40e) so far, 2nd NIC (fm10k) is using it
With comment:
		 * mbuf->vlan_tci_outer is an idle field in fm10k driver,
		 * so it can be selected to store sglort value.
To store some another value under some specific "if".

Also for i40e it is under #ifndef RTE_LIBRTE_I40E_16BYTE_RX_DESC which per doc should be
Enabled for high throughput of small packets. So in default case (disabled) it anyway has
some performance penalty with using 32 bytes descriptor and moving it to 2nd CL would
not hit big additional penalty. Unfortunately I have no such NIC to measure.
Is there any data how often double tagging in being used  in  DPDK applications?

Another my thought was to have at the end of 1st CL enum which may hold
Reserved fields per specific use cases and data widths (uint8, 2xuint4, 4xuint2, 8xbytes).
 
> 
> About the timestamp, it would be valuable to have other opinions, not only
> about the placement of the field in the structure, but also to check that this
> API is also usable for other NICs.

Sure, but I didn't change timesync/timestamping API itself.

> Have you measured the impact of having the timestamp in the second part
> of the mbuf?

Yes, the worst case with prefetching of 2nd CL it is 5.7-5.9 % penalty for combined RX+TX
And expectedly much worse without prefetching.
In the best case it is 0.3..0.5 % for RX only. It can be explained by much harder cache
trashing when TX is "on".
 
> Changing the mbuf structure should happen as rarely as possible, and we
> have to make sure we take the correct decisions. I think we will discuss this at
> dpdk userland 2016.

Oh, yes, please discuss, I would not be able to join. :(

> Apart from that, I wonder if an ol_flag should be added to tell that the
> timestamp field is valid in the mbuf.

Oliver, there is PKT_RX_IEEE1588_TMST flag already.

Best regards,
Oleg.

^ permalink raw reply

* Re: [PATCH 1/3] mbuf: embedding timestamp into the packet
From: Oleg Kuporosov @ 2016-10-19 17:40 UTC (permalink / raw)
  To: Pattan, Reshma, Olivier Matz
  Cc: Richardson, Bruce, Ananyev, Konstantin, dev@dpdk.org
In-Reply-To: <3AEA2BF9852C6F48A459DA490692831F010AFE30@IRSMSX109.ger.corp.intel.com>

Hello Reshma

> I just read this mail chain, to make every one aware again, I am emphasizing
> on the point that I am also adding new "time_arraived" field to mbuf struct as
> part of  below 17.02 Road map item.

Thanks for your work for extending statistics support in DPDK.

"time_arrived" points here for mostly RX path, while more common term used "timestamp"
Allows also using it for TX path as well in the future.

Best regards,
Oleg.

^ permalink raw reply

* Re: [PATCH] vhost: use after free
From: Mcnamara, John @ 2016-10-19 20:21 UTC (permalink / raw)
  To: Chas Williams, dev@dpdk.org; +Cc: Liu, Yuanhan
In-Reply-To: <1476893390-4192-1-git-send-email-3chas3@gmail.com>



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Chas Williams
> Sent: Wednesday, October 19, 2016 5:10 PM
> To: dev@dpdk.org
> Cc: Chas Williams <3chas3@gmail.com>
> Subject: [dpdk-dev] [PATCH] vhost: use after free
> 
> Don't dereference freed memory.
> 
> Fixes: a277c7159876 ("vhost: refactor code structure")

Hi Chas,

Thanks for that. 

Just to note that there was another patch submitted for this a few hours ago by Yuanhan (CCed):

    http://dpdk.org/dev/patchwork/patch/16674/

John

^ permalink raw reply

* Re: [PATCH v6 1/6] ethdev: add Tx preparation
From: Ananyev, Konstantin @ 2016-10-19 22:07 UTC (permalink / raw)
  To: Kulasek, TomaszX, Olivier Matz, dev@dpdk.org; +Cc: thomas.monjalon@6wind.com
In-Reply-To: <3042915272161B4EB253DA4D77EB373A14F37118@IRSMSX102.ger.corp.intel.com>

Hi guys,

> >
> > > +static inline int
> > > +rte_phdr_cksum_fix(struct rte_mbuf *m) {
> > > +	struct ipv4_hdr *ipv4_hdr;
> > > +	struct ipv6_hdr *ipv6_hdr;
> > > +	struct tcp_hdr *tcp_hdr;
> > > +	struct udp_hdr *udp_hdr;
> > > +	uint64_t ol_flags = m->ol_flags;
> > > +	uint64_t inner_l3_offset = m->l2_len;
> > > +
> > > +	if (ol_flags & PKT_TX_OUTER_IP_CKSUM)
> > > +		inner_l3_offset += m->outer_l2_len + m->outer_l3_len;
> > > +
> > > +	if ((ol_flags & PKT_TX_UDP_CKSUM) == PKT_TX_UDP_CKSUM) {
> > > +		if (ol_flags & PKT_TX_IPV4) {
> > > +			ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
> > > +					inner_l3_offset);
> > > +
> > > +			if (ol_flags & PKT_TX_IP_CKSUM)
> > > +				ipv4_hdr->hdr_checksum = 0;
> > > +
> > > +			udp_hdr = (struct udp_hdr *)((char *)ipv4_hdr + m-
> > >l3_len);
> > > +			udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum(ipv4_hdr,
> > ol_flags);
> > > +		} else {
> > > +			ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct ipv6_hdr *,
> > > +					inner_l3_offset);
> > > +			/* non-TSO udp */
> > > +			udp_hdr = rte_pktmbuf_mtod_offset(m, struct udp_hdr *,
> > > +					inner_l3_offset + m->l3_len);
> > > +			udp_hdr->dgram_cksum = rte_ipv6_phdr_cksum(ipv6_hdr,
> > ol_flags);
> > > +		}
> > > +	} else if ((ol_flags & PKT_TX_TCP_CKSUM) ||
> > > +			(ol_flags & PKT_TX_TCP_SEG)) {
> > > +		if (ol_flags & PKT_TX_IPV4) {
> > > +			ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
> > > +					inner_l3_offset);
> > > +
> > > +			if (ol_flags & PKT_TX_IP_CKSUM)
> > > +				ipv4_hdr->hdr_checksum = 0;
> > > +
> > > +			/* non-TSO tcp or TSO */
> > > +			tcp_hdr = (struct tcp_hdr *)((char *)ipv4_hdr + m-
> > >l3_len);
> > > +			tcp_hdr->cksum = rte_ipv4_phdr_cksum(ipv4_hdr, ol_flags);
> > > +		} else {
> > > +			ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct ipv6_hdr *,
> > > +					inner_l3_offset);
> > > +			/* non-TSO tcp or TSO */
> > > +			tcp_hdr = rte_pktmbuf_mtod_offset(m, struct tcp_hdr *,
> > > +					inner_l3_offset + m->l3_len);
> > > +			tcp_hdr->cksum = rte_ipv6_phdr_cksum(ipv6_hdr, ol_flags);
> > > +		}
> > > +	}
> > > +	return 0;
> > > +}
> > > +
> > > +#endif /* _RTE_PKT_H_ */
> > >
> >
> > The function expects that all the network headers are in the first, and
> > that each of them is contiguous.
> >
> 
> Yes, I see...

Yes it does.
I suppose it is a legitimate restriction  (assumptions) for those who'd like to use that function.
But that's a good point and I suppose we need to state it explicitly in the API comments.

> 
> > Also, I had an interesting remark from Stephen [1] on a similar code.
> > If the mbuf is a clone, it will modify the data of the direct mbuf, which
> > should be read-only. Note that it is likely to happen in a TCP stack,
> > because the packet is kept locally in case it has to be retransmitted.
> > Cloning a mbuf is more efficient than duplicating it.
> >
> > I plan to fix it in virtio code by "uncloning" the headers.
> >
> > [1] http://dpdk.org/ml/archives/dev/2016-October/048873.html

This subject is probably a bit off-topic... 
My position on it - it shouldn't be a PMD responsibility to make these kind of checks.
I think it should be upper layer responsibility to provide to the PMD an mbuf
that can be safely used (and in that case modified) by the driver.
Let say if upper layer would like to use packet clone for TCP retransmissions,
It can easily overcome that problem by cloning only data part of the packet,
and putting L2/L3/L4 headers in a separate (head) mbuf and chaining it with
cloned data mbuf.

Konstantin 

^ permalink raw reply

* [PATCH] net/ixgbe: prevent duplicate callback on list
From: E. Scott Daniels @ 2016-10-19 22:23 UTC (permalink / raw)
  To: helin.zhang, bernard.iremonger; +Cc: dev, az5157, E. Scott Daniels
In-Reply-To: <1476915787-28205-1-git-send-email-daniels@research.att.com>

This change prevents the attempt to add a structure which is
already on the callback list. If a struct with matching
parameters is found on the list, then no action is taken. If
a struct with matching parameters is found on the list, then
no action is taken.

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
---
 lib/librte_ether/rte_ethdev.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index 0d9d9c1..fde8112 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -2449,14 +2449,15 @@ rte_eth_dev_callback_register(uint8_t port_id,
 	}
 
 	/* create a new callback. */
-	if (user_cb == NULL)
+	if (user_cb == NULL) {
 		user_cb = rte_zmalloc("INTR_USER_CALLBACK",
 					sizeof(struct rte_eth_dev_callback), 0);
-	if (user_cb != NULL) {
-		user_cb->cb_fn = cb_fn;
-		user_cb->cb_arg = cb_arg;
-		user_cb->event = event;
-		TAILQ_INSERT_TAIL(&(dev->link_intr_cbs), user_cb, next);
+		if (user_cb != NULL) {
+			user_cb->cb_fn = cb_fn;
+			user_cb->cb_arg = cb_arg;
+			user_cb->event = event;
+			TAILQ_INSERT_TAIL(&(dev->link_intr_cbs), user_cb, next);
+		}
 	}
 
 	rte_spinlock_unlock(&rte_eth_dev_cb_lock);
-- 
1.9.1

^ permalink raw reply related

* [PATCH] net/ixgbe: prevent duplicate callback on list
From: E. Scott Daniels @ 2016-10-19 22:23 UTC (permalink / raw)
  To: helin.zhang, bernard.iremonger; +Cc: dev, az5157, E. Scott Daniels

If rte_eth_dev_callback_register() is invoked with parameters
which match a callback struct that is already on the list, an
attempt is made to add that same struct onto the tail of the
list. Adding a struct which is already on the list will have
undesired results.

This is an edge case, but I think it should be corrected;
patch prevents the attempt to add a struct which is already
on the list.

E. Scott Daniels (1):
  net/ixgbe: prevent duplicate callback on list

 lib/librte_ether/rte_ethdev.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

-- 
1.9.1

^ permalink raw reply

* Re: [PATCH] net/ixgbe: fix vlan insert parameter type and its use
From: Lu, Wenzhuo @ 2016-10-20  0:51 UTC (permalink / raw)
  To: Iremonger, Bernard, E. Scott Daniels, Zhang, Helin
  Cc: dev@dpdk.org, az5157@att.com
In-Reply-To: <8CEF83825BEC744B83065625E567D7C21A093CC1@IRSMSX108.ger.corp.intel.com>

Hi Bernard,


> -----Original Message-----
> From: Iremonger, Bernard
> Sent: Wednesday, October 19, 2016 6:56 PM
> To: Lu, Wenzhuo; E. Scott Daniels; Zhang, Helin
> Cc: dev@dpdk.org; az5157@att.com
> Subject: RE: [dpdk-dev] [PATCH] net/ixgbe: fix vlan insert parameter type and its
> use
> 
> Hi Wenzhuo, Scott,
> 
> <snip>
> 
> > > Subject: [dpdk-dev] [PATCH] net/ixgbe: fix vlan insert parameter
> > > type and its use
> > >
> > > The final parameter to rte_pmd_ixgbe_set_vf_vlan_insert is uint8_t
> > > and treated as a binary flag when it needs to be a uint16_t and
> > > treated as a VLAN id.  The data sheet (sect 8.2.3.27.13) describes
> > > the right most
> > > 16 bits as the VLAN id that is to be inserted; the
> > > 16.11  code is accepting only a 1 or 0 thus effectively only
> > > allowing the VLAN id 1 to be inserted (0 disables the insertion setting).
> > >
> > > This patch changes the final parm name to represent the data that is
> > > being accepted (vlan_id), changes the type to permit all valid VLAN
> > > ids, and validates the parameter based on the range of 0 to 4095.
> > > Corresponding changes to prototype and documentation in the .h file.
> > >
> > > Fixes:  49e248223e9f71 ("net/ixgbe: add API for VF management")
> > >
> > > Signed-off-by: E. Scott Daniels <daniels@research.att.com>
> > > ---
> > >  drivers/net/ixgbe/ixgbe_ethdev.c  | 8 ++++----
> > > drivers/net/ixgbe/rte_pmd_ixgbe.h | 9 +++++----
> > >  2 files changed, 9 insertions(+), 8 deletions(-)
> > >
> > > diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c
> > > b/drivers/net/ixgbe/ixgbe_ethdev.c
> > > index 4ca5747..316af73 100644
> > > --- a/drivers/net/ixgbe/ixgbe_ethdev.c
> > > +++ b/drivers/net/ixgbe/ixgbe_ethdev.c
> > > @@ -4727,7 +4727,7 @@ rte_pmd_ixgbe_set_vf_mac_anti_spoof(uint8_t
> > > port, uint16_t vf, uint8_t on)  }
> > >
> > >  int
> > > -rte_pmd_ixgbe_set_vf_vlan_insert(uint8_t port, uint16_t vf, uint8_t
> > > on)
> > > +rte_pmd_ixgbe_set_vf_vlan_insert(uint8_t port, uint16_t vf,
> > > +uint16_t
> > > +vlan_id)
> > >  {
> > >  	struct ixgbe_hw *hw;
> > >  	uint32_t ctrl;
> > > @@ -4742,13 +4742,13 @@ rte_pmd_ixgbe_set_vf_vlan_insert(uint8_t
> > port,
> > > uint16_t vf, uint8_t on)
> > >  	if (vf >= dev_info.max_vfs)
> > >  		return -EINVAL;
> > >
> > > -	if (on > 1)
> > > +	if (vlan_id > 4095)
> > >  		return -EINVAL;
> > >
> > >  	hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> > >  	ctrl = IXGBE_READ_REG(hw, IXGBE_VMVIR(vf));
> > > -	if (on) {
> > > -		ctrl = on;
> > > +	if (vlan_id) {
> > > +		ctrl = vlan_id;
> > I believe this patch is a good idea of an enhancement. But you cannot
> > leverage the existing code like this.
> > The register IXGBE_VMVIR is only for enable/disable. We cannot write
> > the VLAN ID into it.
> > If you want to merge the 2 things, enabling/disabling and setting VLAN
> > ID together. I think we need a totally new implementation. So NACK.
> 
> Reading the 82599 data sheet (sect 8.2.3.27.13), the change from Scott is
> correct.
> The NACK is not correct.
You're right. I made a mistake about the words 'default vlan'. It's not fixed but the value of 'Port VLAN ID'.
So the previous patch is not good, we need to fix it. Thanks for correcting me.

> 
> However changing the API means that where it is called needs to be changed
> too.
> It is called at present from app/testpmd/cmdline.c  line 4731.
> 
> 
> 
> > >  		ctrl |= IXGBE_VMVIR_VLANA_DEFAULT;
> > >  	} else {
> > >  		ctrl = 0;
> > > diff --git a/drivers/net/ixgbe/rte_pmd_ixgbe.h
> > > b/drivers/net/ixgbe/rte_pmd_ixgbe.h
> > > index 2fdf530..c2fb826 100644
> > > --- a/drivers/net/ixgbe/rte_pmd_ixgbe.h
> > > +++ b/drivers/net/ixgbe/rte_pmd_ixgbe.h
> > > @@ -99,16 +99,17 @@ int rte_pmd_ixgbe_set_vf_mac_anti_spoof(uint8_t
> > > port, uint16_t vf, uint8_t on);
> > >   *    The port identifier of the Ethernet device.
> > >   * @param vf
> > >   *    ID specifying VF.
> > > - * @param on
> > > - *    1 - Enable VF's vlan insert.
> > > - *    0 - Disable VF's vlan insert
> > > + * @param vlan_id
> > > + *    0 - Disable VF's vlan insert.
> > > + *    n - Enable; n is inserted as the vlan id.
> > >   *
> > >   * @return
> > >   *   - (0) if successful.
> > >   *   - (-ENODEV) if *port* invalid.
> > >   *   - (-EINVAL) if bad parameter.
> > >   */
> > > -int rte_pmd_ixgbe_set_vf_vlan_insert(uint8_t port, uint16_t vf,
> > > uint8_t on);
> > > +int rte_pmd_ixgbe_set_vf_vlan_insert(uint8_t port, uint16_t vf,
> > > +		uint16_t vlan_id);
> > >
> > >  /**
> > >   * Enable/Disable tx loopback
> > > --
> > > 1.9.1
> 
> Regards,
> 
> Bernard.

^ permalink raw reply

* Re: [PATCH v2 0/2] net/ixgbe: fix VF VLAN insert
From: Lu, Wenzhuo @ 2016-10-20  0:54 UTC (permalink / raw)
  To: Iremonger, Bernard, dev@dpdk.org, daniels@research.att.com,
	az5157@att.com
In-Reply-To: <1476888454-13859-1-git-send-email-bernard.iremonger@intel.com>

Hi,

> -----Original Message-----
> From: Iremonger, Bernard
> Sent: Wednesday, October 19, 2016 10:48 PM
> To: dev@dpdk.org; daniels@research.att.com; Lu, Wenzhuo; az5157@att.com
> Cc: Iremonger, Bernard
> Subject: [PATCH v2 0/2] net/ixgbe: fix VF VLAN insert
> 
> Changes in v2:
> Add testpmd patch.
> Update testpmd for change to rte_pmd_ixgbe_set_vf_vlan_insert function.
> 
> Bernard Iremonger (1):
>   app/test_pmd: change to the VF VLAN insert command
> 
> E. Scott Daniels (1):
>   net/ixgbe: fix VLAN insert parameter type and its use
> 
>  app/test-pmd/cmdline.c                      | 19 +++++++++----------
>  doc/guides/testpmd_app_ug/testpmd_funcs.rst |  2 +-
>  drivers/net/ixgbe/ixgbe_ethdev.c            |  8 ++++----
>  drivers/net/ixgbe/rte_pmd_ixgbe.h           |  9 +++++----
>  4 files changed, 19 insertions(+), 19 deletions(-)
> 
> --
> 2.10.1
Series-Acked-by: Wenzhuo Lu <Wenzhuo.lu@intel.com>

^ permalink raw reply

* Re: [PATCH v5 1/2] net/ixgbe: support multiqueue mode VMDq DCB with SRIOV
From: Lu, Wenzhuo @ 2016-10-20  1:24 UTC (permalink / raw)
  To: Iremonger, Bernard, dev@dpdk.org, Shah, Rahul R
In-Reply-To: <1476872471-23362-2-git-send-email-bernard.iremonger@intel.com>

Hi,

> -----Original Message-----
> From: Iremonger, Bernard
> Sent: Wednesday, October 19, 2016 6:21 PM
> To: dev@dpdk.org; Shah, Rahul R; Lu, Wenzhuo
> Cc: Iremonger, Bernard
> Subject: [PATCH v5 1/2] net/ixgbe: support multiqueue mode VMDq DCB with
> SRIOV
> 
> Modify ixgbe_check_mq_mode function,
> when SRIOV is enabled, enable mq_mode
> ETH_MQ_RX_VMDQ_DCB and ETH_MQ_TX_VMDQ_DCB.
> 
> Modify ixgbe_dcb_tx_hw_config function,
> replace the struct ixgbe_hw parameter with a struct rte_eth_dev parameter and
> handle SRIOV enabled.
> 
> Modify ixgbe_dev_mq_rx_configure function, when SRIOV is enabled, enable
> mq_mode ETH_MQ_RX_VMDQ_DCB.
> 
> Modify ixgbe_configure_dcb function,
> drop check on dev->data->nb_rx_queues.
> 
> Signed-off-by: Rahul R Shah <rahul.r.shah@intel.com>
> Signed-off-by: Bernard Iremonger <bernard.iremonger@intel.com>
Acked-by: Wenzhuo Lu <Wenzhuo.lu@intel.com>

^ permalink raw reply

* Re: [PATCH] net/ixgbe: prevent duplicate callback on list
From: Lu, Wenzhuo @ 2016-10-20  1:39 UTC (permalink / raw)
  To: E. Scott Daniels, Zhang, Helin, Iremonger, Bernard
  Cc: dev@dpdk.org, az5157@att.com
In-Reply-To: <1476915787-28205-2-git-send-email-daniels@research.att.com>

Hi Scott,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of E. Scott Daniels
> Sent: Thursday, October 20, 2016 6:23 AM
> To: Zhang, Helin; Iremonger, Bernard
> Cc: dev@dpdk.org; az5157@att.com; E. Scott Daniels
> Subject: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on list
> 
> This change prevents the attempt to add a structure which is already on the
> callback list. If a struct with matching parameters is found on the list, then no
> action is taken. If a struct with matching parameters is found on the list, then no
> action is taken.
> 
> Signed-off-by: E. Scott Daniels <daniels@research.att.com>
I think the fix itself is good.  But 2 things,
1, normally we don't create a cover letter for a patch set which only has one patch. Just sending the patch itself is enough.
2, ' net/ixgbe: ' in the title is used to describe the component. So the title should be ' lib/ether: prevent duplicate callback on list'.
Thanks.

^ permalink raw reply

* Re: [PATCH] net/ixgbe: prevent duplicate callback on list
From: Scott Daniels @ 2016-10-20  2:11 UTC (permalink / raw)
  To: Lu, Wenzhuo
  Cc: Zhang, Helin, Iremonger, Bernard, dev@dpdk.org, ZELEZNIAK, ALEX
In-Reply-To: <6A0DE07E22DDAD4C9103DF62FEBC09093933D40B@shsmsx102.ccr.corp.intel.com>



On Wed, 19 Oct 2016, Lu, Wenzhuo wrote:

> Hi Scott,
>
>> -----Original Message-----
>> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of E. Scott Daniels
>> Sent: Thursday, October 20, 2016 6:23 AM
>> To: Zhang, Helin; Iremonger, Bernard
>> Cc: dev@dpdk.org; az5157@att.com; E. Scott Daniels
>> Subject: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on list
>>
>> This change prevents the attempt to add a structure which is already on the
>> callback list. If a struct with matching parameters is found on the list, then no
>> action is taken. If a struct with matching parameters is found on the list, then no
>> action is taken.
>>
>> Signed-off-by: E. Scott Daniels <daniels@research.att.com>
> I think the fix itself is good.  But 2 things,
> 1, normally we don't create a cover letter for a patch set which only has one patch. Just sending the patch itself is enough.
> 2, ' net/ixgbe: ' in the title is used to describe the component. So the title should be ' lib/ether: prevent duplicate callback on list'.
> Thanks.

Thanks for the advice.  My mistake on the component.  Is there an easy way 
to fix, or does it make sense just to nack this and I'll submit one with 
the correct component.

Scott

>
>

^ permalink raw reply

* Re: [PATCH] net/ixgbe: prevent duplicate callback on list
From: Lu, Wenzhuo @ 2016-10-20  2:19 UTC (permalink / raw)
  To: Scott Daniels
  Cc: Zhang, Helin, Iremonger, Bernard, dev@dpdk.org, ZELEZNIAK, ALEX
In-Reply-To: <alpine.LSU.2.20.12.1610192207300.24883@localhost>

Hi Scott,

> -----Original Message-----
> From: Scott Daniels [mailto:daniels@research.att.com]
> Sent: Thursday, October 20, 2016 10:11 AM
> To: Lu, Wenzhuo
> Cc: Zhang, Helin; Iremonger, Bernard; dev@dpdk.org; ZELEZNIAK, ALEX
> Subject: RE: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on list
> 
> 
> 
> On Wed, 19 Oct 2016, Lu, Wenzhuo wrote:
> 
> > Hi Scott,
> >
> >> -----Original Message-----
> >> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of E. Scott Daniels
> >> Sent: Thursday, October 20, 2016 6:23 AM
> >> To: Zhang, Helin; Iremonger, Bernard
> >> Cc: dev@dpdk.org; az5157@att.com; E. Scott Daniels
> >> Subject: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on
> >> list
> >>
> >> This change prevents the attempt to add a structure which is already
> >> on the callback list. If a struct with matching parameters is found
> >> on the list, then no action is taken. If a struct with matching
> >> parameters is found on the list, then no action is taken.
> >>
> >> Signed-off-by: E. Scott Daniels <daniels@research.att.com>
> > I think the fix itself is good.  But 2 things, 1, normally we don't
> > create a cover letter for a patch set which only has one patch. Just sending the
> patch itself is enough.
> > 2, ' net/ixgbe: ' in the title is used to describe the component. So the title
> should be ' lib/ether: prevent duplicate callback on list'.
> > Thanks.
> 
> Thanks for the advice.  My mistake on the component.  Is there an easy way to
> fix, or does it make sense just to nack this and I'll submit one with the correct
> component.
No need to NACK it. You can send a V2 with the correct component. And I think you can add my ack in the V2.
Acked-by: Wenzhuo Lu <Wenzhuo.lu@intel.com>

BTW, I forgot to mention that as it's a fix. We always add a Fixes tag in the commit log. You can find the example from other one's mails :)

> 
> Scott
> 
> >
> >

^ permalink raw reply

* Re: [PATCH V2 2/2] virtio: support IOMMU platform
From: Jason Wang @ 2016-10-20  2:29 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: dev, huawei.xie, yuanhan.liu, vkaplans
In-Reply-To: <20161007042444.jbfoimmbuaxh2ped@redhat.com>



On 2016年10月07日 12:24, Michael S. Tsirkin wrote:
> On Wed, Sep 28, 2016 at 04:25:12PM +0800, Jason Wang wrote:
>> Negotiate VIRTIO_F_IOMMU_PLATFORM to have IOMMU support.
>>
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> ---
>> Changes from v1:
>> - remove unnecessary NEED_MAPPING flag
> One thing we probably should do is enable this flag
> with VFIO but not with UIO or VFIO-noiommu.

Sounds good, will try do it in next version.

>> ---
>>   drivers/net/virtio/virtio_ethdev.h | 3 ++-
>>   drivers/net/virtio/virtio_pci.h    | 3 ++-
>>   2 files changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h
>> index 2ecec6e..04a06e2 100644
>> --- a/drivers/net/virtio/virtio_ethdev.h
>> +++ b/drivers/net/virtio/virtio_ethdev.h
>> @@ -63,7 +63,8 @@
>>   	 1u << VIRTIO_NET_F_CTRL_RX	  |	\
>>   	 1u << VIRTIO_NET_F_CTRL_VLAN	  |	\
>>   	 1u << VIRTIO_NET_F_MRG_RXBUF	  |	\
>> -	 1ULL << VIRTIO_F_VERSION_1)
>> +	 1ULL << VIRTIO_F_VERSION_1       |	\
>> +	 1ULL << VIRTIO_F_IOMMU_PLATFORM )
> Space before ) looks kind of ugly.

Will fix this.

>
>>   
>>   /*
>>    * CQ function prototype
>> diff --git a/drivers/net/virtio/virtio_pci.h b/drivers/net/virtio/virtio_pci.h
>> index 3430a39..0aa0015 100644
>> --- a/drivers/net/virtio/virtio_pci.h
>> +++ b/drivers/net/virtio/virtio_pci.h
>> @@ -138,6 +138,7 @@ struct virtnet_ctl;
>>   #define VIRTIO_RING_F_INDIRECT_DESC	28
>>   
>>   #define VIRTIO_F_VERSION_1		32
>> +#define VIRTIO_F_IOMMU_PLATFORM	33
>>   
>>   /*
>>    * Some VirtIO feature bits (currently bits 28 through 31) are
>> @@ -145,7 +146,7 @@ struct virtnet_ctl;
>>    * rest are per-device feature bits.
>>    */
>>   #define VIRTIO_TRANSPORT_F_START 28
>> -#define VIRTIO_TRANSPORT_F_END   32
>> +#define VIRTIO_TRANSPORT_F_END   34
>>   
> This seems unused. Drop it?

Ok.

>
>>   /* The Guest publishes the used index for which it expects an interrupt
>>    * at the end of the avail ring. Host should ignore the avail->flags field. */
>> -- 
>> 2.7.4

^ permalink raw reply

* Re: [PATCH] net/ixgbe: prevent duplicate callback on list
From: Scott Daniels @ 2016-10-20  2:31 UTC (permalink / raw)
  To: Lu, Wenzhuo
  Cc: Zhang, Helin, Iremonger, Bernard, dev@dpdk.org, ZELEZNIAK, ALEX
In-Reply-To: <6A0DE07E22DDAD4C9103DF62FEBC09093933D428@shsmsx102.ccr.corp.intel.com>



On Wed, 19 Oct 2016, Lu, Wenzhuo wrote:

> Hi Scott,
>
>> -----Original Message-----
>> From: Scott Daniels [mailto:daniels@research.att.com]
>> Sent: Thursday, October 20, 2016 10:11 AM
>> To: Lu, Wenzhuo
>> Cc: Zhang, Helin; Iremonger, Bernard; dev@dpdk.org; ZELEZNIAK, ALEX
>> Subject: RE: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on list
>>
>>
>>
>> On Wed, 19 Oct 2016, Lu, Wenzhuo wrote:
>>
>>> Hi Scott,
>>>
>>>> -----Original Message-----
>>>> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of E. Scott Daniels
>>>> Sent: Thursday, October 20, 2016 6:23 AM
>>>> To: Zhang, Helin; Iremonger, Bernard
>>>> Cc: dev@dpdk.org; az5157@att.com; E. Scott Daniels
>>>> Subject: [dpdk-dev] [PATCH] net/ixgbe: prevent duplicate callback on
>>>> list
>>>>
>>>> This change prevents the attempt to add a structure which is already
>>>> on the callback list. If a struct with matching parameters is found
>>>> on the list, then no action is taken. If a struct with matching
>>>> parameters is found on the list, then no action is taken.
>>>>
>>>> Signed-off-by: E. Scott Daniels <daniels@research.att.com>
>>> I think the fix itself is good.  But 2 things, 1, normally we don't
>>> create a cover letter for a patch set which only has one patch. Just sending the
>> patch itself is enough.
>>> 2, ' net/ixgbe: ' in the title is used to describe the component. So the title
>> should be ' lib/ether: prevent duplicate callback on list'.
>>> Thanks.
>>
>> Thanks for the advice.  My mistake on the component.  Is there an easy way to
>> fix, or does it make sense just to nack this and I'll submit one with the correct
>> component.
> No need to NACK it. You can send a V2 with the correct component. And I think you can add my ack in the V2.
> Acked-by: Wenzhuo Lu <Wenzhuo.lu@intel.com>
>
> BTW, I forgot to mention that as it's a fix. We always add a Fixes tag in the commit log. You can find the example from other one's mails :)

Will do.  The patch checker was squaking about the fixes tag and I remvoed 
it :( So, I need to figure out what it didn't like about it and I'll fix 
with a V2 and add your ack. Getting late here tonight, so tomorrow.

Thanks
Scott


>

>>
>> Scott
>>
>>>
>>>
>

^ permalink raw reply

* [PATCH] net/i40e: fix Rx hang when disable LLDP
From: Qi Zhang @ 2016-10-19 19:40 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, Qi Zhang

Disabling LLDP result in the device firmware
not configuring the receive packet buffer, this
possibly causing a receive hang.

Fixes: 4861cde46116 ("i40e: new poll mode driver")

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 5af0e43..08e4049 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1215,11 +1215,6 @@ eth_i40e_dev_uninit(struct rte_eth_dev *dev)
 	dev->rx_pkt_burst = NULL;
 	dev->tx_pkt_burst = NULL;
 
-	/* Disable LLDP */
-	ret = i40e_aq_stop_lldp(hw, true, NULL);
-	if (ret != I40E_SUCCESS) /* Its failure can be ignored */
-		PMD_INIT_LOG(INFO, "Failed to stop lldp");
-
 	/* Clear PXE mode */
 	i40e_clear_pxe_mode(hw);
 
@@ -9385,10 +9380,6 @@ i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb)
 	 * LLDP MIB change event.
 	 */
 	if (sw_dcb == TRUE) {
-		ret = i40e_aq_stop_lldp(hw, TRUE, NULL);
-		if (ret != I40E_SUCCESS)
-			PMD_INIT_LOG(DEBUG, "Failed to stop lldp");
-
 		ret = i40e_init_dcb(hw);
 		/* if sw_dcb, lldp agent is stopped, the return from
 		 * i40e_init_dcb we expect is failure with I40E_AQ_RC_EPERM
-- 
2.7.4

^ permalink raw reply related

* [PATCH] net/i40e: fix fdir configure failed issue in X710
From: Jeff Guo @ 2016-10-20  2:48 UTC (permalink / raw)
  To: helin.zhang, jingjing.wu; +Cc: dev, jia.guo, ferruh.yigit

The correct way to distinguish the behavior of X722 and X710
or other NICs should be using the mac type but not X722 macro.

Fixes: 8c5cb3c11513 (“net/i40e: add packet type translation for X722”)
Signed-off-by: Jeff Guo <jia.guo@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c    | 103 +++++++++++++++++++++-----------------
 drivers/net/i40e/i40e_ethdev.h    |  17 +++----
 drivers/net/i40e/i40e_ethdev_vf.c |  16 ++++--
 drivers/net/i40e/i40e_fdir.c      |  55 ++++++++++++--------
 4 files changed, 112 insertions(+), 79 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 5af0e43..db5f808 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -6164,7 +6164,7 @@ DONE:
 
 /* Configure hash enable flags for RSS */
 uint64_t
-i40e_config_hena(uint64_t flags)
+i40e_config_hena(uint64_t flags, enum i40e_mac_type type)
 {
 	uint64_t hena = 0;
 
@@ -6173,42 +6173,42 @@ i40e_config_hena(uint64_t flags)
 
 	if (flags & ETH_RSS_FRAG_IPV4)
 		hena |= 1ULL << I40E_FILTER_PCTYPE_FRAG_IPV4;
-	if (flags & ETH_RSS_NONFRAG_IPV4_TCP)
-#ifdef X722_SUPPORT
-		hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK);
-#else
-		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
-#endif
-	if (flags & ETH_RSS_NONFRAG_IPV4_UDP)
-#ifdef X722_SUPPORT
-		hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP);
-#else
-		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-#endif
+	if (flags & ETH_RSS_NONFRAG_IPV4_TCP) {
+		if (type == I40E_MAC_X722) {
+			hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK);
+		} else
+			hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+	}
+	if (flags & ETH_RSS_NONFRAG_IPV4_UDP) {
+		if (type == I40E_MAC_X722) {
+			hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP);
+		} else
+			hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+	}
 	if (flags & ETH_RSS_NONFRAG_IPV4_SCTP)
 		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP;
 	if (flags & ETH_RSS_NONFRAG_IPV4_OTHER)
 		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
 	if (flags & ETH_RSS_FRAG_IPV6)
 		hena |= 1ULL << I40E_FILTER_PCTYPE_FRAG_IPV6;
-	if (flags & ETH_RSS_NONFRAG_IPV6_TCP)
-#ifdef X722_SUPPORT
-		hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK);
-#else
-		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
-#endif
-	if (flags & ETH_RSS_NONFRAG_IPV6_UDP)
-#ifdef X722_SUPPORT
-		hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
-			(1ULL << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP);
-#else
-		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
-#endif
+	if (flags & ETH_RSS_NONFRAG_IPV6_TCP) {
+		if (type == I40E_MAC_X722) {
+			hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK);
+		} else
+			hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
+	}
+	if (flags & ETH_RSS_NONFRAG_IPV6_UDP) {
+		if (type == I40E_MAC_X722) {
+			hena |= (1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
+			 (1ULL << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP);
+		} else
+			hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
+	}
 	if (flags & ETH_RSS_NONFRAG_IPV6_SCTP)
 		hena |= 1ULL << I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
 	if (flags & ETH_RSS_NONFRAG_IPV6_OTHER)
@@ -6282,7 +6282,10 @@ i40e_pf_disable_rss(struct i40e_pf *pf)
 
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1))) << 32;
-	hena &= ~I40E_RSS_HENA_ALL;
+	if (hw->mac.type == I40E_MAC_X722)
+		hena &= ~I40E_RSS_HENA_ALL_X722;
+	else
+		hena &= ~I40E_RSS_HENA_ALL;
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (uint32_t)hena);
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (uint32_t)(hena >> 32));
 	I40E_WRITE_FLUSH(hw);
@@ -6369,8 +6372,11 @@ i40e_hw_rss_hash_set(struct i40e_pf *pf, struct rte_eth_rss_conf *rss_conf)
 	rss_hf = rss_conf->rss_hf;
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1))) << 32;
-	hena &= ~I40E_RSS_HENA_ALL;
-	hena |= i40e_config_hena(rss_hf);
+	if (hw->mac.type == I40E_MAC_X722)
+		hena &= ~I40E_RSS_HENA_ALL_X722;
+	else
+		hena &= ~I40E_RSS_HENA_ALL;
+	hena |= i40e_config_hena(rss_hf, hw->mac.type);
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (uint32_t)hena);
 	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (uint32_t)(hena >> 32));
 	I40E_WRITE_FLUSH(hw);
@@ -6389,7 +6395,9 @@ i40e_dev_rss_hash_update(struct rte_eth_dev *dev,
 
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1))) << 32;
-	if (!(hena & I40E_RSS_HENA_ALL)) { /* RSS disabled */
+	if (!(hena & ((hw->mac.type == I40E_MAC_X722)
+		 ? I40E_RSS_HENA_ALL_X722
+		 : I40E_RSS_HENA_ALL))) { /* RSS disabled */
 		if (rss_hf != 0) /* Enable RSS */
 			return -EINVAL;
 		return 0; /* Nothing to do */
@@ -7690,8 +7698,14 @@ i40e_filter_input_set_init(struct i40e_pf *pf)
 
 	for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
 	     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++) {
-		if (!I40E_VALID_PCTYPE(pctype))
-			continue;
+		if (hw->mac.type == I40E_MAC_X722) {
+			if (!I40E_VALID_PCTYPE_X722(pctype))
+				continue;
+		} else {
+			if (!I40E_VALID_PCTYPE(pctype))
+				continue;
+		}
+
 		input_set = i40e_get_default_input_set(pctype);
 
 		num = i40e_generate_inset_mask_reg(input_set, mask_reg,
@@ -7757,14 +7771,13 @@ i40e_hash_filter_inset_select(struct i40e_hw *hw,
 		return -EINVAL;
 	}
 
-#ifdef X722_SUPPORT
-	/* get translated pctype value in fd pctype register */
-	pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(hw,
-		I40E_GLQF_FD_PCTYPES((int)i40e_flowtype_to_pctype(
-		conf->flow_type)));
-#else
-	pctype = i40e_flowtype_to_pctype(conf->flow_type);
-#endif
+	if (hw->mac.type == I40E_MAC_X722) {
+		/* get translated pctype value in fd pctype register */
+		pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(hw,
+			I40E_GLQF_FD_PCTYPES((int)i40e_flowtype_to_pctype(
+			conf->flow_type)));
+	} else
+		pctype = i40e_flowtype_to_pctype(conf->flow_type);
 
 	ret = i40e_parse_input_set(&input_set, pctype, conf->field,
 				   conf->inset_size);
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 57a8ae1..8079c89 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -149,9 +149,8 @@ enum i40e_flxpld_layer_idx {
 	ETH_RSS_NONFRAG_IPV6_OTHER | \
 	ETH_RSS_L2_PAYLOAD)
 
-/* All bits of RSS hash enable */
-#ifdef X722_SUPPORT
-#define I40E_RSS_HENA_ALL ( \
+/* All bits of RSS hash enable for X722*/
+#define I40E_RSS_HENA_ALL_X722 ( \
 	(1ULL << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
 	(1ULL << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
 	(1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
@@ -172,7 +171,8 @@ enum i40e_flxpld_layer_idx {
 	(1ULL << I40E_FILTER_PCTYPE_FCOE_RX) | \
 	(1ULL << I40E_FILTER_PCTYPE_FCOE_OTHER) | \
 	(1ULL << I40E_FILTER_PCTYPE_L2_PAYLOAD))
-#else
+
+/* All bits of RSS hash enable */
 #define I40E_RSS_HENA_ALL ( \
 	(1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
 	(1ULL << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \
@@ -188,7 +188,6 @@ enum i40e_flxpld_layer_idx {
 	(1ULL << I40E_FILTER_PCTYPE_FCOE_RX) | \
 	(1ULL << I40E_FILTER_PCTYPE_FCOE_OTHER) | \
 	(1ULL << I40E_FILTER_PCTYPE_L2_PAYLOAD))
-#endif
 
 #define I40E_MISC_VEC_ID                RTE_INTR_VEC_ZERO_OFFSET
 #define I40E_RX_VEC_START               RTE_INTR_VEC_RXTX_OFFSET
@@ -601,7 +600,7 @@ int i40e_vsi_vlan_pvid_set(struct i40e_vsi *vsi,
 			   struct i40e_vsi_vlan_pvid_info *info);
 int i40e_vsi_config_vlan_stripping(struct i40e_vsi *vsi, bool on);
 int i40e_vsi_config_vlan_filter(struct i40e_vsi *vsi, bool on);
-uint64_t i40e_config_hena(uint64_t flags);
+uint64_t i40e_config_hena(uint64_t flags, enum i40e_mac_type type);
 uint64_t i40e_parse_hena(uint64_t flags);
 enum i40e_status_code i40e_fdir_setup_tx_resources(struct i40e_pf *pf);
 enum i40e_status_code i40e_fdir_setup_rx_resources(struct i40e_pf *pf);
@@ -723,8 +722,7 @@ i40e_calc_itr_interval(int16_t interval)
 	(flow_type) == RTE_ETH_FLOW_NONFRAG_IPV6_OTHER || \
 	(flow_type) == RTE_ETH_FLOW_L2_PAYLOAD)
 
-#ifdef X722_SUPPORT
-#define I40E_VALID_PCTYPE(pctype) \
+#define I40E_VALID_PCTYPE_X722(pctype) \
 	((pctype) == I40E_FILTER_PCTYPE_FRAG_IPV4 || \
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV4_TCP || \
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK || \
@@ -742,7 +740,7 @@ i40e_calc_itr_interval(int16_t interval)
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV6_SCTP || \
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV6_OTHER || \
 	(pctype) == I40E_FILTER_PCTYPE_L2_PAYLOAD)
-#else
+
 #define I40E_VALID_PCTYPE(pctype) \
 	((pctype) == I40E_FILTER_PCTYPE_FRAG_IPV4 || \
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV4_TCP || \
@@ -755,7 +753,6 @@ i40e_calc_itr_interval(int16_t interval)
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV6_SCTP || \
 	(pctype) == I40E_FILTER_PCTYPE_NONF_IPV6_OTHER || \
 	(pctype) == I40E_FILTER_PCTYPE_L2_PAYLOAD)
-#endif
 
 #define I40E_PHY_TYPE_SUPPORT_40G(phy_type) \
 	(((phy_type) & I40E_CAP_PHY_TYPE_40GBASE_KR4) || \
diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
index 8eb68a3..4b835cb 100644
--- a/drivers/net/i40e/i40e_ethdev_vf.c
+++ b/drivers/net/i40e/i40e_ethdev_vf.c
@@ -2548,8 +2548,11 @@ i40evf_hw_rss_hash_set(struct i40e_vf *vf, struct rte_eth_rss_conf *rss_conf)
 	rss_hf = rss_conf->rss_hf;
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(1))) << 32;
-	hena &= ~I40E_RSS_HENA_ALL;
-	hena |= i40e_config_hena(rss_hf);
+	if (hw->mac.type == I40E_MAC_X722)
+		hena &= ~I40E_RSS_HENA_ALL_X722;
+	else
+		hena &= ~I40E_RSS_HENA_ALL;
+	hena |= i40e_config_hena(rss_hf, hw->mac.type);
 	i40e_write_rx_ctl(hw, I40E_VFQF_HENA(0), (uint32_t)hena);
 	i40e_write_rx_ctl(hw, I40E_VFQF_HENA(1), (uint32_t)(hena >> 32));
 	I40EVF_WRITE_FLUSH(hw);
@@ -2565,7 +2568,10 @@ i40evf_disable_rss(struct i40e_vf *vf)
 
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(1))) << 32;
-	hena &= ~I40E_RSS_HENA_ALL;
+	if (hw->mac.type == I40E_MAC_X722)
+		hena &= ~I40E_RSS_HENA_ALL_X722;
+	else
+		hena &= ~I40E_RSS_HENA_ALL;
 	i40e_write_rx_ctl(hw, I40E_VFQF_HENA(0), (uint32_t)hena);
 	i40e_write_rx_ctl(hw, I40E_VFQF_HENA(1), (uint32_t)(hena >> 32));
 	I40EVF_WRITE_FLUSH(hw);
@@ -2626,7 +2632,9 @@ i40evf_dev_rss_hash_update(struct rte_eth_dev *dev,
 
 	hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(0));
 	hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_VFQF_HENA(1))) << 32;
-	if (!(hena & I40E_RSS_HENA_ALL)) { /* RSS disabled */
+	if (!(hena & ((hw->mac.type == I40E_MAC_X722)
+		 ? I40E_RSS_HENA_ALL_X722
+		 : I40E_RSS_HENA_ALL))) { /* RSS disabled */
 		if (rss_hf != 0) /* Enable RSS */
 			return -EINVAL;
 		return 0;
diff --git a/drivers/net/i40e/i40e_fdir.c b/drivers/net/i40e/i40e_fdir.c
index 5a737ab..335bf15 100644
--- a/drivers/net/i40e/i40e_fdir.c
+++ b/drivers/net/i40e/i40e_fdir.c
@@ -353,8 +353,15 @@ i40e_init_flx_pld(struct i40e_pf *pf)
 	/* initialize the masks */
 	for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
 	     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++) {
-		if (!I40E_VALID_PCTYPE((enum i40e_filter_pctype)pctype))
-			continue;
+		if (hw->mac.type == I40E_MAC_X722) {
+			if (!I40E_VALID_PCTYPE_X722(
+				 (enum i40e_filter_pctype)pctype))
+				continue;
+		} else {
+			if (!I40E_VALID_PCTYPE(
+				 (enum i40e_filter_pctype)pctype))
+				continue;
+		}
 		pf->fdir.flex_mask[pctype].word_mask = 0;
 		i40e_write_rx_ctl(hw, I40E_PRTQF_FD_FLXINSET(pctype), 0);
 		for (i = 0; i < I40E_FDIR_BITMASK_NUM_WORD; i++) {
@@ -664,14 +671,16 @@ i40e_fdir_configure(struct rte_eth_dev *dev)
 		i40e_set_flx_pld_cfg(pf, &conf->flex_set[i]);
 	/* configure flex mask*/
 	for (i = 0; i < conf->nb_flexmasks; i++) {
-#ifdef X722_SUPPORT
-		/* get translated pctype value in fd pctype register */
-		pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(hw,
-			I40E_GLQF_FD_PCTYPES((int)i40e_flowtype_to_pctype(
-			conf->flex_mask[i].flow_type)));
-#else
-		pctype = i40e_flowtype_to_pctype(conf->flex_mask[i].flow_type);
-#endif
+		if (hw->mac.type == I40E_MAC_X722) {
+			/* get translated pctype value in fd pctype register */
+			pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(
+				hw, I40E_GLQF_FD_PCTYPES(
+				(int)i40e_flowtype_to_pctype(
+				conf->flex_mask[i].flow_type)));
+		} else
+			pctype = i40e_flowtype_to_pctype(
+				conf->flex_mask[i].flow_type);
+
 		i40e_set_flex_mask_on_pctype(pf, pctype, &conf->flex_mask[i]);
 	}
 
@@ -1053,14 +1062,14 @@ i40e_add_del_fdir_filter(struct rte_eth_dev *dev,
 		return ret;
 	}
 
-#ifdef X722_SUPPORT
-	/* get translated pctype value in fd pctype register */
-	pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(hw,
-		I40E_GLQF_FD_PCTYPES((int)i40e_flowtype_to_pctype(
-		filter->input.flow_type)));
-#else
-	pctype = i40e_flowtype_to_pctype(filter->input.flow_type);
-#endif
+	if (hw->mac.type == I40E_MAC_X722) {
+		/* get translated pctype value in fd pctype register */
+		pctype = (enum i40e_filter_pctype)i40e_read_rx_ctl(
+			hw, I40E_GLQF_FD_PCTYPES(
+			(int)i40e_flowtype_to_pctype(
+			filter->input.flow_type)));
+	} else
+		pctype = i40e_flowtype_to_pctype(filter->input.flow_type);
 
 	ret = i40e_fdir_filter_programming(pf, pctype, filter, add);
 	if (ret < 0) {
@@ -1290,6 +1299,7 @@ i40e_fdir_info_get_flex_mask(struct i40e_pf *pf,
 {
 	struct i40e_fdir_flex_mask *mask;
 	struct rte_eth_fdir_flex_mask *ptr = flex_mask;
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint16_t flow_type;
 	uint8_t i, j;
 	uint16_t off_bytes, mask_tmp;
@@ -1298,8 +1308,13 @@ i40e_fdir_info_get_flex_mask(struct i40e_pf *pf,
 	     i <= I40E_FILTER_PCTYPE_L2_PAYLOAD;
 	     i++) {
 		mask =  &pf->fdir.flex_mask[i];
-		if (!I40E_VALID_PCTYPE((enum i40e_filter_pctype)i))
-			continue;
+		if (hw->mac.type == I40E_MAC_X722) {
+			if (!I40E_VALID_PCTYPE_X722((enum i40e_filter_pctype)i))
+				continue;
+		} else {
+			if (!I40E_VALID_PCTYPE((enum i40e_filter_pctype)i))
+				continue;
+		}
 		flow_type = i40e_pctype_to_flowtype((enum i40e_filter_pctype)i);
 		for (j = 0; j < I40E_FDIR_MAX_FLEXWORD_NUM; j++) {
 			if (mask->word_mask & I40E_FLEX_WORD_MASK(j)) {
-- 
1.9.3

^ permalink raw reply related

* [PATCH] net/i40e: fix the hash filter invalid calculation in X722
From: Jeff Guo @ 2016-10-20  2:48 UTC (permalink / raw)
  To: helin.zhang, jingjing.wu; +Cc: dev, jia.guo, ferruh.yigit
In-Reply-To: <1476582005-110811-2-git-send-email-jia.guo@intel.com>

As X722 extracts IPv4 header to Field Vector different with XL710/X710,
need to corresponding to modify the fields of IPv4 header in input set
to map different default Field Vector Table of different NICs.

Signed-off-by: Jeff Guo <jia.guo@intel.com>
---
v3:
remove the x722 macro
v2:
fix compile error when x722 macro is not defined and simplify
the code to avoid duplication.
---
 drivers/net/i40e/i40e_ethdev.c | 60 +++++++++++++++++++++++++++++++++---------
 1 file changed, 47 insertions(+), 13 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 5af0e43..6ef5ec6 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -211,6 +211,14 @@
 #define I40E_REG_INSET_L3_SRC_IP4                0x0001800000000000ULL
 /* Destination IPv4 address */
 #define I40E_REG_INSET_L3_DST_IP4                0x0000001800000000ULL
+/* Source IPv4 address for X722 */
+#define I40E_X722_REG_INSET_L3_SRC_IP4           0x0006000000000000ULL
+/* Destination IPv4 address for X722 */
+#define I40E_X722_REG_INSET_L3_DST_IP4           0x0000060000000000ULL
+/* IPv4 Protocol for X722 */
+#define I40E_X722_REG_INSET_L3_IP4_PROTO         0x0010000000000000ULL
+/* IPv4 Time to Live for X722 */
+#define I40E_X722_REG_INSET_L3_IP4_TTL           0x0010000000000000ULL
 /* IPv4 Type of Service (TOS) */
 #define I40E_REG_INSET_L3_IP4_TOS                0x0040000000000000ULL
 /* IPv4 Protocol */
@@ -7560,25 +7568,23 @@ i40e_parse_input_set(uint64_t *inset,
  * and vice versa
  */
 static uint64_t
-i40e_translate_input_set_reg(uint64_t input)
+i40e_translate_input_set_reg(enum i40e_mac_type type, uint64_t input)
 {
 	uint64_t val = 0;
 	uint16_t i;
 
-	static const struct {
+	struct inset_map {
 		uint64_t inset;
 		uint64_t inset_reg;
-	} inset_map[] = {
+	};
+
+	static const struct inset_map inset_map_common[] = {
 		{I40E_INSET_DMAC, I40E_REG_INSET_L2_DMAC},
 		{I40E_INSET_SMAC, I40E_REG_INSET_L2_SMAC},
 		{I40E_INSET_VLAN_OUTER, I40E_REG_INSET_L2_OUTER_VLAN},
 		{I40E_INSET_VLAN_INNER, I40E_REG_INSET_L2_INNER_VLAN},
 		{I40E_INSET_LAST_ETHER_TYPE, I40E_REG_INSET_LAST_ETHER_TYPE},
-		{I40E_INSET_IPV4_SRC, I40E_REG_INSET_L3_SRC_IP4},
-		{I40E_INSET_IPV4_DST, I40E_REG_INSET_L3_DST_IP4},
 		{I40E_INSET_IPV4_TOS, I40E_REG_INSET_L3_IP4_TOS},
-		{I40E_INSET_IPV4_PROTO, I40E_REG_INSET_L3_IP4_PROTO},
-		{I40E_INSET_IPV4_TTL, I40E_REG_INSET_L3_IP4_TTL},
 		{I40E_INSET_IPV6_SRC, I40E_REG_INSET_L3_SRC_IP6},
 		{I40E_INSET_IPV6_DST, I40E_REG_INSET_L3_DST_IP6},
 		{I40E_INSET_IPV6_TC, I40E_REG_INSET_L3_IP6_TC},
@@ -7607,13 +7613,40 @@ i40e_translate_input_set_reg(uint64_t input)
 		{I40E_INSET_FLEX_PAYLOAD_W8, I40E_REG_INSET_FLEX_PAYLOAD_WORD8},
 	};
 
+    /* some different registers map in x722*/
+	static const struct inset_map inset_map_diff_x722[] = {
+		{I40E_INSET_IPV4_SRC, I40E_X722_REG_INSET_L3_SRC_IP4},
+		{I40E_INSET_IPV4_DST, I40E_X722_REG_INSET_L3_DST_IP4},
+		{I40E_INSET_IPV4_PROTO, I40E_X722_REG_INSET_L3_IP4_PROTO},
+		{I40E_INSET_IPV4_TTL, I40E_X722_REG_INSET_L3_IP4_TTL},
+	};
+
+	static const struct inset_map inset_map_diff_not_x722[] = {
+		{I40E_INSET_IPV4_SRC, I40E_REG_INSET_L3_SRC_IP4},
+		{I40E_INSET_IPV4_DST, I40E_REG_INSET_L3_DST_IP4},
+		{I40E_INSET_IPV4_PROTO, I40E_REG_INSET_L3_IP4_PROTO},
+		{I40E_INSET_IPV4_TTL, I40E_REG_INSET_L3_IP4_TTL},
+	};
+
 	if (input == 0)
 		return val;
 
 	/* Translate input set to register aware inset */
-	for (i = 0; i < RTE_DIM(inset_map); i++) {
-		if (input & inset_map[i].inset)
-			val |= inset_map[i].inset_reg;
+	if (type == I40E_MAC_X722) {
+		for (i = 0; i < RTE_DIM(inset_map_diff_x722); i++) {
+			if (input & inset_map_diff_x722[i].inset)
+				val |= inset_map_diff_x722[i].inset_reg;
+		}
+	} else {
+		for (i = 0; i < RTE_DIM(inset_map_diff_not_x722); i++) {
+			if (input & inset_map_diff_not_x722[i].inset)
+				val |= inset_map_diff_not_x722[i].inset_reg;
+		}
+	}
+
+	for (i = 0; i < RTE_DIM(inset_map_common); i++) {
+		if (input & inset_map_common[i].inset)
+			val |= inset_map_common[i].inset_reg;
 	}
 
 	return val;
@@ -7698,7 +7731,8 @@ i40e_filter_input_set_init(struct i40e_pf *pf)
 						   I40E_INSET_MASK_NUM_REG);
 		if (num < 0)
 			return;
-		inset_reg = i40e_translate_input_set_reg(input_set);
+		inset_reg = i40e_translate_input_set_reg(hw->mac.type,
+					input_set);
 
 		i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 0),
 				      (uint32_t)(inset_reg & UINT32_MAX));
@@ -7789,7 +7823,7 @@ i40e_hash_filter_inset_select(struct i40e_hw *hw,
 	if (num < 0)
 		return -EINVAL;
 
-	inset_reg |= i40e_translate_input_set_reg(input_set);
+	inset_reg |= i40e_translate_input_set_reg(hw->mac.type, input_set);
 
 	i40e_check_write_reg(hw, I40E_GLQF_HASH_INSET(0, pctype),
 			      (uint32_t)(inset_reg & UINT32_MAX));
@@ -7867,7 +7901,7 @@ i40e_fdir_filter_inset_select(struct i40e_pf *pf,
 	if (num < 0)
 		return -EINVAL;
 
-	inset_reg |= i40e_translate_input_set_reg(input_set);
+	inset_reg |= i40e_translate_input_set_reg(hw->mac.type, input_set);
 
 	i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 0),
 			      (uint32_t)(inset_reg & UINT32_MAX));
-- 
1.9.3

^ permalink raw reply related

* [PATCH v2] net/i40e: fix Rx hang when disable LLDP
From: Qi Zhang @ 2016-10-19 20:40 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, Qi Zhang

Remove stopping LLDP as a workaround for a known 
errata which can cause Rx hang.

Fixes: 4861cde46116 ("i40e: new poll mode driver")

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---

v2:
- more correct commit log.

 drivers/net/i40e/i40e_ethdev.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 5af0e43..08e4049 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1215,11 +1215,6 @@ eth_i40e_dev_uninit(struct rte_eth_dev *dev)
 	dev->rx_pkt_burst = NULL;
 	dev->tx_pkt_burst = NULL;
 
-	/* Disable LLDP */
-	ret = i40e_aq_stop_lldp(hw, true, NULL);
-	if (ret != I40E_SUCCESS) /* Its failure can be ignored */
-		PMD_INIT_LOG(INFO, "Failed to stop lldp");
-
 	/* Clear PXE mode */
 	i40e_clear_pxe_mode(hw);
 
@@ -9385,10 +9380,6 @@ i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb)
 	 * LLDP MIB change event.
 	 */
 	if (sw_dcb == TRUE) {
-		ret = i40e_aq_stop_lldp(hw, TRUE, NULL);
-		if (ret != I40E_SUCCESS)
-			PMD_INIT_LOG(DEBUG, "Failed to stop lldp");
-
 		ret = i40e_init_dcb(hw);
 		/* if sw_dcb, lldp agent is stopped, the return from
 		 * i40e_init_dcb we expect is failure with I40E_AQ_RC_EPERM
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH V2 2/2] virtio: support IOMMU platform
From: Jason Wang @ 2016-10-20  4:07 UTC (permalink / raw)
  To: Yuanhan Liu, Michael S. Tsirkin; +Cc: dev, huawei.xie, vkaplans
In-Reply-To: <20161011041445.GK1597@yliu-dev.sh.intel.com>



On 2016年10月11日 12:14, Yuanhan Liu wrote:
> On Fri, Oct 07, 2016 at 07:24:44AM +0300, Michael S. Tsirkin wrote:
>> On Wed, Sep 28, 2016 at 04:25:12PM +0800, Jason Wang wrote:
>>> Negotiate VIRTIO_F_IOMMU_PLATFORM to have IOMMU support.
>>>
>>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>>> ---
>>> Changes from v1:
>>> - remove unnecessary NEED_MAPPING flag
>> One thing we probably should do is enable this flag
>> with VFIO but not with UIO or VFIO-noiommu.
> Good suggestion. I think we could do that in another patch.

Yes.

>
>>> ---
>>>   drivers/net/virtio/virtio_ethdev.h | 3 ++-
>>>   drivers/net/virtio/virtio_pci.h    | 3 ++-
>>>   2 files changed, 4 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h
>>> index 2ecec6e..04a06e2 100644
>>> --- a/drivers/net/virtio/virtio_ethdev.h
>>> +++ b/drivers/net/virtio/virtio_ethdev.h
>>> @@ -63,7 +63,8 @@
>>>   	 1u << VIRTIO_NET_F_CTRL_RX	  |	\
>>>   	 1u << VIRTIO_NET_F_CTRL_VLAN	  |	\
>>>   	 1u << VIRTIO_NET_F_MRG_RXBUF	  |	\
>>> -	 1ULL << VIRTIO_F_VERSION_1)
>>> +	 1ULL << VIRTIO_F_VERSION_1       |	\
>>> +	 1ULL << VIRTIO_F_IOMMU_PLATFORM )
>> Space before ) looks kind of ugly.
> Yes, a bit. I will remove it while apply.
>
>>>   #define VIRTIO_TRANSPORT_F_START 28
>>> -#define VIRTIO_TRANSPORT_F_END   32
>>> +#define VIRTIO_TRANSPORT_F_END   34
>>>   
>> This seems unused. Drop it?
> Indeed. I will submit a patch to remove both (_START and _END).
>
> 	--yliu

Thanks

^ permalink raw reply

* Re: [PATCH 1/3] mbuf: embedding timestamp into the packet
From: Oleg Kuporosov @ 2016-10-20  8:03 UTC (permalink / raw)
  To: Ananyev, Konstantin, Olivier Matz, dev@dpdk.org
In-Reply-To: <2601191342CEEE43887BDE71AB9772583F0C3319@irsmsx105.ger.corp.intel.com>

Hello Konstantin,

> 
> My vote also would be to have timestamp in the second cache line.
> About moving seqn to the 2-nd cache line too - that's probably a fair point.

It may impact throughput till ~6% for applications required such embedded 
Timestamps.
 
> About the rest of the patch:
> Do you really need to put that code into the PMDs itself?
> Can't the same result be achieved by using RX callbacks?
> Again that approach would work with any PMD and there would be no need
> to modify PMD code itself.

Correct, the approach with using callbacs (rte_eth_timesync_read_[r|t]x_timestamp())
Has also some Cons for this use case:
- FSI needs the most accurate stamping as possible by reasons were described in
Cover letter
- callback will be called from user app and so you have to take into account 
Difference between time when packet was released by NIC and callback call
- such difference is not easy to estimate correctly due to dependency on CPU type,
Its frequency and current load conditions
- even estimated it would be hard without additional performance penalty to align
Packet with timestamp, taking account that call may actually placed from
Different thread or even process.

It looks the least impacting and correct way is to have timestamp in rte_mbuf and fill
It in Rx burst procedure.

> Another thing, that I am in doubt why to use system time?
> Wouldn't raw HW TSC value (rte_rdtsc()) do here?

System time is being used for periodic clock synchronization between wall
clock and NIC to estimate NIC clock deviation. It is in assumption the system itself is
synchronized by PTP from master clock. It is run on context of control thread.

Thanks,
Oleg.

^ permalink raw reply

* [PATCH] [RFC] Elastic Flow Distributor
From: Pablo de Lara @ 2016-10-20  8:15 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara

The following RFC shows the functionality and usage overview of
the new Elastic Flow Distributor (EFD) library.
Library code is included in the RFC (implemention in progress, API complete),
with a sample application to demonstrate the usage of the library,
based on the existing server/client sample app.

A PDF document has been uploaded with the following and more information
about the library, including color images (not ascii) to improve
readability, in the following link:

https://github.com/pablodelara/perfect_hash_flow_distributor/blob/master/EFD_description.pdf

Introduction
============

In Data Centers today clustering and scheduling of distributed workloads is a
very common task. Many workload requires a deterministic partitioning of a flat
key space among a cluster of machines. When a packet enters the cluster, the
ingress node will direct the packet to its handling node.  For example,
datacenters with disaggregated storage uses storage metadata table to forward
I/O requests to correct backend storage cluster, stateful packet inspection will
use match incoming flows to signatures in flow tables to send incoming packets
to their intended deep packet inspection (DPI) devices, and so on.

EFD is a distributor library that uses perfect hashing to determine a
target/value for a given incoming flow key. It has the following advantages:
first, because it uses perfect hashing it does not store the key itself and
hence lookup performance is not dependent on the key size. Second, the
target/value can be any arbitrary value hence the system designer and/or
operator can better optimize service rates and inter-cluster network traffic
locating.  Third, since the storage requirement is much smaller than a
hash-based flow table (i.e. better fit for CPU cache), EFD can scale to millions
of flow keys. Finally, with current optimized library implementation performance
is fully scalable with number of CPU cores.

Overview
========

The basic idea of EFD is when a given key is to be inserted, a family of hash
functions is searched until the correct hash function that maps the input key to
the correct value is found. However, rather than explicitly storing all keys and
their associated values, EFD stores only indices of hash functions that map keys
to values, and thereby consumes much less space than conventional  flow-based
tables. The lookup operation is very simple, similar to computational-based
scheme, given an input key the lookup operation is reduced to hashing that key
with the correct hash function.

Intuitively, finding a hash function that maps each of a large number (millions)
of input keys to the correct output value is effectively impossible, as a result
EFD, breaks the problem into smaller pieces (divide and conquer). EFD divides
the entire input key set into many small groups. Each group consists of
approximately 20-28 keys (a configurable parameter for the library), then, for
each small group, a brute force search to find a hash function that produces the
correct outputs for each key in the group.
It should be mentioned that since in the online lookup table for EFD doesn’t
store the key itself, the size of the EFD table is independent of the key size
and hence EFD lookup performance which is almost constant irrespective of the
length of the key which is a highly desirable feature especially for longer
keys.

API
===

The EFD library API is created with a very similar semantics of a hash-index or
a flow table, the application creates an EFD table for a given maximum number of
flows, a function is called to insert a flow key with a specific target value,
and another function is used to retrieve target values for a given individual
flow key or a bulk of keys.

EFD Table Create
----------------

The function rte_efd_create("flow table", num_flows, online_socket_bitmask,
offline_socket_bitmask) is used to create and return a pointer to an EFD table
that is sized to hold up to num_flows key. The online version of the EFD table
(the one that does not store the keys and is used for lookups) will be allocated
and created in the last level cache (LLC) of the socket defined by the
online_socket_bitmask, while the offline EFD table (the one that stores the keys
and is used for key inserts and for computing the perfect hashing) is allocated
and created in the LLC of the socket defined by offline_socket_bitmask. It
should be noted, that For highest performance the socket id should match that
where the thread is running, i.e. the online EFD lookup table should be created
on the same socket as where the lookup thread is running.

EFD Insert and Update
---------------------

The EFD function to insert a key or update a key to a new value is
rte_efd_update(struct rte_efd_table *table, unsigned socket_id, const efd_key_t
*key, efd_value_t value). This function will update an existing key to a new
value (target) if the key has already been inserted before, or will insert the
<key,value> pair if this key has not been inserted before. It will return 0 upon
success. It will return EFD_UPDATE_WARN_GROUP_FULL (1) if the operation is
insert, and the last available space in the key's group was just used. It will
return EFD_UPDATE_FAILED (2) when the insertion or update has filed (either it
failed to find a suitable perfect hash or the group was full). The function will
return EFD_UPDATE_NO_CHANGE (3) if there is no change to the EFD table (i.e,
same value already exists).

EFD Lookup
----------

To lookup a certain key in an EFD table, the function rte_efd_lookup(const
struct rte_efd_table *table, unsigned socket_id, const efd_key_t *key) which is
used to return the value associated with single key. As previously mentioned, if
the key has been inserted the correct value inserted is returned, if the key has
not been inserted before a ‘random’ value (based on hashing of the key) is
returned. For better performance and to decrease the overhead of function calls
per key, it is always recommended to use a bulk lookup function (simultaneous
lookup of multiple keys) instead of a single key lookup function.

rte_efd_lookup_bulk(const struct rte_efd_table *table, unsigned socket_id, int
num_keys, const efd_key_t *const *key_list, efd_value_t *value_list) is the bulk
lookup function, that looks up num_keys simultaneously stored in the key_list
and the corresponding return values will be returned in the value_list.

EFD Delete
----------

To delete a certain key in an EFD table, the function rte_efd_delete(struct
rte_efd_table *table, unsigned socket_id, const efd_key_t *key,  efd_value_t
*prev_value) can be used. The function returns zero upon success when the key
has been found and deleted. Socket_id is the one to use to lookup the existing
value and ideally it is the caller's socket id. The previous value associated
with this key will be returned in the prev_value argument.

Example of EFD Library usage
============================

EFD can be used along the data path of many network functions and middle boxes.
As previously mentioned, It can used as an index table for <key,value> pair,
meta-data for objects, flow-level load balancer, etc. The following figure shows
an example of using EFD as a flow-level load balancer, where flows are received
at a front end server before being forwarded to the target backend server for
processing. The system designer would co-locate flows together (needs to
deterministically control the target backend server for each flow) in order to
minimize cross server together (for example, flows requesting certain webpage
objects are co-located together, to minimize forwarding of common objects across
servers).

                                                        Local Table for specific flows serviced at Node 1

                                                         +---------------------------------------------+
                                                         |  Key 1 | Action 1 | ... |  Key x | Action x |
                                                         |        |          |     |        |          |
                                                         +---------------------------------------------+
                                     +-----------+   +--->        |          |     |        |          |
                                     |  Backend  |   |   |        |          |     |        |          |
                                     |   Node 1  +---+   +---------------------------------------------+
                             +------->           |       |  Key y | Action y | ... |  Key N | Action N |
                             |       +-----------+       |        |          |     |        |          |
        +-----------------+  |                           +---------------------------------------------+
        |                 |  |       +-----------+
        | Frontend Server |  |       |  Backend  |
   +----+         or      +---------->   Node 2  |
   |    |   Load Balancer |  |       |           |
   |    |                 |  |       +-----------+       Local Table for specific flows serviced at Node X
   |    +-----------------+  |
   |                         |       +-----------+       +---------------------------------------------+
   |                         |       |  Backend  |       |  Key 1 | Action 1 | ... |  Key x | Action x |
   |                         +-------+   Node X  +----+  |        |          |     |        |          |
   |                                 |           |    |  +---------------------------------------------+
   |                                 +-----------+    +-->        |          |     |        |          |
   |   +-----------------------+                         |        |          |     |        |          |
   |   |          |            |                         +---------------------------------------------+
   |   | Group id |  Hash index|                         |  Key y | Action y | ... | Key N  | Action N |
   +--->          |            |                         |        |          |     |        |          |
       +-----------------------+                         +---------------------------------------------+
       |          |            |
       +-----------------------+
       |          |            |
       +-----------------------+
       |          |            |
       +-----------------------+
       |          |            |
       +-----------------------+
             EFD Table
        (Supports X*N flows)

As shown the figure, the front end server will have an EFD table that stores for
each group what is the perfect hash index that satisfies the correct output.
Because the table size is small and fits in cache (since keys are not stored) it
sustains a large number of flows (N*X, where N is the maximum number of flows
served by each back end server of the X possible targets).
With an input flow key, the group id is computed (for example, using last few
bits of CRC hash) and then the EFD table is indexed with the group id to
retrieve the corresponding hash index to use. Once the index is retrieved the
key is hashed using this hash function and the result will be the intended
correct target where this flow is supposed to be processed.
It should be noted that since EFD is not matching the exact key but rather
distributing the flows to a target backend node based on the perfect hash index,
as a result, a key that has not been inserted before will be distributed to a
valid target. Hence, a local table which stores the flows served at each node is
used, as shown in the figure, and is exact matched with the input key to rule
out new never seen before flows.
---
 config/common_base                             |   5 +
 examples/Makefile                              |   1 +
 examples/flow_distributor/Makefile             |  44 ++
 examples/flow_distributor/distributor/Makefile |  61 ++
 examples/flow_distributor/distributor/args.c   | 199 +++++
 examples/flow_distributor/distributor/args.h   |  39 +
 examples/flow_distributor/distributor/init.c   | 360 +++++++++
 examples/flow_distributor/distributor/init.h   |  76 ++
 examples/flow_distributor/distributor/main.c   | 355 +++++++++
 examples/flow_distributor/node/Makefile        |  48 ++
 examples/flow_distributor/node/node.c          | 404 +++++++++++
 examples/flow_distributor/shared/common.h      |  97 +++
 lib/Makefile                                   |   1 +
 lib/librte_eal/common/include/rte_log.h        |   1 +
 lib/librte_efd/Makefile                        |  56 ++
 lib/librte_efd/rte_efd.c                       | 969 +++++++++++++++++++++++++
 lib/librte_efd/rte_efd.h                       | 423 +++++++++++
 lib/librte_efd/rte_efd_version.map             |  12 +
 mk/rte.app.mk                                  |   1 +
 19 files changed, 3152 insertions(+)
 create mode 100644 examples/flow_distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/args.c
 create mode 100644 examples/flow_distributor/distributor/args.h
 create mode 100644 examples/flow_distributor/distributor/init.c
 create mode 100644 examples/flow_distributor/distributor/init.h
 create mode 100644 examples/flow_distributor/distributor/main.c
 create mode 100644 examples/flow_distributor/node/Makefile
 create mode 100644 examples/flow_distributor/node/node.c
 create mode 100644 examples/flow_distributor/shared/common.h
 create mode 100644 lib/librte_efd/Makefile
 create mode 100644 lib/librte_efd/rte_efd.c
 create mode 100644 lib/librte_efd/rte_efd.h
 create mode 100644 lib/librte_efd/rte_efd_version.map

diff --git a/config/common_base b/config/common_base
index c7fd3db..86a7e53 100644
--- a/config/common_base
+++ b/config/common_base
@@ -458,6 +458,11 @@ CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
 #
+# Compile librte_efd
+#
+CONFIG_RTE_LIBRTE_EFD=y
+
+#
 # Compile librte_jobstats
 #
 CONFIG_RTE_LIBRTE_JOBSTATS=y
diff --git a/examples/Makefile b/examples/Makefile
index d49c7f2..b404982 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -45,6 +45,7 @@ DIRS-y += dpdk_qat
 endif
 DIRS-y += ethtool
 DIRS-y += exception_path
+DIRS-$(CONFIG_RTE_LIBRTE_EFD) += flow_distributor
 DIRS-y += helloworld
 DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += ip_pipeline
 ifeq ($(CONFIG_RTE_LIBRTE_LPM),y)
diff --git a/examples/flow_distributor/Makefile b/examples/flow_distributor/Makefile
new file mode 100644
index 0000000..402c588
--- /dev/null
+++ b/examples/flow_distributor/Makefile
@@ -0,0 +1,44 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += distributor
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += node
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/flow_distributor/distributor/Makefile b/examples/flow_distributor/distributor/Makefile
new file mode 100644
index 0000000..61dfb2f
--- /dev/null
+++ b/examples/flow_distributor/distributor/Makefile
@@ -0,0 +1,61 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = distributor
+
+# all source are stored in SRCS-y
+SRCS-y := main.c init.c args.c
+
+INC := $(wildcard *.h)
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+# for newer gcc, e.g. 4.4, no-strict-aliasing may not be necessary
+# and so the next line can be removed in those cases.
+EXTRA_CFLAGS += -fno-strict-aliasing
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/distributor/args.c b/examples/flow_distributor/distributor/args.c
new file mode 100644
index 0000000..6007711
--- /dev/null
+++ b/examples/flow_distributor/distributor/args.c
@@ -0,0 +1,199 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_string_fns.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/* 1M flows by default */
+#define DEFAULT_NUM_FLOWS    0x100000
+
+/* global var for number of nodes - extern in header */
+uint8_t num_nodes;
+/* global var for number of flows - extern in header */
+uint32_t num_flows = DEFAULT_NUM_FLOWS;
+
+static const char *progname;
+
+/**
+ * Prints out usage information to stdout
+ */
+static void
+usage(void)
+{
+	printf(
+	    "%s [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS\n"
+	    " -p PORTMASK: hexadecimal bitmask of ports to use\n"
+	    " -n NUM_NODES: number of node processes to use\n"
+            " -f NUM_FLOWS: number of flows to be added in the EFD table\n"
+	    , progname);
+}
+
+/**
+ * The ports to be used by the application are passed in
+ * the form of a bitmask. This function parses the bitmask
+ * and places the port numbers to be used into the port[]
+ * array variable
+ */
+static int
+parse_portmask(uint8_t max_ports, const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+	uint8_t count = 0;
+
+	if (portmask == NULL || *portmask == '\0')
+		return -1;
+
+	/* convert parameter to a number and verify */
+	pm = strtoul(portmask, &end, 16);
+	if (end == NULL || *end != '\0' || pm == 0)
+		return -1;
+
+	/* loop through bits of the mask and mark ports */
+	while (pm != 0){
+		if (pm & 0x01){ /* bit is set in mask, use port */
+			if (count >= max_ports)
+				printf("WARNING: requested port %u not present"
+				" - ignoring\n", (unsigned)count);
+			else
+			    info->id[info->num_ports++] = count;
+		}
+		pm = (pm >> 1);
+		count++;
+	}
+
+	return 0;
+}
+
+/**
+ * Take the number of nodes parameter passed to the app
+ * and convert to a number to store in the num_nodes variable
+ */
+static int
+parse_num_nodes(const char *nodes)
+{
+	char *end = NULL;
+	unsigned long temp;
+
+	if (nodes == NULL || *nodes == '\0')
+		return -1;
+
+	temp = strtoul(nodes, &end, 10);
+	if (end == NULL || *end != '\0' || temp == 0)
+		return -1;
+
+	num_nodes = (uint8_t)temp;
+	return 0;
+}
+
+static int
+parse_num_flows(const char *flows)
+{
+        char *end = NULL;
+        /* parse hexadecimal string */
+        num_flows = strtoul(flows, &end, 16);
+        if ((flows[0] == '\0') || (end == NULL) || (*end != '\0'))
+                return -1;
+
+        if (num_flows == 0)
+                return -1;
+
+        return 0;
+}
+
+/**
+ * The application specific arguments follow the DPDK-specific
+ * arguments which are stripped by the DPDK init. This function
+ * processes these application arguments, printing usage info
+ * on error.
+ */
+int
+parse_app_args(uint8_t max_ports, int argc, char *argv[])
+{
+	int option_index, opt;
+	char **argvopt = argv;
+	static struct option lgopts[] = { /* no long options */
+		{NULL, 0, 0, 0 }
+	};
+	progname = argv[0];
+
+	while ((opt = getopt_long(argc, argvopt, "n:f:p:", lgopts,
+		&option_index)) != EOF){
+		switch (opt){
+			case 'p':
+				if (parse_portmask(max_ports, optarg) != 0) {
+					usage();
+					return -1;
+				}
+				break;
+			case 'n':
+				if (parse_num_nodes(optarg) != 0) {
+					usage();
+					return -1;
+				}
+				break;
+			case 'f':
+				if (parse_num_flows(optarg) != 0) {
+					usage();
+					return -1;
+				}
+				break;
+			default:
+				printf("ERROR: Unknown option '%c'\n", opt);
+				usage();
+				return -1;
+		}
+	}
+
+	if (info->num_ports == 0 || num_nodes == 0){
+		usage();
+		return -1;
+	}
+
+	if (info->num_ports % 2 != 0){
+		printf("ERROR: application requires an even number of ports to use\n");
+		return -1;
+	}
+	return 0;
+}
diff --git a/examples/flow_distributor/distributor/args.h b/examples/flow_distributor/distributor/args.h
new file mode 100644
index 0000000..8b36148
--- /dev/null
+++ b/examples/flow_distributor/distributor/args.h
@@ -0,0 +1,39 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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.
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
+
+#endif /* ifndef _ARGS_H_ */
diff --git a/examples/flow_distributor/distributor/init.c b/examples/flow_distributor/distributor/init.c
new file mode 100644
index 0000000..24664e5
--- /dev/null
+++ b/examples/flow_distributor/distributor/init.c
@@ -0,0 +1,360 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_atomic.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_cycles.h>
+#include <rte_efd.h>
+#include <rte_hash.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+#define MBUFS_PER_NODE 1536
+#define MBUFS_PER_PORT 1536
+#define MBUF_CACHE_SIZE 512
+
+#define RTE_MP_RX_DESC_DEFAULT 512
+#define RTE_MP_TX_DESC_DEFAULT 512
+#define NODE_QUEUE_RINGSIZE 128
+
+#define NO_FLAGS 0
+
+/* The mbuf pool for packet rx */
+struct rte_mempool *pktmbuf_pool;
+
+/* array of info/queues for nodes */
+struct node *nodes = NULL;
+
+/* Flow distributor table */
+struct rte_efd_table *efd_table;
+
+/* Shared info between distributor and nodes */
+struct shared_info *info;
+
+/**
+ * Initialise the mbuf pool for packet reception for the NIC, and any other
+ * buffer pools needed by the app - currently none.
+ */
+static int
+init_mbuf_pools(void)
+{
+	const unsigned num_mbufs = (num_nodes * MBUFS_PER_NODE) \
+			+ (info->num_ports * MBUFS_PER_PORT);
+
+	/* don't pass single-producer/single-consumer flags to mbuf create as it
+	 * seems faster to use a cache instead */
+	printf("Creating mbuf pool '%s' [%u mbufs] ...\n",
+			PKTMBUF_POOL_NAME, num_mbufs);
+	pktmbuf_pool = rte_pktmbuf_pool_create(PKTMBUF_POOL_NAME, num_mbufs,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	return pktmbuf_pool == NULL; /* 0  on success */
+}
+
+/**
+ * Initialise an individual port:
+ * - configure number of rx and tx rings
+ * - set up each rx ring, to pull from the main mbuf pool
+ * - set up each tx ring
+ * - start the port and report its status to stdout
+ */
+static int
+init_port(uint8_t port_num)
+{
+	/* for port configuration all features are off by default */
+	const struct rte_eth_conf port_conf = {
+		.rxmode = {
+			.mq_mode = ETH_MQ_RX_RSS
+		}
+	};
+	const uint16_t rx_rings = 1, tx_rings = num_nodes;
+	const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
+	const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
+
+	uint16_t q;
+	int retval;
+
+	printf("Port %u init ... ", (unsigned)port_num);
+	fflush(stdout);
+
+	/* Standard DPDK port initialisation - config port, then set up
+	 * rx and tx rings */
+	if ((retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings,
+		&port_conf)) != 0)
+		return retval;
+
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
+				rte_eth_dev_socket_id(port_num),
+				NULL, pktmbuf_pool);
+		if (retval < 0) return retval;
+	}
+
+	for ( q = 0; q < tx_rings; q ++ ) {
+		retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
+				rte_eth_dev_socket_id(port_num),
+				NULL);
+		if (retval < 0) return retval;
+	}
+
+	rte_eth_promiscuous_enable(port_num);
+
+	retval  = rte_eth_dev_start(port_num);
+	if (retval < 0) return retval;
+
+	printf( "done: \n");
+
+	return 0;
+}
+
+/**
+ * Set up the DPDK rings which will be used to pass packets, via
+ * pointers, between the multi-process distributor and node processes.
+ * Each node needs one RX queue.
+ */
+static int
+init_shm_rings(void)
+{
+	unsigned i;
+	unsigned socket_id;
+	const char * q_name;
+	const unsigned ringsize = NODE_QUEUE_RINGSIZE;
+
+	nodes = rte_malloc("node details",
+		sizeof(*nodes) * num_nodes, 0);
+	if (nodes == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot allocate memory for node program details\n");
+
+	for (i = 0; i < num_nodes; i++) {
+		/* Create an RX queue for each node */
+		socket_id = rte_socket_id();
+		q_name = get_rx_queue_name(i);
+		nodes[i].rx_q = rte_ring_create(q_name,
+				ringsize, socket_id,
+				RING_F_SP_ENQ | RING_F_SC_DEQ ); /* single prod, single cons */
+		if (nodes[i].rx_q == NULL)
+			rte_exit(EXIT_FAILURE, "Cannot create rx ring queue for node %u\n", i);
+	}
+	return 0;
+}
+
+/*
+ * Create flow distributor table which will contain all the flows
+ * that will be distributed among the nodes
+ */
+static void
+create_flow_distributor_table(void)
+{
+	uint8_t socket_id = rte_socket_id();
+
+	/* create table */
+	efd_table = rte_efd_create("flow table", num_flows * 2, 1 << socket_id, socket_id);
+
+	if (efd_table == NULL)
+		rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+}
+
+static void
+populate_flow_distributor_table(void)
+{
+	unsigned int i;
+	int32_t ret;
+	uint32_t ip_dst;
+	uint8_t socket_id = rte_socket_id();
+	uint64_t node_id;
+
+	/* Add flows in tables */
+	for (i = 0; i < num_flows; i++) {
+		node_id = i % num_nodes;
+
+		ip_dst = rte_cpu_to_be_32(i);
+		ret = rte_efd_update(efd_table, socket_id,
+				(efd_key_t *)&ip_dst, (efd_value_t)node_id);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+					"Unable to add entry %u in flow distributor table\n", i);
+	}
+
+	printf("EFD table: Adding 0x%x keys\n", num_flows);
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+	uint8_t portid, count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		all_ports_up = 1;
+		for (portid = 0; portid < port_num; portid++) {
+			if ((port_mask & (1 << info->id[portid])) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(info->id[portid], &link);
+			/* print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port %d Link Up - speed %u "
+						"Mbps - %s\n", info->id[portid],
+						(unsigned)link.link_speed,
+				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+					("full-duplex") : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n",
+						(uint8_t)info->id[portid]);
+				continue;
+			}
+			/* clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* after finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("done\n");
+		}
+	}
+}
+
+/**
+ * Main init function for the multi-process distributor app,
+ * calls subfunctions to do each stage of the initialisation.
+ */
+int
+init(int argc, char *argv[])
+{
+	int retval;
+	const struct rte_memzone *mz;
+	uint8_t i, total_ports;
+
+	/* init EAL, parsing EAL args */
+	retval = rte_eal_init(argc, argv);
+	if (retval < 0)
+		return -1;
+	argc -= retval;
+	argv += retval;
+
+	/* get total number of ports */
+	total_ports = rte_eth_dev_count();
+
+	/* set up array for port data */
+	mz = rte_memzone_reserve(MZ_SHARED_INFO, sizeof(*info),
+				rte_socket_id(), NO_FLAGS);
+	if (mz == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for port information\n");
+	memset(mz->addr, 0, sizeof(*info));
+	info = mz->addr;
+
+	/* parse additional, application arguments */
+	retval = parse_app_args(total_ports, argc, argv);
+	if (retval != 0)
+		return -1;
+
+	/* initialise mbuf pools */
+	retval = init_mbuf_pools();
+	if (retval != 0)
+		rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
+
+	/* now initialise the ports we will use */
+	for (i = 0; i < info->num_ports; i++) {
+		retval = init_port(info->id[i]);
+		if (retval != 0)
+			rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n",
+					(unsigned)i);
+	}
+
+	check_all_ports_link_status(info->num_ports, (~0x0));
+
+	/* initialise the node queues/rings for inter-eu comms */
+	init_shm_rings();
+
+	/* Create the flow distributor table */
+	create_flow_distributor_table();
+
+	/* Populate the flow distributor table */
+	populate_flow_distributor_table();
+
+	/* Share the total number of nodes */
+	info->num_nodes = num_nodes;
+
+	/* Share the total number of flows */
+	info->num_flows = num_flows;
+	return 0;
+}
diff --git a/examples/flow_distributor/distributor/init.h b/examples/flow_distributor/distributor/init.h
new file mode 100644
index 0000000..fd8dc06
--- /dev/null
+++ b/examples/flow_distributor/distributor/init.h
@@ -0,0 +1,76 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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.
+ */
+
+#ifndef _INIT_H_
+#define _INIT_H_
+
+/*
+ * #include <rte_ring.h>
+ * #include "args.h"
+ */
+
+/*
+ * Define a node structure with all needed info, including
+ * stats from the nodes.
+ */
+struct node {
+	struct rte_ring *rx_q;
+	unsigned node_id;
+	/* these stats hold how many packets the node will actually receive,
+	 * and how many packets were dropped because the node's queue was full.
+	 * The port-info stats, in contrast, record how many packets were received
+	 * or transmitted on an actual NIC port.
+	 */
+	struct {
+		volatile uint64_t rx;
+		volatile uint64_t rx_drop;
+	} stats;
+};
+
+extern struct rte_efd_table *efd_table;
+extern struct node *nodes;
+
+/*
+ * shared information between distributor and nodes: number of clients,
+ * port numbers, rx and tx stats etc.
+ */
+extern struct shared_info *info;
+
+extern struct rte_mempool *pktmbuf_pool;
+extern uint8_t num_nodes;
+extern unsigned int num_sockets;
+extern uint32_t num_flows;
+
+int init(int argc, char *argv[]);
+
+#endif /* ifndef _INIT_H_ */
diff --git a/examples/flow_distributor/distributor/main.c b/examples/flow_distributor/distributor/main.c
new file mode 100644
index 0000000..49fe1b9
--- /dev/null
+++ b/examples/flow_distributor/distributor/main.c
@@ -0,0 +1,355 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <inttypes.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <netinet/ip.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_atomic.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_ether.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ethdev.h>
+#include <rte_byteorder.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_efd.h>
+#include <rte_ip.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/*
+ * When doing reads from the NIC or the node queues,
+ * use this batch size
+ */
+#define PACKET_READ_SIZE 32
+
+/*
+ * Local buffers to put packets in, used to send packets in bursts to the
+ * nodes
+ */
+struct node_rx_buf {
+	struct rte_mbuf *buffer[PACKET_READ_SIZE];
+	uint16_t count;
+};
+
+struct flow_distributor_stats {
+	volatile uint64_t distributed;
+	volatile uint64_t drop;
+} flow_dist_stats;
+
+/* One buffer per node rx queue - dynamically allocate array */
+static struct node_rx_buf *cl_rx_buf;
+
+static const char *
+get_printable_mac_addr(uint8_t port)
+{
+	static const char err_address[] = "00:00:00:00:00:00";
+	static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
+
+	if (unlikely(port >= RTE_MAX_ETHPORTS))
+		return err_address;
+	if (unlikely(addresses[port][0]=='\0')){
+		struct ether_addr mac;
+		rte_eth_macaddr_get(port, &mac);
+		snprintf(addresses[port], sizeof(addresses[port]),
+				"%02x:%02x:%02x:%02x:%02x:%02x\n",
+				mac.addr_bytes[0], mac.addr_bytes[1], mac.addr_bytes[2],
+				mac.addr_bytes[3], mac.addr_bytes[4], mac.addr_bytes[5]);
+	}
+	return addresses[port];
+}
+
+/*
+ * This function displays the recorded statistics for each port
+ * and for each node. It uses ANSI terminal codes to clear
+ * screen when called. It is called from a single non-master
+ * thread in the distributor process, when the process is run with more
+ * than one lcore enabled.
+ */
+static void
+do_stats_display(void)
+{
+	unsigned i, j;
+	const char clr[] = { 27, '[', '2', 'J', '\0' };
+	const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' };
+	uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+	uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+	/* to get TX stats, we need to do some summing calculations */
+	memset(port_tx, 0, sizeof(port_tx));
+	memset(port_tx_drop, 0, sizeof(port_tx_drop));
+	memset(node_tx, 0, sizeof(node_tx));
+	memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+	for (i = 0; i < num_nodes; i++){
+		const volatile struct tx_stats *tx = &info->tx_stats[i];
+		for (j = 0; j < info->num_ports; j++){
+			/* assign to local variables here, save re-reading volatile vars */
+			const uint64_t tx_val = tx->tx[info->id[j]];
+			const uint64_t drop_val = tx->tx_drop[info->id[j]];
+			port_tx[j] += tx_val;
+			port_tx_drop[j] += drop_val;
+			node_tx[i] += tx_val;
+			node_tx_drop[i] += drop_val;
+		}
+	}
+
+	/* Clear screen and move to top left */
+	printf("%s%s", clr, topLeft);
+
+	printf("PORTS\n");
+	printf("-----\n");
+	for (i = 0; i < info->num_ports; i++)
+		printf("Port %u: '%s'\t", (unsigned)info->id[i],
+				get_printable_mac_addr(info->id[i]));
+	printf("\n\n");
+	for (i = 0; i < info->num_ports; i++){
+		printf("Port %u - rx: %9"PRIu64"\t"
+				"tx: %9"PRIu64"\n",
+				(unsigned)info->id[i], info->rx_stats.rx[i],
+				port_tx[i]);
+	}
+
+	printf("\nFLOW DISTRIBUTOR\n");
+	printf("-----\n");
+	printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+			flow_dist_stats.distributed, flow_dist_stats.drop);
+
+	printf("\nNODES\n");
+	printf("-------\n");
+	for (i = 0; i < num_nodes; i++){
+		const unsigned long long rx = nodes[i].stats.rx;
+		const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+		const volatile struct filter_stats *filter = &info->filter_stats[i];
+		printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+				"            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+				"            filter_passed: %9"PRIu64", filter_drop: %9"PRIu64"\n",
+				i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+				filter->passed, filter->drop);
+	}
+
+	printf("\n");
+}
+
+/*
+ * The function called from each non-master lcore used by the process.
+ * The test_and_set function is used to randomly pick a single lcore on which
+ * the code to display the statistics will run. Otherwise, the code just
+ * repeatedly sleeps.
+ */
+static int
+sleep_lcore(__attribute__((unused)) void *dummy)
+{
+	/* Used to pick a display thread - static, so zero-initialised */
+	static rte_atomic32_t display_stats;
+
+	/* Only one core should display stats */
+	if (rte_atomic32_test_and_set(&display_stats)) {
+		const unsigned sleeptime = 1;
+		printf("Core %u displaying statistics\n", rte_lcore_id());
+
+		/* Longer initial pause so above printf is seen */
+		sleep(sleeptime * 3);
+
+		/* Loop forever: sleep always returns 0 or <= param */
+		while (sleep(sleeptime) <= sleeptime)
+			do_stats_display();
+	}
+	return 0;
+}
+
+/*
+ * Function to set all the node statistic values to zero.
+ * Called at program startup.
+ */
+static void
+clear_stats(void)
+{
+	unsigned i;
+
+	for (i = 0; i < num_nodes; i++)
+		nodes[i].stats.rx = nodes[i].stats.rx_drop = 0;
+}
+
+/*
+ * send a burst of traffic to a node, assuming there are packets
+ * available to be sent to this node
+ */
+static void
+flush_rx_queue(uint16_t node)
+{
+	uint16_t j;
+	struct node *cl;
+
+	if (cl_rx_buf[node].count == 0)
+		return;
+
+	cl = &nodes[node];
+	if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+			cl_rx_buf[node].count) != 0){
+		for (j = 0; j < cl_rx_buf[node].count; j++)
+			rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+		cl->stats.rx_drop += cl_rx_buf[node].count;
+	}
+	else
+		cl->stats.rx += cl_rx_buf[node].count;
+
+	cl_rx_buf[node].count = 0;
+}
+
+/*
+ * marks a packet down to be sent to a particular node process
+ */
+static inline void
+enqueue_rx_packet(uint8_t node, struct rte_mbuf *buf)
+{
+	cl_rx_buf[node].buffer[cl_rx_buf[node].count++] = buf;
+}
+
+/*
+ * This function takes a group of packets and routes them
+ * individually to the node process. Very simply round-robins the packets
+ * without checking any of the packet contents.
+ */
+static void
+process_packets(uint32_t port_num __rte_unused,
+		struct rte_mbuf *pkts[], uint16_t rx_count, unsigned int socket_id)
+{
+	uint16_t i;
+	uint8_t node;
+	efd_value_t data[EFD_BURST_MAX];
+	const efd_key_t *key_ptrs[EFD_BURST_MAX];
+
+	struct ipv4_hdr *ipv4_hdr;
+	uint32_t ipv4_dst_ip[EFD_BURST_MAX];
+
+	for (i = 0; i < rx_count; i++) {
+		/* Handle IPv4 header.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+		ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+		key_ptrs[i] = (efd_key_t*)&ipv4_dst_ip[i];
+	}
+
+	rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+				(const efd_key_t **) key_ptrs, data);
+	for (i = 0; i < rx_count; i++) {
+		node = (uint8_t) ((uintptr_t)data[i]);
+
+		if (node >= num_nodes) {
+			/* Node is out of range, which means that flow has not been inserted */
+			flow_dist_stats.drop++;
+			rte_pktmbuf_free(pkts[i]);
+		} else {
+			flow_dist_stats.distributed++;
+			enqueue_rx_packet(node, pkts[i]);
+		}
+	}
+
+	for (i = 0; i < num_nodes; i++)
+		flush_rx_queue(i);
+}
+
+/*
+ * Function called by the master lcore of the DPDK process.
+ */
+static void
+do_packet_forwarding(void)
+{
+	unsigned port_num = 0; /* indexes the port[] array */
+	unsigned int socket_id = rte_socket_id();
+
+	for (;;) {
+		struct rte_mbuf *buf[PACKET_READ_SIZE];
+		uint16_t rx_count;
+
+		/* read a port */
+		rx_count = rte_eth_rx_burst(info->id[port_num], 0,
+				buf, PACKET_READ_SIZE);
+		info->rx_stats.rx[port_num] += rx_count;
+
+		/* Now process the NIC packets read */
+		if (likely(rx_count > 0))
+			process_packets(port_num, buf, rx_count, socket_id);
+
+		/* move to next port */
+		if (++port_num == info->num_ports)
+			port_num = 0;
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	/* initialise the system */
+	if (init(argc, argv) < 0 )
+		return -1;
+	RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+	cl_rx_buf = calloc(num_nodes, sizeof(cl_rx_buf[0]));
+
+	/* clear statistics */
+	clear_stats();
+
+	/* put all other cores to sleep bar master */
+	rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER);
+
+	do_packet_forwarding();
+	return 0;
+}
diff --git a/examples/flow_distributor/node/Makefile b/examples/flow_distributor/node/Makefile
new file mode 100644
index 0000000..88c669e
--- /dev/null
+++ b/examples/flow_distributor/node/Makefile
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = node
+
+# all source are stored in SRCS-y
+SRCS-y := node.c
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/node/node.c b/examples/flow_distributor/node/node.c
new file mode 100644
index 0000000..3a52207
--- /dev/null
+++ b/examples/flow_distributor/node/node.c
@@ -0,0 +1,404 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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 <stdint.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_atomic.h>
+#include <rte_branch_prediction.h>
+#include <rte_log.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_ring.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_string_fns.h>
+#include <rte_ip.h>
+
+#include "common.h"
+
+/* Number of packets to attempt to read from queue */
+#define PKT_READ_SIZE  ((uint16_t)32)
+
+/* our node id number - tells us which rx queue to read, and NIC TX
+ * queue to write to. */
+static uint8_t node_id = 0;
+
+#define MBQ_CAPACITY 32
+
+/* maps input ports to output ports for packets */
+static uint8_t output_ports[RTE_MAX_ETHPORTS];
+
+/* buffers up a set of packet that are ready to send */
+struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
+
+/* shared data from distributor. We update statistics here */
+static volatile struct tx_stats *tx_stats;
+
+static volatile struct filter_stats *filter_stats;
+
+/*
+ * print a usage message
+ */
+static void
+usage(const char *progname)
+{
+	printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname);
+}
+
+/*
+ * Convert the node id number from a string to an int.
+ */
+static int
+parse_node_num(const char *node)
+{
+	char *end = NULL;
+	unsigned long temp;
+
+	if (node == NULL || *node == '\0')
+		return -1;
+
+	temp = strtoul(node, &end, 10);
+	if (end == NULL || *end != '\0')
+		return -1;
+
+	node_id = (uint8_t)temp;
+	return 0;
+}
+
+/*
+ * Parse the application arguments to the node app.
+ */
+static int
+parse_app_args(int argc, char *argv[])
+{
+	int option_index, opt;
+	char **argvopt = argv;
+	const char *progname = NULL;
+	static struct option lgopts[] = { /* no long options */
+		{NULL, 0, 0, 0 }
+	};
+	progname = argv[0];
+
+	while ((opt = getopt_long(argc, argvopt, "n:", lgopts,
+		&option_index)) != EOF){
+		switch (opt){
+			case 'n':
+				if (parse_node_num(optarg) != 0){
+					usage(progname);
+					return -1;
+				}
+				break;
+			default:
+				usage(progname);
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Tx buffer error callback
+ */
+static void
+flush_tx_error_callback(struct rte_mbuf **unsent, uint16_t count,
+		void *userdata) {
+	int i;
+	uint8_t port_id = (uintptr_t)userdata;
+
+	tx_stats->tx_drop[port_id] += count;
+
+	/* free the mbufs which failed from transmit */
+	for (i = 0; i < count; i++)
+		rte_pktmbuf_free(unsent[i]);
+
+}
+
+static void
+configure_tx_buffer(uint8_t port_id, uint16_t size)
+{
+	int ret;
+
+	/* Initialize TX buffers */
+	tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer",
+			RTE_ETH_TX_BUFFER_SIZE(size), 0,
+			rte_eth_dev_socket_id(port_id));
+	if (tx_buffer[port_id] == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
+				(unsigned) port_id);
+
+	rte_eth_tx_buffer_init(tx_buffer[port_id], size);
+
+	ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[port_id],
+			flush_tx_error_callback, (void *)(intptr_t)port_id);
+	if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Cannot set error callback for "
+					"tx buffer on port %u\n", (unsigned) port_id);
+}
+
+/*
+ * set up output ports so that all traffic on port gets sent out
+ * its paired port. Index using actual port numbers since that is
+ * what comes in the mbuf structure.
+ */
+static void
+configure_output_ports(const struct shared_info *info)
+{
+	int i;
+	if (info->num_ports > RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE, "Too many ethernet ports. RTE_MAX_ETHPORTS = %u\n",
+				(unsigned)RTE_MAX_ETHPORTS);
+	for (i = 0; i < info->num_ports - 1; i+=2){
+		uint8_t p1 = info->id[i];
+		uint8_t p2 = info->id[i+1];
+		output_ports[p1] = p2;
+		output_ports[p2] = p1;
+
+		configure_tx_buffer(p1, MBQ_CAPACITY);
+		configure_tx_buffer(p2, MBQ_CAPACITY);
+
+	}
+}
+
+/*
+ * Create the hash table that will contain the flows that
+ * the node will handle, which will be used to decide if packet
+ * is transmitted or dropped.
+ */
+static struct rte_hash *
+create_hash_table(const struct shared_info *info)
+{
+	uint32_t num_flows_node = info->num_flows / info->num_nodes;
+	char name[RTE_HASH_NAMESIZE];
+	struct rte_hash *h;
+
+	/* create table */
+	struct rte_hash_parameters hash_params = {
+		.entries = num_flows_node * 2, /* table load = 50% */
+		.key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+		.socket_id = rte_socket_id(),
+		.hash_func_init_val = 0,
+	};
+
+	snprintf(name, sizeof(name), "hash_table_%d", node_id);
+	hash_params.name = name;
+	h = rte_hash_create(&hash_params);
+
+	if (h == NULL)
+		rte_exit(EXIT_FAILURE,
+				"Problem creating the hash table for node %d\n",
+				node_id);
+	return h;
+}
+
+static void
+populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+{
+	unsigned int i;
+	int32_t ret;
+	uint32_t ip_dst;
+	uint32_t num_flows_node = 0;
+	uint64_t target_node;
+
+	/* Add flows in table */
+	for (i = 0; i < info->num_flows; i++) {
+		target_node = i % info->num_nodes;
+		if (target_node != node_id)
+			continue;
+
+		ip_dst = rte_cpu_to_be_32(i);
+
+		ret = rte_hash_add_key(h, (void *) &ip_dst);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Unable to add entry %u in hash table\n", i);
+		else
+			num_flows_node++;
+
+	}
+
+	printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+}
+
+/*
+ * This function performs routing of packets
+ * Just sends each input packet out an output port based solely on the input
+ * port it arrived on.
+ */
+static inline void
+transmit_packet(struct rte_mbuf *buf)
+{
+	int sent;
+	const uint8_t in_port = buf->port;
+	const uint8_t out_port = output_ports[in_port];
+	struct rte_eth_dev_tx_buffer *buffer = tx_buffer[out_port];
+
+	sent = rte_eth_tx_buffer(out_port, node_id, buffer, buf);
+	if (sent)
+		tx_stats->tx[out_port] += sent;
+
+}
+
+static inline int
+handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+{
+	struct ipv4_hdr *ipv4_hdr;
+	uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+	const void *key_ptrs[PKT_READ_SIZE];
+	unsigned int i;
+	int32_t positions[PKT_READ_SIZE] = {0};
+
+	for (i = 0; i < num_packets; i++) {
+		/* Handle IPv4 header.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+		ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+		key_ptrs[i] = &ipv4_dst_ip[i];
+	}
+	/* Check if packets belongs to any flows handled by this node */
+	rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+	for (i = 0; i < num_packets; i++) {
+		if (likely(positions[i] >= 0)) {
+			filter_stats->passed++;
+			transmit_packet(bufs[i]);
+		} else {
+			filter_stats->drop++;
+			/* Drop packet, as flow is not handled by this node */
+			rte_pktmbuf_free(bufs[i]);
+		}
+	}
+
+	return 1;
+}
+
+/*
+ * Application main function - loops through
+ * receiving and processing packets. Never returns
+ */
+int
+main(int argc, char *argv[])
+{
+	const struct rte_memzone *mz;
+	struct rte_ring *rx_ring;
+	struct rte_hash *h;
+	struct rte_mempool *mp;
+	struct shared_info *info;
+	int need_flush = 0; /* indicates whether we have unsent packets */
+	int retval;
+	void *pkts[PKT_READ_SIZE];
+	uint16_t sent;
+
+	if ((retval = rte_eal_init(argc, argv)) < 0)
+		return -1;
+	argc -= retval;
+	argv += retval;
+
+	if (parse_app_args(argc, argv) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n");
+
+	if (rte_eth_dev_count() == 0)
+		rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
+
+	rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+	if (rx_ring == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get RX ring - is distributor process running?\n");
+
+	mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+	if (mp == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+	mz = rte_memzone_lookup(MZ_SHARED_INFO);
+	if (mz == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+	info = mz->addr;
+	tx_stats = &(info->tx_stats[node_id]);
+	filter_stats = &(info->filter_stats[node_id]);
+
+	configure_output_ports(info);
+
+	h = create_hash_table(info);
+
+	populate_hash_table(h, info);
+
+	RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+	printf("\nNode process %d handling packets\n", node_id);
+	printf("[Press Ctrl-C to quit ...]\n");
+
+	for (;;) {
+		uint16_t  rx_pkts = PKT_READ_SIZE;
+		uint8_t port;
+
+		/* try dequeuing max possible packets first, if that fails, get the
+		 * most we can. Loop body should only execute once, maximum */
+		while (rx_pkts > 0 &&
+				unlikely(rte_ring_dequeue_bulk(rx_ring, pkts, rx_pkts) != 0))
+			rx_pkts = (uint16_t)RTE_MIN(rte_ring_count(rx_ring), PKT_READ_SIZE);
+
+		if (unlikely(rx_pkts == 0)){
+			if (need_flush)
+				for (port = 0; port < info->num_ports; port++) {
+					sent = rte_eth_tx_buffer_flush(info->id[port], node_id,
+							tx_buffer[port]);
+					if (unlikely(sent))
+						tx_stats->tx[port] += sent;
+				}
+			need_flush = 0;
+			continue;
+		}
+
+		handle_packets(h, (struct rte_mbuf **)pkts, rx_pkts);
+
+		need_flush = 1;
+	}
+}
diff --git a/examples/flow_distributor/shared/common.h b/examples/flow_distributor/shared/common.h
new file mode 100644
index 0000000..b635b71
--- /dev/null
+++ b/examples/flow_distributor/shared/common.h
@@ -0,0 +1,97 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-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.
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <rte_hash_crc.h>
+#include <rte_hash.h>
+
+#define MAX_NODES             16
+/*
+ * Shared port info, including statistics information for display by distributor.
+ * Structure will be put in a memzone.
+ * - All port id values share one cache line as this data will be read-only
+ * during operation.
+ * - All rx statistic values share cache lines, as this data is written only
+ * by the distributor process. (rare reads by stats display)
+ * - The tx statistics have values for all ports per cache line, but the stats
+ * themselves are written by the nodes, so we have a distinct set, on different
+ * cache lines for each node to use.
+ */
+struct rx_stats {
+	uint64_t rx[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct tx_stats {
+	uint64_t tx[RTE_MAX_ETHPORTS];
+	uint64_t tx_drop[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct filter_stats {
+	uint64_t drop;
+	uint64_t passed;
+} __rte_cache_aligned;
+
+struct shared_info {
+	uint8_t num_nodes;
+	uint8_t num_ports;
+	uint32_t num_flows;
+	uint8_t id[RTE_MAX_ETHPORTS];
+	volatile struct rx_stats rx_stats;
+	volatile struct tx_stats tx_stats[MAX_NODES];
+	volatile struct filter_stats filter_stats[MAX_NODES];
+};
+
+/* define common names for structures shared between distributor and node */
+#define MP_NODE_RXQ_NAME "MProc_Node_%u_RX"
+#define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool"
+#define MZ_SHARED_INFO "MProc_shared_info"
+
+/*
+ * Given the rx queue name template above, get the queue name
+ */
+static inline const char *
+get_rx_queue_name(unsigned id)
+{
+	/* buffer for return value. Size calculated by %u being replaced
+	 * by maximum 3 digits (plus an extra byte for safety) */
+	static char buffer[sizeof(MP_NODE_RXQ_NAME) + 2];
+
+	snprintf(buffer, sizeof(buffer) - 1, MP_NODE_RXQ_NAME, id);
+	return buffer;
+}
+
+#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index 990f23a..9a41188 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -43,6 +43,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
 DIRS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += librte_cryptodev
 DIRS-$(CONFIG_RTE_LIBRTE_VHOST) += librte_vhost
 DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash
+DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
 DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net
diff --git a/lib/librte_eal/common/include/rte_log.h b/lib/librte_eal/common/include/rte_log.h
index 29f7d19..70e150d 100644
--- a/lib/librte_eal/common/include/rte_log.h
+++ b/lib/librte_eal/common/include/rte_log.h
@@ -79,6 +79,7 @@ extern struct rte_logs rte_logs;
 #define RTE_LOGTYPE_PIPELINE 0x00008000 /**< Log related to pipeline. */
 #define RTE_LOGTYPE_MBUF    0x00010000 /**< Log related to mbuf. */
 #define RTE_LOGTYPE_CRYPTODEV 0x00020000 /**< Log related to cryptodev. */
+#define RTE_LOGTYPE_EFD     0x00040000 /**< Log related to cryptodev. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1   0x01000000 /**< User-defined log type 1. */
diff --git a/lib/librte_efd/Makefile b/lib/librte_efd/Makefile
new file mode 100644
index 0000000..ae21bc7
--- /dev/null
+++ b/lib/librte_efd/Makefile
@@ -0,0 +1,56 @@
+#   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 $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_efd.a
+
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
+CFLAGS += -D_GNU_SOURCE
+
+EXPORT_MAP := rte_efd_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_EFD) := rte_efd.c
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_EFD)-include := rte_efd.h
+
+# this lib depends upon:
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_mbuf
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_mempool
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_eal
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_ether
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_efd/rte_efd.c b/lib/librte_efd/rte_efd.c
new file mode 100644
index 0000000..4c9606e
--- /dev/null
+++ b/lib/librte_efd/rte_efd.c
@@ -0,0 +1,969 @@
+/*-
+ *   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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <immintrin.h>
+#include <math.h>
+#include <sys/queue.h>
+
+#include <rte_log.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <rte_prefetch.h>
+#include <rte_branch_prediction.h>
+
+#include "rte_efd.h"
+
+/** Hash function used to determine chunk_id and bin_id for a group */
+#define EFD_HASH(key) (efd_hash_internal((key), 0xbc9f1d34))
+/** Hash function used as constant component of perfect hash search */
+#define EFD_HASHFUNCA(key) (efd_hash_internal((key), 0xbc9f1d35))
+/** Hash function used as multiplicative component of perfect hash search */
+#define EFD_HASHFUNCB(key) (efd_hash_internal((key), 0xbc9f1d36))
+
+TAILQ_HEAD(rte_efd_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_efd_tailq = {
+	.name = "RTE_EFD",
+};
+EAL_REGISTER_TAILQ(rte_efd_tailq);
+
+/** Internal permutation array used to shuffle bins into pseudorandom groups */
+const uint32_t efd_bin_to_group[EFD_CHUNK_NUM_BIN_TO_GROUP_SETS][EFD_CHUNK_NUM_BINS] = {
+	{
+		0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+		4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
+		8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
+		12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
+		16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
+		20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
+		24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
+		28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
+		32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
+		36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
+		40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
+		44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
+		48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
+		52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
+		56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
+		60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63
+	},
+	{
+		34, 33, 48, 59, 0, 21, 36, 18, 9, 49, 54, 38, 51, 23, 31, 5,
+		44, 23, 37, 52, 11, 4, 58, 20, 38, 40, 38, 22, 26, 28, 42, 6,
+		46, 16, 31, 28, 46, 14, 60, 0, 35, 53, 16, 58, 16, 29, 39, 7,
+		1, 54, 15, 11, 48, 3, 62, 9, 58, 5, 30, 43, 17, 7, 36, 34,
+		6, 36, 2, 14, 10, 1, 47, 47, 20, 45, 62, 56, 34, 25, 39, 18,
+		51, 41, 61, 25, 56, 40, 41, 37, 52, 35, 30, 57, 11, 42, 37, 27,
+		54, 19, 26, 13, 48, 31, 46, 15, 12, 10, 16, 20, 43, 17, 12, 55,
+		45, 18, 8, 41, 7, 31, 42, 63, 12, 14, 21, 57, 24, 40, 5, 41,
+		13, 44, 23, 59, 25, 57, 52, 50, 62, 1, 2, 49, 32, 57, 26, 43,
+		56, 60, 55, 5, 49, 6, 3, 50, 46, 39, 27, 33, 17, 4, 53, 13,
+		2, 19, 36, 51, 63, 0, 22, 33, 59, 28, 29, 23, 45, 33, 53, 27,
+		22, 21, 40, 56, 4, 18, 44, 47, 28, 17, 4, 50, 21, 62, 8, 39,
+		0, 8, 15, 24, 29, 24, 9, 11, 48, 61, 35, 55, 43, 1, 54, 42,
+		53, 60, 22, 3, 32, 52, 25, 8, 15, 60, 7, 55, 27, 63, 19, 10,
+		63, 24, 61, 19, 12, 38, 6, 29, 13, 37, 10, 3, 45, 32, 32, 30,
+		49, 61, 44, 14, 20, 58, 35, 30, 2, 26, 34, 51, 9, 59, 47, 50
+	},
+	{
+		32, 35, 32, 34, 55, 5, 6, 23, 49, 11, 6, 23, 52, 37, 29, 54,
+		55, 40, 63, 50, 29, 52, 61, 25, 12, 56, 39, 38, 29, 11, 46, 1,
+		40, 11, 19, 56, 7, 28, 51, 16, 15, 48, 21, 51, 60, 31, 14, 22,
+		41, 47, 59, 56, 53, 28, 58, 26, 43, 27, 41, 33, 24, 52, 44, 38,
+		13, 59, 48, 51, 60, 15, 3, 30, 15, 0, 10, 62, 44, 14, 28, 51,
+		38, 2, 41, 26, 25, 49, 10, 12, 55, 57, 27, 35, 19, 33, 0, 30,
+		5, 36, 47, 53, 5, 53, 20, 43, 34, 37, 52, 41, 21, 63, 59, 9,
+		24, 1, 45, 24, 39, 44, 45, 16, 9, 17, 7, 50, 57, 22, 18, 28,
+		25, 45, 2, 40, 58, 15, 17, 3, 1, 27, 61, 39, 19, 0, 19, 21,
+		57, 62, 54, 60, 54, 40, 48, 33, 36, 37, 4, 42, 1, 43, 58, 8,
+		13, 42, 10, 56, 35, 22, 48, 61, 63, 10, 49, 9, 24, 9, 25, 57,
+		33, 18, 13, 31, 42, 36, 36, 55, 30, 37, 53, 34, 59, 4, 4, 23,
+		8, 16, 58, 14, 30, 11, 12, 63, 49, 62, 2, 39, 47, 22, 2, 60,
+		18, 8, 46, 31, 6, 20, 32, 29, 46, 42, 20, 31, 32, 61, 34, 4,
+		47, 26, 20, 43, 26, 21, 7, 3, 16, 35, 18, 44, 27, 62, 13, 23,
+		6, 50, 12, 8, 45, 17, 3, 46, 50, 7, 14, 5, 17, 54, 38, 0
+	},
+	{
+		29, 56, 5, 7, 54, 48, 23, 37, 35, 44, 52, 40, 33, 49, 60, 0,
+		59, 51, 28, 12, 41, 26, 2, 23, 34, 5, 59, 40, 3, 19, 6, 26,
+		35, 53, 45, 49, 29, 57, 28, 62, 58, 59, 19, 53, 59, 62, 6, 54,
+		13, 15, 48, 50, 45, 21, 41, 12, 34, 40, 24, 56, 19, 21, 35, 18,
+		55, 45, 9, 61, 47, 61, 19, 15, 16, 39, 17, 31, 3, 51, 21, 50,
+		17, 25, 25, 11, 44, 16, 18, 28, 14, 2, 37, 61, 58, 27, 62, 4,
+		14, 17, 1, 9, 46, 28, 37, 0, 53, 43, 57, 7, 57, 46, 21, 41,
+		39, 14, 52, 60, 44, 53, 49, 60, 49, 63, 13, 11, 29, 1, 55, 47,
+		55, 12, 60, 43, 54, 37, 13, 6, 42, 10, 36, 13, 9, 8, 34, 51,
+		31, 32, 12, 7, 57, 2, 26, 14, 3, 30, 63, 3, 32, 1, 5, 11,
+		27, 24, 26, 44, 31, 23, 56, 38, 62, 0, 40, 30, 6, 23, 38, 2,
+		47, 5, 15, 27, 16, 10, 31, 25, 22, 63, 30, 25, 20, 33, 32, 50,
+		29, 43, 55, 10, 50, 45, 56, 20, 4, 7, 27, 46, 11, 16, 22, 52,
+		35, 20, 41, 54, 46, 33, 42, 18, 63, 8, 22, 58, 36, 4, 51, 42,
+		38, 32, 38, 22, 17, 0, 47, 8, 48, 8, 48, 1, 61, 36, 33, 20,
+		24, 39, 39, 18, 30, 36, 9, 43, 42, 24, 10, 58, 4, 15, 34, 52
+	},
+};
+
+
+/**
+ * Internal function used to compute the hash of a key given a particular seed
+ *
+ * @param[in] key   key to hash
+ * @param[in] seed  seed for the hash function
+ *
+ * @return 32-bit hash
+ */
+static inline uint32_t
+efd_hash_internal(const efd_key_t *const key, const uint32_t seed)
+{
+	return (uint32_t)(rte_hash_crc(key->bytes, EFD_KEY_LEN, seed));
+}
+
+
+/**
+ * Computes the chunk ID for a given key hash
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  hashed_key      32-bit key hash returned by EFD_HASH
+ *
+ * @return chunk ID containing this key hash
+ */
+static inline uint32_t
+efd_get_chunk_id(const struct rte_efd_table *const table,
+				const uint32_t hashed_key)
+{
+	return hashed_key & (table->num_chunks - 1);
+}
+
+/**
+ * Computes the bin ID for a given key hash
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  hashed_key      32-bit key hash returned by EFD_HASH
+ *
+ * @return bin ID containing this key hash
+ */
+static inline uint32_t
+efd_get_bin_id(const struct rte_efd_table *const table,
+			const uint32_t hashed_key)
+{
+	return (hashed_key >> table->num_chunks_shift) & (EFD_CHUNK_NUM_BINS - 1);
+}
+
+/**
+ * Looks up the current permutation choice for a particular bin in the online table
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  socket_id       Socket ID to use to look up existing values
+ *                             (ideally caller's socket id)
+ * @param[in]  chunk_id        Chunk ID of bin to look up
+ * @param[in]  bin_id          Bin ID to look up
+ *
+ * @return
+ *   Currently active permutation choice in the online table
+ */
+static inline uint8_t
+efd_get_choice(const struct rte_efd_table *const table,
+				const unsigned int socket_id,
+				const uint32_t chunk_id,
+				const uint32_t bin_id)
+{
+	struct efd_online_chunk *chunk = &table->chunks[socket_id][chunk_id];
+
+	/* Grab the chunk (byte) that contains the choices for four neighboring bins */
+	uint8_t choice_chunk =
+		chunk->bin_choice_list[bin_id / EFD_CHUNK_NUM_BIN_TO_GROUP_SETS];
+
+	/* Compute the offset into the chunk that contains the group_id lookup position */
+	int offset = (bin_id & 0x3) * 2;
+
+	/* Extract from the byte just the desired lookup position */
+	return (uint8_t)((choice_chunk >> offset) & 0x3);
+}
+
+/**
+ * Compute the chunk_id and bin_id for a given key
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  key             Key to hash and find location of
+ * @param[out] chunk_id        Computed chunk ID
+ * @param[out] bin_id          Computed bin ID
+ *
+ */
+static inline void
+efd_compute_ids(const struct rte_efd_table *const table, const efd_key_t *const key,
+                                   uint32_t *const chunk_id, uint32_t *const bin_id)
+{
+	/* Compute the position of the entry in the hash table */
+	uint32_t h = EFD_HASH(key);
+
+	/* Compute the chunk_id where that entry can be found */
+	*chunk_id = efd_get_chunk_id(table, h);
+
+	/* Compute the bin within that chunk where the entry can be found (0 - 255) */
+	*bin_id = efd_get_bin_id(table, h);
+}
+
+/**
+ * Search for a hash function for a group that satisfies all group results
+ */
+static inline int
+efd_search_hash(const struct efd_offline_group_rules *const off_group,
+                    struct efd_online_group_entry *const on_group)
+{
+	efd_hashfunc_t hash_idx;
+	efd_hashfunc_t start_hash_idx[EFD_VALUE_NUM_BITS];
+	efd_lookuptbl_t start_lookup_table[EFD_VALUE_NUM_BITS];
+
+	uint32_t i, j, rule_id;
+	uint32_t hash_val_a[EFD_MAX_GROUP_NUM_RULES];
+	uint32_t hash_val_b[EFD_MAX_GROUP_NUM_RULES];
+	uint32_t hash_val[EFD_MAX_GROUP_NUM_RULES];
+
+
+	rte_prefetch0(off_group->value);
+
+	/*
+	 * Prepopulate the hash_val tables by running the two hash functions
+	 * for each provided rule
+	 */
+	for (i = 0; i < off_group->num_rules; i++) {
+		hash_val_b[i] = EFD_HASHFUNCB(&off_group->key[i]);
+		hash_val_a[i] = EFD_HASHFUNCA(&off_group->key[i]);
+	}
+
+	for (i = 0; i < EFD_VALUE_NUM_BITS; i++) {
+		hash_idx = on_group->hash_idx[i];
+		start_hash_idx[i] = hash_idx;
+
+		do {
+			efd_lookuptbl_t lookup_table = 0;
+			efd_lookuptbl_t lookup_table_complement = 0;
+
+			for (rule_id = 0; rule_id < off_group->num_rules; rule_id++)
+				hash_val[rule_id] = hash_val_a[rule_id] + hash_idx *
+									hash_val_b[rule_id];
+
+			/*
+			 * The goal here is to find a hash function for this particular
+			 * bit entry that meets the following criteria:
+			 * - The most significant bits of the hash result define a shift
+			 *   into the lookup table where the bit will be stored
+			 */
+
+			/* Iterate over each provided rule */
+			for (rule_id = 0; rule_id < off_group->num_rules; rule_id++) {
+				/*
+				 * Use the few most significant bits (number based on
+				 * EFD_LOOKUPTBL_SIZE) to see what position the
+				 * expected bit should be set in the lookup_table
+				 */
+				uint32_t bucket_idx = hash_val[rule_id] >>
+						EFD_LOOKUPTBL_SHIFT;
+
+				/*
+				 * Get the current bit of interest. This only finds
+				 * an appropriate hash function for one bit
+				 * at a time of the rule
+				 */
+				efd_lookuptbl_t expected =
+					(off_group->value[rule_id] >> i) & 0x1;
+
+				/*
+				 * Add the expected bit (if set) to a map (lookup_table).
+				 * Also set its complement in lookup_table_complement
+				 */
+				lookup_table |= expected << bucket_idx;
+				lookup_table_complement |= (1 - expected) << bucket_idx;
+
+				/*
+				 * If ever the hash function of two different
+				 * elements result in different values at the
+				 * same location in the lookup_table,
+				 * the current hash_idx is not valid.
+				 */
+				if (lookup_table & lookup_table_complement)
+					break;
+			}
+
+			/* Check if the previous loop completed without breaking early */
+			if (rule_id == off_group->num_rules) {
+				/* Current hash function worked, store it
+				 * for the current group */
+				on_group->hash_idx[i] = hash_idx;
+				on_group->lookup_table[i] = lookup_table;
+
+				/*
+				 * Make sure that the hash function has changed
+				 * from the starting value
+				 */
+				hash_idx = start_hash_idx[i] + 1;
+				break;
+			}
+			hash_idx++;
+
+		} while (hash_idx != start_hash_idx[i]);
+
+		/* Failed to find perfect hash for this group */
+		if (hash_idx == start_hash_idx[i]) {
+			/* Restore previous hash_idx and lookup_table for all value bits */
+			for (j = 0; j < i; j++) {
+				on_group->hash_idx[j] = start_hash_idx[j];
+				on_group->lookup_table[j] = start_lookup_table[j];
+			}
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+struct rte_efd_table *
+rte_efd_create(const char *name, uint32_t max_num_rules,
+			uint8_t online_cpu_socket_bitmask,
+			uint8_t offline_cpu_socket)
+{
+	struct rte_efd_table *table = NULL;
+	uint32_t num_chunks, num_chunks_shift;
+	uint8_t socket_id;
+	struct rte_efd_list* efd_list = NULL;
+	struct rte_tailq_entry *te;
+
+	efd_list = RTE_TAILQ_CAST(rte_efd_tailq.head, rte_efd_list);
+
+	if (online_cpu_socket_bitmask == 0) {
+		RTE_LOG(ERR, EFD, "At least one CPU socket must be enabled in the bitmask\n");
+		return NULL;
+	}
+
+	if (max_num_rules == 0) {
+		RTE_LOG(ERR, EFD, "Max num rules must be higher than 0\n");
+		return NULL;
+	}
+
+	/*
+	 * Compute the minimum number of chunks (smallest power of 2)
+	 * that can hold all of the rules
+	 */
+	if (max_num_rules % EFD_TARGET_CHUNK_NUM_RULES == 0)
+		num_chunks = rte_align32pow2(max_num_rules/EFD_TARGET_CHUNK_NUM_RULES);
+	else
+		num_chunks = rte_align32pow2((max_num_rules/EFD_TARGET_CHUNK_NUM_RULES) + 1);
+
+	num_chunks_shift = log2(num_chunks);
+
+	rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	/* guarantee there's no existing: this is normally already checked
+	 * by ring creation above */
+	TAILQ_FOREACH(te, efd_list, next) {
+		table = (struct rte_efd_table *) te->data;
+		if (strncmp(name, table->name, RTE_EFD_NAMESIZE) == 0)
+			break;
+	}
+
+	table = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		te = NULL;
+		goto error_unlock_exit;
+	}
+
+	te = rte_zmalloc("EFD_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, EFD, "tailq entry allocation failed\n");
+		goto error_unlock_exit;
+	}
+
+	/* Create a new EFD table management structure */
+	table = (struct rte_efd_table *) rte_zmalloc_socket(NULL,
+				sizeof(struct rte_efd_table),
+				RTE_CACHE_LINE_SIZE,
+				offline_cpu_socket);
+	if (table == NULL) {
+		RTE_LOG(ERR, EFD, "Allocating EFD table management structure"
+				" on socket %u failed\n",
+				offline_cpu_socket);
+		goto error_unlock_exit;
+	}
+
+
+	RTE_LOG(DEBUG, EFD, "Allocated EFD table management structure on socket %u\n",
+			offline_cpu_socket);
+
+	table->max_num_rules = num_chunks * EFD_TARGET_CHUNK_NUM_RULES;
+	table->num_rules = 0;
+	table->num_chunks = num_chunks;
+	table->num_chunks_shift = num_chunks_shift;
+	snprintf(table->name, sizeof(table->name), "%s", name);
+
+	RTE_LOG(DEBUG, EFD, "Creating an EFD table with %u chunks,"
+			" which potentially supports %u entries\n",
+			num_chunks, table->max_num_rules);
+
+	/* Make sure all the allocatable table pointers are NULL initially */
+	for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++) {
+		table->chunks[socket_id] = NULL;
+	}
+	table->offline_chunks = NULL;
+
+	/* Allocate one online table per socket specified in the user-supplied bitmask */
+	uint64_t online_table_size = num_chunks * sizeof(struct efd_online_chunk) +
+	                             EFD_NUM_CHUNK_PADDING_BYTES;
+
+	for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++) {
+		if ((online_cpu_socket_bitmask >> socket_id) & 0x01) {
+			/*
+			 * Allocate all of the EFD table chunks (the online portion)
+			 * as a continuous block
+			 */
+			table->chunks[socket_id] =
+				(struct efd_online_chunk *) rte_zmalloc_socket(NULL,
+					online_table_size,
+					RTE_CACHE_LINE_SIZE,
+					socket_id);
+			if (table->chunks[socket_id] == NULL) {
+				RTE_LOG(ERR, EFD,
+						"Allocating EFD online table on "
+						"socket %u failed\n",
+						socket_id);
+				goto error_unlock_exit;
+			}
+			RTE_LOG(DEBUG, EFD,
+					"Allocated EFD online table of size %lu bytes (%.2f MB) "
+					"on socket %u\n", online_table_size,
+			       (float)online_table_size / (1024.0F * 1024.0F), socket_id);
+		}
+	}
+
+	/*
+	 * Allocate the EFD table offline portion (with the actual rules mapping keys
+	 * to values) as a continuous block. This could be several gigabytes of memory.
+	 */
+	uint64_t offline_table_size = num_chunks * sizeof(struct efd_offline_chunk_rules);
+	table->offline_chunks =
+			(struct efd_offline_chunk_rules *) rte_zmalloc_socket(NULL,
+							offline_table_size,
+							RTE_CACHE_LINE_SIZE,
+							offline_cpu_socket);
+	if (table->offline_chunks == NULL) {
+		RTE_LOG(ERR, EFD, "Allocating EFD offline table on socket %u failed\n",
+				offline_cpu_socket);
+		goto error_unlock_exit;
+	}
+
+	RTE_LOG(DEBUG, EFD,
+			"Allocated EFD offline table of size %lu bytes (%.2f MB) on socket %u\n",
+			offline_table_size,
+	       (float)offline_table_size / (1024.0F * 1024.0F),
+		   offline_cpu_socket);
+
+	te->data = (void *) table;
+	TAILQ_INSERT_TAIL(efd_list, te, next);
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	return table;
+
+error_unlock_exit:
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+	rte_efd_free(table);
+
+	return NULL;
+}
+
+struct rte_efd_table *
+rte_efd_find_existing(const char *name)
+{
+	struct rte_efd_table *table = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_efd_list *efd_list;
+
+	efd_list = RTE_TAILQ_CAST(rte_efd_tailq.head, rte_efd_list);
+
+	rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK);
+	TAILQ_FOREACH(te, efd_list, next) {
+		table = (struct rte_efd_table *) te->data;
+		if (strncmp(name, table->name, RTE_EFD_NAMESIZE) == 0)
+			break;
+	}
+	rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+	return table;
+}
+
+void
+rte_efd_free(struct rte_efd_table *table)
+{
+	if (table != NULL) {
+		uint8_t socket_id;
+		for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++)
+			if (table->chunks[socket_id] != NULL)
+				rte_free(table->chunks[socket_id]);
+
+		if (table->offline_chunks != NULL) rte_free(table->offline_chunks);
+		rte_free(table);
+	}
+}
+
+/**
+ * Applies a previously computed table entry to the specified table for all
+ * socket-local copies of the online table.
+ * Intended to apply an update for only a single change to a key/value pair at a time
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  socket_id       Socket ID to use to lookup existing values (ideally caller's socket id)
+ * @param[in]  chunk_id        Chunk index to update
+ * @param[in]  group_id        Group index to update
+ * @param[in]  bin_id          Bin within the group that this update affects
+ * @param[in]  new_bin_choice  Newly chosen permutation which this bin should use - only lower 2 bits
+ * @param[in]  new_group_entry Previously computed updated chunk/group entry
+ */
+static inline void
+efd_apply_update(struct rte_efd_table *const table, const unsigned int socket_id,
+                      const uint32_t chunk_id, const uint32_t group_id,
+                      const uint32_t bin_id, const uint8_t new_bin_choice,
+                      const struct efd_online_group_entry *const new_group_entry)
+{
+	int i;
+	struct efd_online_chunk *chunk = &table->chunks[socket_id][chunk_id];
+
+	/* Grab the current byte that contains the choices for four neighboring bins */
+	uint8_t choice_chunk = chunk->bin_choice_list[bin_id / EFD_CHUNK_NUM_BIN_TO_GROUP_SETS];
+
+
+	/* Compute the offset into the chunk that needs to be updated */
+	int offset = (bin_id & 0x3) * 2;
+
+	/* Zero the two bits of interest and set them to new_bin_choice */
+	choice_chunk = (choice_chunk & (~(0x03 << offset)))
+				   | ((new_bin_choice & 0x03) << offset);
+
+	/* Update the online table with the new data across all sockets */
+	for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+		if (table->chunks[i] != NULL) {
+			memcpy(&(table->chunks[i][chunk_id].groups[group_id]), new_group_entry,
+				   sizeof(struct efd_online_group_entry));
+			table->chunks[i][chunk_id].bin_choice_list[bin_id / EFD_CHUNK_NUM_BIN_TO_GROUP_SETS] =
+					choice_chunk;
+		}
+	}
+}
+
+
+/**
+ * Computes an updated table entry where the supplied key points to a new host.
+ * If no entry exists, one is inserted.
+ *
+ * This function does NOT modify the online table(s)
+ * This function DOES modify the offline table
+ *
+ * @param[in]  table           EFD table to reference
+ * @param[in]  socket_id       Socket ID to use to lookup existing values (ideally caller's socket id)
+ * @param[in]  key             Key to insert
+ * @param[in]  value           Value to associate with key
+ * @param[out] chunk_id        Chunk ID of the chunk that was modified
+ * @param[out] group_id        Group ID of the group that was modified
+ * @param[out] bin_id          Bin ID that was modified
+ * @param[out] new_bin_choice  Newly chosen permutation which this bin will use
+ * @param[out] entry           Newly computed online entry to apply later with efd_apply_update
+ *
+ * @return
+ *  EFD_UPDATE_WARN_GROUP_FULL
+ *     Operation is insert, and the last available space in the key's group was just used
+ *     Future inserts may fail as groups fill up
+ *     This operation was still successful, and entry contains a valid update
+ *  EFD_UPDATE_FAILED
+ *     Either the EFD failed to find a suitable perfect hash or the group was full
+ *     This is a fatal error, and the table is now in an indeterminite state
+ *  EFD_UPDATE_NO_CHANGE
+ *     Operation resulted in no change to the table (same value already exists)
+ *  0
+ *     Insert or update was succesful, and the new efd_online_group_entry is stored in *entry
+ *
+ * @warning
+ *   Note that entry will be UNCHANGED if the update has no effect, and thus any
+ *   subsequent use of the entry content will likely be invalid
+ */
+static inline int
+efd_compute_update(struct rte_efd_table *const table, const unsigned int socket_id,
+                       const efd_key_t *const key, const efd_value_t value,
+                       uint32_t *const chunk_id, uint32_t *const group_id,
+                       uint32_t *const bin_id, uint8_t *const new_bin_choice,
+                       struct efd_online_group_entry *const entry)
+{
+	unsigned int i;
+	int status = EXIT_SUCCESS;
+	int found = 0;
+
+	efd_compute_ids(table, key, chunk_id, bin_id);
+
+	struct efd_offline_chunk_rules *const chunk = &table->offline_chunks[*chunk_id];
+	struct efd_offline_group_rules *new_group;
+	struct efd_offline_group_rules current_group_copy;
+	struct efd_offline_group_rules new_group_copy;
+
+	uint8_t current_choice = efd_get_choice(table, socket_id, *chunk_id, *bin_id);
+	uint32_t current_group_id = efd_bin_to_group[current_choice][*bin_id];
+	struct efd_offline_group_rules *const current_group = &chunk->group_rules[current_group_id];
+	uint8_t bin_size = 0;
+
+	/* Scan the current group and see if the key is already present */
+	for (i = 0; i < current_group->num_rules; i++) {
+		if (current_group->bin_id[i] == *bin_id) {
+			bin_size++;
+		}
+
+		if (found == 0 && unlikely(memcmp(&current_group->key[i], key, EFD_KEY_LEN) == 0)) {
+			/* Key is already present */
+
+			/* If previous value is same as new value, no additional work is required */
+			if (current_group->value[i] == value) {
+				return EFD_UPDATE_NO_CHANGE;
+			}
+
+			/* Save the original group state, if update fails */
+			memcpy(&current_group_copy, current_group,
+					sizeof(struct efd_offline_group_rules));
+			current_group->value[i] = value;
+			found = 1;
+		}
+	}
+
+	if (found == 0) {
+		/* Key does not exist. Insert the rule into the bin/group */
+		if (unlikely(current_group->num_rules >= EFD_MAX_GROUP_NUM_RULES)) {
+			RTE_LOG(ERR, EFD,
+					"Fatal: No room remaining for insert into "
+					"chunk %u group %u bin %u\n",
+					*chunk_id,
+					current_group_id, *bin_id);
+			return EFD_UPDATE_FAILED;
+		} else {
+			/* Save the original group state, if update fails */
+			memcpy(&current_group_copy, current_group,
+					sizeof(struct efd_offline_group_rules));
+
+			if (unlikely(current_group->num_rules == (EFD_MAX_GROUP_NUM_RULES - 1))) {
+				RTE_LOG(INFO, EFD, "Warn: Insert into last available slot in "
+							"chunk %u group %u bin %u\n", *chunk_id,
+							current_group_id, *bin_id);
+				status = EFD_UPDATE_WARN_GROUP_FULL;
+			}
+			memcpy(&current_group->key[current_group->num_rules], key, EFD_KEY_LEN);
+			current_group->value[current_group->num_rules] = value;
+			current_group->bin_id[current_group->num_rules] = *bin_id;
+			table->num_rules++;
+			current_group->num_rules++;
+			bin_size++;
+		}
+	}
+
+	/* Group need to be rebalanced when it starts to get loaded */
+	if (current_group->num_rules > EFD_MIN_BALANCED_NUM_RULES) {
+
+		/* Subtract the number of entries in the bin from the original group */
+		current_group->num_rules -= bin_size;
+
+		/*
+		 * Figure out which of the available groups that this bin can map to is the smallest
+		 * (using the current group as baseline)
+		 */
+		uint8_t smallest_choice = current_choice;
+		uint8_t smallest_size = current_group->num_rules;
+		uint32_t smallest_group_id = current_group_id;
+		unsigned char choice;
+		for (choice = 0; choice < EFD_CHUNK_NUM_BIN_TO_GROUP_SETS; choice ++) {
+			uint32_t test_group_id = efd_bin_to_group[choice][*bin_id];
+			uint32_t num_rules = chunk->group_rules[test_group_id].num_rules;
+			if (num_rules < smallest_size) {
+				smallest_choice = choice;
+				smallest_size = num_rules;
+				smallest_group_id = test_group_id;
+			}
+		}
+
+		*new_bin_choice = smallest_choice;
+		*group_id = smallest_group_id;
+		new_group = &chunk->group_rules[smallest_group_id];
+
+		if (smallest_group_id == current_group_id) {
+			new_group->num_rules += bin_size;
+			RTE_LOG(DEBUG, EFD, "chunk %u: Left bin %u (%u entries) "
+					"in group %u (%u entries)\n", *chunk_id, *bin_id,
+					bin_size, *group_id, new_group->num_rules);
+		} else {
+			/*
+			 * Remove the bin from the group it was previously assigned to
+			 * and add it to the new group
+			 */
+
+			RTE_LOG(DEBUG, EFD, "chunk %u: Moving bin %u (%u entries) "
+					"from group %u (%u entries) to %u (%u entries)\n",
+					*chunk_id, *bin_id, bin_size, current_group_id,
+					current_group->num_rules + bin_size,
+					smallest_group_id, new_group->num_rules + bin_size);
+
+			/* Save the original group state, if update fails */
+			memcpy(&new_group_copy, new_group,
+					sizeof(struct efd_offline_group_rules));
+
+			uint8_t empty_idx = 0;
+			for (i = 0; i < current_group->num_rules + bin_size; i++) {
+				/* Move keys that belong to the same bin to the new group */
+				if (current_group->bin_id[i] == *bin_id) {
+					new_group->key[new_group->num_rules] = current_group->key[i];
+					new_group->value[new_group->num_rules] = current_group->value[i];
+					new_group->bin_id[new_group->num_rules] = current_group->bin_id[i];
+					new_group->num_rules++;
+				} else {
+					if (i != empty_idx) {
+						/* Need to move this key towards the top of the array */
+						current_group->key[empty_idx] = current_group->key[i];
+						current_group->value[empty_idx] = current_group->value[i];
+						current_group->bin_id[empty_idx] = current_group->bin_id[i];
+					}
+					empty_idx++;
+				}
+
+			}
+		}
+	} else {
+		*new_bin_choice = current_choice;
+		*group_id = current_group_id;
+		new_group = current_group;
+	}
+
+	/*
+	 * Recompute the hash function for the modified group,
+	 * and return it to the caller
+	 */
+	int ret = efd_search_hash(new_group, entry);
+	if (ret != 0) {
+		RTE_LOG(ERR, EFD,
+				"Failed to find perfect hash for group containing %u entries\n",
+				new_group->num_rules);
+		/* Restore table to the previous state */
+		if (new_group != current_group)
+			memcpy(new_group, &new_group_copy,
+					sizeof(struct efd_offline_group_rules));
+		memcpy(current_group, &current_group_copy,
+				sizeof(struct efd_offline_group_rules));
+
+		return EFD_UPDATE_FAILED;
+	}
+
+	return status;
+}
+
+int
+rte_efd_update(struct rte_efd_table *const table, const unsigned socket_id,
+					const efd_key_t *const key,
+					const efd_value_t value)
+{
+	uint32_t chunk_id, group_id, bin_id;
+	uint8_t new_bin_choice;
+	struct efd_online_group_entry entry;
+
+	int status = efd_compute_update(table, socket_id, key, value,
+					&chunk_id, &group_id, &bin_id, &new_bin_choice,
+					&entry);
+	if (status == EFD_UPDATE_NO_CHANGE)
+		return EXIT_SUCCESS;
+	else if (status != EXIT_SUCCESS)
+		return status;
+	else
+		efd_apply_update(table, socket_id, chunk_id, group_id, bin_id,
+	                 new_bin_choice, &entry);
+	return status;
+}
+
+int
+rte_efd_delete(struct rte_efd_table *const table, const unsigned socket_id,
+				const efd_key_t *const key, efd_value_t *const prev_value)
+{
+	unsigned int i;
+	uint32_t chunk_id, bin_id;
+	uint8_t not_found = 1;
+
+	efd_compute_ids(table, key, &chunk_id, &bin_id);
+
+	struct efd_offline_chunk_rules *const chunk = &table->offline_chunks[chunk_id];
+
+	uint8_t current_choice = efd_get_choice(table, socket_id, chunk_id, bin_id);
+	uint32_t current_group_id = efd_bin_to_group[current_choice][bin_id];
+	struct efd_offline_group_rules *const current_group =
+					&chunk->group_rules[current_group_id];
+
+	/*
+	 * Search the current group for the specified key.
+	 * If it exists, remove it and re-pack the other values
+	 */
+	for (i = 0; i < current_group->num_rules; i++) {
+		if (not_found) {
+			/* Found key that needs to be removed */
+			if (memcmp(&current_group->key[i], key, EFD_KEY_LEN) == 0) {
+				/* Store previous value if requested by caller */
+				if (prev_value != NULL) {
+					*prev_value = current_group->value[i];
+				}
+
+				not_found = 0;
+			}
+		} else {
+			/* If the desired key has been found, need to shift other values up one */
+
+			/* Need to shift this entry back up one index */
+			current_group->key[i - 1] = current_group->key[i];
+			current_group->value[i - 1] = current_group->value[i];
+			current_group->bin_id[i - 1] = current_group->bin_id[i];
+		}
+	}
+
+	if (not_found == 0) {
+		table->num_rules--;
+		current_group->num_rules--;
+	}
+
+	return not_found;
+}
+
+
+#if (EFD_VALUE_NUM_BITS == 8 || EFD_VALUE_NUM_BITS == 16 || EFD_VALUE_NUM_BITS == 24 || EFD_VALUE_NUM_BITS == 32)
+#define EFD_LOAD_SI128(val) _mm_load_si128(val)
+#else
+#define EFD_LOAD_SI128(val) _mm_lddqu_si128(val)
+#endif
+
+static inline efd_value_t efd_lookup_internal(const struct efd_online_group_entry *const group,
+                                              const uint32_t hash_val_a, const uint32_t hash_val_b)
+{
+	efd_value_t value = 0;
+
+#if EFD_VALUE_NUM_BITS > 3 && defined(RTE_MACHINE_CPUFLAG_AVX2)
+	uint32_t byte_idx;
+
+	__m256i vhash_val_a = _mm256_set1_epi32(hash_val_a);
+	__m256i vhash_val_b = _mm256_set1_epi32(hash_val_b);
+
+	for (byte_idx = 0; byte_idx < EFD_VALUE_NUM_BITS; byte_idx += 8) {
+#if (EFD_HASHFUNC_SIZE == 8)
+		__m256i vhash_idx = _mm256_cvtepu8_epi32(EFD_LOAD_SI128((__m128i const *)
+                                                        &group->hash_idx[byte_idx]));
+#elif (EFD_HASHFUNC_SIZE == 16)
+		__m256i vhash_idx = _mm256_cvtepu16_epi32(EFD_LOAD_SI128((__m128i const *)
+		                                                         &group->hash_idx[byte_idx]));
+#endif
+#if (EFD_LOOKUPTBL_SIZE == 8)
+		__m256i vlookup_table = _mm256_cvtepu8_epi32(EFD_LOAD_SI128((__m128i const *)
+		                                             &group->lookup_table[byte_idx]));
+#elif (EFD_LOOKUPTBL_SIZE == 16)
+		__m256i vlookup_table = _mm256_cvtepu16_epi32(EFD_LOAD_SI128((__m128i const *)
+		                                              &group->lookup_table[byte_idx]));
+#endif
+		__m256i vhash = _mm256_add_epi32(vhash_val_a, _mm256_mullo_epi32(vhash_idx, vhash_val_b));
+		__m256i vbucket_idx = _mm256_srli_epi32(vhash, EFD_LOOKUPTBL_SHIFT);
+		__m256i vresult = _mm256_srlv_epi32(vlookup_table, vbucket_idx);
+		value |= (_mm256_movemask_ps((__m256)_mm256_slli_epi32(vresult, 31))
+		          & ((1 << (EFD_VALUE_NUM_BITS - byte_idx)) - 1)) << byte_idx;
+	}
+#else
+	uint32_t bit;
+
+	for (bit = 0; bit < EFD_VALUE_NUM_BITS; bit++) {
+		value <<= 1;
+
+		uint32_t h = hash_val_a + hash_val_b * group->hash_idx[EFD_VALUE_NUM_BITS - bit - 1];
+		uint16_t bucket_idx = h >> EFD_LOOKUPTBL_SHIFT;
+
+		value |= (group->lookup_table[EFD_VALUE_NUM_BITS - bit - 1] >> bucket_idx) & 0x1;
+	}
+#endif
+
+	return value;
+}
+
+efd_value_t
+rte_efd_lookup(const struct rte_efd_table *const table, const unsigned socket_id,
+                       const efd_key_t *const key)
+{
+	uint32_t chunk_id, group_id, bin_id;
+	uint8_t bin_choice;
+	const struct efd_online_chunk *const chunks = table->chunks[socket_id];
+
+	/* Determine the chunk and group location for the given key */
+	efd_compute_ids(table, key, &chunk_id, &bin_id);
+	bin_choice = efd_get_choice(table, socket_id, chunk_id, bin_id);
+	group_id = efd_bin_to_group[bin_choice][bin_id];
+
+	return efd_lookup_internal(&chunks[chunk_id].groups[group_id], EFD_HASHFUNCA(key),
+	                           EFD_HASHFUNCB(key));
+}
+
+void rte_efd_lookup_bulk(const struct rte_efd_table *const table, const unsigned socket_id,
+                     const int num_keys, const efd_key_t *const *const key_list,
+                     efd_value_t *const value_list)
+{
+	int i;
+	uint32_t chunk_id_list[EFD_BURST_MAX];
+	uint32_t bin_id_list[EFD_BURST_MAX];
+	uint8_t bin_choice_list[EFD_BURST_MAX];
+	uint32_t group_id_list[EFD_BURST_MAX];
+
+	struct efd_online_chunk *chunks = table->chunks[socket_id];
+
+	for (i = 0; i < num_keys; i++) {
+		efd_compute_ids(table, key_list[i], &chunk_id_list[i], &bin_id_list[i]);
+		rte_prefetch0(&chunks[chunk_id_list[i]].bin_choice_list);
+	}
+
+	for (i = 0; i < num_keys; i++) {
+		bin_choice_list[i] = efd_get_choice(table, socket_id, chunk_id_list[i], bin_id_list[i]);
+		group_id_list[i] = efd_bin_to_group[bin_choice_list[i]][bin_id_list[i]];
+		rte_prefetch0(&chunks[chunk_id_list[i]].groups[group_id_list[i]]);
+	}
+
+	for (i = 0; i < num_keys; i++) {
+		value_list[i] = efd_lookup_internal(&chunks[chunk_id_list[i]].groups[group_id_list[i]],
+		                                    EFD_HASHFUNCA(key_list[i]), EFD_HASHFUNCB(key_list[i]));
+	}
+}
+
diff --git a/lib/librte_efd/rte_efd.h b/lib/librte_efd/rte_efd.h
new file mode 100644
index 0000000..aacb62a
--- /dev/null
+++ b/lib/librte_efd/rte_efd.h
@@ -0,0 +1,423 @@
+/*-
+ *   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.
+ */
+
+#ifndef __LIBEFD_H__
+#define __LIBEFD_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_hash_crc.h>
+#include "rte_lcore.h"
+
+/**************************************************************************************************
+ * User selectable constants
+ *************************************************************************************************/
+
+/*
+ * If possible, best lookup performance will be achieved by ensuring that
+ * the entire table fits in the L3 cache.
+ *
+ * Some formulas for calculating various sizes are listed below:
+ *
+ * # of chunks =
+ *   2 ^ (ceiling(log2((requested # of rules) /
+ *   		(EFD_CHUNK_NUM_GROUPS * EFD_TARGET_GROUP_NUM_RULES))))
+ *
+ * Target # of rules = (# of chunks) * EFD_CHUNK_NUM_GROUPS *
+ * 			EFD_TARGET_GROUP_NUM_RULES
+ *
+ * Group Size (in bytes) = (EFD_HASHFUNC_SIZE + EFD_LOOKUPTBL_SIZE + 8) / 8
+ *
+ * Table size (in bytes) = EFD_VALUE_NUM_BITS * (# of chunks) *
+ * 			EFD_CHUNK_NUM_GROUPS * (group size)
+ */
+
+/**
+ * !!! This parameter should be adjusted for your application !!!
+ *
+ * This parameter adjusts the number of bits of value that can be stored in the table.
+ * For example, setting the number of bits to 3 will allow storing 8 values
+ * in the table (between 0 and 7).
+ *
+ * This number directly affects the performance of both lookups and insertion.
+ * In general, performance decreases as more bits are stored in the table.
+ *
+ * This number is directly proportional to the size of the online region
+ * used for lookups.
+ *
+ * Note that due to the way the CPU operates on memory, best lookup performance
+ * will be achieved when EFD_VALUE_NUM_BITS is a multiple of 8.
+ * These values align the hash indexes on 16-byte boundaries.
+ * The greatest performance drop is moving from 8->9 bits, 16->17 bits, etc.
+ *
+ * This value must be between 1 and 32
+ */
+#ifndef EFD_VALUE_NUM_BITS
+#define EFD_VALUE_NUM_BITS (8)
+#endif
+
+/**
+ * !!! This parameter should be adjusted for your application !!!
+ *
+ * This parameter adjusts the size of the keys stored in the table (in bytes).
+ * There are no restrictions around valid key sizes, and larger keys
+ * do not change the size of the online table used for lookups.
+ */
+#ifndef EFD_KEY_LEN
+#define EFD_KEY_LEN (4)
+#endif
+
+/*
+ * EFD_TARGET_GROUP_NUM_RULES:
+ *   Adjusts how many groups/chunks are allocated at table creation time
+ *   to support the requested number of rules. Higher values pack entries
+ *   more tightly in memory, resulting in a smaller memory footprint
+ *   for the online table.
+ *   This comes at the cost of lower insert/update performance.
+ *
+ * EFD_MAX_GROUP_NUM_RULES:
+ *   This adjusts the amount of offline memory allocated to store key/value
+ *   pairs for the table. The recommended numbers are upper-bounds for this parameter
+ *   - any higher and it becomes very unlikely that a perfect hash function
+ *   can be found for that group size. This value should be at
+ *   least 40% larger than EFD_TARGET_GROUP_NUM_RULES
+ *
+ * Recommended values for various lookuptable and hashfunc sizes are:
+ *
+ *   HASH_FUNC_SIZE = 16, LOOKUPTBL_SIZE = 16:
+ *     EFD_TARGET_GROUP_NUM_RULES = 22
+ *     EFD_MAX_GROUP_NUM_RULES = 28
+ */
+#define EFD_TARGET_GROUP_NUM_RULES (22)
+#define EFD_MAX_GROUP_NUM_RULES (28)
+
+#define EFD_MIN_BALANCED_NUM_RULES      5
+
+/**
+ * Adjusts the number of bits used to store the hash function index
+ */
+#define EFD_HASHFUNC_SIZE (16)
+
+/**
+ * Adjusts the number of bits used to store the lookup table result map
+ */
+#define EFD_LOOKUPTBL_SIZE (16)
+
+/**
+ * Maximum number of keys that can be looked up in one call to efd_lookup_bulk
+ */
+#ifndef EFD_BURST_MAX
+#define EFD_BURST_MAX (32)
+#endif
+
+/*****************************************************************************
+ * Fixed constants
+ *****************************************************************************/
+
+/** Maximum number of characters in efd name.*/
+#define RTE_EFD_NAMESIZE			32
+
+/* These parameters are fixed by the efd_bin_to_group balancing table */
+#define EFD_CHUNK_NUM_GROUPS (64)
+#define EFD_CHUNK_NUM_BINS   (256)
+#define EFD_CHUNK_NUM_BIN_TO_GROUP_SETS (EFD_CHUNK_NUM_BINS / EFD_CHUNK_NUM_GROUPS)
+
+/*
+ * Target number of rules that each chunk is created to handle.
+ * Used when initially allocating the table
+ */
+#define EFD_TARGET_CHUNK_NUM_RULES  (EFD_CHUNK_NUM_GROUPS * EFD_TARGET_GROUP_NUM_RULES)
+
+/** This is fixed based on the bin_to_group permutation array */
+#define EFD_MAX_GROUP_NUM_BINS (16)
+
+/**
+ * The end of the chunks array needs some extra padding to ensure
+ * that vectorization over-reads on the last online chunk stay within allocated memory
+ */
+#define EFD_NUM_CHUNK_PADDING_BYTES (256)
+
+typedef uint16_t efd_lookuptbl_t;
+#define EFD_LOOKUPTBL_SHIFT (32 - 4)
+
+typedef uint16_t efd_hashfunc_t;
+
+#if (EFD_VALUE_NUM_BITS > 0 && EFD_VALUE_NUM_BITS <= 8)
+typedef uint8_t efd_value_t;
+#elif (EFD_VALUE_NUM_BITS > 8 && EFD_VALUE_NUM_BITS <= 16)
+typedef uint16_t efd_value_t;
+#elif (EFD_VALUE_NUM_BITS > 16 && EFD_VALUE_NUM_BITS <= 32)
+typedef uint32_t efd_value_t;
+#else
+#error("EFD_VALUE_NUM_BITS must be in the range [1:32]")
+#endif
+
+#if (EFD_KEY_LEN <= 0)
+#error("EFD_KEY_LEN must be an integer greater than 0")
+#endif
+
+/* Variable size keys */
+typedef struct {
+	uint8_t bytes[EFD_KEY_LEN];
+} efd_key_t;
+
+/*****************************************************************************
+ * Offline region structures
+ *****************************************************************************/
+
+struct efd_offline_group_rules {
+	/** Sum of the number of rules in all bins assigned to this group */
+	uint32_t num_rules;
+
+	/* Indexes >= num_rules are unused/undefined */
+	efd_key_t key[EFD_MAX_GROUP_NUM_RULES];
+	efd_value_t value[EFD_MAX_GROUP_NUM_RULES];
+
+	/* Stores the bin for each correspending key to avoid having to recompute it */
+	uint8_t bin_id[EFD_MAX_GROUP_NUM_RULES];
+};
+
+struct efd_offline_chunk_rules {
+	/** Number of rules in the entire chunk; used to detect unbalanced groups */
+	uint16_t num_rules;
+
+	struct efd_offline_group_rules group_rules[EFD_CHUNK_NUM_GROUPS];
+};
+
+/*****************************************************************************
+ * Online region structures
+ *****************************************************************************/
+
+struct efd_online_group_entry {
+	efd_hashfunc_t hash_idx[EFD_VALUE_NUM_BITS];
+	efd_lookuptbl_t lookup_table[EFD_VALUE_NUM_BITS];
+} __attribute__((__packed__));
+
+/**
+ * A single chunk record, containing EFD_TARGET_CHUNK_NUM_RULES rules.
+ * Those rules are split into EFD_CHUNK_NUM_GROUPS groups per chunk.
+ */
+struct efd_online_chunk {
+	/**
+	 * This is a packed indirection index into the 'groups' array.
+	 * Each byte contains four two-bit values which index into
+	 * the efd_bin_to_group array.
+	 * The efd_bin_to_group array returns the index into the groups array
+	 */
+	uint8_t bin_choice_list[(EFD_CHUNK_NUM_BINS * 2 + 7) / 8];
+
+	struct efd_online_group_entry groups[EFD_CHUNK_NUM_GROUPS];
+} __attribute__((__packed__));
+
+struct rte_efd_table {
+	char name[RTE_EFD_NAMESIZE];    /**< Name of the efd table. */
+	/**
+	 * Static maximum number of entries the table was constructed to hold
+	 */
+	uint32_t max_num_rules;
+
+	/**
+	 * Number of entries currently in the table
+	 *
+	 * Note that this is only correct if all group modifications/insertions
+	 * were performed using this table structure.
+	 * If groups are updated by applying chunks computed elsewhere,
+	 * there is no way to know how many rules are in the table
+	 */
+	uint32_t num_rules;
+
+	/**
+	 * Number of chunks in the table needed to support num_rules.
+	 * Each chunk contains EFD_TARGET_CHUNK_NUM_RULES entries.
+	 * When the table is constructed, num_chunks is set such that
+	 *   num_chunks * EFD_TARGET_CHUNK_NUM_RULES > num_rules
+	 * and num_chunks is always a power of two.
+	 */
+	uint32_t num_chunks;
+
+	/**
+	 * For faster math operations, rather than divide by num_chunks,
+	 * shift by num_chunks_shift
+	 */
+	uint32_t num_chunks_shift;
+
+	/**
+	 * Dynamic array of size num_chunks of chunk records
+	 */
+	struct efd_online_chunk *chunks[RTE_MAX_NUMA_NODES];
+
+	/**
+	 * Dynamic array of size num_chunks of key-value pairs
+	 */
+	struct efd_offline_chunk_rules *offline_chunks;
+};
+
+
+/*****************************************************************************
+ * Common user functions
+ *****************************************************************************/
+
+/**
+ * Creates an EFD table with a single offline region and multiple per-socket
+ * internally-managed copies of the online table used for lookups
+ *
+ * @param[in] max_num_rules
+ *   Minimum number of rules the table should be sized to hold. Will be rounded up to the next
+ *   smallest valid table size
+ * @param[in] online_cpu_socket_bitmask
+ *   Bitmask specifying which sockets should get a copy of the online table. LSB = socket 0, etc.
+ * @param[in] offline_cpu_socket
+ *   Identifies the socket where the offline table will be allocated (and most efficiently accessed
+ *   in the case of updates/insertions)
+ *
+ * @return
+ *   EFD table, or NULL if table allocation failed or the bitmask is invalid
+ */
+struct rte_efd_table *
+rte_efd_create(const char *name, uint32_t max_num_rules, uint8_t online_cpu_socket_bitmask,
+                             uint8_t offline_cpu_socket);
+
+/**
+ * Releases the resources from an EFD table
+ *
+ * @param table
+ *   Table to free
+ */
+void
+rte_efd_free(struct rte_efd_table *table);
+
+/**
+ * Find an existing EFD table object and return a pointer to it.
+ *
+ * @param name
+ *   Name of the EFD table as passed to rte_hash_create()
+ * @return
+ *   Pointer to EFD table or NULL if object not found
+ *   with rte_errno set appropriately. Possible rte_errno values include:
+ *    - ENOENT - value not available for return
+ */
+struct rte_efd_table*
+rte_efd_find_existing(const char *name);
+
+#define EFD_UPDATE_WARN_GROUP_FULL   (1)
+#define EFD_UPDATE_NO_CHANGE         (2)
+#define EFD_UPDATE_FAILED            (3)
+
+/**
+ * Computes an updated table entry for the supplied key/value pair.
+ * The update is then immediately applied to the provided table and all socket-local
+ * copies of the chunks are updated.
+ *
+ * @param[in]  table     EFD table to reference
+ * @param[in]  socket_id Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param[in]  key       EFD table key to modify
+ * @param[in]  value     Value to associate with the key
+ *
+ * @return
+ *  EFD_UPDATE_WARN_GROUP_FULL
+ *     Operation is insert, and the last available space in the key's group was just used
+ *     Future inserts may fail as groups fill up
+ *     This operation was still successful, and entry contains a valid update
+ *  EFD_UPDATE_FAILED
+ *     Either the EFD failed to find a suitable perfect hash or the group was full
+ *     This is a fatal error, and the table is now in an indeterminite state
+ *  EFD_UPDATE_NO_CHANGE
+ *     Operation resulted in no change to the table (same value already exists)
+ *  0 - success
+ */
+int
+rte_efd_update(struct rte_efd_table *table, unsigned socket_id, const efd_key_t *key,
+               efd_value_t value);
+
+/**
+ * Removes any value currently associated with the specified key from the table
+ *
+ * @param[in]  table      EFD table to reference
+ * @param[in]  socket_id  Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param[in]  key        EFD table key to delete
+ * @param[out] prev_value If not NULL, will store the previous value here before deleting it
+ *
+ * @return
+ *   0 - successfully found and deleted the key
+ *   nonzero otherwise
+ */
+int
+rte_efd_delete(struct rte_efd_table *table, unsigned socket_id, const efd_key_t *key,
+               efd_value_t *prev_value);
+
+/**
+ * Looks up the value associated with a key
+ *
+ * NOTE: Lookups will *always* succeed - this is a property of using a perfect hash table.
+ * If the specified key was never inserted, a pseudorandom answer will be returned. There is no
+ * way to know based on the lookup if the key was ever inserted originally - this must be tracked
+ * elsewhere.
+ *
+ * @param[in]  table      EFD table to reference
+ * @param[in]  socket_id  Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param[in]  key        EFD table key to look up
+ *
+ * @return
+ *  Value associated with the key, or random junk if they key was never inserted
+ */
+efd_value_t
+rte_efd_lookup(const struct rte_efd_table *table, unsigned socket_id,
+                       const efd_key_t *key);
+
+/**
+ * Looks up the value associated with several keys.
+ *
+ * NOTE: Lookups will *always* succeed - this is a property of using a perfect hash table.
+ * If the specified key was never inserted, a pseudorandom answer will be returned. There is no
+ * way to know based on the lookup if the key was ever inserted originally - this must be tracked
+ * elsewhere.
+ *
+ * @param[in]  table      EFD table to reference
+ * @param[in]  socket_id  Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param[in]  num_keys   Number of keys in the key_list array, must be less than EFD_BURST_MAX
+ * @param[in]  key_list   Array of num_keys pointers which point to keys to look up
+ * @param[out] value_list Array of size num_keys where lookup values will be stored
+ */
+void
+rte_efd_lookup_bulk(const struct rte_efd_table *table, unsigned socket_id,
+                     int num_keys, const efd_key_t *const *key_list,
+                     efd_value_t *value_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBEFD_H__ */
+
diff --git a/lib/librte_efd/rte_efd_version.map b/lib/librte_efd/rte_efd_version.map
new file mode 100644
index 0000000..91810b3
--- /dev/null
+++ b/lib/librte_efd/rte_efd_version.map
@@ -0,0 +1,12 @@
+DPDK_17.02 {
+	global:
+
+	rte_efd_create;
+	rte_efd_delete;
+	rte_efd_free;
+	rte_efd_lookup;
+	rte_efd_lookup_bulk;
+	rte_efd_update;
+
+	local: *;
+};
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 72c2fe7..60535c0 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -86,6 +86,7 @@ _LDLIBS-y += --whole-archive
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_HASH)           += -lrte_hash
+_LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
 _LDLIBS-$(CONFIG_RTE_LIBRTE_VHOST)          += -lrte_vhost
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KVARGS)         += -lrte_kvargs
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/2] drivers: add name alias registration for rte_driver
From: Jan Blunck @ 2016-10-20  9:37 UTC (permalink / raw)
  To: dev; +Cc: pablo.de.lara.guarch, thomas.monjalon, yuanhan.liu, john.mcnamara

This adds infrastructure for drivers to allow being requested by an alias
so that a renamed driver can still get loaded by its legacy name.

Signed-off-by: Jan Blunck <jblunck@infradead.org>
---
 lib/librte_eal/common/eal_common_vdev.c  | 8 ++++++++
 lib/librte_eal/common/include/rte_dev.h  | 1 +
 lib/librte_eal/common/include/rte_vdev.h | 5 +++++
 3 files changed, 14 insertions(+)

diff --git a/lib/librte_eal/common/eal_common_vdev.c b/lib/librte_eal/common/eal_common_vdev.c
index 8b05f50..0ff2377 100644
--- a/lib/librte_eal/common/eal_common_vdev.c
+++ b/lib/librte_eal/common/eal_common_vdev.c
@@ -79,6 +79,14 @@ rte_eal_vdev_init(const char *name, const char *args)
 			return driver->probe(name, args);
 	}
 
+	/* Give new names precedence over aliases. */
+	TAILQ_FOREACH(driver, &vdev_driver_list, next) {
+		if (driver->driver.alias &&
+		    !strncmp(driver->driver.alias, name,
+			    strlen(driver->driver.alias)))
+			return driver->probe(name, args);
+	}
+
 	RTE_LOG(ERR, EAL, "no driver found for %s\n", name);
 	return -EINVAL;
 }
diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h
index b3873bd..8840380 100644
--- a/lib/librte_eal/common/include/rte_dev.h
+++ b/lib/librte_eal/common/include/rte_dev.h
@@ -149,6 +149,7 @@ void rte_eal_device_remove(struct rte_device *dev);
 struct rte_driver {
 	TAILQ_ENTRY(rte_driver) next;  /**< Next in list. */
 	const char *name;                   /**< Driver name. */
+	const char *alias;              /**< Driver alias. */
 };
 
 /**
diff --git a/lib/librte_eal/common/include/rte_vdev.h b/lib/librte_eal/common/include/rte_vdev.h
index 97260b2..ee5060e 100644
--- a/lib/librte_eal/common/include/rte_vdev.h
+++ b/lib/librte_eal/common/include/rte_vdev.h
@@ -83,13 +83,18 @@ void rte_eal_vdrv_unregister(struct rte_vdev_driver *driver);
 
 #define RTE_PMD_REGISTER_VDEV(nm, vdrv)\
 RTE_INIT(vdrvinitfn_ ##vdrv);\
+static const char * vdrvinit_ ## nm ## _alias;\
 static void vdrvinitfn_ ##vdrv(void)\
 {\
 	(vdrv).driver.name = RTE_STR(nm);\
+	(vdrv).driver.alias = vdrvinit_ ## nm ## _alias;\
 	rte_eal_vdrv_register(&vdrv);\
 } \
 RTE_PMD_EXPORT_NAME(nm, __COUNTER__)
 
+#define RTE_PMD_REGISTER_ALIAS(nm, alias)\
+static const char * vdrvinit_ ## nm ## _alias = RTE_STR(alias)
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.6.6

^ permalink raw reply related

* [PATCH 2/2] drivers: register aliases for renamed VDEV drivers
From: Jan Blunck @ 2016-10-20  9:37 UTC (permalink / raw)
  To: dev; +Cc: pablo.de.lara.guarch, thomas.monjalon, yuanhan.liu, john.mcnamara
In-Reply-To: <1476956223-30308-1-git-send-email-jblunck@infradead.org>

This registers the legacy names of the driver being renamed in commit
2f45703c17acb943aaded9f79676fd56a72542b2.

Signed-off-by: Jan Blunck <jblunck@infradead.org>
---
 drivers/net/af_packet/rte_eth_af_packet.c | 1 +
 drivers/net/bonding/rte_eth_bond_pmd.c    | 1 +
 drivers/net/mpipe/mpipe_tilegx.c          | 2 ++
 drivers/net/null/rte_eth_null.c           | 1 +
 drivers/net/pcap/rte_eth_pcap.c           | 1 +
 drivers/net/ring/rte_eth_ring.c           | 1 +
 drivers/net/vhost/rte_eth_vhost.c         | 1 +
 drivers/net/virtio/virtio_user_ethdev.c   | 1 +
 drivers/net/xenvirt/rte_eth_xenvirt.c     | 1 +
 9 files changed, 10 insertions(+)

diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index 201c1be..ff45068 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -895,6 +895,7 @@ static struct rte_vdev_driver pmd_af_packet_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_af_packet, pmd_af_packet_drv);
+RTE_PMD_REGISTER_ALIAS(net_af_packet, eth_af_packet);
 RTE_PMD_REGISTER_PARAM_STRING(net_af_packet,
 	"iface=<string> "
 	"qpairs=<int> "
diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c
index 9e38ec9..9df245e 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -2560,6 +2560,7 @@ static struct rte_vdev_driver bond_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_bonding, bond_drv);
+RTE_PMD_REGISTER_ALIAS(net_bonding, eth_bond);
 
 RTE_PMD_REGISTER_PARAM_STRING(net_bonding,
 	"slave=<ifc> "
diff --git a/drivers/net/mpipe/mpipe_tilegx.c b/drivers/net/mpipe/mpipe_tilegx.c
index adf299b..fbbbb00 100644
--- a/drivers/net/mpipe/mpipe_tilegx.c
+++ b/drivers/net/mpipe/mpipe_tilegx.c
@@ -1632,7 +1632,9 @@ static struct rte_vdev_driver pmd_mpipe_gbe_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_mpipe_xgbe, pmd_mpipe_xgbe_drv);
+RTE_PMD_REGISTER_ALIAS(net_mpipe_xgbe, xgbe);
 RTE_PMD_REGISTER_VDEV(net_mpipe_gbe, pmd_mpipe_gbe_drv);
+RTE_PMD_REGISTER_ALIAS(net_mpipe_gbe, gbe);
 
 static void __attribute__((constructor, used))
 mpipe_init_contexts(void)
diff --git a/drivers/net/null/rte_eth_null.c b/drivers/net/null/rte_eth_null.c
index 0b7cc37..836d982 100644
--- a/drivers/net/null/rte_eth_null.c
+++ b/drivers/net/null/rte_eth_null.c
@@ -692,6 +692,7 @@ static struct rte_vdev_driver pmd_null_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_null, pmd_null_drv);
+RTE_PMD_REGISTER_ALIAS(net_null, eth_null);
 RTE_PMD_REGISTER_PARAM_STRING(net_null,
 	"size=<int> "
 	"copy=<int>");
diff --git a/drivers/net/pcap/rte_eth_pcap.c b/drivers/net/pcap/rte_eth_pcap.c
index 0c4711d..0162f44 100644
--- a/drivers/net/pcap/rte_eth_pcap.c
+++ b/drivers/net/pcap/rte_eth_pcap.c
@@ -1065,6 +1065,7 @@ static struct rte_vdev_driver pmd_pcap_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_pcap, pmd_pcap_drv);
+RTE_PMD_REGISTER_ALIAS(net_pcap, eth_pcap);
 RTE_PMD_REGISTER_PARAM_STRING(net_pcap,
 	ETH_PCAP_RX_PCAP_ARG "=<string> "
 	ETH_PCAP_TX_PCAP_ARG "=<string> "
diff --git a/drivers/net/ring/rte_eth_ring.c b/drivers/net/ring/rte_eth_ring.c
index ee1fb76..6d2a8c1 100644
--- a/drivers/net/ring/rte_eth_ring.c
+++ b/drivers/net/ring/rte_eth_ring.c
@@ -629,5 +629,6 @@ static struct rte_vdev_driver pmd_ring_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_ring, pmd_ring_drv);
+RTE_PMD_REGISTER_ALIAS(net_ring, eth_ring);
 RTE_PMD_REGISTER_PARAM_STRING(net_ring,
 	ETH_RING_NUMA_NODE_ACTION_ARG "=name:node:action(ATTACH|CREATE)");
diff --git a/drivers/net/vhost/rte_eth_vhost.c b/drivers/net/vhost/rte_eth_vhost.c
index 6f58476..766d4ef 100644
--- a/drivers/net/vhost/rte_eth_vhost.c
+++ b/drivers/net/vhost/rte_eth_vhost.c
@@ -1244,6 +1244,7 @@ static struct rte_vdev_driver pmd_vhost_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_vhost, pmd_vhost_drv);
+RTE_PMD_REGISTER_ALIAS(net_vhost, eth_vhost);
 RTE_PMD_REGISTER_PARAM_STRING(net_vhost,
 	"iface=<ifc> "
 	"queues=<int>");
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index bfdc3d0..406beea 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -479,6 +479,7 @@ static struct rte_vdev_driver virtio_user_driver = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_virtio_user, virtio_user_driver);
+RTE_PMD_REGISTER_ALIAS(net_virtio_user, virtio_user);
 RTE_PMD_REGISTER_PARAM_STRING(net_virtio_user,
 	"path=<path> "
 	"mac=<mac addr> "
diff --git a/drivers/net/xenvirt/rte_eth_xenvirt.c b/drivers/net/xenvirt/rte_eth_xenvirt.c
index 5a897b9..c08a056 100644
--- a/drivers/net/xenvirt/rte_eth_xenvirt.c
+++ b/drivers/net/xenvirt/rte_eth_xenvirt.c
@@ -765,5 +765,6 @@ static struct rte_vdev_driver pmd_xenvirt_drv = {
 };
 
 RTE_PMD_REGISTER_VDEV(net_xenvirt, pmd_xenvirt_drv);
+RTE_PMD_REGISTER_ALIAS(net_xenvirt, eth_xenvirt);
 RTE_PMD_REGISTER_PARAM_STRING(net_xenvirt,
 	"mac=<mac addr>");
-- 
2.6.6

^ permalink raw reply related

* Re: [PATCH 1/3] mbuf: embedding timestamp into the packet
From: Jan Blunck @ 2016-10-20 10:57 UTC (permalink / raw)
  To: Oleg Kuporosov; +Cc: Ananyev, Konstantin, Olivier Matz, dev@dpdk.org
In-Reply-To: <AM4PR0501MB2785719BC5F3F80A197318CACFD50@AM4PR0501MB2785.eurprd05.prod.outlook.com>

On Thu, Oct 20, 2016 at 4:03 AM, Oleg Kuporosov <olegk@mellanox.com> wrote:
> Hello Konstantin,
>
>>
>> My vote also would be to have timestamp in the second cache line.
>> About moving seqn to the 2-nd cache line too - that's probably a fair point.
>
> It may impact throughput till ~6% for applications required such embedded
> Timestamps.
>
>> About the rest of the patch:
>> Do you really need to put that code into the PMDs itself?
>> Can't the same result be achieved by using RX callbacks?
>> Again that approach would work with any PMD and there would be no need
>> to modify PMD code itself.
>
> Correct, the approach with using callbacs (rte_eth_timesync_read_[r|t]x_timestamp())
> Has also some Cons for this use case:
> - FSI needs the most accurate stamping as possible by reasons were described in
> Cover letter

>From my experience this is only true if there is near-zero performance
impact. From my perspective this is only relevant if the used hardware
supports offloading of writing the timestamps. Everything else is a
huge impact if its unconditionally enabled.

The regulatory requirements are already covered by the exchange
protocols which means that timestamps are already present in the
network packet payload (generated by the exchange trading system
and/or the trading application itself). In the end it is the exchange
itself and its members that are regulated. I can see that this might
be interesting for exchange members allowing sponsored naked access
(for non-exchange members) to generate data that they are not
front-running their clients.

I doubt that this non-functional requirement is important enough to
sacrifice the functional requirement of supporting QinQ.

> - callback will be called from user app and so you have to take into account
> Difference between time when packet was released by NIC and callback call

Have you looked at using dedicated preallocated trace buffers that are
filled with timestamps values? This should work fine for getting some
inside into the latency between application readiness and the actual
time the burst happened.

Thanks,
Jan

> - such difference is not easy to estimate correctly due to dependency on CPU type,
> Its frequency and current load conditions
> - even estimated it would be hard without additional performance penalty to align
> Packet with timestamp, taking account that call may actually placed from
> Different thread or even process.
>
> It looks the least impacting and correct way is to have timestamp in rte_mbuf and fill
> It in Rx burst procedure.
>
>> Another thing, that I am in doubt why to use system time?
>> Wouldn't raw HW TSC value (rte_rdtsc()) do here?
>
> System time is being used for periodic clock synchronization between wall
> clock and NIC to estimate NIC clock deviation. It is in assumption the system itself is
> synchronized by PTP from master clock. It is run on context of control thread.
>
> Thanks,
> Oleg.

^ permalink raw reply

* DPDK Summit Userspace - streaming live
From: St Leger, Jim @ 2016-10-20 11:04 UTC (permalink / raw)
  To: dev@dpdk.org

If you couldn't make it to the Userspace event we are streaming it live on Periscope. 
https://www.periscope.tv/jimstleger

The video quality isn't the best, but maybe some talks are worth listening to. The audio quality seems okay. 

The agenda and most of the presentations are posted online. https://dpdksummit.com/us/en/events

Once the event is finished high quality video recordings will also get posted. 

Enjoy. 
Jim

^ permalink raw reply


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