Skip to content

Commit

Permalink
ionic: add initial framework for XDP support
Browse files Browse the repository at this point in the history
Set up the basics for running Rx packets through XDP programs.
Add new queue setup and teardown steps for adding/removing an
XDP program, and add the call to run the XDP on a packet.

The XDP frame size needs to be the MTU plus standard ethernet
header, plus head room for XDP scribblings and tail room for a
struct skb_shared_info.  Also, at this point, we don't support
XDP frags, only a single contiguous Rx buffer.  This means
that our page splitting is not very useful, so when XDP is in
use we need to use the full Rx buffer size and not do sharing.

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>
  • Loading branch information
Shannon Nelson authored and David S. Miller committed Feb 16, 2024
1 parent d67ee21 commit 180e35c
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 14 deletions.
7 changes: 7 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/bpf_trace.h>

#include "ionic_if.h"
#include "ionic_regs.h"
Expand Down Expand Up @@ -195,6 +196,11 @@ typedef void (*ionic_desc_cb)(struct ionic_queue *q,
#define IONIC_PAGE_GFP_MASK (GFP_ATOMIC | __GFP_NOWARN |\
__GFP_COMP | __GFP_MEMALLOC)

#define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \
(VLAN_ETH_HLEN + \
XDP_PACKET_HEADROOM + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info))))

struct ionic_buf_info {
struct page *page;
dma_addr_t dma_addr;
Expand Down Expand Up @@ -256,6 +262,7 @@ struct ionic_queue {
struct ionic_txq_sg_desc *txq_sgl;
struct ionic_rxq_sg_desc *rxq_sgl;
};
struct xdp_rxq_info *xdp_rxq_info;
dma_addr_t base_pa;
dma_addr_t cmb_base_pa;
dma_addr_t sg_base_pa;
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,11 @@ static int ionic_set_channels(struct net_device *netdev,

ionic_init_queue_params(lif, &qparam);

if ((ch->rx_count || ch->tx_count) && lif->xdp_prog) {
netdev_info(lif->netdev, "Split Tx/Rx interrupts not available when using XDP\n");
return -EOPNOTSUPP;
}

if (ch->rx_count != ch->tx_count) {
netdev_info(netdev, "The rx and tx count must be equal\n");
return -EINVAL;
Expand Down
175 changes: 173 additions & 2 deletions drivers/net/ethernet/pensando/ionic/ionic_lif.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ static int ionic_start_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 int ionic_xdp_queues_config(struct ionic_lif *lif);
static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q);

static void ionic_dim_work(struct work_struct *work)
{
struct dim *dim = container_of(work, struct dim, work);
Expand Down Expand Up @@ -422,6 +425,7 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
qcq->sg_base_pa = 0;
}

ionic_xdp_unregister_rxq_info(&qcq->q);
ionic_qcq_intr_free(lif, qcq);

vfree(qcq->cq.info);
Expand Down Expand Up @@ -862,8 +866,7 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
.type = q->type,
.ver = lif->qtype_info[q->type].version,
.index = cpu_to_le32(q->index),
.flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
IONIC_QINIT_F_SG),
.flags = cpu_to_le16(IONIC_QINIT_F_IRQ),
.intr_index = cpu_to_le16(cq->bound_intr->index),
.pid = cpu_to_le16(q->pid),
.ring_size = ilog2(q->num_descs),
Expand All @@ -875,6 +878,9 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
};
int err;

if (!lif->xdp_prog)
ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_SG);

if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) {
ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB);
ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa);
Expand Down Expand Up @@ -1640,6 +1646,8 @@ static int ionic_init_nic_features(struct ionic_lif *lif)
netdev->priv_flags |= IFF_UNICAST_FLT |
IFF_LIVE_ADDR_CHANGE;

netdev->xdp_features = NETDEV_XDP_ACT_BASIC;

return 0;
}

Expand Down Expand Up @@ -1777,6 +1785,18 @@ static int ionic_start_queues_reconfig(struct ionic_lif *lif)
return err;
}

static bool ionic_xdp_is_valid_mtu(struct ionic_lif *lif, u32 mtu,
struct bpf_prog *xdp_prog)
{
if (!xdp_prog)
return true;

if (mtu <= IONIC_XDP_MAX_LINEAR_MTU)
return true;

return false;
}

static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
{
struct ionic_lif *lif = netdev_priv(netdev);
Expand All @@ -1789,8 +1809,13 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
.mtu = cpu_to_le32(new_mtu),
},
};
struct bpf_prog *xdp_prog;
int err;

xdp_prog = READ_ONCE(lif->xdp_prog);
if (!ionic_xdp_is_valid_mtu(lif, new_mtu, xdp_prog))
return -EINVAL;

err = ionic_adminq_post_wait(lif, &ctx);
if (err)
return err;
Expand Down Expand Up @@ -2166,6 +2191,10 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
int derr = 0;
int i, err;

err = ionic_xdp_queues_config(lif);
if (err)
return err;

for (i = 0; i < lif->nxqs; i++) {
if (!(lif->rxqcqs[i] && lif->txqcqs[i])) {
dev_err(lif->ionic->dev, "%s: bad qcq %d\n", __func__, i);
Expand Down Expand Up @@ -2211,6 +2240,8 @@ static int ionic_txrx_enable(struct ionic_lif *lif)
derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr);
}

ionic_xdp_queues_config(lif);

return err;
}

Expand Down Expand Up @@ -2668,11 +2699,150 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif)
ionic_vf_start(ionic);
}

static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q)
{
struct xdp_rxq_info *xi;

if (!q->xdp_rxq_info)
return;

xi = q->xdp_rxq_info;
q->xdp_rxq_info = NULL;

xdp_rxq_info_unreg(xi);
kfree(xi);
}

static int ionic_xdp_register_rxq_info(struct ionic_queue *q, unsigned int napi_id)
{
struct xdp_rxq_info *rxq_info;
int err;

rxq_info = kzalloc(sizeof(*rxq_info), GFP_KERNEL);
if (!rxq_info)
return -ENOMEM;

err = xdp_rxq_info_reg(rxq_info, q->lif->netdev, q->index, napi_id);
if (err) {
dev_err(q->dev, "Queue %d xdp_rxq_info_reg failed, err %d\n",
q->index, err);
goto err_out;
}

err = xdp_rxq_info_reg_mem_model(rxq_info, MEM_TYPE_PAGE_ORDER0, NULL);
if (err) {
dev_err(q->dev, "Queue %d xdp_rxq_info_reg_mem_model failed, err %d\n",
q->index, err);
xdp_rxq_info_unreg(rxq_info);
goto err_out;
}

q->xdp_rxq_info = rxq_info;

return 0;

err_out:
kfree(rxq_info);
return err;
}

static int ionic_xdp_queues_config(struct ionic_lif *lif)
{
unsigned int i;
int err;

if (!lif->rxqcqs)
return 0;

/* 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;

for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) {
struct ionic_queue *q = &lif->rxqcqs[i]->q;

if (q->xdp_rxq_info) {
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)
{
struct ionic_lif *lif = netdev_priv(netdev);
struct bpf_prog *old_prog;
u32 maxfs;

if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) {
#define XDP_ERR_SPLIT "XDP not available with split Tx/Rx interrupts"
NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_SPLIT);
netdev_info(lif->netdev, XDP_ERR_SPLIT);
return -EOPNOTSUPP;
}

if (!ionic_xdp_is_valid_mtu(lif, netdev->mtu, bpf->prog)) {
#define XDP_ERR_MTU "MTU is too large for XDP without frags support"
NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_MTU);
netdev_info(lif->netdev, XDP_ERR_MTU);
return -EINVAL;
}

maxfs = __le32_to_cpu(lif->identity->eth.max_frame_size) - VLAN_ETH_HLEN;
if (bpf->prog)
maxfs = min_t(u32, maxfs, IONIC_XDP_MAX_LINEAR_MTU);
netdev->max_mtu = maxfs;

if (!netif_running(netdev)) {
old_prog = xchg(&lif->xdp_prog, bpf->prog);
} else {
mutex_lock(&lif->queue_lock);
ionic_stop_queues_reconfig(lif);
old_prog = xchg(&lif->xdp_prog, bpf->prog);
ionic_start_queues_reconfig(lif);
mutex_unlock(&lif->queue_lock);
}

if (old_prog)
bpf_prog_put(old_prog);

return 0;
}

static int ionic_xdp(struct net_device *netdev, struct netdev_bpf *bpf)
{
switch (bpf->command) {
case XDP_SETUP_PROG:
return ionic_xdp_config(netdev, bpf);
default:
return -EINVAL;
}
}

static const struct net_device_ops ionic_netdev_ops = {
.ndo_open = ionic_open,
.ndo_stop = ionic_stop,
.ndo_eth_ioctl = ionic_eth_ioctl,
.ndo_start_xmit = ionic_start_xmit,
.ndo_bpf = ionic_xdp,
.ndo_get_stats64 = ionic_get_stats64,
.ndo_set_rx_mode = ionic_ndo_set_rx_mode,
.ndo_set_features = ionic_set_features,
Expand Down Expand Up @@ -2755,6 +2925,7 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)
swap(a->q.base, b->q.base);
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_base, b->q_base);
swap(a->q_base_pa, b->q_base_pa);
swap(a->q_size, b->q_size);
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_lif.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ struct ionic_rx_stats {
u64 alloc_err;
u64 hwstamp_valid;
u64 hwstamp_invalid;
u64 xdp_drop;
u64 xdp_aborted;
u64 xdp_pass;
};

#define IONIC_QCQ_F_INITED BIT(0)
Expand Down Expand Up @@ -135,6 +138,9 @@ struct ionic_lif_sw_stats {
u64 hw_rx_over_errors;
u64 hw_rx_missed_errors;
u64 hw_tx_aborted_errors;
u64 xdp_drop;
u64 xdp_aborted;
u64 xdp_pass;
};

enum ionic_lif_state_flags {
Expand Down Expand Up @@ -230,6 +236,7 @@ struct ionic_lif {
struct ionic_phc *phc;

struct dentry *dentry;
struct bpf_prog *xdp_prog;
};

struct ionic_phc {
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static const struct ionic_stat_desc ionic_lif_stats_desc[] = {
IONIC_LIF_STAT_DESC(hw_rx_over_errors),
IONIC_LIF_STAT_DESC(hw_rx_missed_errors),
IONIC_LIF_STAT_DESC(hw_tx_aborted_errors),
IONIC_LIF_STAT_DESC(xdp_drop),
IONIC_LIF_STAT_DESC(xdp_aborted),
IONIC_LIF_STAT_DESC(xdp_pass),
};

static const struct ionic_stat_desc ionic_port_stats_desc[] = {
Expand Down Expand Up @@ -149,6 +152,9 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
IONIC_RX_STAT_DESC(hwstamp_invalid),
IONIC_RX_STAT_DESC(dropped),
IONIC_RX_STAT_DESC(vlan_stripped),
IONIC_RX_STAT_DESC(xdp_drop),
IONIC_RX_STAT_DESC(xdp_aborted),
IONIC_RX_STAT_DESC(xdp_pass),
};

#define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc)
Expand Down Expand Up @@ -185,6 +191,9 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
stats->rx_csum_error += rxstats->csum_error;
stats->rx_hwstamp_valid += rxstats->hwstamp_valid;
stats->rx_hwstamp_invalid += rxstats->hwstamp_invalid;
stats->xdp_drop += rxstats->xdp_drop;
stats->xdp_aborted += rxstats->xdp_aborted;
stats->xdp_pass += rxstats->xdp_pass;
}

static void ionic_get_lif_stats(struct ionic_lif *lif,
Expand Down
Loading

0 comments on commit 180e35c

Please sign in to comment.