Merge branch 'dpaa2-eth-add-support-for-xdp-bulk-enqueue'

Ioana Ciornei says:

====================
dpaa2-eth: add support for xdp bulk enqueue

The first patch moves the DEV_MAP_BULK_SIZE macro into the xdp.h header
file so that drivers can take advantage of it and use it.

The following 3 patches are there to setup the scene for using the bulk
enqueue feature.  First of all, the prototype of the enqueue function is
changed so that it returns the number of enqueued frames. Second, the
bulk enqueue interface is used but without any functional changes, still
one frame at a time is enqueued.  Third, the .ndo_xdp_xmit callback is
split into two stages, create all FDs for the xdp_frames received and
then enqueue them.

The last patch of the series builds on top of the others and instead of
issuing an enqueue operation for each FD it issues a bulk enqueue call
for as many frames as possible. This is repeated until all frames are
enqueued or the maximum number of retries is hit. We do not use the
XDP_XMIT_FLUSH flag since the architecture is not capable to store all
frames dequeued in a NAPI cycle, instead we send out right away all
frames received in a .ndo_xdp_xmit call.

Changes in v2:
 - statically allocate an array of dpaa2_fd by frame queue
 - use the DEV_MAP_BULK_SIZE as the maximum number of xdp_frames
   received in .ndo_xdp_xmit()
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-04-22 20:11:29 -07:00
commit 30685b2a43
4 changed files with 88 additions and 61 deletions

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2014-2016 Freescale Semiconductor Inc.
* Copyright 2016-2019 NXP
* Copyright 2016-2020 NXP
*/
#include <linux/init.h>
#include <linux/module.h>
@ -268,7 +268,7 @@ static int xdp_enqueue(struct dpaa2_eth_priv *priv, struct dpaa2_fd *fd,
fq = &priv->fq[queue_id];
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
err = priv->enqueue(priv, fq, fd, 0);
err = priv->enqueue(priv, fq, fd, 0, 1, NULL);
if (err != -EBUSY)
break;
}
@ -847,7 +847,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
* the Tx confirmation callback for this frame
*/
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
err = priv->enqueue(priv, fq, &fd, prio);
err = priv->enqueue(priv, fq, &fd, prio, 1, NULL);
if (err != -EBUSY)
break;
}
@ -1880,20 +1880,16 @@ static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
return 0;
}
static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
struct xdp_frame *xdpf)
static int dpaa2_eth_xdp_create_fd(struct net_device *net_dev,
struct xdp_frame *xdpf,
struct dpaa2_fd *fd)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
struct device *dev = net_dev->dev.parent;
struct rtnl_link_stats64 *percpu_stats;
struct dpaa2_eth_drv_stats *percpu_extras;
unsigned int needed_headroom;
struct dpaa2_eth_swa *swa;
struct dpaa2_eth_fq *fq;
struct dpaa2_fd fd;
void *buffer_start, *aligned_start;
dma_addr_t addr;
int err, i;
/* We require a minimum headroom to be able to transmit the frame.
* Otherwise return an error and let the original net_device handle it
@ -1902,11 +1898,8 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
if (xdpf->headroom < needed_headroom)
return -EINVAL;
percpu_stats = this_cpu_ptr(priv->percpu_stats);
percpu_extras = this_cpu_ptr(priv->percpu_extras);
/* Setup the FD fields */
memset(&fd, 0, sizeof(fd));
memset(fd, 0, sizeof(*fd));
/* Align FD address, if possible */
buffer_start = xdpf->data - needed_headroom;
@ -1924,32 +1917,14 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
addr = dma_map_single(dev, buffer_start,
swa->xdp.dma_size,
DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(dev, addr))) {
percpu_stats->tx_dropped++;
if (unlikely(dma_mapping_error(dev, addr)))
return -ENOMEM;
}
dpaa2_fd_set_addr(&fd, addr);
dpaa2_fd_set_offset(&fd, xdpf->data - buffer_start);
dpaa2_fd_set_len(&fd, xdpf->len);
dpaa2_fd_set_format(&fd, dpaa2_fd_single);
dpaa2_fd_set_ctrl(&fd, FD_CTRL_PTA);
fq = &priv->fq[smp_processor_id() % dpaa2_eth_queue_count(priv)];
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
err = priv->enqueue(priv, fq, &fd, 0);
if (err != -EBUSY)
break;
}
percpu_extras->tx_portal_busy += i;
if (unlikely(err < 0)) {
percpu_stats->tx_errors++;
/* let the Rx device handle the cleanup */
return err;
}
percpu_stats->tx_packets++;
percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
dpaa2_fd_set_addr(fd, addr);
dpaa2_fd_set_offset(fd, xdpf->data - buffer_start);
dpaa2_fd_set_len(fd, xdpf->len);
dpaa2_fd_set_format(fd, dpaa2_fd_single);
dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA);
return 0;
}
@ -1957,8 +1932,13 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, int n,
struct xdp_frame **frames, u32 flags)
{
int drops = 0;
int i, err;
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
int total_enqueued = 0, retries = 0, enqueued;
struct dpaa2_eth_drv_stats *percpu_extras;
struct rtnl_link_stats64 *percpu_stats;
int num_fds, i, err, max_retries;
struct dpaa2_eth_fq *fq;
struct dpaa2_fd *fds;
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
return -EINVAL;
@ -1966,17 +1946,40 @@ static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, int n,
if (!netif_running(net_dev))
return -ENETDOWN;
for (i = 0; i < n; i++) {
struct xdp_frame *xdpf = frames[i];
fq = &priv->fq[smp_processor_id()];
fds = fq->xdp_fds;
err = dpaa2_eth_xdp_xmit_frame(net_dev, xdpf);
if (err) {
xdp_return_frame_rx_napi(xdpf);
drops++;
percpu_stats = this_cpu_ptr(priv->percpu_stats);
percpu_extras = this_cpu_ptr(priv->percpu_extras);
/* create a FD for each xdp_frame in the list received */
for (i = 0; i < n; i++) {
err = dpaa2_eth_xdp_create_fd(net_dev, frames[i], &fds[i]);
if (err)
break;
}
num_fds = i;
/* try to enqueue all the FDs until the max number of retries is hit */
max_retries = num_fds * DPAA2_ETH_ENQUEUE_RETRIES;
while (total_enqueued < num_fds && retries < max_retries) {
err = priv->enqueue(priv, fq, &fds[total_enqueued],
0, num_fds - total_enqueued, &enqueued);
if (err == -EBUSY) {
percpu_extras->tx_portal_busy += ++retries;
continue;
}
total_enqueued += enqueued;
}
return n - drops;
/* update statistics */
percpu_stats->tx_packets += total_enqueued;
for (i = 0; i < total_enqueued; i++)
percpu_stats->tx_bytes += dpaa2_fd_get_len(&fds[i]);
for (i = total_enqueued; i < n; i++)
xdp_return_frame_rx_napi(frames[i]);
return total_enqueued;
}
static int update_xps(struct dpaa2_eth_priv *priv)
@ -2523,19 +2526,38 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
static inline int dpaa2_eth_enqueue_qd(struct dpaa2_eth_priv *priv,
struct dpaa2_eth_fq *fq,
struct dpaa2_fd *fd, u8 prio)
struct dpaa2_fd *fd, u8 prio,
u32 num_frames __always_unused,
int *frames_enqueued)
{
return dpaa2_io_service_enqueue_qd(fq->channel->dpio,
priv->tx_qdid, prio,
fq->tx_qdbin, fd);
int err;
err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
priv->tx_qdid, prio,
fq->tx_qdbin, fd);
if (!err && frames_enqueued)
*frames_enqueued = 1;
return err;
}
static inline int dpaa2_eth_enqueue_fq(struct dpaa2_eth_priv *priv,
struct dpaa2_eth_fq *fq,
struct dpaa2_fd *fd, u8 prio)
static inline int dpaa2_eth_enqueue_fq_multiple(struct dpaa2_eth_priv *priv,
struct dpaa2_eth_fq *fq,
struct dpaa2_fd *fd,
u8 prio, u32 num_frames,
int *frames_enqueued)
{
return dpaa2_io_service_enqueue_fq(fq->channel->dpio,
fq->tx_fqid[prio], fd);
int err;
err = dpaa2_io_service_enqueue_multiple_fq(fq->channel->dpio,
fq->tx_fqid[prio],
fd, num_frames);
if (err == 0)
return -EBUSY;
if (frames_enqueued)
*frames_enqueued = err;
return 0;
}
static void set_enqueue_mode(struct dpaa2_eth_priv *priv)
@ -2544,7 +2566,7 @@ static void set_enqueue_mode(struct dpaa2_eth_priv *priv)
DPNI_ENQUEUE_FQID_VER_MINOR) < 0)
priv->enqueue = dpaa2_eth_enqueue_qd;
else
priv->enqueue = dpaa2_eth_enqueue_fq;
priv->enqueue = dpaa2_eth_enqueue_fq_multiple;
}
static int set_pause(struct dpaa2_eth_priv *priv)
@ -2605,7 +2627,7 @@ static void update_tx_fqids(struct dpaa2_eth_priv *priv)
}
}
priv->enqueue = dpaa2_eth_enqueue_fq;
priv->enqueue = dpaa2_eth_enqueue_fq_multiple;
return;

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2014-2016 Freescale Semiconductor Inc.
* Copyright 2016 NXP
* Copyright 2016-2020 NXP
*/
#ifndef __DPAA2_ETH_H
@ -325,6 +325,8 @@ struct dpaa2_eth_fq {
const struct dpaa2_fd *fd,
struct dpaa2_eth_fq *fq);
struct dpaa2_eth_fq_stats stats;
struct dpaa2_fd xdp_fds[DEV_MAP_BULK_SIZE];
};
struct dpaa2_eth_ch_xdp {
@ -371,7 +373,9 @@ struct dpaa2_eth_priv {
struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES];
int (*enqueue)(struct dpaa2_eth_priv *priv,
struct dpaa2_eth_fq *fq,
struct dpaa2_fd *fd, u8 prio);
struct dpaa2_fd *fd, u8 prio,
u32 num_frames,
int *frames_enqueued);
u8 num_channels;
struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS];

View File

@ -181,4 +181,6 @@ bool xdp_attachment_flags_ok(struct xdp_attachment_info *info,
void xdp_attachment_setup(struct xdp_attachment_info *info,
struct netdev_bpf *bpf);
#define DEV_MAP_BULK_SIZE 16
#endif /* __LINUX_NET_XDP_H__ */

View File

@ -52,7 +52,6 @@
#define DEV_CREATE_FLAG_MASK \
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
#define DEV_MAP_BULK_SIZE 16
struct xdp_dev_bulk_queue {
struct xdp_frame *q[DEV_MAP_BULK_SIZE];
struct list_head flush_node;