ionic: Add XDP_TX support
authorShannon Nelson <shannon.nelson@amd.com>
Wed, 14 Feb 2024 17:59:06 +0000 (09:59 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 16 Feb 2024 08:48:08 +0000 (08:48 +0000)
The XDP_TX packets get fed back into the Rx queue's partnered
Tx queue as an xdp_frame.

Co-developed-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Shannon Nelson <shannon.nelson@amd.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/pensando/ionic/ionic_dev.h
drivers/net/ethernet/pensando/ionic/ionic_lif.c
drivers/net/ethernet/pensando/ionic/ionic_lif.h
drivers/net/ethernet/pensando/ionic/ionic_stats.c
drivers/net/ethernet/pensando/ionic/ionic_txrx.c

index 70f5725fdfe8f79beb63ddf745e05ab070322359..76425bb546bae1f0144f83d91683862e9fb9255e 100644 (file)
@@ -228,6 +228,8 @@ struct ionic_desc_info {
        struct ionic_buf_info bufs[MAX_SKB_FRAGS + 1];
        ionic_desc_cb cb;
        void *cb_arg;
+       struct xdp_frame *xdpf;
+       enum xdp_action act;
 };
 
 #define IONIC_QUEUE_NAME_MAX_SZ                16
@@ -263,6 +265,7 @@ struct ionic_queue {
                struct ionic_rxq_sg_desc *rxq_sgl;
        };
        struct xdp_rxq_info *xdp_rxq_info;
+       struct ionic_queue *partner;
        dma_addr_t base_pa;
        dma_addr_t cmb_base_pa;
        dma_addr_t sg_base_pa;
index d8c83ff47b66ce8acec1ee8cfbaba9d1002205db..72bbab1576ab735f299779bbe68db95c0e020134 100644 (file)
@@ -878,6 +878,9 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
        };
        int err;
 
+       q->partner = &lif->txqcqs[q->index]->q;
+       q->partner->partner = q;
+
        if (!lif->xdp_prog)
                ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_SG);
 
@@ -2926,6 +2929,7 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)
        swap(a->q.base_pa,    b->q.base_pa);
        swap(a->q.info,       b->q.info);
        swap(a->q.xdp_rxq_info, b->q.xdp_rxq_info);
+       swap(a->q.partner,    b->q.partner);
        swap(a->q_base,       b->q_base);
        swap(a->q_base_pa,    b->q_base_pa);
        swap(a->q_size,       b->q_size);
index 61fa4ea4f04c012a721b5c2fefea3e9b86f1bcbe..092ff08fc7e0cbb97d49af7ce8532259eae5ed85 100644 (file)
@@ -37,6 +37,7 @@ struct ionic_tx_stats {
        u64 dma_map_err;
        u64 hwstamp_valid;
        u64 hwstamp_invalid;
+       u64 xdp_frames;
 };
 
 struct ionic_rx_stats {
@@ -54,6 +55,7 @@ struct ionic_rx_stats {
        u64 xdp_drop;
        u64 xdp_aborted;
        u64 xdp_pass;
+       u64 xdp_tx;
 };
 
 #define IONIC_QCQ_F_INITED             BIT(0)
@@ -141,6 +143,8 @@ struct ionic_lif_sw_stats {
        u64 xdp_drop;
        u64 xdp_aborted;
        u64 xdp_pass;
+       u64 xdp_tx;
+       u64 xdp_frames;
 };
 
 enum ionic_lif_state_flags {
index 2fb20173b2c6f3041f4bbfb4d2b3420327f7047e..5d48226e66cd44238a1d80fbad018bab5b69dd83 100644 (file)
@@ -30,6 +30,8 @@ static const struct ionic_stat_desc ionic_lif_stats_desc[] = {
        IONIC_LIF_STAT_DESC(xdp_drop),
        IONIC_LIF_STAT_DESC(xdp_aborted),
        IONIC_LIF_STAT_DESC(xdp_pass),
+       IONIC_LIF_STAT_DESC(xdp_tx),
+       IONIC_LIF_STAT_DESC(xdp_frames),
 };
 
 static const struct ionic_stat_desc ionic_port_stats_desc[] = {
@@ -138,6 +140,7 @@ static const struct ionic_stat_desc ionic_tx_stats_desc[] = {
        IONIC_TX_STAT_DESC(csum_none),
        IONIC_TX_STAT_DESC(csum),
        IONIC_TX_STAT_DESC(vlan_inserted),
+       IONIC_TX_STAT_DESC(xdp_frames),
 };
 
 static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
@@ -155,6 +158,7 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
        IONIC_RX_STAT_DESC(xdp_drop),
        IONIC_RX_STAT_DESC(xdp_aborted),
        IONIC_RX_STAT_DESC(xdp_pass),
+       IONIC_RX_STAT_DESC(xdp_tx),
 };
 
 #define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc)
@@ -177,6 +181,7 @@ static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num,
        stats->tx_csum += txstats->csum;
        stats->tx_hwstamp_valid += txstats->hwstamp_valid;
        stats->tx_hwstamp_invalid += txstats->hwstamp_invalid;
+       stats->xdp_frames += txstats->xdp_frames;
 }
 
 static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
@@ -194,6 +199,7 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
        stats->xdp_drop += rxstats->xdp_drop;
        stats->xdp_aborted += rxstats->xdp_aborted;
        stats->xdp_pass += rxstats->xdp_pass;
+       stats->xdp_tx += rxstats->xdp_tx;
 }
 
 static void ionic_get_lif_stats(struct ionic_lif *lif,
index a4f6dd5030c00c1f0133f3ff72c8aba23aecb820..3d4cadcf1c0c16414ba8953979bdc8184cb96e0e 100644 (file)
 #include "ionic_lif.h"
 #include "ionic_txrx.h"
 
+static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs);
+
+static dma_addr_t ionic_tx_map_single(struct ionic_queue *q,
+                                     void *data, size_t len);
+
+static void ionic_tx_clean(struct ionic_queue *q,
+                          struct ionic_desc_info *desc_info,
+                          struct ionic_cq_info *cq_info,
+                          void *cb_arg);
+
 static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell,
                                  ionic_desc_cb cb_func, void *cb_arg)
 {
@@ -297,6 +307,75 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q,
        return skb;
 }
 
+static void ionic_xdp_tx_desc_clean(struct ionic_queue *q,
+                                   struct ionic_desc_info *desc_info)
+{
+       unsigned int nbufs = desc_info->nbufs;
+       struct ionic_buf_info *buf_info;
+       struct device *dev = q->dev;
+
+       if (!nbufs)
+               return;
+
+       buf_info = desc_info->bufs;
+       dma_unmap_single(dev, buf_info->dma_addr,
+                        buf_info->len, DMA_TO_DEVICE);
+       __free_pages(buf_info->page, 0);
+       buf_info->page = NULL;
+
+       desc_info->nbufs = 0;
+       desc_info->xdpf = NULL;
+       desc_info->act = 0;
+}
+
+static int ionic_xdp_post_frame(struct net_device *netdev,
+                               struct ionic_queue *q, struct xdp_frame *frame,
+                               enum xdp_action act, struct page *page, int off,
+                               bool ring_doorbell)
+{
+       struct ionic_desc_info *desc_info;
+       struct ionic_buf_info *buf_info;
+       struct ionic_tx_stats *stats;
+       struct ionic_txq_desc *desc;
+       size_t len = frame->len;
+       dma_addr_t dma_addr;
+       u64 cmd;
+
+       desc_info = &q->info[q->head_idx];
+       desc = desc_info->txq_desc;
+       buf_info = desc_info->bufs;
+       stats = q_to_tx_stats(q);
+
+       dma_addr = ionic_tx_map_single(q, frame->data, len);
+       if (dma_mapping_error(q->dev, dma_addr)) {
+               stats->dma_map_err++;
+               return -EIO;
+       }
+       buf_info->dma_addr = dma_addr;
+       buf_info->len = len;
+       buf_info->page = page;
+       buf_info->page_offset = off;
+
+       desc_info->nbufs = 1;
+       desc_info->xdpf = frame;
+       desc_info->act = act;
+
+       cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE,
+                                 0, 0, buf_info->dma_addr);
+       desc->cmd = cpu_to_le64(cmd);
+       desc->len = cpu_to_le16(len);
+       desc->csum_start = 0;
+       desc->csum_offset = 0;
+
+       stats->xdp_frames++;
+       stats->pkts++;
+       stats->bytes += len;
+
+       ionic_txq_post(q, ring_doorbell, ionic_tx_clean, NULL);
+
+       return 0;
+}
+
 static bool ionic_run_xdp(struct ionic_rx_stats *stats,
                          struct net_device *netdev,
                          struct bpf_prog *xdp_prog,
@@ -306,6 +385,10 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 {
        u32 xdp_action = XDP_ABORTED;
        struct xdp_buff xdp_buf;
+       struct ionic_queue *txq;
+       struct netdev_queue *nq;
+       struct xdp_frame *xdpf;
+       int err = 0;
 
        xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info);
        xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info),
@@ -330,14 +413,51 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
                break;
 
        case XDP_TX:
+               xdpf = xdp_convert_buff_to_frame(&xdp_buf);
+               if (!xdpf)
+                       goto out_xdp_abort;
+
+               txq = rxq->partner;
+               nq = netdev_get_tx_queue(netdev, txq->index);
+               __netif_tx_lock(nq, smp_processor_id());
+               txq_trans_cond_update(nq);
+
+               if (netif_tx_queue_stopped(nq) ||
+                   unlikely(ionic_maybe_stop_tx(txq, 1))) {
+                       __netif_tx_unlock(nq);
+                       goto out_xdp_abort;
+               }
+
+               dma_unmap_page(rxq->dev, buf_info->dma_addr,
+                              IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
+
+               err = ionic_xdp_post_frame(netdev, txq, xdpf, XDP_TX,
+                                          buf_info->page,
+                                          buf_info->page_offset,
+                                          true);
+               __netif_tx_unlock(nq);
+               if (err) {
+                       netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err);
+                       goto out_xdp_abort;
+               }
+               stats->xdp_tx++;
+
+               /* the Tx completion will free the buffers */
+               break;
+
        case XDP_REDIRECT:
        case XDP_ABORTED:
        default:
-               trace_xdp_exception(netdev, xdp_prog, xdp_action);
-               ionic_rx_page_free(rxq, buf_info);
-               stats->xdp_aborted++;
+               goto out_xdp_abort;
        }
 
+       return true;
+
+out_xdp_abort:
+       trace_xdp_exception(netdev, xdp_prog, xdp_action);
+       ionic_rx_page_free(rxq, buf_info);
+       stats->xdp_aborted++;
+
        return true;
 }
 
@@ -893,6 +1013,16 @@ static void ionic_tx_clean(struct ionic_queue *q,
        struct sk_buff *skb = cb_arg;
        u16 qi;
 
+       if (desc_info->xdpf) {
+               ionic_xdp_tx_desc_clean(q->partner, desc_info);
+               stats->clean++;
+
+               if (unlikely(__netif_subqueue_stopped(q->lif->netdev, q->index)))
+                       netif_wake_subqueue(q->lif->netdev, q->index);
+
+               return;
+       }
+
        ionic_tx_desc_unmap_bufs(q, desc_info);
 
        if (!skb)