| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
- /* Copyright (c) 2021, Microsoft Corporation. */
- #include <linux/inetdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/mm.h>
- #include <linux/bpf.h>
- #include <linux/bpf_trace.h>
- #include <net/xdp.h>
- #include <net/mana/mana.h>
- void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev)
- {
- u16 txq_idx = skb_get_queue_mapping(skb);
- struct netdev_queue *ndevtxq;
- int rc;
- __skb_push(skb, ETH_HLEN);
- ndevtxq = netdev_get_tx_queue(ndev, txq_idx);
- __netif_tx_lock(ndevtxq, smp_processor_id());
- rc = mana_start_xmit(skb, ndev);
- __netif_tx_unlock(ndevtxq);
- if (dev_xmit_complete(rc))
- return;
- dev_kfree_skb_any(skb);
- ndev->stats.tx_dropped++;
- }
- static int mana_xdp_xmit_fm(struct net_device *ndev, struct xdp_frame *frame,
- u16 q_idx)
- {
- struct sk_buff *skb;
- skb = xdp_build_skb_from_frame(frame, ndev);
- if (unlikely(!skb))
- return -ENOMEM;
- skb_set_queue_mapping(skb, q_idx);
- mana_xdp_tx(skb, ndev);
- return 0;
- }
- int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
- u32 flags)
- {
- struct mana_port_context *apc = netdev_priv(ndev);
- struct mana_stats_tx *tx_stats;
- int i, count = 0;
- u16 q_idx;
- if (unlikely(!apc->port_is_up))
- return 0;
- q_idx = smp_processor_id() % ndev->real_num_tx_queues;
- for (i = 0; i < n; i++) {
- if (mana_xdp_xmit_fm(ndev, frames[i], q_idx))
- break;
- count++;
- }
- tx_stats = &apc->tx_qp[q_idx].txq.stats;
- u64_stats_update_begin(&tx_stats->syncp);
- tx_stats->xdp_xmit += count;
- u64_stats_update_end(&tx_stats->syncp);
- return count;
- }
- u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
- struct xdp_buff *xdp, void *buf_va, uint pkt_len)
- {
- struct mana_stats_rx *rx_stats;
- struct bpf_prog *prog;
- u32 act = XDP_PASS;
- rcu_read_lock();
- prog = rcu_dereference(rxq->bpf_prog);
- if (!prog)
- goto out;
- xdp_init_buff(xdp, PAGE_SIZE, &rxq->xdp_rxq);
- xdp_prepare_buff(xdp, buf_va, XDP_PACKET_HEADROOM, pkt_len, false);
- act = bpf_prog_run_xdp(prog, xdp);
- rx_stats = &rxq->stats;
- switch (act) {
- case XDP_PASS:
- case XDP_TX:
- case XDP_DROP:
- break;
- case XDP_REDIRECT:
- rxq->xdp_rc = xdp_do_redirect(ndev, xdp, prog);
- if (!rxq->xdp_rc) {
- rxq->xdp_flush = true;
- u64_stats_update_begin(&rx_stats->syncp);
- rx_stats->packets++;
- rx_stats->bytes += pkt_len;
- rx_stats->xdp_redirect++;
- u64_stats_update_end(&rx_stats->syncp);
- break;
- }
- fallthrough;
- case XDP_ABORTED:
- trace_xdp_exception(ndev, prog, act);
- break;
- default:
- bpf_warn_invalid_xdp_action(ndev, prog, act);
- }
- out:
- rcu_read_unlock();
- return act;
- }
- struct bpf_prog *mana_xdp_get(struct mana_port_context *apc)
- {
- ASSERT_RTNL();
- return apc->bpf_prog;
- }
- static struct bpf_prog *mana_chn_xdp_get(struct mana_port_context *apc)
- {
- return rtnl_dereference(apc->rxqs[0]->bpf_prog);
- }
- /* Set xdp program on channels */
- void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog)
- {
- struct bpf_prog *old_prog = mana_chn_xdp_get(apc);
- unsigned int num_queues = apc->num_queues;
- int i;
- ASSERT_RTNL();
- if (old_prog == prog)
- return;
- if (prog)
- bpf_prog_add(prog, num_queues);
- for (i = 0; i < num_queues; i++)
- rcu_assign_pointer(apc->rxqs[i]->bpf_prog, prog);
- if (old_prog)
- for (i = 0; i < num_queues; i++)
- bpf_prog_put(old_prog);
- }
- static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
- struct netlink_ext_ack *extack)
- {
- struct mana_port_context *apc = netdev_priv(ndev);
- struct bpf_prog *old_prog;
- struct gdma_context *gc;
- gc = apc->ac->gdma_dev->gdma_context;
- old_prog = mana_xdp_get(apc);
- if (!old_prog && !prog)
- return 0;
- if (prog && ndev->mtu > MANA_XDP_MTU_MAX) {
- netdev_err(ndev, "XDP: mtu:%u too large, mtu_max:%lu\n",
- ndev->mtu, MANA_XDP_MTU_MAX);
- NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
- return -EOPNOTSUPP;
- }
- /* One refcnt of the prog is hold by the caller already, so
- * don't increase refcnt for this one.
- */
- apc->bpf_prog = prog;
- if (old_prog)
- bpf_prog_put(old_prog);
- if (apc->port_is_up)
- mana_chn_setxdp(apc, prog);
- if (prog)
- ndev->max_mtu = MANA_XDP_MTU_MAX;
- else
- ndev->max_mtu = gc->adapter_mtu - ETH_HLEN;
- return 0;
- }
- int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
- {
- struct netlink_ext_ack *extack = bpf->extack;
- int ret;
- switch (bpf->command) {
- case XDP_SETUP_PROG:
- return mana_xdp_set(ndev, bpf->prog, extack);
- default:
- return -EOPNOTSUPP;
- }
- return ret;
- }
|