|
|
|
@@ -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;
|
|
|
|
|
|
|
|
|
|