1ed5356b5SHaiyang Zhang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2ed5356b5SHaiyang Zhang /* Copyright (c) 2021, Microsoft Corporation. */
3ed5356b5SHaiyang Zhang 
4ed5356b5SHaiyang Zhang #include <linux/inetdevice.h>
5ed5356b5SHaiyang Zhang #include <linux/etherdevice.h>
6ed5356b5SHaiyang Zhang #include <linux/mm.h>
7ed5356b5SHaiyang Zhang #include <linux/bpf.h>
8ed5356b5SHaiyang Zhang #include <linux/bpf_trace.h>
9ed5356b5SHaiyang Zhang #include <net/xdp.h>
10ed5356b5SHaiyang Zhang 
11fd325cd6SLong Li #include <net/mana/mana.h>
12ed5356b5SHaiyang Zhang 
mana_xdp_tx(struct sk_buff * skb,struct net_device * ndev)13ed5356b5SHaiyang Zhang void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev)
14ed5356b5SHaiyang Zhang {
15ed5356b5SHaiyang Zhang 	u16 txq_idx = skb_get_queue_mapping(skb);
16ed5356b5SHaiyang Zhang 	struct netdev_queue *ndevtxq;
17ed5356b5SHaiyang Zhang 	int rc;
18ed5356b5SHaiyang Zhang 
19ed5356b5SHaiyang Zhang 	__skb_push(skb, ETH_HLEN);
20ed5356b5SHaiyang Zhang 
21ed5356b5SHaiyang Zhang 	ndevtxq = netdev_get_tx_queue(ndev, txq_idx);
22ed5356b5SHaiyang Zhang 	__netif_tx_lock(ndevtxq, smp_processor_id());
23ed5356b5SHaiyang Zhang 
24ed5356b5SHaiyang Zhang 	rc = mana_start_xmit(skb, ndev);
25ed5356b5SHaiyang Zhang 
26ed5356b5SHaiyang Zhang 	__netif_tx_unlock(ndevtxq);
27ed5356b5SHaiyang Zhang 
28ed5356b5SHaiyang Zhang 	if (dev_xmit_complete(rc))
29ed5356b5SHaiyang Zhang 		return;
30ed5356b5SHaiyang Zhang 
31ed5356b5SHaiyang Zhang 	dev_kfree_skb_any(skb);
32ed5356b5SHaiyang Zhang 	ndev->stats.tx_dropped++;
33ed5356b5SHaiyang Zhang }
34ed5356b5SHaiyang Zhang 
mana_xdp_xmit_fm(struct net_device * ndev,struct xdp_frame * frame,u16 q_idx)357a8938cdSHaiyang Zhang static int mana_xdp_xmit_fm(struct net_device *ndev, struct xdp_frame *frame,
367a8938cdSHaiyang Zhang 			    u16 q_idx)
377a8938cdSHaiyang Zhang {
387a8938cdSHaiyang Zhang 	struct sk_buff *skb;
397a8938cdSHaiyang Zhang 
407a8938cdSHaiyang Zhang 	skb = xdp_build_skb_from_frame(frame, ndev);
417a8938cdSHaiyang Zhang 	if (unlikely(!skb))
427a8938cdSHaiyang Zhang 		return -ENOMEM;
437a8938cdSHaiyang Zhang 
447a8938cdSHaiyang Zhang 	skb_set_queue_mapping(skb, q_idx);
457a8938cdSHaiyang Zhang 
467a8938cdSHaiyang Zhang 	mana_xdp_tx(skb, ndev);
477a8938cdSHaiyang Zhang 
487a8938cdSHaiyang Zhang 	return 0;
497a8938cdSHaiyang Zhang }
507a8938cdSHaiyang Zhang 
mana_xdp_xmit(struct net_device * ndev,int n,struct xdp_frame ** frames,u32 flags)517a8938cdSHaiyang Zhang int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
527a8938cdSHaiyang Zhang 		  u32 flags)
537a8938cdSHaiyang Zhang {
547a8938cdSHaiyang Zhang 	struct mana_port_context *apc = netdev_priv(ndev);
557a8938cdSHaiyang Zhang 	struct mana_stats_tx *tx_stats;
567a8938cdSHaiyang Zhang 	int i, count = 0;
577a8938cdSHaiyang Zhang 	u16 q_idx;
587a8938cdSHaiyang Zhang 
597a8938cdSHaiyang Zhang 	if (unlikely(!apc->port_is_up))
607a8938cdSHaiyang Zhang 		return 0;
617a8938cdSHaiyang Zhang 
627a8938cdSHaiyang Zhang 	q_idx = smp_processor_id() % ndev->real_num_tx_queues;
637a8938cdSHaiyang Zhang 
647a8938cdSHaiyang Zhang 	for (i = 0; i < n; i++) {
657a8938cdSHaiyang Zhang 		if (mana_xdp_xmit_fm(ndev, frames[i], q_idx))
667a8938cdSHaiyang Zhang 			break;
677a8938cdSHaiyang Zhang 
687a8938cdSHaiyang Zhang 		count++;
697a8938cdSHaiyang Zhang 	}
707a8938cdSHaiyang Zhang 
717a8938cdSHaiyang Zhang 	tx_stats = &apc->tx_qp[q_idx].txq.stats;
727a8938cdSHaiyang Zhang 
737a8938cdSHaiyang Zhang 	u64_stats_update_begin(&tx_stats->syncp);
747a8938cdSHaiyang Zhang 	tx_stats->xdp_xmit += count;
757a8938cdSHaiyang Zhang 	u64_stats_update_end(&tx_stats->syncp);
767a8938cdSHaiyang Zhang 
777a8938cdSHaiyang Zhang 	return count;
787a8938cdSHaiyang Zhang }
797a8938cdSHaiyang Zhang 
mana_run_xdp(struct net_device * ndev,struct mana_rxq * rxq,struct xdp_buff * xdp,void * buf_va,uint pkt_len)80ed5356b5SHaiyang Zhang u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
81ed5356b5SHaiyang Zhang 		 struct xdp_buff *xdp, void *buf_va, uint pkt_len)
82ed5356b5SHaiyang Zhang {
837a8938cdSHaiyang Zhang 	struct mana_stats_rx *rx_stats;
84ed5356b5SHaiyang Zhang 	struct bpf_prog *prog;
85ed5356b5SHaiyang Zhang 	u32 act = XDP_PASS;
86ed5356b5SHaiyang Zhang 
87ed5356b5SHaiyang Zhang 	rcu_read_lock();
88ed5356b5SHaiyang Zhang 	prog = rcu_dereference(rxq->bpf_prog);
89ed5356b5SHaiyang Zhang 
90ed5356b5SHaiyang Zhang 	if (!prog)
91ed5356b5SHaiyang Zhang 		goto out;
92ed5356b5SHaiyang Zhang 
93ed5356b5SHaiyang Zhang 	xdp_init_buff(xdp, PAGE_SIZE, &rxq->xdp_rxq);
94ed5356b5SHaiyang Zhang 	xdp_prepare_buff(xdp, buf_va, XDP_PACKET_HEADROOM, pkt_len, false);
95ed5356b5SHaiyang Zhang 
96ed5356b5SHaiyang Zhang 	act = bpf_prog_run_xdp(prog, xdp);
97ed5356b5SHaiyang Zhang 
987a8938cdSHaiyang Zhang 	rx_stats = &rxq->stats;
997a8938cdSHaiyang Zhang 
100ed5356b5SHaiyang Zhang 	switch (act) {
101ed5356b5SHaiyang Zhang 	case XDP_PASS:
102ed5356b5SHaiyang Zhang 	case XDP_TX:
103ed5356b5SHaiyang Zhang 	case XDP_DROP:
104ed5356b5SHaiyang Zhang 		break;
105ed5356b5SHaiyang Zhang 
1067a8938cdSHaiyang Zhang 	case XDP_REDIRECT:
1077a8938cdSHaiyang Zhang 		rxq->xdp_rc = xdp_do_redirect(ndev, xdp, prog);
1087a8938cdSHaiyang Zhang 		if (!rxq->xdp_rc) {
1097a8938cdSHaiyang Zhang 			rxq->xdp_flush = true;
1107a8938cdSHaiyang Zhang 
1117a8938cdSHaiyang Zhang 			u64_stats_update_begin(&rx_stats->syncp);
1127a8938cdSHaiyang Zhang 			rx_stats->packets++;
1137a8938cdSHaiyang Zhang 			rx_stats->bytes += pkt_len;
1147a8938cdSHaiyang Zhang 			rx_stats->xdp_redirect++;
1157a8938cdSHaiyang Zhang 			u64_stats_update_end(&rx_stats->syncp);
1167a8938cdSHaiyang Zhang 
1177a8938cdSHaiyang Zhang 			break;
1187a8938cdSHaiyang Zhang 		}
1197a8938cdSHaiyang Zhang 
1207a8938cdSHaiyang Zhang 		fallthrough;
1217a8938cdSHaiyang Zhang 
122ed5356b5SHaiyang Zhang 	case XDP_ABORTED:
123ed5356b5SHaiyang Zhang 		trace_xdp_exception(ndev, prog, act);
124ed5356b5SHaiyang Zhang 		break;
125ed5356b5SHaiyang Zhang 
126ed5356b5SHaiyang Zhang 	default:
127c8064e5bSPaolo Abeni 		bpf_warn_invalid_xdp_action(ndev, prog, act);
128ed5356b5SHaiyang Zhang 	}
129ed5356b5SHaiyang Zhang 
130ed5356b5SHaiyang Zhang out:
131ed5356b5SHaiyang Zhang 	rcu_read_unlock();
132ed5356b5SHaiyang Zhang 
133ed5356b5SHaiyang Zhang 	return act;
134ed5356b5SHaiyang Zhang }
135ed5356b5SHaiyang Zhang 
mana_xdp_get(struct mana_port_context * apc)136ed5356b5SHaiyang Zhang struct bpf_prog *mana_xdp_get(struct mana_port_context *apc)
137ed5356b5SHaiyang Zhang {
138ed5356b5SHaiyang Zhang 	ASSERT_RTNL();
139ed5356b5SHaiyang Zhang 
140ed5356b5SHaiyang Zhang 	return apc->bpf_prog;
141ed5356b5SHaiyang Zhang }
142ed5356b5SHaiyang Zhang 
mana_chn_xdp_get(struct mana_port_context * apc)143ed5356b5SHaiyang Zhang static struct bpf_prog *mana_chn_xdp_get(struct mana_port_context *apc)
144ed5356b5SHaiyang Zhang {
145ed5356b5SHaiyang Zhang 	return rtnl_dereference(apc->rxqs[0]->bpf_prog);
146ed5356b5SHaiyang Zhang }
147ed5356b5SHaiyang Zhang 
148ed5356b5SHaiyang Zhang /* Set xdp program on channels */
mana_chn_setxdp(struct mana_port_context * apc,struct bpf_prog * prog)149ed5356b5SHaiyang Zhang void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog)
150ed5356b5SHaiyang Zhang {
151ed5356b5SHaiyang Zhang 	struct bpf_prog *old_prog = mana_chn_xdp_get(apc);
152ed5356b5SHaiyang Zhang 	unsigned int num_queues = apc->num_queues;
153ed5356b5SHaiyang Zhang 	int i;
154ed5356b5SHaiyang Zhang 
155ed5356b5SHaiyang Zhang 	ASSERT_RTNL();
156ed5356b5SHaiyang Zhang 
157ed5356b5SHaiyang Zhang 	if (old_prog == prog)
158ed5356b5SHaiyang Zhang 		return;
159ed5356b5SHaiyang Zhang 
160ed5356b5SHaiyang Zhang 	if (prog)
161ed5356b5SHaiyang Zhang 		bpf_prog_add(prog, num_queues);
162ed5356b5SHaiyang Zhang 
163ed5356b5SHaiyang Zhang 	for (i = 0; i < num_queues; i++)
164ed5356b5SHaiyang Zhang 		rcu_assign_pointer(apc->rxqs[i]->bpf_prog, prog);
165ed5356b5SHaiyang Zhang 
166ed5356b5SHaiyang Zhang 	if (old_prog)
167ed5356b5SHaiyang Zhang 		for (i = 0; i < num_queues; i++)
168ed5356b5SHaiyang Zhang 			bpf_prog_put(old_prog);
169ed5356b5SHaiyang Zhang }
170ed5356b5SHaiyang Zhang 
mana_xdp_set(struct net_device * ndev,struct bpf_prog * prog,struct netlink_ext_ack * extack)171ed5356b5SHaiyang Zhang static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
172ed5356b5SHaiyang Zhang 			struct netlink_ext_ack *extack)
173ed5356b5SHaiyang Zhang {
174ed5356b5SHaiyang Zhang 	struct mana_port_context *apc = netdev_priv(ndev);
175ed5356b5SHaiyang Zhang 	struct bpf_prog *old_prog;
176*80f6215bSHaiyang Zhang 	struct gdma_context *gc;
177*80f6215bSHaiyang Zhang 
178*80f6215bSHaiyang Zhang 	gc = apc->ac->gdma_dev->gdma_context;
179ed5356b5SHaiyang Zhang 
180ed5356b5SHaiyang Zhang 	old_prog = mana_xdp_get(apc);
181ed5356b5SHaiyang Zhang 
182ed5356b5SHaiyang Zhang 	if (!old_prog && !prog)
183ed5356b5SHaiyang Zhang 		return 0;
184ed5356b5SHaiyang Zhang 
185*80f6215bSHaiyang Zhang 	if (prog && ndev->mtu > MANA_XDP_MTU_MAX) {
186*80f6215bSHaiyang Zhang 		netdev_err(ndev, "XDP: mtu:%u too large, mtu_max:%lu\n",
187*80f6215bSHaiyang Zhang 			   ndev->mtu, MANA_XDP_MTU_MAX);
188ed5356b5SHaiyang Zhang 		NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
189ed5356b5SHaiyang Zhang 
190ed5356b5SHaiyang Zhang 		return -EOPNOTSUPP;
191ed5356b5SHaiyang Zhang 	}
192ed5356b5SHaiyang Zhang 
193ed5356b5SHaiyang Zhang 	/* One refcnt of the prog is hold by the caller already, so
194ed5356b5SHaiyang Zhang 	 * don't increase refcnt for this one.
195ed5356b5SHaiyang Zhang 	 */
196ed5356b5SHaiyang Zhang 	apc->bpf_prog = prog;
197ed5356b5SHaiyang Zhang 
198ed5356b5SHaiyang Zhang 	if (old_prog)
199ed5356b5SHaiyang Zhang 		bpf_prog_put(old_prog);
200ed5356b5SHaiyang Zhang 
201ed5356b5SHaiyang Zhang 	if (apc->port_is_up)
202ed5356b5SHaiyang Zhang 		mana_chn_setxdp(apc, prog);
203ed5356b5SHaiyang Zhang 
204*80f6215bSHaiyang Zhang 	if (prog)
205*80f6215bSHaiyang Zhang 		ndev->max_mtu = MANA_XDP_MTU_MAX;
206*80f6215bSHaiyang Zhang 	else
207*80f6215bSHaiyang Zhang 		ndev->max_mtu = gc->adapter_mtu - ETH_HLEN;
208*80f6215bSHaiyang Zhang 
209ed5356b5SHaiyang Zhang 	return 0;
210ed5356b5SHaiyang Zhang }
211ed5356b5SHaiyang Zhang 
mana_bpf(struct net_device * ndev,struct netdev_bpf * bpf)212ed5356b5SHaiyang Zhang int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
213ed5356b5SHaiyang Zhang {
214ed5356b5SHaiyang Zhang 	struct netlink_ext_ack *extack = bpf->extack;
215ed5356b5SHaiyang Zhang 	int ret;
216ed5356b5SHaiyang Zhang 
217ed5356b5SHaiyang Zhang 	switch (bpf->command) {
218ed5356b5SHaiyang Zhang 	case XDP_SETUP_PROG:
219ed5356b5SHaiyang Zhang 		return mana_xdp_set(ndev, bpf->prog, extack);
220ed5356b5SHaiyang Zhang 
221ed5356b5SHaiyang Zhang 	default:
222ed5356b5SHaiyang Zhang 		return -EOPNOTSUPP;
223ed5356b5SHaiyang Zhang 	}
224ed5356b5SHaiyang Zhang 
225ed5356b5SHaiyang Zhang 	return ret;
226ed5356b5SHaiyang Zhang }
227