summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c
blob: 2e6f486ec67d703ff2549a58ee7587d58f8ec62f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0+

#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/filter.h>

#include "lan966x_main.h"

static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp)
{
	struct lan966x_port *port = netdev_priv(dev);
	struct lan966x *lan966x = port->lan966x;
	struct bpf_prog *old_prog;
	bool old_xdp, new_xdp;
	int err;

	if (!lan966x->fdma) {
		NL_SET_ERR_MSG_MOD(xdp->extack,
				   "Allow to set xdp only when using fdma");
		return -EOPNOTSUPP;
	}

	old_xdp = lan966x_xdp_present(lan966x);
	old_prog = xchg(&port->xdp_prog, xdp->prog);
	new_xdp = lan966x_xdp_present(lan966x);

	if (old_xdp == new_xdp)
		goto out;

	err = lan966x_fdma_reload_page_pool(lan966x);
	if (err) {
		xchg(&port->xdp_prog, old_prog);
		return err;
	}

out:
	if (old_prog)
		bpf_prog_put(old_prog);

	return 0;
}

int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp)
{
	switch (xdp->command) {
	case XDP_SETUP_PROG:
		return lan966x_xdp_setup(dev, xdp);
	default:
		return -EINVAL;
	}
}

int lan966x_xdp_xmit(struct net_device *dev,
		     int n,
		     struct xdp_frame **frames,
		     u32 flags)
{
	struct lan966x_port *port = netdev_priv(dev);
	int nxmit = 0;

	for (int i = 0; i < n; ++i) {
		struct xdp_frame *xdpf = frames[i];
		int err;

		err = lan966x_fdma_xmit_xdpf(port, xdpf, NULL, true);
		if (err)
			break;

		nxmit++;
	}

	return nxmit;
}

int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len)
{
	struct bpf_prog *xdp_prog = port->xdp_prog;
	struct lan966x *lan966x = port->lan966x;
	struct xdp_frame *xdpf;
	struct xdp_buff xdp;
	u32 act;

	xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order,
		      &port->xdp_rxq);
	xdp_prepare_buff(&xdp, page_address(page),
			 IFH_LEN_BYTES + XDP_PACKET_HEADROOM,
			 data_len - IFH_LEN_BYTES, false);
	act = bpf_prog_run_xdp(xdp_prog, &xdp);
	switch (act) {
	case XDP_PASS:
		return FDMA_PASS;
	case XDP_TX:
		xdpf = xdp_convert_buff_to_frame(&xdp);
		if (!xdpf)
			return FDMA_DROP;

		return lan966x_fdma_xmit_xdpf(port, xdpf, page, false) ?
		       FDMA_DROP : FDMA_TX;
	case XDP_REDIRECT:
		if (xdp_do_redirect(port->dev, &xdp, xdp_prog))
			return FDMA_DROP;

		return FDMA_REDIRECT;
	default:
		bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act);
		fallthrough;
	case XDP_ABORTED:
		trace_xdp_exception(port->dev, xdp_prog, act);
		fallthrough;
	case XDP_DROP:
		return FDMA_DROP;
	}
}

bool lan966x_xdp_present(struct lan966x *lan966x)
{
	for (int p = 0; p < lan966x->num_phys_ports; ++p) {
		if (!lan966x->ports[p])
			continue;

		if (lan966x_xdp_port_present(lan966x->ports[p]))
			return true;
	}

	return false;
}

int lan966x_xdp_port_init(struct lan966x_port *port)
{
	struct lan966x *lan966x = port->lan966x;

	return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0,
				lan966x->napi.napi_id);
}

void lan966x_xdp_port_deinit(struct lan966x_port *port)
{
	if (xdp_rxq_info_is_reg(&port->xdp_rxq))
		xdp_rxq_info_unreg(&port->xdp_rxq);
}