1 /* SPDX-License-Identifier: BSD-3-Clause
2 *
3 * Copyright(c) 2021 Xilinx, Inc.
4 */
5
6 #include "efx.h"
7 #include "efx_impl.h"
8
9 #if EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL
10
11 /* Match by Ether-type */
12 #define EFX_VNIC_ENCAP_RULE_MATCH_ETHER_TYPE \
13 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_ETHER_TYPE_LBN)
14 /* Match by outer VLAN ID */
15 #define EFX_VNIC_ENCAP_RULE_MATCH_OUTER_VID \
16 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_OUTER_VLAN_LBN)
17 /* Match by local IP host address */
18 #define EFX_VNIC_ENCAP_RULE_MATCH_LOC_HOST \
19 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_IP_LBN)
20 /* Match by IP transport protocol */
21 #define EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO \
22 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_IP_PROTO_LBN)
23 /* Match by local TCP/UDP port */
24 #define EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT \
25 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_PORT_LBN)
26
27 /*
28 * Helper structure to pass parameters to MCDI function to add a VNIC
29 * encapsulation rule.
30 */
31 typedef struct efx_vnic_encap_rule_spec_s {
32 uint32_t evers_mport_selector; /* Host-endian */
33 uint32_t evers_match_flags; /* Host-endian */
34 uint16_t evers_ether_type; /* Host-endian */
35 uint16_t evers_outer_vid; /* Host-endian */
36 efx_oword_t evers_loc_host; /* Big-endian */
37 uint8_t evers_ip_proto;
38 uint16_t evers_loc_port; /* Host-endian */
39 efx_tunnel_protocol_t evers_encap_type;
40 } efx_vnic_encap_rule_spec_t;
41
42 static uint32_t
efx_tunnel_protocol2mae_encap_type(__in efx_tunnel_protocol_t proto,__out uint32_t * typep)43 efx_tunnel_protocol2mae_encap_type(
44 __in efx_tunnel_protocol_t proto,
45 __out uint32_t *typep)
46 {
47 efx_rc_t rc;
48
49 switch (proto) {
50 case EFX_TUNNEL_PROTOCOL_NONE:
51 *typep = MAE_MCDI_ENCAP_TYPE_NONE;
52 break;
53 case EFX_TUNNEL_PROTOCOL_VXLAN:
54 *typep = MAE_MCDI_ENCAP_TYPE_VXLAN;
55 break;
56 case EFX_TUNNEL_PROTOCOL_GENEVE:
57 *typep = MAE_MCDI_ENCAP_TYPE_GENEVE;
58 break;
59 case EFX_TUNNEL_PROTOCOL_NVGRE:
60 *typep = MAE_MCDI_ENCAP_TYPE_NVGRE;
61 break;
62 default:
63 rc = EINVAL;
64 goto fail1;
65 }
66
67 return (0);
68
69 fail1:
70 EFSYS_PROBE1(fail1, efx_rc_t, rc);
71
72 return (rc);
73 }
74
75 static __checkReturn efx_rc_t
efx_mcdi_vnic_encap_rule_add(__in efx_nic_t * enp,__in const efx_vnic_encap_rule_spec_t * spec,__out efx_vnic_encap_rule_handle_t * handle)76 efx_mcdi_vnic_encap_rule_add(
77 __in efx_nic_t *enp,
78 __in const efx_vnic_encap_rule_spec_t *spec,
79 __out efx_vnic_encap_rule_handle_t *handle)
80
81 {
82 efx_mcdi_req_t req;
83 EFX_MCDI_DECLARE_BUF(payload,
84 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN,
85 MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN);
86 uint32_t encap_type;
87 efx_rc_t rc;
88
89 req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_ADD;
90 req.emr_in_buf = payload;
91 req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN;
92 req.emr_out_buf = payload;
93 req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN;
94
95 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MPORT_SELECTOR,
96 spec->evers_mport_selector);
97 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MATCH_FLAGS,
98 spec->evers_match_flags);
99
100 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_ETHER_TYPE,
101 __CPU_TO_BE_16(spec->evers_ether_type));
102 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_OUTER_VLAN_WORD,
103 __CPU_TO_BE_16(spec->evers_outer_vid));
104
105 /*
106 * Address is already in network order as well as the MCDI field,
107 * so plain copy is used.
108 */
109 EFX_STATIC_ASSERT(sizeof (spec->evers_loc_host) ==
110 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN);
111 memcpy(MCDI_IN2(req, uint8_t, VNIC_ENCAP_RULE_ADD_IN_DST_IP),
112 &spec->evers_loc_host.eo_byte[0],
113 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN);
114
115 MCDI_IN_SET_BYTE(req, VNIC_ENCAP_RULE_ADD_IN_IP_PROTO,
116 spec->evers_ip_proto);
117 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_DST_PORT,
118 __CPU_TO_BE_16(spec->evers_loc_port));
119
120 rc = efx_tunnel_protocol2mae_encap_type(spec->evers_encap_type,
121 &encap_type);
122 if (rc != 0)
123 goto fail1;
124
125 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_ENCAP_TYPE, encap_type);
126
127 efx_mcdi_execute(enp, &req);
128
129 if (req.emr_rc != 0) {
130 rc = req.emr_rc;
131 goto fail2;
132 }
133
134 if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN) {
135 rc = EMSGSIZE;
136 goto fail3;
137 }
138
139 if (handle != NULL)
140 *handle = MCDI_OUT_DWORD(req, VNIC_ENCAP_RULE_ADD_OUT_HANDLE);
141
142 return (0);
143
144 fail3:
145 EFSYS_PROBE(fail3);
146
147 fail2:
148 EFSYS_PROBE(fail2);
149
150 fail1:
151 EFSYS_PROBE1(fail1, efx_rc_t, rc);
152
153 return (rc);
154 }
155
156 static __checkReturn efx_rc_t
efx_mcdi_vnic_encap_rule_remove(__in efx_nic_t * enp,__in efx_vnic_encap_rule_handle_t handle)157 efx_mcdi_vnic_encap_rule_remove(
158 __in efx_nic_t *enp,
159 __in efx_vnic_encap_rule_handle_t handle)
160
161 {
162 efx_mcdi_req_t req;
163 EFX_MCDI_DECLARE_BUF(payload,
164 MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN,
165 MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN);
166 efx_rc_t rc;
167
168 req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_REMOVE;
169 req.emr_in_buf = payload;
170 req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN;
171 req.emr_out_buf = payload;
172 req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN;
173
174 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_REMOVE_IN_HANDLE, handle);
175
176 efx_mcdi_execute(enp, &req);
177
178 if (req.emr_rc != 0) {
179 rc = req.emr_rc;
180 goto fail1;
181 }
182
183 if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN) {
184 rc = EMSGSIZE;
185 goto fail2;
186 }
187
188 return (0);
189
190 fail2:
191 EFSYS_PROBE(fail2);
192
193 fail1:
194 EFSYS_PROBE1(fail1, efx_rc_t, rc);
195
196 return (rc);
197 }
198
199 static void
rhead_vnic_encap_rule_spec_init(__in const efx_tunnel_udp_entry_t * etuep,__out efx_vnic_encap_rule_spec_t * spec)200 rhead_vnic_encap_rule_spec_init(
201 __in const efx_tunnel_udp_entry_t *etuep,
202 __out efx_vnic_encap_rule_spec_t *spec)
203 {
204 memset(spec, 0, sizeof (*spec));
205
206 spec->evers_mport_selector = MAE_MPORT_SELECTOR_ASSIGNED;
207 spec->evers_match_flags = EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO |
208 EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT;
209 spec->evers_ip_proto = EFX_IPPROTO_UDP;
210 spec->evers_loc_port = etuep->etue_port;
211 spec->evers_encap_type = etuep->etue_protocol;
212 }
213
214 static __checkReturn efx_rc_t
rhead_udp_port_tunnel_add(__in efx_nic_t * enp,__inout efx_tunnel_udp_entry_t * etuep)215 rhead_udp_port_tunnel_add(
216 __in efx_nic_t *enp,
217 __inout efx_tunnel_udp_entry_t *etuep)
218 {
219 efx_vnic_encap_rule_spec_t spec;
220
221 rhead_vnic_encap_rule_spec_init(etuep, &spec);
222 return (efx_mcdi_vnic_encap_rule_add(enp, &spec, &etuep->etue_handle));
223 }
224
225 static __checkReturn efx_rc_t
rhead_udp_port_tunnel_remove(__in efx_nic_t * enp,__in efx_tunnel_udp_entry_t * etuep)226 rhead_udp_port_tunnel_remove(
227 __in efx_nic_t *enp,
228 __in efx_tunnel_udp_entry_t *etuep)
229 {
230 return (efx_mcdi_vnic_encap_rule_remove(enp, etuep->etue_handle));
231 }
232
233 __checkReturn efx_rc_t
rhead_tunnel_reconfigure(__in efx_nic_t * enp)234 rhead_tunnel_reconfigure(
235 __in efx_nic_t *enp)
236 {
237 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
238 efx_rc_t rc;
239 efsys_lock_state_t state;
240 efx_tunnel_cfg_t etc;
241 efx_tunnel_cfg_t added;
242 unsigned int i;
243 unsigned int j;
244
245 memset(&added, 0, sizeof(added));
246
247 /*
248 * Make a local copy of UDP tunnel table to release the lock
249 * when executing MCDIs.
250 */
251 EFSYS_LOCK(enp->en_eslp, state);
252 memcpy(&etc, etcp, sizeof (etc));
253 EFSYS_UNLOCK(enp->en_eslp, state);
254
255 for (i = 0; i < etc.etc_udp_entries_num; i++) {
256 efx_tunnel_udp_entry_t *etc_entry = &etc.etc_udp_entries[i];
257
258 if (etc_entry->etue_busy == B_FALSE)
259 continue;
260
261 switch (etc_entry->etue_state) {
262 case EFX_TUNNEL_UDP_ENTRY_APPLIED:
263 break;
264 case EFX_TUNNEL_UDP_ENTRY_ADDED:
265 rc = rhead_udp_port_tunnel_add(enp, etc_entry);
266 if (rc != 0)
267 goto fail1;
268 added.etc_udp_entries[added.etc_udp_entries_num] =
269 *etc_entry;
270 added.etc_udp_entries_num++;
271 break;
272 case EFX_TUNNEL_UDP_ENTRY_REMOVED:
273 rc = rhead_udp_port_tunnel_remove(enp, etc_entry);
274 if (rc != 0)
275 goto fail2;
276 break;
277 default:
278 EFSYS_ASSERT(0);
279 break;
280 }
281 }
282
283 EFSYS_LOCK(enp->en_eslp, state);
284
285 /*
286 * Adding or removing non-busy entries does not change the
287 * order of busy entries. Therefore one linear search iteration
288 * suffices.
289 */
290 for (i = 0, j = 0; i < etcp->etc_udp_entries_num; i++) {
291 efx_tunnel_udp_entry_t *cur_entry = &etcp->etc_udp_entries[i];
292 efx_tunnel_udp_entry_t *added_entry = &added.etc_udp_entries[j];
293
294 if (cur_entry->etue_state == EFX_TUNNEL_UDP_ENTRY_ADDED &&
295 cur_entry->etue_port == added_entry->etue_port) {
296 cur_entry->etue_handle = added_entry->etue_handle;
297 j++;
298 }
299 }
300
301 EFSYS_UNLOCK(enp->en_eslp, state);
302
303 return (0);
304
305 fail2:
306 EFSYS_PROBE(fail2);
307
308 fail1:
309 EFSYS_PROBE1(fail1, efx_rc_t, rc);
310
311 while (i-- > 0) {
312 if (etc.etc_udp_entries[i].etue_busy == B_FALSE)
313 continue;
314
315 switch (etc.etc_udp_entries[i].etue_state) {
316 case EFX_TUNNEL_UDP_ENTRY_APPLIED:
317 break;
318 case EFX_TUNNEL_UDP_ENTRY_ADDED:
319 (void) rhead_udp_port_tunnel_remove(enp,
320 &etc.etc_udp_entries[i]);
321 break;
322 case EFX_TUNNEL_UDP_ENTRY_REMOVED:
323 (void) rhead_udp_port_tunnel_add(enp,
324 &etc.etc_udp_entries[i]);
325 break;
326 default:
327 EFSYS_ASSERT(0);
328 break;
329 }
330 }
331
332 return (rc);
333 }
334
335 void
rhead_tunnel_fini(__in efx_nic_t * enp)336 rhead_tunnel_fini(
337 __in efx_nic_t *enp)
338 {
339 (void) efx_tunnel_config_clear(enp);
340 (void) efx_tunnel_reconfigure(enp);
341 }
342
343 #endif /* EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL */
344