aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c
blob: 820adf390b8e8f65c33e9cc897c1b77ed0a53b38 (plain) (blame)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// SPDX-License-Identifier: GPL-2.0
/* Marvell OcteonTx2 RVU Admin Function driver
 *
 * Copyright (C) 2021 Marvell.
 */

#include <linux/bitfield.h>
#include "rvu.h"

static int rvu_switch_install_rx_rule(struct rvu *rvu, u16 pcifunc,
				      u16 chan_mask)
{
	struct npc_install_flow_req req = { 0 };
	struct npc_install_flow_rsp rsp = { 0 };
	struct rvu_pfvf *pfvf;

	pfvf = rvu_get_pfvf(rvu, pcifunc);
	/* If the pcifunc is not initialized then nothing to do.
	 * This same function will be called again via rvu_switch_update_rules
	 * after pcifunc is initialized.
	 */
	if (!test_bit(NIXLF_INITIALIZED, &pfvf->flags))
		return 0;

	ether_addr_copy(req.packet.dmac, pfvf->mac_addr);
	eth_broadcast_addr((u8 *)&req.mask.dmac);
	req.hdr.pcifunc = 0; /* AF is requester */
	req.vf = pcifunc;
	req.features = BIT_ULL(NPC_DMAC);
	req.channel = pfvf->rx_chan_base;
	req.chan_mask = chan_mask;
	req.intf = pfvf->nix_rx_intf;
	req.op = NIX_RX_ACTION_DEFAULT;
	req.default_rule = 1;

	return rvu_mbox_handler_npc_install_flow(rvu, &req, &rsp);
}

static int rvu_switch_install_tx_rule(struct rvu *rvu, u16 pcifunc, u16 entry)
{
	struct npc_install_flow_req req = { 0 };
	struct npc_install_flow_rsp rsp = { 0 };
	struct rvu_pfvf *pfvf;
	u8 lbkid;

	pfvf = rvu_get_pfvf(rvu, pcifunc);
	/* If the pcifunc is not initialized then nothing to do.
	 * This same function will be called again via rvu_switch_update_rules
	 * after pcifunc is initialized.
	 */
	if (!test_bit(NIXLF_INITIALIZED, &pfvf->flags))
		return 0;

	lbkid = pfvf->nix_blkaddr == BLKADDR_NIX0 ? 0 : 1;
	ether_addr_copy(req.packet.dmac, pfvf->mac_addr);
	eth_broadcast_addr((u8 *)&req.mask.dmac);
	req.hdr.pcifunc = 0; /* AF is requester */
	req.vf = pcifunc;
	req.entry = entry;
	req.features = BIT_ULL(NPC_DMAC);
	req.intf = pfvf->nix_tx_intf;
	req.op = NIX_TX_ACTIONOP_UCAST_CHAN;
	req.index = (lbkid << 8) | RVU_SWITCH_LBK_CHAN;
	req.set_cntr = 1;

	return rvu_mbox_handler_npc_install_flow(rvu, &req, &rsp);
}

static int rvu_switch_install_rules(struct rvu *rvu)
{
	struct rvu_switch *rswitch = &rvu->rswitch;
	u16 start = rswitch->start_entry;
	struct rvu_hwinfo *hw = rvu->hw;
	u16 pcifunc, entry = 0;
	int pf, vf, numvfs;
	int err;

	for (pf = 1; pf < hw->total_pfs; pf++) {
		if (!is_pf_cgxmapped(rvu, pf))
			continue;

		pcifunc = pf << 10;
		/* rvu_get_nix_blkaddr sets up the corresponding NIX block
		 * address and NIX RX and TX interfaces for a pcifunc.
		 * Generally it is called during attach call of a pcifunc but it
		 * is called here since we are pre-installing rules before
		 * nixlfs are attached
		 */
		rvu_get_nix_blkaddr(rvu, pcifunc);

		/* MCAM RX rule for a PF/VF already exists as default unicast
		 * rules installed by AF. Hence change the channel in those
		 * rules to ignore channel so that packets with the required
		 * DMAC received from LBK(by other PF/VFs in system) or from
		 * external world (from wire) are accepted.
		 */
		err = rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
		if (err) {
			dev_err(rvu->dev, "RX rule for PF%d failed(%d)\n",
				pf, err);
			return err;
		}

		err = rvu_switch_install_tx_rule(rvu, pcifunc, start + entry);
		if (err) {
			dev_err(rvu->dev, "TX rule for PF%d failed(%d)\n",
				pf, err);
			return err;
		}

		rswitch->entry2pcifunc[entry++] = pcifunc;

		rvu_get_pf_numvfs(rvu, pf, &numvfs, NULL);
		for (vf = 0; vf < numvfs; vf++) {
			pcifunc = pf << 10 | ((vf + 1) & 0x3FF);
			rvu_get_nix_blkaddr(rvu, pcifunc);

			err = rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
			if (err) {
				dev_err(rvu->dev,
					"RX rule for PF%dVF%d failed(%d)\n",
					pf, vf, err);
				return err;
			}

			err = rvu_switch_install_tx_rule(rvu, pcifunc,
							 start + entry);
			if (err) {
				dev_err(rvu->dev,
					"TX rule for PF%dVF%d failed(%d)\n",
					pf, vf, err);
				return err;
			}

			rswitch->entry2pcifunc[entry++] = pcifunc;
		}
	}

	return 0;
}

void rvu_switch_enable(struct rvu *rvu)
{
	struct npc_mcam_alloc_entry_req alloc_req = { 0 };
	struct npc_mcam_alloc_entry_rsp alloc_rsp = { 0 };
	struct npc_delete_flow_req uninstall_req = { 0 };
	struct npc_mcam_free_entry_req free_req = { 0 };
	struct rvu_switch *rswitch = &rvu->rswitch;
	struct msg_rsp rsp;
	int ret;

	alloc_req.contig = true;
	alloc_req.count = rvu->cgx_mapped_pfs + rvu->cgx_mapped_vfs;
	ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &alloc_req,
						    &alloc_rsp);
	if (ret) {
		dev_err(rvu->dev,
			"Unable to allocate MCAM entries\n");
		goto exit;
	}

	if (alloc_rsp.count != alloc_req.count) {
		dev_err(rvu->dev,
			"Unable to allocate %d MCAM entries, got %d\n",
			alloc_req.count, alloc_rsp.count);
		goto free_entries;
	}

	rswitch->entry2pcifunc = kcalloc(alloc_req.count, sizeof(u16),
					 GFP_KERNEL);
	if (!rswitch->entry2pcifunc)
		goto free_entries;

	rswitch->used_entries = alloc_rsp.count;
	rswitch->start_entry = alloc_rsp.entry;

	ret = rvu_switch_install_rules(rvu);
	if (ret)
		goto uninstall_rules;

	return;

uninstall_rules:
	uninstall_req.start = rswitch->start_entry;
	uninstall_req.end =  rswitch->start_entry + rswitch->used_entries - 1;
	rvu_mbox_handler_npc_delete_flow(rvu, &uninstall_req, &rsp);
	kfree(rswitch->entry2pcifunc);
free_entries:
	free_req.all = 1;
	rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp);
exit:
	return;
}

void rvu_switch_disable(struct rvu *rvu)
{
	struct npc_delete_flow_req uninstall_req = { 0 };
	struct npc_mcam_free_entry_req free_req = { 0 };
	struct rvu_switch *rswitch = &rvu->rswitch;
	struct rvu_hwinfo *hw = rvu->hw;
	int pf, vf, numvfs;
	struct msg_rsp rsp;
	u16 pcifunc;
	int err;

	if (!rswitch->used_entries)
		return;

	for (pf = 1; pf < hw->total_pfs; pf++) {
		if (!is_pf_cgxmapped(rvu, pf))
			continue;

		pcifunc = pf << 10;
		err = rvu_switch_install_rx_rule(rvu, pcifunc, 0xFFF);
		if (err)
			dev_err(rvu->dev,
				"Reverting RX rule for PF%d failed(%d)\n",
				pf, err);

		rvu_get_pf_numvfs(rvu, pf, &numvfs, NULL);
		for (vf = 0; vf < numvfs; vf++) {
			pcifunc = pf << 10 | ((vf + 1) & 0x3FF);
			err = rvu_switch_install_rx_rule(rvu, pcifunc, 0xFFF);
			if (err)
				dev_err(rvu->dev,
					"Reverting RX rule for PF%dVF%d failed(%d)\n",
					pf, vf, err);
		}
	}

	uninstall_req.start = rswitch->start_entry;
	uninstall_req.end =  rswitch->start_entry + rswitch->used_entries - 1;
	free_req.all = 1;
	rvu_mbox_handler_npc_delete_flow(rvu, &uninstall_req, &rsp);
	rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp);
	rswitch->used_entries = 0;
	kfree(rswitch->entry2pcifunc);
}

void rvu_switch_update_rules(struct rvu *rvu, u16 pcifunc)
{
	struct rvu_switch *rswitch = &rvu->rswitch;
	u32 max = rswitch->used_entries;
	u16 entry;

	if (!rswitch->used_entries)
		return;

	for (entry = 0; entry < max; entry++) {
		if (rswitch->entry2pcifunc[entry] == pcifunc)
			break;
	}

	if (entry >= max)
		return;

	rvu_switch_install_tx_rule(rvu, pcifunc, rswitch->start_entry + entry);
	rvu_switch_install_rx_rule(rvu, pcifunc, 0x0);
}