diff mbox series

[net-next,v2,10/12] net: dpaa2-eth: AF_XDP RX zero copy support

Message ID 20220923154556.721511-11-ioana.ciornei@nxp.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: dpaa2-eth: AF_XDP zero-copy support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 11 of 11 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Ioana Ciornei Sept. 23, 2022, 3:45 p.m. UTC
From: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>

This patch adds the support for receiving packets via the AF_XDP
zero-copy mechanism in the dpaa2-eth driver. The support is available
only on the LX2160A SoC and variants because we are relying on the HW
capability to associate a buffer pool to a specific queue (QDBIN), only
available on newer WRIOP versions.

On the control path, the dpaa2_xsk_enable_pool() function is responsible
to allocate a buffer pool (BP), setup this new BP to be used only on the
requested queue and change the consume function to point to the XSK ZC
one.
We are forced to call dev_close() in order to change the queue to buffer
pool association (dpaa2_xsk_set_bp_per_qdbin) . This also works in our
favor since at dev_close() the buffer pools will be drained and at the
later dev_open() call they will be again seeded, this time with buffers
allocated from the XSK pool if needed.

On the data path, a new software annotation type is defined to be used
only for the XSK scenarios. This will enable us to pass keep necessary
information about a packet buffer between the moment in which it was
seeded and when it's received by the driver. In the XSK case, we are
keeping the associated xdp_buff.
Depending on the action returned by the BPF program, we will do the
following:
 - XDP_PASS: copy the contents of the packet into a brand new skb,
   recycle the initial buffer.
 - XDP_TX: just enqueue the same frame descriptor back into the Tx path,
   the buffer will get automatically released into the initial BP.
 - XDP_REDIRECT: call xdp_do_redirect() and exit.

Signed-off-by: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
---
Changes in v2:
 - none

 MAINTAINERS                                   |   1 +
 drivers/net/ethernet/freescale/dpaa2/Makefile |   2 +-
 .../net/ethernet/freescale/dpaa2/dpaa2-eth.c  | 131 ++++---
 .../net/ethernet/freescale/dpaa2/dpaa2-eth.h  |  36 +-
 .../net/ethernet/freescale/dpaa2/dpaa2-xsk.c  | 327 ++++++++++++++++++
 5 files changed, 452 insertions(+), 45 deletions(-)
 create mode 100644 drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c

Comments

Paolo Abeni Sept. 27, 2022, 1:09 p.m. UTC | #1
On Fri, 2022-09-23 at 18:45 +0300, Ioana Ciornei wrote:
> From: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>
> 
> This patch adds the support for receiving packets via the AF_XDP
> zero-copy mechanism in the dpaa2-eth driver. The support is available
> only on the LX2160A SoC and variants because we are relying on the HW
> capability to associate a buffer pool to a specific queue (QDBIN), only
> available on newer WRIOP versions.
> 
> On the control path, the dpaa2_xsk_enable_pool() function is responsible
> to allocate a buffer pool (BP), setup this new BP to be used only on the
> requested queue and change the consume function to point to the XSK ZC
> one.
> We are forced to call dev_close() in order to change the queue to buffer
> pool association (dpaa2_xsk_set_bp_per_qdbin) . This also works in our
> favor since at dev_close() the buffer pools will be drained and at the
> later dev_open() call they will be again seeded, this time with buffers
> allocated from the XSK pool if needed.
> 
> On the data path, a new software annotation type is defined to be used
> only for the XSK scenarios. This will enable us to pass keep necessary
> information about a packet buffer between the moment in which it was
> seeded and when it's received by the driver. In the XSK case, we are
> keeping the associated xdp_buff.
> Depending on the action returned by the BPF program, we will do the
> following:
>  - XDP_PASS: copy the contents of the packet into a brand new skb,
>    recycle the initial buffer.
>  - XDP_TX: just enqueue the same frame descriptor back into the Tx path,
>    the buffer will get automatically released into the initial BP.
>  - XDP_REDIRECT: call xdp_do_redirect() and exit.
> 
> Signed-off-by: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>
> Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
> ---
> Changes in v2:
>  - none
> 
>  MAINTAINERS                                   |   1 +
>  drivers/net/ethernet/freescale/dpaa2/Makefile |   2 +-
>  .../net/ethernet/freescale/dpaa2/dpaa2-eth.c  | 131 ++++---
>  .../net/ethernet/freescale/dpaa2/dpaa2-eth.h  |  36 +-
>  .../net/ethernet/freescale/dpaa2/dpaa2-xsk.c  | 327 ++++++++++++++++++
>  5 files changed, 452 insertions(+), 45 deletions(-)
>  create mode 100644 drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1415a1498d68..004411566f48 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6290,6 +6290,7 @@ F:	drivers/net/ethernet/freescale/dpaa2/Kconfig
>  F:	drivers/net/ethernet/freescale/dpaa2/Makefile
>  F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-eth*
>  F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
> +F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk*
>  F:	drivers/net/ethernet/freescale/dpaa2/dpkg.h
>  F:	drivers/net/ethernet/freescale/dpaa2/dpmac*
>  F:	drivers/net/ethernet/freescale/dpaa2/dpni*
> diff --git a/drivers/net/ethernet/freescale/dpaa2/Makefile b/drivers/net/ethernet/freescale/dpaa2/Makefile
> index 3d9842af7f10..1b05ba8d1cbf 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/Makefile
> +++ b/drivers/net/ethernet/freescale/dpaa2/Makefile
> @@ -7,7 +7,7 @@ obj-$(CONFIG_FSL_DPAA2_ETH)		+= fsl-dpaa2-eth.o
>  obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)	+= fsl-dpaa2-ptp.o
>  obj-$(CONFIG_FSL_DPAA2_SWITCH)		+= fsl-dpaa2-switch.o
>  
> -fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o
> +fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o dpaa2-xsk.o
>  fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o
>  fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
>  fsl-dpaa2-ptp-objs	:= dpaa2-ptp.o dprtc.o
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> index d786740d1bdd..1e94506bf9e6 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> @@ -19,6 +19,7 @@
>  #include <net/pkt_cls.h>
>  #include <net/sock.h>
>  #include <net/tso.h>
> +#include <net/xdp_sock_drv.h>
>  
>  #include "dpaa2-eth.h"
>  
> @@ -104,8 +105,8 @@ static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv)
>  	priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct;
>  }
>  
> -static void *dpaa2_iova_to_virt(struct iommu_domain *domain,
> -				dma_addr_t iova_addr)
> +void *dpaa2_iova_to_virt(struct iommu_domain *domain,
> +			 dma_addr_t iova_addr)
>  {
>  	phys_addr_t phys_addr;
>  
> @@ -279,23 +280,33 @@ static struct sk_buff *dpaa2_eth_build_frag_skb(struct dpaa2_eth_priv *priv,
>   * be released in the pool
>   */
>  static void dpaa2_eth_free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array,
> -				int count)
> +				int count, bool xsk_zc)
>  {
>  	struct device *dev = priv->net_dev->dev.parent;
> +	struct dpaa2_eth_swa *swa;
> +	struct xdp_buff *xdp_buff;
>  	void *vaddr;
>  	int i;
>  
>  	for (i = 0; i < count; i++) {
>  		vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
> -		dma_unmap_page(dev, buf_array[i], priv->rx_buf_size,
> -			       DMA_BIDIRECTIONAL);
> -		free_pages((unsigned long)vaddr, 0);
> +
> +		if (!xsk_zc) {
> +			dma_unmap_page(dev, buf_array[i], priv->rx_buf_size,
> +				       DMA_BIDIRECTIONAL);
> +			free_pages((unsigned long)vaddr, 0);
> +		} else {
> +			swa = (struct dpaa2_eth_swa *)
> +				(vaddr + DPAA2_ETH_RX_HWA_SIZE);
> +			xdp_buff = swa->xsk.xdp_buff;
> +			xsk_buff_free(xdp_buff);
> +		}
>  	}
>  }
>  
> -static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
> -				  struct dpaa2_eth_channel *ch,
> -				  dma_addr_t addr)
> +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
> +			   struct dpaa2_eth_channel *ch,
> +			   dma_addr_t addr)
>  {
>  	int retries = 0;
>  	int err;
> @@ -313,7 +324,8 @@ static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
>  	}
>  
>  	if (err) {
> -		dpaa2_eth_free_bufs(priv, ch->recycled_bufs, ch->recycled_bufs_cnt);
> +		dpaa2_eth_free_bufs(priv, ch->recycled_bufs,
> +				    ch->recycled_bufs_cnt, ch->xsk_zc);
>  		ch->buf_count -= ch->recycled_bufs_cnt;
>  	}
>  
> @@ -377,10 +389,10 @@ static void dpaa2_eth_xdp_tx_flush(struct dpaa2_eth_priv *priv,
>  	fq->xdp_tx_fds.num = 0;
>  }
>  
> -static void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
> -				  struct dpaa2_eth_channel *ch,
> -				  struct dpaa2_fd *fd,
> -				  void *buf_start, u16 queue_id)
> +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
> +			   struct dpaa2_eth_channel *ch,
> +			   struct dpaa2_fd *fd,
> +			   void *buf_start, u16 queue_id)
>  {
>  	struct dpaa2_faead *faead;
>  	struct dpaa2_fd *dest_fd;
> @@ -1652,37 +1664,63 @@ static int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable)
>  static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv,
>  			      struct dpaa2_eth_channel *ch)
>  {
> +	struct xdp_buff *xdp_buffs[DPAA2_ETH_BUFS_PER_CMD];
>  	struct device *dev = priv->net_dev->dev.parent;
>  	u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
> +	struct dpaa2_eth_swa *swa;
>  	struct page *page;
>  	dma_addr_t addr;
>  	int retries = 0;
> -	int i, err;
> -
> -	for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
> -		/* Allocate buffer visible to WRIOP + skb shared info +
> -		 * alignment padding
> -		 */
> -		/* allocate one page for each Rx buffer. WRIOP sees
> -		 * the entire page except for a tailroom reserved for
> -		 * skb shared info
> +	int i = 0, err;
> +	u32 batch;
> +
> +	/* Allocate buffers visible to WRIOP */
> +	if (!ch->xsk_zc) {
> +		for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
> +			/* Also allocate skb shared info and alignment padding.
> +			 * There is one page for each Rx buffer. WRIOP sees
> +			 * the entire page except for a tailroom reserved for
> +			 * skb shared info
> +			 */
> +			page = dev_alloc_pages(0);
> +			if (!page)
> +				goto err_alloc;
> +
> +			addr = dma_map_page(dev, page, 0, priv->rx_buf_size,
> +					    DMA_BIDIRECTIONAL);
> +			if (unlikely(dma_mapping_error(dev, addr)))
> +				goto err_map;
> +
> +			buf_array[i] = addr;
> +
> +			/* tracing point */
> +			trace_dpaa2_eth_buf_seed(priv->net_dev,
> +						 page_address(page),
> +						 DPAA2_ETH_RX_BUF_RAW_SIZE,
> +						 addr, priv->rx_buf_size,
> +						 ch->bp->bpid);
> +		}
> +	} else if (xsk_buff_can_alloc(ch->xsk_pool, DPAA2_ETH_BUFS_PER_CMD)) {
> +		/* Allocate XSK buffers for AF_XDP fast path in batches
> +		 * of DPAA2_ETH_BUFS_PER_CMD. Bail out if the UMEM cannot
> +		 * provide enough buffers at the moment
>  		 */
> -		page = dev_alloc_pages(0);
> -		if (!page)
> +		batch = xsk_buff_alloc_batch(ch->xsk_pool, xdp_buffs,
> +					     DPAA2_ETH_BUFS_PER_CMD);
> +		if (!batch)
>  			goto err_alloc;
>  
> -		addr = dma_map_page(dev, page, 0, priv->rx_buf_size,
> -				    DMA_BIDIRECTIONAL);
> -		if (unlikely(dma_mapping_error(dev, addr)))
> -			goto err_map;
> +		for (i = 0; i < batch; i++) {
> +			swa = (struct dpaa2_eth_swa *)(xdp_buffs[i]->data_hard_start +
> +						       DPAA2_ETH_RX_HWA_SIZE);
> +			swa->xsk.xdp_buff = xdp_buffs[i];
>  
> -		buf_array[i] = addr;
> +			addr = xsk_buff_xdp_get_frame_dma(xdp_buffs[i]);
> +			if (unlikely(dma_mapping_error(dev, addr)))
> +				goto err_map;
>  
> -		/* tracing point */
> -		trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page),
> -					 DPAA2_ETH_RX_BUF_RAW_SIZE,
> -					 addr, priv->rx_buf_size,
> -					 ch->bp->bpid);
> +			buf_array[i] = addr;
> +		}
>  	}
>  
>  release_bufs:
> @@ -1698,14 +1736,19 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv,
>  	 * not much else we can do about it
>  	 */
>  	if (err) {
> -		dpaa2_eth_free_bufs(priv, buf_array, i);
> +		dpaa2_eth_free_bufs(priv, buf_array, i, ch->xsk_zc);
>  		return 0;
>  	}
>  
>  	return i;
>  
>  err_map:
> -	__free_pages(page, 0);
> +	if (!ch->xsk_zc) {
> +		__free_pages(page, 0);
> +	} else {
> +		for (; i < batch; i++)
> +			xsk_buff_free(xdp_buffs[i]);
> +	}
>  err_alloc:
>  	/* If we managed to allocate at least some buffers,
>  	 * release them to hardware
> @@ -1764,8 +1807,13 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid,
>  				 int count)
>  {
>  	u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
> +	bool xsk_zc = false;
>  	int retries = 0;
> -	int ret;
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_channels; i++)
> +		if (priv->channel[i]->bp->bpid == bpid)
> +			xsk_zc = priv->channel[i]->xsk_zc;
>  
>  	do {
>  		ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count);
> @@ -1776,7 +1824,7 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid,
>  			netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n");
>  			return;
>  		}
> -		dpaa2_eth_free_bufs(priv, buf_array, ret);
> +		dpaa2_eth_free_bufs(priv, buf_array, ret, xsk_zc);
>  		retries = 0;
>  	} while (ret);
>  }
> @@ -2694,6 +2742,8 @@ static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
>  	switch (xdp->command) {
>  	case XDP_SETUP_PROG:
>  		return dpaa2_eth_setup_xdp(dev, xdp->prog);
> +	case XDP_SETUP_XSK_POOL:
> +		return dpaa2_xsk_setup_pool(dev, xdp->xsk.pool, xdp->xsk.queue_id);
>  	default:
>  		return -EINVAL;
>  	}
> @@ -2924,6 +2974,7 @@ static const struct net_device_ops dpaa2_eth_ops = {
>  	.ndo_change_mtu = dpaa2_eth_change_mtu,
>  	.ndo_bpf = dpaa2_eth_xdp,
>  	.ndo_xdp_xmit = dpaa2_eth_xdp_xmit,
> +	.ndo_xsk_wakeup = dpaa2_xsk_wakeup,
>  	.ndo_setup_tc = dpaa2_eth_setup_tc,
>  	.ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid,
>  	.ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid
> @@ -4246,8 +4297,8 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv)
>  {
>  	struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX];
>  	struct net_device *net_dev = priv->net_dev;
> +	struct dpni_pools_cfg pools_params = { 0 };
>  	struct device *dev = net_dev->dev.parent;
> -	struct dpni_pools_cfg pools_params;
>  	struct dpni_error_cfg err_cfg;
>  	int err = 0;
>  	int i;
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
> index 3c4fc46b1324..38f67b98865f 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
> @@ -130,6 +130,7 @@ enum dpaa2_eth_swa_type {
>  	DPAA2_ETH_SWA_SINGLE,
>  	DPAA2_ETH_SWA_SG,
>  	DPAA2_ETH_SWA_XDP,
> +	DPAA2_ETH_SWA_XSK,
>  	DPAA2_ETH_SWA_SW_TSO,
>  };
>  
> @@ -151,6 +152,9 @@ struct dpaa2_eth_swa {
>  			int dma_size;
>  			struct xdp_frame *xdpf;
>  		} xdp;
> +		struct {
> +			struct xdp_buff *xdp_buff;
> +		} xsk;
>  		struct {
>  			struct sk_buff *skb;
>  			int num_sg;
> @@ -429,12 +433,19 @@ enum dpaa2_eth_fq_type {
>  };
>  
>  struct dpaa2_eth_priv;
> +struct dpaa2_eth_channel;
> +struct dpaa2_eth_fq;
>  
>  struct dpaa2_eth_xdp_fds {
>  	struct dpaa2_fd fds[DEV_MAP_BULK_SIZE];
>  	ssize_t num;
>  };
>  
> +typedef void dpaa2_eth_consume_cb_t(struct dpaa2_eth_priv *priv,
> +				    struct dpaa2_eth_channel *ch,
> +				    const struct dpaa2_fd *fd,
> +				    struct dpaa2_eth_fq *fq);
> +
>  struct dpaa2_eth_fq {
>  	u32 fqid;
>  	u32 tx_qdbin;
> @@ -447,10 +458,7 @@ struct dpaa2_eth_fq {
>  	struct dpaa2_eth_channel *channel;
>  	enum dpaa2_eth_fq_type type;
>  
> -	void (*consume)(struct dpaa2_eth_priv *priv,
> -			struct dpaa2_eth_channel *ch,
> -			const struct dpaa2_fd *fd,
> -			struct dpaa2_eth_fq *fq);
> +	dpaa2_eth_consume_cb_t *consume;
>  	struct dpaa2_eth_fq_stats stats;
>  
>  	struct dpaa2_eth_xdp_fds xdp_redirect_fds;
> @@ -486,6 +494,8 @@ struct dpaa2_eth_channel {
>  	u64 recycled_bufs[DPAA2_ETH_BUFS_PER_CMD];
>  	int recycled_bufs_cnt;
>  
> +	bool xsk_zc;
> +	struct xsk_buff_pool *xsk_pool;
>  	struct dpaa2_eth_bp *bp;
>  };
>  
> @@ -808,4 +818,22 @@ void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
>  		  struct dpaa2_eth_channel *ch,
>  		  const struct dpaa2_fd *fd,
>  		  struct dpaa2_eth_fq *fq);
> +
> +struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv);
> +void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv,
> +			 struct dpaa2_eth_bp *bp);
> +
> +void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr);
> +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
> +			   struct dpaa2_eth_channel *ch,
> +			   dma_addr_t addr);
> +
> +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
> +			   struct dpaa2_eth_channel *ch,
> +			   struct dpaa2_fd *fd,
> +			   void *buf_start, u16 queue_id);
> +
> +int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags);
> +int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid);
> +
>  #endif	/* __DPAA2_H */
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c
> new file mode 100644
> index 000000000000..2df7bffec5a7
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/* Copyright 2022 NXP
> + */
> +#include <linux/filter.h>
> +#include <linux/compiler.h>
> +#include <linux/bpf_trace.h>
> +#include <net/xdp.h>
> +#include <net/xdp_sock_drv.h>
> +
> +#include "dpaa2-eth.h"
> +
> +static void dpaa2_eth_setup_consume_func(struct dpaa2_eth_priv *priv,
> +					 struct dpaa2_eth_channel *ch,
> +					 enum dpaa2_eth_fq_type type,
> +					 dpaa2_eth_consume_cb_t *consume)
> +{
> +	struct dpaa2_eth_fq *fq;
> +	int i;
> +
> +	for (i = 0; i < priv->num_fqs; i++) {
> +		fq = &priv->fq[i];
> +
> +		if (fq->type != type)
> +			continue;
> +		if (fq->channel != ch)
> +			continue;
> +
> +		fq->consume = consume;
> +	}
> +}
> +
> +static u32 dpaa2_xsk_run_xdp(struct dpaa2_eth_priv *priv,
> +			     struct dpaa2_eth_channel *ch,
> +			     struct dpaa2_eth_fq *rx_fq,
> +			     struct dpaa2_fd *fd, void *vaddr)
> +{
> +	dma_addr_t addr = dpaa2_fd_get_addr(fd);
> +	struct bpf_prog *xdp_prog;
> +	struct xdp_buff *xdp_buff;
> +	struct dpaa2_eth_swa *swa;
> +	u32 xdp_act = XDP_PASS;
> +	int err;
> +
> +	xdp_prog = READ_ONCE(ch->xdp.prog);
> +	if (!xdp_prog)
> +		goto out;
> +
> +	swa = (struct dpaa2_eth_swa *)(vaddr + DPAA2_ETH_RX_HWA_SIZE +
> +				       ch->xsk_pool->umem->headroom);
> +	xdp_buff = swa->xsk.xdp_buff;
> +
> +	xdp_buff->data_hard_start = vaddr;
> +	xdp_buff->data = vaddr + dpaa2_fd_get_offset(fd);
> +	xdp_buff->data_end = xdp_buff->data + dpaa2_fd_get_len(fd);
> +	xdp_set_data_meta_invalid(xdp_buff);
> +	xdp_buff->rxq = &ch->xdp_rxq;
> +
> +	xsk_buff_dma_sync_for_cpu(xdp_buff, ch->xsk_pool);
> +	xdp_act = bpf_prog_run_xdp(xdp_prog, xdp_buff);
> +
> +	/* xdp.data pointer may have changed */
> +	dpaa2_fd_set_offset(fd, xdp_buff->data - vaddr);
> +	dpaa2_fd_set_len(fd, xdp_buff->data_end - xdp_buff->data);
> +
> +	if (likely(xdp_act == XDP_REDIRECT)) {
> +		err = xdp_do_redirect(priv->net_dev, xdp_buff, xdp_prog);
> +		if (unlikely(err)) {
> +			ch->stats.xdp_drop++;
> +			dpaa2_eth_recycle_buf(priv, ch, addr);
> +		} else {
> +			ch->buf_count--;
> +			ch->stats.xdp_redirect++;
> +		}
> +
> +		goto xdp_redir;
> +	}
> +
> +	switch (xdp_act) {
> +	case XDP_PASS:
> +		break;
> +	case XDP_TX:
> +		dpaa2_eth_xdp_enqueue(priv, ch, fd, vaddr, rx_fq->flowid);
> +		break;
> +	default:
> +		bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act);
> +		fallthrough;
> +	case XDP_ABORTED:
> +		trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
> +		fallthrough;
> +	case XDP_DROP:
> +		dpaa2_eth_recycle_buf(priv, ch, addr);
> +		ch->stats.xdp_drop++;
> +		break;
> +	}
> +
> +xdp_redir:
> +	ch->xdp.res |= xdp_act;
> +out:
> +	return xdp_act;
> +}
> +
> +/* Rx frame processing routine for the AF_XDP fast path */
> +static void dpaa2_xsk_rx(struct dpaa2_eth_priv *priv,
> +			 struct dpaa2_eth_channel *ch,
> +			 const struct dpaa2_fd *fd,
> +			 struct dpaa2_eth_fq *fq)
> +{
> +	dma_addr_t addr = dpaa2_fd_get_addr(fd);
> +	u8 fd_format = dpaa2_fd_get_format(fd);
> +	struct rtnl_link_stats64 *percpu_stats;
> +	u32 fd_length = dpaa2_fd_get_len(fd);
> +	struct sk_buff *skb;
> +	void *vaddr;
> +	u32 xdp_act;
> +
> +	vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
> +	percpu_stats = this_cpu_ptr(priv->percpu_stats);
> +
> +	if (fd_format != dpaa2_fd_single) {
> +		WARN_ON(priv->xdp_prog);
> +		/* AF_XDP doesn't support any other formats */
> +		goto err_frame_format;
> +	}
> +
> +	xdp_act = dpaa2_xsk_run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr);
> +	if (xdp_act != XDP_PASS) {
> +		percpu_stats->rx_packets++;
> +		percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
> +		return;
> +	}
> +
> +	/* Build skb */
> +	skb = dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, vaddr);
> +	if (!skb)
> +		/* Nothing else we can do, recycle the buffer and
> +		 * drop the frame.
> +		 */
> +		goto err_alloc_skb;
> +
> +	/* Send the skb to the Linux networking stack */
> +	dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb);
> +
> +	return;
> +
> +err_alloc_skb:
> +	dpaa2_eth_recycle_buf(priv, ch, addr);
> +err_frame_format:
> +	percpu_stats->rx_dropped++;
> +}
> +
> +static void dpaa2_xsk_set_bp_per_qdbin(struct dpaa2_eth_priv *priv,
> +				       struct dpni_pools_cfg *pools_params)
> +{
> +	int curr_bp = 0, i, j;
> +
> +	pools_params->pool_options = DPNI_POOL_ASSOC_QDBIN;
> +	for (i = 0; i < priv->num_bps; i++) {
> +		for (j = 0; j < priv->num_channels; j++)
> +			if (priv->bp[i] == priv->channel[j]->bp)
> +				pools_params->pools[curr_bp].priority_mask |= (1 << j);
> +		if (!pools_params->pools[curr_bp].priority_mask)
> +			continue;
> +
> +		pools_params->pools[curr_bp].dpbp_id = priv->bp[i]->bpid;
> +		pools_params->pools[curr_bp].buffer_size = priv->rx_buf_size;
> +		pools_params->pools[curr_bp++].backup_pool = 0;
> +	}
> +	pools_params->num_dpbp = curr_bp;
> +}
> +
> +static int dpaa2_xsk_disable_pool(struct net_device *dev, u16 qid)
> +{
> +	struct xsk_buff_pool *pool = xsk_get_pool_from_qid(dev, qid);
> +	struct dpaa2_eth_priv *priv = netdev_priv(dev);
> +	struct dpni_pools_cfg pools_params = { 0 };
> +	struct dpaa2_eth_channel *ch;
> +	int err;
> +	bool up;
> +
> +	ch = priv->channel[qid];
> +	if (!ch->xsk_pool)
> +		return -EINVAL;
> +
> +	up = netif_running(dev);
> +	if (up)
> +		dev_close(dev);
> +
> +	xsk_pool_dma_unmap(pool, 0);
> +	err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq,
> +					 MEM_TYPE_PAGE_ORDER0, NULL);
> +	if (err)
> +		netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed (err = %d)\n",
> +			   err);
> +
> +	dpaa2_eth_free_dpbp(priv, ch->bp);
> +
> +	ch->xsk_zc = false;
> +	ch->xsk_pool = NULL;
> +	ch->bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX];
> +
> +	dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_eth_rx);
> +
> +	dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params);
> +	err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params);
> +	if (err)
> +		netdev_err(dev, "dpni_set_pools() failed\n");
> +
> +	if (up) {
> +		err = dev_open(dev, NULL);
> +		if (err)
> +			return err;

Same problem of patch 7/12, it's better to avoid the device changing
status for this kind of operations - even on unlikely error paths.

Thanks,

Paolo
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 1415a1498d68..004411566f48 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6290,6 +6290,7 @@  F:	drivers/net/ethernet/freescale/dpaa2/Kconfig
 F:	drivers/net/ethernet/freescale/dpaa2/Makefile
 F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-eth*
 F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
+F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk*
 F:	drivers/net/ethernet/freescale/dpaa2/dpkg.h
 F:	drivers/net/ethernet/freescale/dpaa2/dpmac*
 F:	drivers/net/ethernet/freescale/dpaa2/dpni*
diff --git a/drivers/net/ethernet/freescale/dpaa2/Makefile b/drivers/net/ethernet/freescale/dpaa2/Makefile
index 3d9842af7f10..1b05ba8d1cbf 100644
--- a/drivers/net/ethernet/freescale/dpaa2/Makefile
+++ b/drivers/net/ethernet/freescale/dpaa2/Makefile
@@ -7,7 +7,7 @@  obj-$(CONFIG_FSL_DPAA2_ETH)		+= fsl-dpaa2-eth.o
 obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)	+= fsl-dpaa2-ptp.o
 obj-$(CONFIG_FSL_DPAA2_SWITCH)		+= fsl-dpaa2-switch.o
 
-fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o
+fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o dpaa2-xsk.o
 fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o
 fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
 fsl-dpaa2-ptp-objs	:= dpaa2-ptp.o dprtc.o
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index d786740d1bdd..1e94506bf9e6 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -19,6 +19,7 @@ 
 #include <net/pkt_cls.h>
 #include <net/sock.h>
 #include <net/tso.h>
+#include <net/xdp_sock_drv.h>
 
 #include "dpaa2-eth.h"
 
@@ -104,8 +105,8 @@  static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv)
 	priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct;
 }
 
-static void *dpaa2_iova_to_virt(struct iommu_domain *domain,
-				dma_addr_t iova_addr)
+void *dpaa2_iova_to_virt(struct iommu_domain *domain,
+			 dma_addr_t iova_addr)
 {
 	phys_addr_t phys_addr;
 
@@ -279,23 +280,33 @@  static struct sk_buff *dpaa2_eth_build_frag_skb(struct dpaa2_eth_priv *priv,
  * be released in the pool
  */
 static void dpaa2_eth_free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array,
-				int count)
+				int count, bool xsk_zc)
 {
 	struct device *dev = priv->net_dev->dev.parent;
+	struct dpaa2_eth_swa *swa;
+	struct xdp_buff *xdp_buff;
 	void *vaddr;
 	int i;
 
 	for (i = 0; i < count; i++) {
 		vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
-		dma_unmap_page(dev, buf_array[i], priv->rx_buf_size,
-			       DMA_BIDIRECTIONAL);
-		free_pages((unsigned long)vaddr, 0);
+
+		if (!xsk_zc) {
+			dma_unmap_page(dev, buf_array[i], priv->rx_buf_size,
+				       DMA_BIDIRECTIONAL);
+			free_pages((unsigned long)vaddr, 0);
+		} else {
+			swa = (struct dpaa2_eth_swa *)
+				(vaddr + DPAA2_ETH_RX_HWA_SIZE);
+			xdp_buff = swa->xsk.xdp_buff;
+			xsk_buff_free(xdp_buff);
+		}
 	}
 }
 
-static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
-				  struct dpaa2_eth_channel *ch,
-				  dma_addr_t addr)
+void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
+			   struct dpaa2_eth_channel *ch,
+			   dma_addr_t addr)
 {
 	int retries = 0;
 	int err;
@@ -313,7 +324,8 @@  static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
 	}
 
 	if (err) {
-		dpaa2_eth_free_bufs(priv, ch->recycled_bufs, ch->recycled_bufs_cnt);
+		dpaa2_eth_free_bufs(priv, ch->recycled_bufs,
+				    ch->recycled_bufs_cnt, ch->xsk_zc);
 		ch->buf_count -= ch->recycled_bufs_cnt;
 	}
 
@@ -377,10 +389,10 @@  static void dpaa2_eth_xdp_tx_flush(struct dpaa2_eth_priv *priv,
 	fq->xdp_tx_fds.num = 0;
 }
 
-static void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
-				  struct dpaa2_eth_channel *ch,
-				  struct dpaa2_fd *fd,
-				  void *buf_start, u16 queue_id)
+void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
+			   struct dpaa2_eth_channel *ch,
+			   struct dpaa2_fd *fd,
+			   void *buf_start, u16 queue_id)
 {
 	struct dpaa2_faead *faead;
 	struct dpaa2_fd *dest_fd;
@@ -1652,37 +1664,63 @@  static int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable)
 static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv,
 			      struct dpaa2_eth_channel *ch)
 {
+	struct xdp_buff *xdp_buffs[DPAA2_ETH_BUFS_PER_CMD];
 	struct device *dev = priv->net_dev->dev.parent;
 	u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
+	struct dpaa2_eth_swa *swa;
 	struct page *page;
 	dma_addr_t addr;
 	int retries = 0;
-	int i, err;
-
-	for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
-		/* Allocate buffer visible to WRIOP + skb shared info +
-		 * alignment padding
-		 */
-		/* allocate one page for each Rx buffer. WRIOP sees
-		 * the entire page except for a tailroom reserved for
-		 * skb shared info
+	int i = 0, err;
+	u32 batch;
+
+	/* Allocate buffers visible to WRIOP */
+	if (!ch->xsk_zc) {
+		for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
+			/* Also allocate skb shared info and alignment padding.
+			 * There is one page for each Rx buffer. WRIOP sees
+			 * the entire page except for a tailroom reserved for
+			 * skb shared info
+			 */
+			page = dev_alloc_pages(0);
+			if (!page)
+				goto err_alloc;
+
+			addr = dma_map_page(dev, page, 0, priv->rx_buf_size,
+					    DMA_BIDIRECTIONAL);
+			if (unlikely(dma_mapping_error(dev, addr)))
+				goto err_map;
+
+			buf_array[i] = addr;
+
+			/* tracing point */
+			trace_dpaa2_eth_buf_seed(priv->net_dev,
+						 page_address(page),
+						 DPAA2_ETH_RX_BUF_RAW_SIZE,
+						 addr, priv->rx_buf_size,
+						 ch->bp->bpid);
+		}
+	} else if (xsk_buff_can_alloc(ch->xsk_pool, DPAA2_ETH_BUFS_PER_CMD)) {
+		/* Allocate XSK buffers for AF_XDP fast path in batches
+		 * of DPAA2_ETH_BUFS_PER_CMD. Bail out if the UMEM cannot
+		 * provide enough buffers at the moment
 		 */
-		page = dev_alloc_pages(0);
-		if (!page)
+		batch = xsk_buff_alloc_batch(ch->xsk_pool, xdp_buffs,
+					     DPAA2_ETH_BUFS_PER_CMD);
+		if (!batch)
 			goto err_alloc;
 
-		addr = dma_map_page(dev, page, 0, priv->rx_buf_size,
-				    DMA_BIDIRECTIONAL);
-		if (unlikely(dma_mapping_error(dev, addr)))
-			goto err_map;
+		for (i = 0; i < batch; i++) {
+			swa = (struct dpaa2_eth_swa *)(xdp_buffs[i]->data_hard_start +
+						       DPAA2_ETH_RX_HWA_SIZE);
+			swa->xsk.xdp_buff = xdp_buffs[i];
 
-		buf_array[i] = addr;
+			addr = xsk_buff_xdp_get_frame_dma(xdp_buffs[i]);
+			if (unlikely(dma_mapping_error(dev, addr)))
+				goto err_map;
 
-		/* tracing point */
-		trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page),
-					 DPAA2_ETH_RX_BUF_RAW_SIZE,
-					 addr, priv->rx_buf_size,
-					 ch->bp->bpid);
+			buf_array[i] = addr;
+		}
 	}
 
 release_bufs:
@@ -1698,14 +1736,19 @@  static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv,
 	 * not much else we can do about it
 	 */
 	if (err) {
-		dpaa2_eth_free_bufs(priv, buf_array, i);
+		dpaa2_eth_free_bufs(priv, buf_array, i, ch->xsk_zc);
 		return 0;
 	}
 
 	return i;
 
 err_map:
-	__free_pages(page, 0);
+	if (!ch->xsk_zc) {
+		__free_pages(page, 0);
+	} else {
+		for (; i < batch; i++)
+			xsk_buff_free(xdp_buffs[i]);
+	}
 err_alloc:
 	/* If we managed to allocate at least some buffers,
 	 * release them to hardware
@@ -1764,8 +1807,13 @@  static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid,
 				 int count)
 {
 	u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
+	bool xsk_zc = false;
 	int retries = 0;
-	int ret;
+	int i, ret;
+
+	for (i = 0; i < priv->num_channels; i++)
+		if (priv->channel[i]->bp->bpid == bpid)
+			xsk_zc = priv->channel[i]->xsk_zc;
 
 	do {
 		ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count);
@@ -1776,7 +1824,7 @@  static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid,
 			netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n");
 			return;
 		}
-		dpaa2_eth_free_bufs(priv, buf_array, ret);
+		dpaa2_eth_free_bufs(priv, buf_array, ret, xsk_zc);
 		retries = 0;
 	} while (ret);
 }
@@ -2694,6 +2742,8 @@  static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
 	switch (xdp->command) {
 	case XDP_SETUP_PROG:
 		return dpaa2_eth_setup_xdp(dev, xdp->prog);
+	case XDP_SETUP_XSK_POOL:
+		return dpaa2_xsk_setup_pool(dev, xdp->xsk.pool, xdp->xsk.queue_id);
 	default:
 		return -EINVAL;
 	}
@@ -2924,6 +2974,7 @@  static const struct net_device_ops dpaa2_eth_ops = {
 	.ndo_change_mtu = dpaa2_eth_change_mtu,
 	.ndo_bpf = dpaa2_eth_xdp,
 	.ndo_xdp_xmit = dpaa2_eth_xdp_xmit,
+	.ndo_xsk_wakeup = dpaa2_xsk_wakeup,
 	.ndo_setup_tc = dpaa2_eth_setup_tc,
 	.ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid,
 	.ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid
@@ -4246,8 +4297,8 @@  static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv)
 {
 	struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX];
 	struct net_device *net_dev = priv->net_dev;
+	struct dpni_pools_cfg pools_params = { 0 };
 	struct device *dev = net_dev->dev.parent;
-	struct dpni_pools_cfg pools_params;
 	struct dpni_error_cfg err_cfg;
 	int err = 0;
 	int i;
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 3c4fc46b1324..38f67b98865f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -130,6 +130,7 @@  enum dpaa2_eth_swa_type {
 	DPAA2_ETH_SWA_SINGLE,
 	DPAA2_ETH_SWA_SG,
 	DPAA2_ETH_SWA_XDP,
+	DPAA2_ETH_SWA_XSK,
 	DPAA2_ETH_SWA_SW_TSO,
 };
 
@@ -151,6 +152,9 @@  struct dpaa2_eth_swa {
 			int dma_size;
 			struct xdp_frame *xdpf;
 		} xdp;
+		struct {
+			struct xdp_buff *xdp_buff;
+		} xsk;
 		struct {
 			struct sk_buff *skb;
 			int num_sg;
@@ -429,12 +433,19 @@  enum dpaa2_eth_fq_type {
 };
 
 struct dpaa2_eth_priv;
+struct dpaa2_eth_channel;
+struct dpaa2_eth_fq;
 
 struct dpaa2_eth_xdp_fds {
 	struct dpaa2_fd fds[DEV_MAP_BULK_SIZE];
 	ssize_t num;
 };
 
+typedef void dpaa2_eth_consume_cb_t(struct dpaa2_eth_priv *priv,
+				    struct dpaa2_eth_channel *ch,
+				    const struct dpaa2_fd *fd,
+				    struct dpaa2_eth_fq *fq);
+
 struct dpaa2_eth_fq {
 	u32 fqid;
 	u32 tx_qdbin;
@@ -447,10 +458,7 @@  struct dpaa2_eth_fq {
 	struct dpaa2_eth_channel *channel;
 	enum dpaa2_eth_fq_type type;
 
-	void (*consume)(struct dpaa2_eth_priv *priv,
-			struct dpaa2_eth_channel *ch,
-			const struct dpaa2_fd *fd,
-			struct dpaa2_eth_fq *fq);
+	dpaa2_eth_consume_cb_t *consume;
 	struct dpaa2_eth_fq_stats stats;
 
 	struct dpaa2_eth_xdp_fds xdp_redirect_fds;
@@ -486,6 +494,8 @@  struct dpaa2_eth_channel {
 	u64 recycled_bufs[DPAA2_ETH_BUFS_PER_CMD];
 	int recycled_bufs_cnt;
 
+	bool xsk_zc;
+	struct xsk_buff_pool *xsk_pool;
 	struct dpaa2_eth_bp *bp;
 };
 
@@ -808,4 +818,22 @@  void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
 		  struct dpaa2_eth_channel *ch,
 		  const struct dpaa2_fd *fd,
 		  struct dpaa2_eth_fq *fq);
+
+struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv);
+void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv,
+			 struct dpaa2_eth_bp *bp);
+
+void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr);
+void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv,
+			   struct dpaa2_eth_channel *ch,
+			   dma_addr_t addr);
+
+void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv,
+			   struct dpaa2_eth_channel *ch,
+			   struct dpaa2_fd *fd,
+			   void *buf_start, u16 queue_id);
+
+int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags);
+int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid);
+
 #endif	/* __DPAA2_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c
new file mode 100644
index 000000000000..2df7bffec5a7
--- /dev/null
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c
@@ -0,0 +1,327 @@ 
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Copyright 2022 NXP
+ */
+#include <linux/filter.h>
+#include <linux/compiler.h>
+#include <linux/bpf_trace.h>
+#include <net/xdp.h>
+#include <net/xdp_sock_drv.h>
+
+#include "dpaa2-eth.h"
+
+static void dpaa2_eth_setup_consume_func(struct dpaa2_eth_priv *priv,
+					 struct dpaa2_eth_channel *ch,
+					 enum dpaa2_eth_fq_type type,
+					 dpaa2_eth_consume_cb_t *consume)
+{
+	struct dpaa2_eth_fq *fq;
+	int i;
+
+	for (i = 0; i < priv->num_fqs; i++) {
+		fq = &priv->fq[i];
+
+		if (fq->type != type)
+			continue;
+		if (fq->channel != ch)
+			continue;
+
+		fq->consume = consume;
+	}
+}
+
+static u32 dpaa2_xsk_run_xdp(struct dpaa2_eth_priv *priv,
+			     struct dpaa2_eth_channel *ch,
+			     struct dpaa2_eth_fq *rx_fq,
+			     struct dpaa2_fd *fd, void *vaddr)
+{
+	dma_addr_t addr = dpaa2_fd_get_addr(fd);
+	struct bpf_prog *xdp_prog;
+	struct xdp_buff *xdp_buff;
+	struct dpaa2_eth_swa *swa;
+	u32 xdp_act = XDP_PASS;
+	int err;
+
+	xdp_prog = READ_ONCE(ch->xdp.prog);
+	if (!xdp_prog)
+		goto out;
+
+	swa = (struct dpaa2_eth_swa *)(vaddr + DPAA2_ETH_RX_HWA_SIZE +
+				       ch->xsk_pool->umem->headroom);
+	xdp_buff = swa->xsk.xdp_buff;
+
+	xdp_buff->data_hard_start = vaddr;
+	xdp_buff->data = vaddr + dpaa2_fd_get_offset(fd);
+	xdp_buff->data_end = xdp_buff->data + dpaa2_fd_get_len(fd);
+	xdp_set_data_meta_invalid(xdp_buff);
+	xdp_buff->rxq = &ch->xdp_rxq;
+
+	xsk_buff_dma_sync_for_cpu(xdp_buff, ch->xsk_pool);
+	xdp_act = bpf_prog_run_xdp(xdp_prog, xdp_buff);
+
+	/* xdp.data pointer may have changed */
+	dpaa2_fd_set_offset(fd, xdp_buff->data - vaddr);
+	dpaa2_fd_set_len(fd, xdp_buff->data_end - xdp_buff->data);
+
+	if (likely(xdp_act == XDP_REDIRECT)) {
+		err = xdp_do_redirect(priv->net_dev, xdp_buff, xdp_prog);
+		if (unlikely(err)) {
+			ch->stats.xdp_drop++;
+			dpaa2_eth_recycle_buf(priv, ch, addr);
+		} else {
+			ch->buf_count--;
+			ch->stats.xdp_redirect++;
+		}
+
+		goto xdp_redir;
+	}
+
+	switch (xdp_act) {
+	case XDP_PASS:
+		break;
+	case XDP_TX:
+		dpaa2_eth_xdp_enqueue(priv, ch, fd, vaddr, rx_fq->flowid);
+		break;
+	default:
+		bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act);
+		fallthrough;
+	case XDP_ABORTED:
+		trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
+		fallthrough;
+	case XDP_DROP:
+		dpaa2_eth_recycle_buf(priv, ch, addr);
+		ch->stats.xdp_drop++;
+		break;
+	}
+
+xdp_redir:
+	ch->xdp.res |= xdp_act;
+out:
+	return xdp_act;
+}
+
+/* Rx frame processing routine for the AF_XDP fast path */
+static void dpaa2_xsk_rx(struct dpaa2_eth_priv *priv,
+			 struct dpaa2_eth_channel *ch,
+			 const struct dpaa2_fd *fd,
+			 struct dpaa2_eth_fq *fq)
+{
+	dma_addr_t addr = dpaa2_fd_get_addr(fd);
+	u8 fd_format = dpaa2_fd_get_format(fd);
+	struct rtnl_link_stats64 *percpu_stats;
+	u32 fd_length = dpaa2_fd_get_len(fd);
+	struct sk_buff *skb;
+	void *vaddr;
+	u32 xdp_act;
+
+	vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
+	percpu_stats = this_cpu_ptr(priv->percpu_stats);
+
+	if (fd_format != dpaa2_fd_single) {
+		WARN_ON(priv->xdp_prog);
+		/* AF_XDP doesn't support any other formats */
+		goto err_frame_format;
+	}
+
+	xdp_act = dpaa2_xsk_run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr);
+	if (xdp_act != XDP_PASS) {
+		percpu_stats->rx_packets++;
+		percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
+		return;
+	}
+
+	/* Build skb */
+	skb = dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, vaddr);
+	if (!skb)
+		/* Nothing else we can do, recycle the buffer and
+		 * drop the frame.
+		 */
+		goto err_alloc_skb;
+
+	/* Send the skb to the Linux networking stack */
+	dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb);
+
+	return;
+
+err_alloc_skb:
+	dpaa2_eth_recycle_buf(priv, ch, addr);
+err_frame_format:
+	percpu_stats->rx_dropped++;
+}
+
+static void dpaa2_xsk_set_bp_per_qdbin(struct dpaa2_eth_priv *priv,
+				       struct dpni_pools_cfg *pools_params)
+{
+	int curr_bp = 0, i, j;
+
+	pools_params->pool_options = DPNI_POOL_ASSOC_QDBIN;
+	for (i = 0; i < priv->num_bps; i++) {
+		for (j = 0; j < priv->num_channels; j++)
+			if (priv->bp[i] == priv->channel[j]->bp)
+				pools_params->pools[curr_bp].priority_mask |= (1 << j);
+		if (!pools_params->pools[curr_bp].priority_mask)
+			continue;
+
+		pools_params->pools[curr_bp].dpbp_id = priv->bp[i]->bpid;
+		pools_params->pools[curr_bp].buffer_size = priv->rx_buf_size;
+		pools_params->pools[curr_bp++].backup_pool = 0;
+	}
+	pools_params->num_dpbp = curr_bp;
+}
+
+static int dpaa2_xsk_disable_pool(struct net_device *dev, u16 qid)
+{
+	struct xsk_buff_pool *pool = xsk_get_pool_from_qid(dev, qid);
+	struct dpaa2_eth_priv *priv = netdev_priv(dev);
+	struct dpni_pools_cfg pools_params = { 0 };
+	struct dpaa2_eth_channel *ch;
+	int err;
+	bool up;
+
+	ch = priv->channel[qid];
+	if (!ch->xsk_pool)
+		return -EINVAL;
+
+	up = netif_running(dev);
+	if (up)
+		dev_close(dev);
+
+	xsk_pool_dma_unmap(pool, 0);
+	err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq,
+					 MEM_TYPE_PAGE_ORDER0, NULL);
+	if (err)
+		netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed (err = %d)\n",
+			   err);
+
+	dpaa2_eth_free_dpbp(priv, ch->bp);
+
+	ch->xsk_zc = false;
+	ch->xsk_pool = NULL;
+	ch->bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX];
+
+	dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_eth_rx);
+
+	dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params);
+	err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params);
+	if (err)
+		netdev_err(dev, "dpni_set_pools() failed\n");
+
+	if (up) {
+		err = dev_open(dev, NULL);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int dpaa2_xsk_enable_pool(struct net_device *dev,
+				 struct xsk_buff_pool *pool,
+				 u16 qid)
+{
+	struct dpaa2_eth_priv *priv = netdev_priv(dev);
+	struct dpni_pools_cfg pools_params = { 0 };
+	struct dpaa2_eth_channel *ch;
+	int err, err2;
+	bool up;
+
+	if (priv->dpni_attrs.wriop_version < DPAA2_WRIOP_VERSION(3, 0, 0)) {
+		netdev_err(dev, "AF_XDP zero-copy not supported on devices <= WRIOP(3, 0, 0)\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (priv->dpni_attrs.num_queues > 8) {
+		netdev_err(dev, "AF_XDP zero-copy not supported on DPNI with more then 8 queues\n");
+		return -EOPNOTSUPP;
+	}
+
+	up = netif_running(dev);
+	if (up)
+		dev_close(dev);
+
+	err = xsk_pool_dma_map(pool, priv->net_dev->dev.parent, 0);
+	if (err) {
+		netdev_err(dev, "xsk_pool_dma_map() failed (err = %d)\n",
+			   err);
+		goto err_dma_unmap;
+	}
+
+	ch = priv->channel[qid];
+	err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL);
+	if (err) {
+		netdev_err(dev, "xdp_rxq_info_reg_mem_model() failed (err = %d)\n", err);
+		goto err_mem_model;
+	}
+	xsk_pool_set_rxq_info(pool, &ch->xdp_rxq);
+
+	priv->bp[priv->num_bps] = dpaa2_eth_allocate_dpbp(priv);
+	if (IS_ERR(priv->bp[priv->num_bps])) {
+		err = PTR_ERR(priv->bp[priv->num_bps]);
+		goto err_bp_alloc;
+	}
+	ch->xsk_zc = true;
+	ch->xsk_pool = pool;
+	ch->bp = priv->bp[priv->num_bps++];
+
+	dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_xsk_rx);
+
+	dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params);
+	err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params);
+	if (err) {
+		netdev_err(dev, "dpni_set_pools() failed\n");
+		goto err_set_pools;
+	}
+
+	if (up) {
+		err = dev_open(dev, NULL);
+		if (err)
+			return err;
+	}
+
+	return 0;
+
+err_set_pools:
+	err2 = dpaa2_xsk_disable_pool(dev, qid);
+	if (err2)
+		netdev_err(dev, "dpaa2_xsk_disable_pool() failed %d\n", err2);
+err_bp_alloc:
+	err2 = xdp_rxq_info_reg_mem_model(&priv->channel[qid]->xdp_rxq,
+					  MEM_TYPE_PAGE_ORDER0, NULL);
+	if (err2)
+		netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed with %d)\n", err2);
+err_mem_model:
+	xsk_pool_dma_unmap(pool, 0);
+err_dma_unmap:
+	if (up)
+		dev_open(dev, NULL);
+
+	return err;
+}
+
+int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid)
+{
+	return pool ? dpaa2_xsk_enable_pool(dev, pool, qid) :
+		      dpaa2_xsk_disable_pool(dev, qid);
+}
+
+int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
+{
+	struct dpaa2_eth_priv *priv = netdev_priv(dev);
+	struct dpaa2_eth_channel *ch = priv->channel[qid];
+
+	if (!priv->link_state.up)
+		return -ENETDOWN;
+
+	if (!priv->xdp_prog)
+		return -EINVAL;
+
+	if (!ch->xsk_zc)
+		return -EINVAL;
+
+	/* We do not have access to a per channel SW interrupt, so instead we
+	 * schedule a NAPI instance.
+	 */
+	if (!napi_if_scheduled_mark_missed(&ch->napi))
+		napi_schedule(&ch->napi);
+
+	return 0;
+}