Commit a9b1fab3 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'ionic-convert-rx-queue-buffers-to-use-page_pool'

Brett Creeley says:

====================
ionic: convert Rx queue buffers to use page_pool

Our home-grown buffer management needs to go away and we need to play
nicely with the page_pool infrastructure.  This patchset cleans up some
of our API use and converts the Rx traffic queues to use page_pool.
The first few patches are for tidying up things, then a small XDP
configuration refactor, adding page_pool support, and finally adding
support to hot swap an XDP program without having to reconfigure
anything.

The result is code that more closely follows current patterns, as well as
a either a performance boost or equivalent performance as seen with
iperf testing:

  mss   netio    tx_pps      rx_pps      total_pps      tx_bw    rx_bw    total_bw
  ----  -------  ----------  ----------  -----------  -------  -------  ----------
Before:
  256   bidir    13,839,293  15,515,227  29,354,520        34       38          71
  512   bidir    13,913,249  14,671,693  28,584,942        62       65         127
  1024  bidir    13,006,189  13,695,413  26,701,602       109      115         224
  1448  bidir    12,489,905  12,791,734  25,281,639       145      149         294
  2048  bidir    9,195,622   9,247,649   18,443,271       148      149         297
  4096  bidir    5,149,716   5,247,917   10,397,633       160      163         323
  8192  bidir    3,029,993   3,008,882   6,038,875        179      179         358
  9000  bidir    2,789,358   2,800,744   5,590,102        181      180         361

After:
  256   bidir    21,540,037  21,344,644  42,884,681        52       52         104
  512   bidir    23,170,014  19,207,260  42,377,274       103       85         188
  1024  bidir    17,934,280  17,819,247  35,753,527       150      149         299
  1448  bidir    15,242,515  14,907,030  30,149,545       167      174         341
  2048  bidir    10,692,542  10,663,023  21,355,565       177      176         353
  4096  bidir    6,024,977   6,083,580   12,108,557       187      180         367
  8192  bidir    3,090,449   3,048,266   6,138,715        180      176         356
  9000  bidir    2,859,146   2,864,226   5,723,372        178      180         358

v2: https://lore.kernel.org/20240826184422.21895-1-brett.creeley@amd.com
v1: https://lore.kernel.org/20240625165658.34598-1-shannon.nelson@amd.com
====================

Link: https://patch.msgid.link/20240906232623.39651-1-brett.creeley@amd.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 760664b7 3c0bf13f
...@@ -23,6 +23,7 @@ config IONIC ...@@ -23,6 +23,7 @@ config IONIC
depends on PTP_1588_CLOCK_OPTIONAL depends on PTP_1588_CLOCK_OPTIONAL
select NET_DEVLINK select NET_DEVLINK
select DIMLIB select DIMLIB
select PAGE_POOL
help help
This enables the support for the Pensando family of Ethernet This enables the support for the Pensando family of Ethernet
adapters. More specific information on this driver can be adapters. More specific information on this driver can be
......
...@@ -181,10 +181,7 @@ struct ionic_queue; ...@@ -181,10 +181,7 @@ struct ionic_queue;
struct ionic_qcq; struct ionic_qcq;
#define IONIC_MAX_BUF_LEN ((u16)-1) #define IONIC_MAX_BUF_LEN ((u16)-1)
#define IONIC_PAGE_SIZE PAGE_SIZE #define IONIC_PAGE_SIZE MIN(PAGE_SIZE, IONIC_MAX_BUF_LEN)
#define IONIC_PAGE_SPLIT_SZ (PAGE_SIZE / 2)
#define IONIC_PAGE_GFP_MASK (GFP_ATOMIC | __GFP_NOWARN |\
__GFP_COMP | __GFP_MEMALLOC)
#define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \ #define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \
(VLAN_ETH_HLEN + \ (VLAN_ETH_HLEN + \
...@@ -238,9 +235,8 @@ struct ionic_queue { ...@@ -238,9 +235,8 @@ struct ionic_queue {
unsigned int index; unsigned int index;
unsigned int num_descs; unsigned int num_descs;
unsigned int max_sg_elems; unsigned int max_sg_elems;
u64 features; u64 features;
unsigned int type;
unsigned int hw_index;
unsigned int hw_type; unsigned int hw_type;
bool xdp_flush; bool xdp_flush;
union { union {
...@@ -249,11 +245,6 @@ struct ionic_queue { ...@@ -249,11 +245,6 @@ struct ionic_queue {
struct ionic_rxq_desc *rxq; struct ionic_rxq_desc *rxq;
struct ionic_admin_cmd *adminq; struct ionic_admin_cmd *adminq;
}; };
union {
void __iomem *cmb_base;
struct ionic_txq_desc __iomem *cmb_txq;
struct ionic_rxq_desc __iomem *cmb_rxq;
};
union { union {
void *sg_base; void *sg_base;
struct ionic_txq_sg_desc *txq_sgl; struct ionic_txq_sg_desc *txq_sgl;
...@@ -261,7 +252,17 @@ struct ionic_queue { ...@@ -261,7 +252,17 @@ struct ionic_queue {
struct ionic_rxq_sg_desc *rxq_sgl; struct ionic_rxq_sg_desc *rxq_sgl;
}; };
struct xdp_rxq_info *xdp_rxq_info; struct xdp_rxq_info *xdp_rxq_info;
struct bpf_prog *xdp_prog;
struct page_pool *page_pool;
struct ionic_queue *partner; struct ionic_queue *partner;
union {
void __iomem *cmb_base;
struct ionic_txq_desc __iomem *cmb_txq;
struct ionic_rxq_desc __iomem *cmb_rxq;
};
unsigned int type;
unsigned int hw_index;
dma_addr_t base_pa; dma_addr_t base_pa;
dma_addr_t cmb_base_pa; dma_addr_t cmb_base_pa;
dma_addr_t sg_base_pa; dma_addr_t sg_base_pa;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <net/page_pool/helpers.h>
#include "ionic.h" #include "ionic.h"
#include "ionic_bus.h" #include "ionic_bus.h"
...@@ -46,8 +47,9 @@ static int ionic_start_queues(struct ionic_lif *lif); ...@@ -46,8 +47,9 @@ static int ionic_start_queues(struct ionic_lif *lif);
static void ionic_stop_queues(struct ionic_lif *lif); static void ionic_stop_queues(struct ionic_lif *lif);
static void ionic_lif_queue_identify(struct ionic_lif *lif); static void ionic_lif_queue_identify(struct ionic_lif *lif);
static int ionic_xdp_queues_config(struct ionic_lif *lif); static void ionic_xdp_rxqs_prog_update(struct ionic_lif *lif);
static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q); static void ionic_unregister_rxq_info(struct ionic_queue *q);
static int ionic_register_rxq_info(struct ionic_queue *q, unsigned int napi_id);
static void ionic_dim_work(struct work_struct *work) static void ionic_dim_work(struct work_struct *work)
{ {
...@@ -380,6 +382,7 @@ static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -380,6 +382,7 @@ static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq)
if (!(qcq->flags & IONIC_QCQ_F_INITED)) if (!(qcq->flags & IONIC_QCQ_F_INITED))
return; return;
ionic_unregister_rxq_info(&qcq->q);
if (qcq->flags & IONIC_QCQ_F_INTR) { if (qcq->flags & IONIC_QCQ_F_INTR) {
ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
IONIC_INTR_MASK_SET); IONIC_INTR_MASK_SET);
...@@ -437,9 +440,10 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -437,9 +440,10 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
qcq->sg_base_pa = 0; qcq->sg_base_pa = 0;
} }
ionic_xdp_unregister_rxq_info(&qcq->q); page_pool_destroy(qcq->q.page_pool);
ionic_qcq_intr_free(lif, qcq); qcq->q.page_pool = NULL;
ionic_qcq_intr_free(lif, qcq);
vfree(qcq->q.info); vfree(qcq->q.info);
qcq->q.info = NULL; qcq->q.info = NULL;
} }
...@@ -553,7 +557,8 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -553,7 +557,8 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
unsigned int cq_desc_size, unsigned int cq_desc_size,
unsigned int sg_desc_size, unsigned int sg_desc_size,
unsigned int desc_info_size, unsigned int desc_info_size,
unsigned int pid, struct ionic_qcq **qcq) unsigned int pid, struct bpf_prog *xdp_prog,
struct ionic_qcq **qcq)
{ {
struct ionic_dev *idev = &lif->ionic->idev; struct ionic_dev *idev = &lif->ionic->idev;
struct device *dev = lif->ionic->dev; struct device *dev = lif->ionic->dev;
...@@ -579,6 +584,31 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -579,6 +584,31 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
goto err_out_free_qcq; goto err_out_free_qcq;
} }
if (type == IONIC_QTYPE_RXQ) {
struct page_pool_params pp_params = {
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.order = 0,
.pool_size = num_descs,
.nid = NUMA_NO_NODE,
.dev = lif->ionic->dev,
.napi = &new->napi,
.dma_dir = DMA_FROM_DEVICE,
.max_len = PAGE_SIZE,
.netdev = lif->netdev,
};
if (xdp_prog)
pp_params.dma_dir = DMA_BIDIRECTIONAL;
new->q.page_pool = page_pool_create(&pp_params);
if (IS_ERR(new->q.page_pool)) {
netdev_err(lif->netdev, "Cannot create page_pool\n");
err = PTR_ERR(new->q.page_pool);
new->q.page_pool = NULL;
goto err_out_free_q_info;
}
}
new->q.type = type; new->q.type = type;
new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems; new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems;
...@@ -586,12 +616,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -586,12 +616,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
desc_size, sg_desc_size, pid); desc_size, sg_desc_size, pid);
if (err) { if (err) {
netdev_err(lif->netdev, "Cannot initialize queue\n"); netdev_err(lif->netdev, "Cannot initialize queue\n");
goto err_out_free_q_info; goto err_out_free_page_pool;
} }
err = ionic_alloc_qcq_interrupt(lif, new); err = ionic_alloc_qcq_interrupt(lif, new);
if (err) if (err)
goto err_out_free_q_info; goto err_out_free_page_pool;
err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size); err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size);
if (err) { if (err) {
...@@ -712,6 +742,8 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -712,6 +742,8 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
devm_free_irq(dev, new->intr.vector, &new->napi); devm_free_irq(dev, new->intr.vector, &new->napi);
ionic_intr_free(lif->ionic, new->intr.index); ionic_intr_free(lif->ionic, new->intr.index);
} }
err_out_free_page_pool:
page_pool_destroy(new->q.page_pool);
err_out_free_q_info: err_out_free_q_info:
vfree(new->q.info); vfree(new->q.info);
err_out_free_qcq: err_out_free_qcq:
...@@ -734,7 +766,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) ...@@ -734,7 +766,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
sizeof(struct ionic_admin_comp), sizeof(struct ionic_admin_comp),
0, 0,
sizeof(struct ionic_admin_desc_info), sizeof(struct ionic_admin_desc_info),
lif->kern_pid, &lif->adminqcq); lif->kern_pid, NULL, &lif->adminqcq);
if (err) if (err)
return err; return err;
ionic_debugfs_add_qcq(lif, lif->adminqcq); ionic_debugfs_add_qcq(lif, lif->adminqcq);
...@@ -747,7 +779,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) ...@@ -747,7 +779,7 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)
sizeof(union ionic_notifyq_comp), sizeof(union ionic_notifyq_comp),
0, 0,
sizeof(struct ionic_admin_desc_info), sizeof(struct ionic_admin_desc_info),
lif->kern_pid, &lif->notifyqcq); lif->kern_pid, NULL, &lif->notifyqcq);
if (err) if (err)
goto err_out; goto err_out;
ionic_debugfs_add_qcq(lif, lif->notifyqcq); ionic_debugfs_add_qcq(lif, lif->notifyqcq);
...@@ -925,6 +957,11 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) ...@@ -925,6 +957,11 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi); netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi);
else else
netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi); netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi);
err = ionic_register_rxq_info(q, qcq->napi.napi_id);
if (err) {
netif_napi_del(&qcq->napi);
return err;
}
qcq->flags |= IONIC_QCQ_F_INITED; qcq->flags |= IONIC_QCQ_F_INITED;
...@@ -960,7 +997,7 @@ int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) ...@@ -960,7 +997,7 @@ int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif)
err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_tx_desc_info), sizeof(struct ionic_tx_desc_info),
lif->kern_pid, &txq); lif->kern_pid, NULL, &txq);
if (err) if (err)
goto err_qcq_alloc; goto err_qcq_alloc;
...@@ -1020,7 +1057,7 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) ...@@ -1020,7 +1057,7 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif)
err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_rx_desc_info), sizeof(struct ionic_rx_desc_info),
lif->kern_pid, &rxq); lif->kern_pid, NULL, &rxq);
if (err) if (err)
goto err_qcq_alloc; goto err_qcq_alloc;
...@@ -1037,7 +1074,7 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) ...@@ -1037,7 +1074,7 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif)
goto err_qcq_init; goto err_qcq_init;
if (test_bit(IONIC_LIF_F_UP, lif->state)) { if (test_bit(IONIC_LIF_F_UP, lif->state)) {
ionic_rx_fill(&rxq->q); ionic_rx_fill(&rxq->q, NULL);
err = ionic_qcq_enable(rxq); err = ionic_qcq_enable(rxq);
if (err) if (err)
goto err_qcq_enable; goto err_qcq_enable;
...@@ -2046,7 +2083,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) ...@@ -2046,7 +2083,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)
err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_tx_desc_info), sizeof(struct ionic_tx_desc_info),
lif->kern_pid, &lif->txqcqs[i]); lif->kern_pid, NULL, &lif->txqcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
...@@ -2078,7 +2115,8 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) ...@@ -2078,7 +2115,8 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)
err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_rx_desc_info), sizeof(struct ionic_rx_desc_info),
lif->kern_pid, &lif->rxqcqs[i]); lif->kern_pid, lif->xdp_prog,
&lif->rxqcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
...@@ -2143,9 +2181,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -2143,9 +2181,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
int derr = 0; int derr = 0;
int i, err; int i, err;
err = ionic_xdp_queues_config(lif); ionic_xdp_rxqs_prog_update(lif);
if (err)
return err;
for (i = 0; i < lif->nxqs; i++) { for (i = 0; i < lif->nxqs; i++) {
if (!(lif->rxqcqs[i] && lif->txqcqs[i])) { if (!(lif->rxqcqs[i] && lif->txqcqs[i])) {
...@@ -2154,7 +2190,8 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -2154,7 +2190,8 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
goto err_out; goto err_out;
} }
ionic_rx_fill(&lif->rxqcqs[i]->q); ionic_rx_fill(&lif->rxqcqs[i]->q,
READ_ONCE(lif->rxqcqs[i]->q.xdp_prog));
err = ionic_qcq_enable(lif->rxqcqs[i]); err = ionic_qcq_enable(lif->rxqcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
...@@ -2167,7 +2204,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -2167,7 +2204,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
} }
if (lif->hwstamp_rxq) { if (lif->hwstamp_rxq) {
ionic_rx_fill(&lif->hwstamp_rxq->q); ionic_rx_fill(&lif->hwstamp_rxq->q, NULL);
err = ionic_qcq_enable(lif->hwstamp_rxq); err = ionic_qcq_enable(lif->hwstamp_rxq);
if (err) if (err)
goto err_out_hwstamp_rx; goto err_out_hwstamp_rx;
...@@ -2192,7 +2229,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif) ...@@ -2192,7 +2229,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr); derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr);
} }
ionic_xdp_queues_config(lif); ionic_xdp_rxqs_prog_update(lif);
return err; return err;
} }
...@@ -2651,7 +2688,7 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif) ...@@ -2651,7 +2688,7 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif)
ionic_vf_start(ionic); ionic_vf_start(ionic);
} }
static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q) static void ionic_unregister_rxq_info(struct ionic_queue *q)
{ {
struct xdp_rxq_info *xi; struct xdp_rxq_info *xi;
...@@ -2665,7 +2702,7 @@ static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q) ...@@ -2665,7 +2702,7 @@ static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q)
kfree(xi); kfree(xi);
} }
static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_id) static int ionic_register_rxq_info(struct ionic_queue *q, unsigned int napi_id)
{ {
struct xdp_rxq_info *rxq_info; struct xdp_rxq_info *rxq_info;
int err; int err;
...@@ -2676,14 +2713,14 @@ static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_ ...@@ -2676,14 +2713,14 @@ static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_
err = xdp_rxq_info_reg(rxq_info, q->lif->netdev, q->index, napi_id); err = xdp_rxq_info_reg(rxq_info, q->lif->netdev, q->index, napi_id);
if (err) { if (err) {
dev_err(q->dev, "Queue %d xdp_rxq_info_reg failed, err %d\n", netdev_err(q->lif->netdev, "q%d xdp_rxq_info_reg failed, err %d\n",
q->index, err); q->index, err);
goto err_out; goto err_out;
} }
err = xdp_rxq_info_reg_mem_model(rxq_info, MEM_TYPE_PAGE_ORDER0, NULL); err = xdp_rxq_info_reg_mem_model(rxq_info, MEM_TYPE_PAGE_POOL, q->page_pool);
if (err) { if (err) {
dev_err(q->dev, "Queue %d xdp_rxq_info_reg_mem_model failed, err %d\n", netdev_err(q->lif->netdev, "q%d xdp_rxq_info_reg_mem_model failed, err %d\n",
q->index, err); q->index, err);
xdp_rxq_info_unreg(rxq_info); xdp_rxq_info_unreg(rxq_info);
goto err_out; goto err_out;
...@@ -2698,44 +2735,20 @@ static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_ ...@@ -2698,44 +2735,20 @@ static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_
return err; return err;
} }
static int ionic_xdp_queues_config(struct ionic_lif *lif) static void ionic_xdp_rxqs_prog_update(struct ionic_lif *lif)
{ {
struct bpf_prog *xdp_prog;
unsigned int i; unsigned int i;
int err;
if (!lif->rxqcqs) if (!lif->rxqcqs)
return 0; return;
/* There's no need to rework memory if not going to/from NULL program.
* If there is no lif->xdp_prog, there should also be no q.xdp_rxq_info
* This way we don't need to keep an *xdp_prog in every queue struct.
*/
if (!lif->xdp_prog == !lif->rxqcqs[0]->q.xdp_rxq_info)
return 0;
xdp_prog = READ_ONCE(lif->xdp_prog);
for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) { for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) {
struct ionic_queue *q = &lif->rxqcqs[i]->q; struct ionic_queue *q = &lif->rxqcqs[i]->q;
if (q->xdp_rxq_info) { WRITE_ONCE(q->xdp_prog, xdp_prog);
ionic_xdp_unregister_rxq_info(q);
continue;
} }
err = ionic_xdp_register_rxq_info(q, lif->rxqcqs[i]->napi.napi_id);
if (err) {
dev_err(lif->ionic->dev, "failed to register RX queue %d info for XDP, err %d\n",
i, err);
goto err_out;
}
}
return 0;
err_out:
for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++)
ionic_xdp_unregister_rxq_info(&lif->rxqcqs[i]->q);
return err;
} }
static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf) static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf)
...@@ -2765,11 +2778,17 @@ static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf) ...@@ -2765,11 +2778,17 @@ static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf)
if (!netif_running(netdev)) { if (!netif_running(netdev)) {
old_prog = xchg(&lif->xdp_prog, bpf->prog); old_prog = xchg(&lif->xdp_prog, bpf->prog);
} else if (lif->xdp_prog && bpf->prog) {
old_prog = xchg(&lif->xdp_prog, bpf->prog);
ionic_xdp_rxqs_prog_update(lif);
} else { } else {
struct ionic_queue_params qparams;
ionic_init_queue_params(lif, &qparams);
qparams.xdp_prog = bpf->prog;
mutex_lock(&lif->queue_lock); mutex_lock(&lif->queue_lock);
ionic_stop_queues_reconfig(lif); ionic_reconfigure_queues(lif, &qparams);
old_prog = xchg(&lif->xdp_prog, bpf->prog); old_prog = xchg(&lif->xdp_prog, bpf->prog);
ionic_start_queues_reconfig(lif);
mutex_unlock(&lif->queue_lock); mutex_unlock(&lif->queue_lock);
} }
...@@ -2871,13 +2890,23 @@ static int ionic_cmb_reconfig(struct ionic_lif *lif, ...@@ -2871,13 +2890,23 @@ static int ionic_cmb_reconfig(struct ionic_lif *lif,
static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b) static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)
{ {
/* only swapping the queues, not the napi, flags, or other stuff */ /* only swapping the queues and napi, not flags or other stuff */
swap(a->napi, b->napi);
if (a->q.type == IONIC_QTYPE_RXQ) {
swap(a->q.page_pool, b->q.page_pool);
a->q.page_pool->p.napi = &a->napi;
if (b->q.page_pool) /* is NULL when increasing queue count */
b->q.page_pool->p.napi = &b->napi;
}
swap(a->q.features, b->q.features); swap(a->q.features, b->q.features);
swap(a->q.num_descs, b->q.num_descs); swap(a->q.num_descs, b->q.num_descs);
swap(a->q.desc_size, b->q.desc_size); swap(a->q.desc_size, b->q.desc_size);
swap(a->q.base, b->q.base); swap(a->q.base, b->q.base);
swap(a->q.base_pa, b->q.base_pa); swap(a->q.base_pa, b->q.base_pa);
swap(a->q.info, b->q.info); swap(a->q.info, b->q.info);
swap(a->q.xdp_prog, b->q.xdp_prog);
swap(a->q.xdp_rxq_info, b->q.xdp_rxq_info); swap(a->q.xdp_rxq_info, b->q.xdp_rxq_info);
swap(a->q.partner, b->q.partner); swap(a->q.partner, b->q.partner);
swap(a->q_base, b->q_base); swap(a->q_base, b->q_base);
...@@ -2928,7 +2957,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2928,7 +2957,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
} }
if (qparam->nxqs != lif->nxqs || if (qparam->nxqs != lif->nxqs ||
qparam->nrxq_descs != lif->nrxq_descs || qparam->nrxq_descs != lif->nrxq_descs ||
qparam->rxq_features != lif->rxq_features) { qparam->rxq_features != lif->rxq_features ||
qparam->xdp_prog != lif->xdp_prog) {
rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif, rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif,
sizeof(struct ionic_qcq *), GFP_KERNEL); sizeof(struct ionic_qcq *), GFP_KERNEL);
if (!rx_qcqs) { if (!rx_qcqs) {
...@@ -2959,7 +2989,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2959,7 +2989,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
4, desc_sz, comp_sz, sg_desc_sz, 4, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_tx_desc_info), sizeof(struct ionic_tx_desc_info),
lif->kern_pid, &lif->txqcqs[i]); lif->kern_pid, NULL, &lif->txqcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
} }
...@@ -2968,7 +2998,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2968,7 +2998,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_tx_desc_info), sizeof(struct ionic_tx_desc_info),
lif->kern_pid, &tx_qcqs[i]); lif->kern_pid, NULL, &tx_qcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
} }
...@@ -2990,7 +3020,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2990,7 +3020,7 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
4, desc_sz, comp_sz, sg_desc_sz, 4, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_rx_desc_info), sizeof(struct ionic_rx_desc_info),
lif->kern_pid, &lif->rxqcqs[i]); lif->kern_pid, NULL, &lif->rxqcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
} }
...@@ -2999,11 +3029,12 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2999,11 +3029,12 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
num_desc, desc_sz, comp_sz, sg_desc_sz, num_desc, desc_sz, comp_sz, sg_desc_sz,
sizeof(struct ionic_rx_desc_info), sizeof(struct ionic_rx_desc_info),
lif->kern_pid, &rx_qcqs[i]); lif->kern_pid, qparam->xdp_prog, &rx_qcqs[i]);
if (err) if (err)
goto err_out; goto err_out;
rx_qcqs[i]->q.features = qparam->rxq_features; rx_qcqs[i]->q.features = qparam->rxq_features;
rx_qcqs[i]->q.xdp_prog = qparam->xdp_prog;
} }
} }
......
...@@ -268,6 +268,7 @@ struct ionic_queue_params { ...@@ -268,6 +268,7 @@ struct ionic_queue_params {
unsigned int ntxq_descs; unsigned int ntxq_descs;
unsigned int nrxq_descs; unsigned int nrxq_descs;
u64 rxq_features; u64 rxq_features;
struct bpf_prog *xdp_prog;
bool intr_split; bool intr_split;
bool cmb_tx; bool cmb_tx;
bool cmb_rx; bool cmb_rx;
...@@ -280,6 +281,7 @@ static inline void ionic_init_queue_params(struct ionic_lif *lif, ...@@ -280,6 +281,7 @@ static inline void ionic_init_queue_params(struct ionic_lif *lif,
qparam->ntxq_descs = lif->ntxq_descs; qparam->ntxq_descs = lif->ntxq_descs;
qparam->nrxq_descs = lif->nrxq_descs; qparam->nrxq_descs = lif->nrxq_descs;
qparam->rxq_features = lif->rxq_features; qparam->rxq_features = lif->rxq_features;
qparam->xdp_prog = lif->xdp_prog;
qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
qparam->cmb_tx = test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state); qparam->cmb_tx = test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
qparam->cmb_rx = test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state); qparam->cmb_rx = test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <net/netdev_queues.h> #include <net/netdev_queues.h>
#include <net/page_pool/helpers.h>
#include "ionic.h" #include "ionic.h"
#include "ionic_lif.h" #include "ionic_lif.h"
...@@ -118,108 +119,57 @@ static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info) ...@@ -118,108 +119,57 @@ static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info)
static dma_addr_t ionic_rx_buf_pa(struct ionic_buf_info *buf_info) static dma_addr_t ionic_rx_buf_pa(struct ionic_buf_info *buf_info)
{ {
return buf_info->dma_addr + buf_info->page_offset; return page_pool_get_dma_addr(buf_info->page) + buf_info->page_offset;
} }
static unsigned int ionic_rx_buf_size(struct ionic_buf_info *buf_info) static void __ionic_rx_put_buf(struct ionic_queue *q,
{ struct ionic_buf_info *buf_info,
return min_t(u32, IONIC_MAX_BUF_LEN, IONIC_PAGE_SIZE - buf_info->page_offset); bool recycle_direct)
}
static int ionic_rx_page_alloc(struct ionic_queue *q,
struct ionic_buf_info *buf_info)
{ {
struct device *dev = q->dev; if (!buf_info->page)
dma_addr_t dma_addr; return;
struct page *page;
page = alloc_pages(IONIC_PAGE_GFP_MASK, 0);
if (unlikely(!page)) {
net_err_ratelimited("%s: %s page alloc failed\n",
dev_name(dev), q->name);
q_to_rx_stats(q)->alloc_err++;
return -ENOMEM;
}
dma_addr = dma_map_page(dev, page, 0,
IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(dev, dma_addr))) {
__free_pages(page, 0);
net_err_ratelimited("%s: %s dma map failed\n",
dev_name(dev), q->name);
q_to_rx_stats(q)->dma_map_err++;
return -EIO;
}
buf_info->dma_addr = dma_addr; page_pool_put_full_page(q->page_pool, buf_info->page, recycle_direct);
buf_info->page = page; buf_info->page = NULL;
buf_info->len = 0;
buf_info->page_offset = 0; buf_info->page_offset = 0;
return 0;
} }
static void ionic_rx_page_free(struct ionic_queue *q,
static void ionic_rx_put_buf(struct ionic_queue *q,
struct ionic_buf_info *buf_info) struct ionic_buf_info *buf_info)
{ {
struct device *dev = q->dev; __ionic_rx_put_buf(q, buf_info, false);
if (unlikely(!buf_info)) {
net_err_ratelimited("%s: %s invalid buf_info in free\n",
dev_name(dev), q->name);
return;
}
if (!buf_info->page)
return;
dma_unmap_page(dev, buf_info->dma_addr, IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
__free_pages(buf_info->page, 0);
buf_info->page = NULL;
} }
static bool ionic_rx_buf_recycle(struct ionic_queue *q, static void ionic_rx_put_buf_direct(struct ionic_queue *q,
struct ionic_buf_info *buf_info, u32 len) struct ionic_buf_info *buf_info)
{ {
u32 size; __ionic_rx_put_buf(q, buf_info, true);
/* don't re-use pages allocated in low-mem condition */
if (page_is_pfmemalloc(buf_info->page))
return false;
/* don't re-use buffers from non-local numa nodes */
if (page_to_nid(buf_info->page) != numa_mem_id())
return false;
size = ALIGN(len, q->xdp_rxq_info ? IONIC_PAGE_SIZE : IONIC_PAGE_SPLIT_SZ);
buf_info->page_offset += size;
if (buf_info->page_offset >= IONIC_PAGE_SIZE)
return false;
get_page(buf_info->page);
return true;
} }
static void ionic_rx_add_skb_frag(struct ionic_queue *q, static void ionic_rx_add_skb_frag(struct ionic_queue *q,
struct sk_buff *skb, struct sk_buff *skb,
struct ionic_buf_info *buf_info, struct ionic_buf_info *buf_info,
u32 off, u32 len, u32 headroom, u32 len,
bool synced) bool synced)
{ {
if (!synced) if (!synced)
dma_sync_single_range_for_cpu(q->dev, ionic_rx_buf_pa(buf_info), page_pool_dma_sync_for_cpu(q->page_pool,
off, len, DMA_FROM_DEVICE); buf_info->page,
buf_info->page_offset + headroom,
len);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf_info->page, buf_info->page_offset + off, buf_info->page, buf_info->page_offset + headroom,
len, len, buf_info->len);
IONIC_PAGE_SIZE);
if (!ionic_rx_buf_recycle(q, buf_info, len)) { /* napi_gro_frags() will release/recycle the
dma_unmap_page(q->dev, buf_info->dma_addr, * page_pool buffers from the frags list
IONIC_PAGE_SIZE, DMA_FROM_DEVICE); */
buf_info->page = NULL; buf_info->page = NULL;
} buf_info->len = 0;
buf_info->page_offset = 0;
} }
static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q, static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q,
...@@ -244,12 +194,13 @@ static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q, ...@@ -244,12 +194,13 @@ static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q,
q_to_rx_stats(q)->alloc_err++; q_to_rx_stats(q)->alloc_err++;
return NULL; return NULL;
} }
skb_mark_for_recycle(skb);
if (headroom) if (headroom)
frag_len = min_t(u16, len, frag_len = min_t(u16, len,
IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
else else
frag_len = min_t(u16, len, ionic_rx_buf_size(buf_info)); frag_len = min_t(u16, len, IONIC_PAGE_SIZE);
if (unlikely(!buf_info->page)) if (unlikely(!buf_info->page))
goto err_bad_buf_page; goto err_bad_buf_page;
...@@ -260,7 +211,7 @@ static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q, ...@@ -260,7 +211,7 @@ static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q,
for (i = 0; i < num_sg_elems; i++, buf_info++) { for (i = 0; i < num_sg_elems; i++, buf_info++) {
if (unlikely(!buf_info->page)) if (unlikely(!buf_info->page))
goto err_bad_buf_page; goto err_bad_buf_page;
frag_len = min_t(u16, len, ionic_rx_buf_size(buf_info)); frag_len = min_t(u16, len, buf_info->len);
ionic_rx_add_skb_frag(q, skb, buf_info, 0, frag_len, synced); ionic_rx_add_skb_frag(q, skb, buf_info, 0, frag_len, synced);
len -= frag_len; len -= frag_len;
} }
...@@ -277,11 +228,13 @@ static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev, ...@@ -277,11 +228,13 @@ static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev,
struct ionic_rx_desc_info *desc_info, struct ionic_rx_desc_info *desc_info,
unsigned int headroom, unsigned int headroom,
unsigned int len, unsigned int len,
unsigned int num_sg_elems,
bool synced) bool synced)
{ {
struct ionic_buf_info *buf_info; struct ionic_buf_info *buf_info;
struct device *dev = q->dev; struct device *dev = q->dev;
struct sk_buff *skb; struct sk_buff *skb;
int i;
buf_info = &desc_info->bufs[0]; buf_info = &desc_info->bufs[0];
...@@ -292,54 +245,52 @@ static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev, ...@@ -292,54 +245,52 @@ static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev,
q_to_rx_stats(q)->alloc_err++; q_to_rx_stats(q)->alloc_err++;
return NULL; return NULL;
} }
skb_mark_for_recycle(skb);
if (unlikely(!buf_info->page)) {
dev_kfree_skb(skb);
return NULL;
}
if (!synced) if (!synced)
dma_sync_single_range_for_cpu(dev, ionic_rx_buf_pa(buf_info), page_pool_dma_sync_for_cpu(q->page_pool,
headroom, len, DMA_FROM_DEVICE); buf_info->page,
buf_info->page_offset + headroom,
len);
skb_copy_to_linear_data(skb, ionic_rx_buf_va(buf_info) + headroom, len); skb_copy_to_linear_data(skb, ionic_rx_buf_va(buf_info) + headroom, len);
dma_sync_single_range_for_device(dev, ionic_rx_buf_pa(buf_info),
headroom, len, DMA_FROM_DEVICE);
skb_put(skb, len); skb_put(skb, len);
skb->protocol = eth_type_trans(skb, netdev); skb->protocol = eth_type_trans(skb, netdev);
/* recycle the Rx buffer now that we're done with it */
ionic_rx_put_buf_direct(q, buf_info);
buf_info++;
for (i = 0; i < num_sg_elems; i++, buf_info++)
ionic_rx_put_buf_direct(q, buf_info);
return skb; return skb;
} }
static void ionic_xdp_tx_desc_clean(struct ionic_queue *q, static void ionic_xdp_tx_desc_clean(struct ionic_queue *q,
struct ionic_tx_desc_info *desc_info) struct ionic_tx_desc_info *desc_info,
bool in_napi)
{ {
unsigned int nbufs = desc_info->nbufs; struct xdp_frame_bulk bq;
struct ionic_buf_info *buf_info;
struct device *dev = q->dev;
int i;
if (!nbufs) if (!desc_info->nbufs)
return; return;
buf_info = desc_info->bufs; xdp_frame_bulk_init(&bq);
dma_unmap_single(dev, buf_info->dma_addr, rcu_read_lock(); /* need for xdp_return_frame_bulk */
buf_info->len, DMA_TO_DEVICE);
if (desc_info->act == XDP_TX)
__free_pages(buf_info->page, 0);
buf_info->page = NULL;
buf_info++; if (desc_info->act == XDP_TX) {
for (i = 1; i < nbufs + 1 && buf_info->page; i++, buf_info++) { if (likely(in_napi))
dma_unmap_page(dev, buf_info->dma_addr, xdp_return_frame_rx_napi(desc_info->xdpf);
buf_info->len, DMA_TO_DEVICE); else
if (desc_info->act == XDP_TX) xdp_return_frame(desc_info->xdpf);
__free_pages(buf_info->page, 0); } else if (desc_info->act == XDP_REDIRECT) {
buf_info->page = NULL; ionic_tx_desc_unmap_bufs(q, desc_info);
xdp_return_frame_bulk(desc_info->xdpf, &bq);
} }
if (desc_info->act == XDP_REDIRECT) xdp_flush_frame_bulk(&bq);
xdp_return_frame(desc_info->xdpf); rcu_read_unlock();
desc_info->nbufs = 0; desc_info->nbufs = 0;
desc_info->xdpf = NULL; desc_info->xdpf = NULL;
...@@ -363,9 +314,17 @@ static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame, ...@@ -363,9 +314,17 @@ static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame,
buf_info = desc_info->bufs; buf_info = desc_info->bufs;
stats = q_to_tx_stats(q); stats = q_to_tx_stats(q);
if (act == XDP_TX) {
dma_addr = page_pool_get_dma_addr(page) +
off + XDP_PACKET_HEADROOM;
dma_sync_single_for_device(q->dev, dma_addr,
len, DMA_TO_DEVICE);
} else /* XDP_REDIRECT */ {
dma_addr = ionic_tx_map_single(q, frame->data, len); dma_addr = ionic_tx_map_single(q, frame->data, len);
if (!dma_addr) if (!dma_addr)
return -EIO; return -EIO;
}
buf_info->dma_addr = dma_addr; buf_info->dma_addr = dma_addr;
buf_info->len = len; buf_info->len = len;
buf_info->page = page; buf_info->page = page;
...@@ -387,11 +346,22 @@ static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame, ...@@ -387,11 +346,22 @@ static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame,
frag = sinfo->frags; frag = sinfo->frags;
elem = ionic_tx_sg_elems(q); elem = ionic_tx_sg_elems(q);
for (i = 0; i < sinfo->nr_frags; i++, frag++, bi++) { for (i = 0; i < sinfo->nr_frags; i++, frag++, bi++) {
dma_addr = ionic_tx_map_frag(q, frag, 0, skb_frag_size(frag)); if (act == XDP_TX) {
if (!dma_addr) { struct page *pg = skb_frag_page(frag);
dma_addr = page_pool_get_dma_addr(pg) +
skb_frag_off(frag);
dma_sync_single_for_device(q->dev, dma_addr,
skb_frag_size(frag),
DMA_TO_DEVICE);
} else {
dma_addr = ionic_tx_map_frag(q, frag, 0,
skb_frag_size(frag));
if (dma_mapping_error(q->dev, dma_addr)) {
ionic_tx_desc_unmap_bufs(q, desc_info); ionic_tx_desc_unmap_bufs(q, desc_info);
return -EIO; return -EIO;
} }
}
bi->dma_addr = dma_addr; bi->dma_addr = dma_addr;
bi->len = skb_frag_size(frag); bi->len = skb_frag_size(frag);
bi->page = skb_frag_page(frag); bi->page = skb_frag_page(frag);
...@@ -481,15 +451,13 @@ int ionic_xdp_xmit(struct net_device *netdev, int n, ...@@ -481,15 +451,13 @@ int ionic_xdp_xmit(struct net_device *netdev, int n,
return nxmit; return nxmit;
} }
static void ionic_xdp_rx_put_bufs(struct ionic_queue *q, static void ionic_xdp_rx_unlink_bufs(struct ionic_queue *q,
struct ionic_buf_info *buf_info, struct ionic_buf_info *buf_info,
int nbufs) int nbufs)
{ {
int i; int i;
for (i = 0; i < nbufs; i++) { for (i = 0; i < nbufs; i++) {
dma_unmap_page(q->dev, buf_info->dma_addr,
IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
buf_info->page = NULL; buf_info->page = NULL;
buf_info++; buf_info++;
} }
...@@ -516,11 +484,9 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -516,11 +484,9 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info), xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info),
XDP_PACKET_HEADROOM, frag_len, false); XDP_PACKET_HEADROOM, frag_len, false);
page_pool_dma_sync_for_cpu(rxq->page_pool, buf_info->page,
dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(buf_info), buf_info->page_offset + XDP_PACKET_HEADROOM,
XDP_PACKET_HEADROOM, frag_len, frag_len);
DMA_FROM_DEVICE);
prefetchw(&xdp_buf.data_hard_start); prefetchw(&xdp_buf.data_hard_start);
/* We limit MTU size to one buffer if !xdp_has_frags, so /* We limit MTU size to one buffer if !xdp_has_frags, so
...@@ -542,15 +508,16 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -542,15 +508,16 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
do { do {
if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) { if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) {
err = -ENOSPC; err = -ENOSPC;
goto out_xdp_abort; break;
} }
frag = &sinfo->frags[sinfo->nr_frags]; frag = &sinfo->frags[sinfo->nr_frags];
sinfo->nr_frags++; sinfo->nr_frags++;
bi++; bi++;
frag_len = min_t(u16, remain_len, ionic_rx_buf_size(bi)); frag_len = min_t(u16, remain_len, bi->len);
dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(bi), page_pool_dma_sync_for_cpu(rxq->page_pool, bi->page,
0, frag_len, DMA_FROM_DEVICE); buf_info->page_offset,
frag_len);
skb_frag_fill_page_desc(frag, bi->page, 0, frag_len); skb_frag_fill_page_desc(frag, bi->page, 0, frag_len);
sinfo->xdp_frags_size += frag_len; sinfo->xdp_frags_size += frag_len;
remain_len -= frag_len; remain_len -= frag_len;
...@@ -569,14 +536,16 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -569,14 +536,16 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
return false; /* false = we didn't consume the packet */ return false; /* false = we didn't consume the packet */
case XDP_DROP: case XDP_DROP:
ionic_rx_page_free(rxq, buf_info); ionic_rx_put_buf_direct(rxq, buf_info);
stats->xdp_drop++; stats->xdp_drop++;
break; break;
case XDP_TX: case XDP_TX:
xdpf = xdp_convert_buff_to_frame(&xdp_buf); xdpf = xdp_convert_buff_to_frame(&xdp_buf);
if (!xdpf) if (!xdpf) {
goto out_xdp_abort; err = -ENOSPC;
break;
}
txq = rxq->partner; txq = rxq->partner;
nq = netdev_get_tx_queue(netdev, txq->index); nq = netdev_get_tx_queue(netdev, txq->index);
...@@ -588,7 +557,8 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -588,7 +557,8 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
ionic_q_space_avail(txq), ionic_q_space_avail(txq),
1, 1)) { 1, 1)) {
__netif_tx_unlock(nq); __netif_tx_unlock(nq);
goto out_xdp_abort; err = -EIO;
break;
} }
err = ionic_xdp_post_frame(txq, xdpf, XDP_TX, err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
...@@ -598,49 +568,47 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -598,49 +568,47 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
__netif_tx_unlock(nq); __netif_tx_unlock(nq);
if (unlikely(err)) { if (unlikely(err)) {
netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err); netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err);
goto out_xdp_abort; break;
} }
ionic_xdp_rx_put_bufs(rxq, buf_info, nbufs); ionic_xdp_rx_unlink_bufs(rxq, buf_info, nbufs);
stats->xdp_tx++; stats->xdp_tx++;
/* the Tx completion will free the buffers */
break; break;
case XDP_REDIRECT: case XDP_REDIRECT:
err = xdp_do_redirect(netdev, &xdp_buf, xdp_prog); err = xdp_do_redirect(netdev, &xdp_buf, xdp_prog);
if (unlikely(err)) { if (unlikely(err)) {
netdev_dbg(netdev, "xdp_do_redirect err %d\n", err); netdev_dbg(netdev, "xdp_do_redirect err %d\n", err);
goto out_xdp_abort; break;
} }
ionic_xdp_rx_put_bufs(rxq, buf_info, nbufs); ionic_xdp_rx_unlink_bufs(rxq, buf_info, nbufs);
rxq->xdp_flush = true; rxq->xdp_flush = true;
stats->xdp_redirect++; stats->xdp_redirect++;
break; break;
case XDP_ABORTED: case XDP_ABORTED:
default: default:
goto out_xdp_abort; err = -EIO;
break;
} }
return true; if (err) {
ionic_rx_put_buf_direct(rxq, buf_info);
out_xdp_abort:
trace_xdp_exception(netdev, xdp_prog, xdp_action); trace_xdp_exception(netdev, xdp_prog, xdp_action);
ionic_rx_page_free(rxq, buf_info);
stats->xdp_aborted++; stats->xdp_aborted++;
}
return true; return true;
} }
static void ionic_rx_clean(struct ionic_queue *q, static void ionic_rx_clean(struct ionic_queue *q,
struct ionic_rx_desc_info *desc_info, struct ionic_rx_desc_info *desc_info,
struct ionic_rxq_comp *comp) struct ionic_rxq_comp *comp,
struct bpf_prog *xdp_prog)
{ {
struct net_device *netdev = q->lif->netdev; struct net_device *netdev = q->lif->netdev;
struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_qcq *qcq = q_to_qcq(q);
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct bpf_prog *xdp_prog; unsigned int headroom = 0;
unsigned int headroom;
struct sk_buff *skb; struct sk_buff *skb;
bool synced = false; bool synced = false;
bool use_copybreak; bool use_copybreak;
...@@ -648,7 +616,14 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -648,7 +616,14 @@ static void ionic_rx_clean(struct ionic_queue *q,
stats = q_to_rx_stats(q); stats = q_to_rx_stats(q);
if (comp->status) { if (unlikely(comp->status)) {
/* Most likely status==2 and the pkt received was bigger
* than the buffer available: comp->len will show the
* pkt size received that didn't fit the advertised desc.len
*/
dev_dbg(q->dev, "q%d drop comp->status %d comp->len %d desc->len %d\n",
q->index, comp->status, comp->len, q->rxq[q->head_idx].len);
stats->dropped++; stats->dropped++;
return; return;
} }
...@@ -657,18 +632,18 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -657,18 +632,18 @@ static void ionic_rx_clean(struct ionic_queue *q,
stats->pkts++; stats->pkts++;
stats->bytes += len; stats->bytes += len;
xdp_prog = READ_ONCE(q->lif->xdp_prog);
if (xdp_prog) { if (xdp_prog) {
if (ionic_run_xdp(stats, netdev, xdp_prog, q, desc_info->bufs, len)) if (ionic_run_xdp(stats, netdev, xdp_prog, q, desc_info->bufs, len))
return; return;
synced = true; synced = true;
headroom = XDP_PACKET_HEADROOM;
} }
headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0;
use_copybreak = len <= q->lif->rx_copybreak; use_copybreak = len <= q->lif->rx_copybreak;
if (use_copybreak) if (use_copybreak)
skb = ionic_rx_copybreak(netdev, q, desc_info, skb = ionic_rx_copybreak(netdev, q, desc_info,
headroom, len, synced); headroom, len,
comp->num_sg_elems, synced);
else else
skb = ionic_rx_build_skb(q, desc_info, headroom, len, skb = ionic_rx_build_skb(q, desc_info, headroom, len,
comp->num_sg_elems, synced); comp->num_sg_elems, synced);
...@@ -744,7 +719,7 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -744,7 +719,7 @@ static void ionic_rx_clean(struct ionic_queue *q,
napi_gro_frags(&qcq->napi); napi_gro_frags(&qcq->napi);
} }
bool ionic_rx_service(struct ionic_cq *cq) static bool __ionic_rx_service(struct ionic_cq *cq, struct bpf_prog *xdp_prog)
{ {
struct ionic_rx_desc_info *desc_info; struct ionic_rx_desc_info *desc_info;
struct ionic_queue *q = cq->bound_q; struct ionic_queue *q = cq->bound_q;
...@@ -766,11 +741,16 @@ bool ionic_rx_service(struct ionic_cq *cq) ...@@ -766,11 +741,16 @@ bool ionic_rx_service(struct ionic_cq *cq)
q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
/* clean the related q entry, only one per qc completion */ /* clean the related q entry, only one per qc completion */
ionic_rx_clean(q, desc_info, comp); ionic_rx_clean(q, desc_info, comp, xdp_prog);
return true; return true;
} }
bool ionic_rx_service(struct ionic_cq *cq)
{
return __ionic_rx_service(cq, NULL);
}
static inline void ionic_write_cmb_desc(struct ionic_queue *q, static inline void ionic_write_cmb_desc(struct ionic_queue *q,
void *desc) void *desc)
{ {
...@@ -781,7 +761,7 @@ static inline void ionic_write_cmb_desc(struct ionic_queue *q, ...@@ -781,7 +761,7 @@ static inline void ionic_write_cmb_desc(struct ionic_queue *q,
memcpy_toio(&q->cmb_txq[q->head_idx], desc, sizeof(q->cmb_txq[0])); memcpy_toio(&q->cmb_txq[q->head_idx], desc, sizeof(q->cmb_txq[0]));
} }
void ionic_rx_fill(struct ionic_queue *q) void ionic_rx_fill(struct ionic_queue *q, struct bpf_prog *xdp_prog)
{ {
struct net_device *netdev = q->lif->netdev; struct net_device *netdev = q->lif->netdev;
struct ionic_rx_desc_info *desc_info; struct ionic_rx_desc_info *desc_info;
...@@ -789,6 +769,9 @@ void ionic_rx_fill(struct ionic_queue *q) ...@@ -789,6 +769,9 @@ void ionic_rx_fill(struct ionic_queue *q)
struct ionic_buf_info *buf_info; struct ionic_buf_info *buf_info;
unsigned int fill_threshold; unsigned int fill_threshold;
struct ionic_rxq_desc *desc; struct ionic_rxq_desc *desc;
unsigned int first_frag_len;
unsigned int first_buf_len;
unsigned int headroom = 0;
unsigned int remain_len; unsigned int remain_len;
unsigned int frag_len; unsigned int frag_len;
unsigned int nfrags; unsigned int nfrags;
...@@ -806,34 +789,42 @@ void ionic_rx_fill(struct ionic_queue *q) ...@@ -806,34 +789,42 @@ void ionic_rx_fill(struct ionic_queue *q)
len = netdev->mtu + VLAN_ETH_HLEN; len = netdev->mtu + VLAN_ETH_HLEN;
for (i = n_fill; i; i--) { if (xdp_prog) {
unsigned int headroom; /* Always alloc the full size buffer, but only need
unsigned int buf_len; * the actual frag_len in the descriptor
* XDP uses space in the first buffer, so account for
* head room, tail room, and ip header in the first frag size.
*/
headroom = XDP_PACKET_HEADROOM;
first_buf_len = IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN + headroom;
first_frag_len = min_t(u16, len + headroom, first_buf_len);
} else {
/* Use MTU size if smaller than max buffer size */
first_frag_len = min_t(u16, len, IONIC_PAGE_SIZE);
first_buf_len = first_frag_len;
}
for (i = n_fill; i; i--) {
/* fill main descriptor - buf[0] */
nfrags = 0; nfrags = 0;
remain_len = len; remain_len = len;
desc = &q->rxq[q->head_idx]; desc = &q->rxq[q->head_idx];
desc_info = &q->rx_info[q->head_idx]; desc_info = &q->rx_info[q->head_idx];
buf_info = &desc_info->bufs[0]; buf_info = &desc_info->bufs[0];
if (!buf_info->page) { /* alloc a new buffer? */ buf_info->len = first_buf_len;
if (unlikely(ionic_rx_page_alloc(q, buf_info))) { frag_len = first_frag_len - headroom;
desc->addr = 0;
desc->len = 0; /* get a new buffer if we can't reuse one */
if (!buf_info->page)
buf_info->page = page_pool_alloc(q->page_pool,
&buf_info->page_offset,
&buf_info->len,
GFP_ATOMIC);
if (unlikely(!buf_info->page)) {
buf_info->len = 0;
return; return;
} }
}
/* fill main descriptor - buf[0]
* XDP uses space in the first buffer, so account for
* head room, tail room, and ip header in the first frag size.
*/
headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0;
if (q->xdp_rxq_info)
buf_len = IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN;
else
buf_len = ionic_rx_buf_size(buf_info);
frag_len = min_t(u16, len, buf_len);
desc->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info) + headroom); desc->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info) + headroom);
desc->len = cpu_to_le16(frag_len); desc->len = cpu_to_le16(frag_len);
...@@ -844,16 +835,26 @@ void ionic_rx_fill(struct ionic_queue *q) ...@@ -844,16 +835,26 @@ void ionic_rx_fill(struct ionic_queue *q)
/* fill sg descriptors - buf[1..n] */ /* fill sg descriptors - buf[1..n] */
sg_elem = q->rxq_sgl[q->head_idx].elems; sg_elem = q->rxq_sgl[q->head_idx].elems;
for (j = 0; remain_len > 0 && j < q->max_sg_elems; j++, sg_elem++) { for (j = 0; remain_len > 0 && j < q->max_sg_elems; j++, sg_elem++) {
if (!buf_info->page) { /* alloc a new sg buffer? */ frag_len = min_t(u16, remain_len, IONIC_PAGE_SIZE);
if (unlikely(ionic_rx_page_alloc(q, buf_info))) {
sg_elem->addr = 0; /* Recycle any leftover buffers that are too small to reuse */
sg_elem->len = 0; if (unlikely(buf_info->page && buf_info->len < frag_len))
ionic_rx_put_buf_direct(q, buf_info);
/* Get new buffer if needed */
if (!buf_info->page) {
buf_info->len = frag_len;
buf_info->page = page_pool_alloc(q->page_pool,
&buf_info->page_offset,
&buf_info->len,
GFP_ATOMIC);
if (unlikely(!buf_info->page)) {
buf_info->len = 0;
return; return;
} }
} }
sg_elem->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info)); sg_elem->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info));
frag_len = min_t(u16, remain_len, ionic_rx_buf_size(buf_info));
sg_elem->len = cpu_to_le16(frag_len); sg_elem->len = cpu_to_le16(frag_len);
remain_len -= frag_len; remain_len -= frag_len;
buf_info++; buf_info++;
...@@ -883,17 +884,12 @@ void ionic_rx_fill(struct ionic_queue *q) ...@@ -883,17 +884,12 @@ void ionic_rx_fill(struct ionic_queue *q)
void ionic_rx_empty(struct ionic_queue *q) void ionic_rx_empty(struct ionic_queue *q)
{ {
struct ionic_rx_desc_info *desc_info; struct ionic_rx_desc_info *desc_info;
struct ionic_buf_info *buf_info;
unsigned int i, j; unsigned int i, j;
for (i = 0; i < q->num_descs; i++) { for (i = 0; i < q->num_descs; i++) {
desc_info = &q->rx_info[i]; desc_info = &q->rx_info[i];
for (j = 0; j < ARRAY_SIZE(desc_info->bufs); j++) { for (j = 0; j < ARRAY_SIZE(desc_info->bufs); j++)
buf_info = &desc_info->bufs[j]; ionic_rx_put_buf(q, &desc_info->bufs[j]);
if (buf_info->page)
ionic_rx_page_free(q, buf_info);
}
desc_info->nbufs = 0; desc_info->nbufs = 0;
} }
...@@ -974,6 +970,32 @@ static void ionic_xdp_do_flush(struct ionic_cq *cq) ...@@ -974,6 +970,32 @@ static void ionic_xdp_do_flush(struct ionic_cq *cq)
} }
} }
static unsigned int ionic_rx_cq_service(struct ionic_cq *cq,
unsigned int work_to_do)
{
struct ionic_queue *q = cq->bound_q;
unsigned int work_done = 0;
struct bpf_prog *xdp_prog;
if (work_to_do == 0)
return 0;
xdp_prog = READ_ONCE(q->xdp_prog);
while (__ionic_rx_service(cq, xdp_prog)) {
if (cq->tail_idx == cq->num_descs - 1)
cq->done_color = !cq->done_color;
cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1);
if (++work_done >= work_to_do)
break;
}
ionic_rx_fill(q, xdp_prog);
ionic_xdp_do_flush(cq);
return work_done;
}
int ionic_rx_napi(struct napi_struct *napi, int budget) int ionic_rx_napi(struct napi_struct *napi, int budget)
{ {
struct ionic_qcq *qcq = napi_to_qcq(napi); struct ionic_qcq *qcq = napi_to_qcq(napi);
...@@ -984,12 +1006,8 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) ...@@ -984,12 +1006,8 @@ int ionic_rx_napi(struct napi_struct *napi, int budget)
if (unlikely(!budget)) if (unlikely(!budget))
return budget; return budget;
work_done = ionic_cq_service(cq, budget, work_done = ionic_rx_cq_service(cq, budget);
ionic_rx_service, NULL, NULL);
ionic_rx_fill(cq->bound_q);
ionic_xdp_do_flush(cq);
if (work_done < budget && napi_complete_done(napi, work_done)) { if (work_done < budget && napi_complete_done(napi, work_done)) {
ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR);
flags |= IONIC_INTR_CRED_UNMASK; flags |= IONIC_INTR_CRED_UNMASK;
...@@ -1030,12 +1048,8 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) ...@@ -1030,12 +1048,8 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget)
if (unlikely(!budget)) if (unlikely(!budget))
return budget; return budget;
rx_work_done = ionic_cq_service(rxcq, budget, rx_work_done = ionic_rx_cq_service(rxcq, budget);
ionic_rx_service, NULL, NULL);
ionic_rx_fill(rxcq->bound_q);
ionic_xdp_do_flush(rxcq);
if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) {
ionic_dim_update(rxqcq, 0); ionic_dim_update(rxqcq, 0);
flags |= IONIC_INTR_CRED_UNMASK; flags |= IONIC_INTR_CRED_UNMASK;
...@@ -1166,7 +1180,7 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -1166,7 +1180,7 @@ static void ionic_tx_clean(struct ionic_queue *q,
struct sk_buff *skb; struct sk_buff *skb;
if (desc_info->xdpf) { if (desc_info->xdpf) {
ionic_xdp_tx_desc_clean(q->partner, desc_info); ionic_xdp_tx_desc_clean(q->partner, desc_info, in_napi);
stats->clean++; stats->clean++;
if (unlikely(__netif_subqueue_stopped(q->lif->netdev, q->index))) if (unlikely(__netif_subqueue_stopped(q->lif->netdev, q->index)))
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
#ifndef _IONIC_TXRX_H_ #ifndef _IONIC_TXRX_H_
#define _IONIC_TXRX_H_ #define _IONIC_TXRX_H_
struct bpf_prog;
void ionic_tx_flush(struct ionic_cq *cq); void ionic_tx_flush(struct ionic_cq *cq);
void ionic_rx_fill(struct ionic_queue *q); void ionic_rx_fill(struct ionic_queue *q, struct bpf_prog *xdp_prog);
void ionic_rx_empty(struct ionic_queue *q); void ionic_rx_empty(struct ionic_queue *q);
void ionic_tx_empty(struct ionic_queue *q); void ionic_tx_empty(struct ionic_queue *q);
int ionic_rx_napi(struct napi_struct *napi, int budget); int ionic_rx_napi(struct napi_struct *napi, int budget);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment