1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2017-2018 Solarflare Communications Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
31 */
32
33 #include <sys/cdefs.h>
34 #include "efx.h"
35 #include "efx_impl.h"
36
37 #if EFSYS_OPT_TUNNEL
38
39 #if EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON
40 static const efx_tunnel_ops_t __efx_tunnel_dummy_ops = {
41 NULL, /* eto_udp_encap_supported */
42 NULL, /* eto_reconfigure */
43 };
44 #endif /* EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON */
45
46 #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
47 static __checkReturn boolean_t
48 ef10_udp_encap_supported(
49 __in efx_nic_t *enp);
50
51 static __checkReturn efx_rc_t
52 ef10_tunnel_reconfigure(
53 __in efx_nic_t *enp);
54
55 static const efx_tunnel_ops_t __efx_tunnel_ef10_ops = {
56 ef10_udp_encap_supported, /* eto_udp_encap_supported */
57 ef10_tunnel_reconfigure, /* eto_reconfigure */
58 };
59 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
60
61 static __checkReturn efx_rc_t
efx_mcdi_set_tunnel_encap_udp_ports(__in efx_nic_t * enp,__in efx_tunnel_cfg_t * etcp,__in boolean_t unloading,__out boolean_t * resetting)62 efx_mcdi_set_tunnel_encap_udp_ports(
63 __in efx_nic_t *enp,
64 __in efx_tunnel_cfg_t *etcp,
65 __in boolean_t unloading,
66 __out boolean_t *resetting)
67 {
68 efx_mcdi_req_t req;
69 EFX_MCDI_DECLARE_BUF(payload,
70 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX,
71 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN);
72 efx_word_t flags;
73 efx_rc_t rc;
74 unsigned int i;
75 unsigned int entries_num;
76
77 if (etcp == NULL)
78 entries_num = 0;
79 else
80 entries_num = etcp->etc_udp_entries_num;
81
82 req.emr_cmd = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS;
83 req.emr_in_buf = payload;
84 req.emr_in_length =
85 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(entries_num);
86 req.emr_out_buf = payload;
87 req.emr_out_length = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN;
88
89 EFX_POPULATE_WORD_1(flags,
90 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING,
91 (unloading == B_TRUE) ? 1 : 0);
92 MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS,
93 EFX_WORD_FIELD(flags, EFX_WORD_0));
94
95 MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES,
96 entries_num);
97
98 for (i = 0; i < entries_num; ++i) {
99 uint16_t mcdi_udp_protocol;
100
101 switch (etcp->etc_udp_entries[i].etue_protocol) {
102 case EFX_TUNNEL_PROTOCOL_VXLAN:
103 mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN;
104 break;
105 case EFX_TUNNEL_PROTOCOL_GENEVE:
106 mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE;
107 break;
108 default:
109 rc = EINVAL;
110 goto fail1;
111 }
112
113 /*
114 * UDP port is MCDI native little-endian in the request
115 * and EFX_POPULATE_DWORD cares about conversion from
116 * host/CPU byte order to little-endian.
117 */
118 EFX_STATIC_ASSERT(sizeof (efx_dword_t) ==
119 TUNNEL_ENCAP_UDP_PORT_ENTRY_LEN);
120 EFX_POPULATE_DWORD_2(
121 MCDI_IN2(req, efx_dword_t,
122 SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES)[i],
123 TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT,
124 etcp->etc_udp_entries[i].etue_port,
125 TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL,
126 mcdi_udp_protocol);
127 }
128
129 efx_mcdi_execute(enp, &req);
130
131 if (req.emr_rc != 0) {
132 rc = req.emr_rc;
133 goto fail2;
134 }
135
136 if (req.emr_out_length_used !=
137 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN) {
138 rc = EMSGSIZE;
139 goto fail3;
140 }
141
142 *resetting = MCDI_OUT_WORD_FIELD(req,
143 SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS,
144 SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING);
145
146 return (0);
147
148 fail3:
149 EFSYS_PROBE(fail3);
150
151 fail2:
152 EFSYS_PROBE(fail2);
153
154 fail1:
155 EFSYS_PROBE1(fail1, efx_rc_t, rc);
156
157 return (rc);
158 }
159
160 __checkReturn efx_rc_t
efx_tunnel_init(__in efx_nic_t * enp)161 efx_tunnel_init(
162 __in efx_nic_t *enp)
163 {
164 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
165 const efx_tunnel_ops_t *etop;
166 efx_rc_t rc;
167
168 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
169 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
170 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TUNNEL));
171
172 EFX_STATIC_ASSERT(EFX_TUNNEL_MAXNENTRIES ==
173 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM);
174
175 switch (enp->en_family) {
176 #if EFSYS_OPT_SIENA
177 case EFX_FAMILY_SIENA:
178 etop = &__efx_tunnel_dummy_ops;
179 break;
180 #endif /* EFSYS_OPT_SIENA */
181
182 #if EFSYS_OPT_HUNTINGTON
183 case EFX_FAMILY_HUNTINGTON:
184 etop = &__efx_tunnel_dummy_ops;
185 break;
186 #endif /* EFSYS_OPT_HUNTINGTON */
187
188 #if EFSYS_OPT_MEDFORD
189 case EFX_FAMILY_MEDFORD:
190 etop = &__efx_tunnel_ef10_ops;
191 break;
192 #endif /* EFSYS_OPT_MEDFORD */
193
194 #if EFSYS_OPT_MEDFORD2
195 case EFX_FAMILY_MEDFORD2:
196 etop = &__efx_tunnel_ef10_ops;
197 break;
198 #endif /* EFSYS_OPT_MEDFORD2 */
199
200 default:
201 EFSYS_ASSERT(0);
202 rc = ENOTSUP;
203 goto fail1;
204 }
205
206 memset(etcp->etc_udp_entries, 0, sizeof (etcp->etc_udp_entries));
207 etcp->etc_udp_entries_num = 0;
208
209 enp->en_etop = etop;
210 enp->en_mod_flags |= EFX_MOD_TUNNEL;
211
212 return (0);
213
214 fail1:
215 EFSYS_PROBE1(fail1, efx_rc_t, rc);
216
217 enp->en_etop = NULL;
218 enp->en_mod_flags &= ~EFX_MOD_TUNNEL;
219
220 return (rc);
221 }
222
223 void
efx_tunnel_fini(__in efx_nic_t * enp)224 efx_tunnel_fini(
225 __in efx_nic_t *enp)
226 {
227 boolean_t resetting;
228
229 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
230 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
231 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
232
233 if ((enp->en_etop->eto_udp_encap_supported != NULL) &&
234 enp->en_etop->eto_udp_encap_supported(enp)) {
235 /*
236 * The UNLOADING flag allows the MC to suppress the datapath
237 * reset if it was set on the last call to
238 * MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS by all functions
239 */
240 (void) efx_mcdi_set_tunnel_encap_udp_ports(enp, NULL, B_TRUE,
241 &resetting);
242 }
243
244 enp->en_etop = NULL;
245 enp->en_mod_flags &= ~EFX_MOD_TUNNEL;
246 }
247
248 static __checkReturn efx_rc_t
efx_tunnel_config_find_udp_tunnel_entry(__in efx_tunnel_cfg_t * etcp,__in uint16_t port,__out unsigned int * entryp)249 efx_tunnel_config_find_udp_tunnel_entry(
250 __in efx_tunnel_cfg_t *etcp,
251 __in uint16_t port,
252 __out unsigned int *entryp)
253 {
254 unsigned int i;
255
256 for (i = 0; i < etcp->etc_udp_entries_num; ++i) {
257 efx_tunnel_udp_entry_t *p = &etcp->etc_udp_entries[i];
258
259 if (p->etue_port == port) {
260 *entryp = i;
261 return (0);
262 }
263 }
264
265 return (ENOENT);
266 }
267
268 __checkReturn efx_rc_t
efx_tunnel_config_udp_add(__in efx_nic_t * enp,__in uint16_t port,__in efx_tunnel_protocol_t protocol)269 efx_tunnel_config_udp_add(
270 __in efx_nic_t *enp,
271 __in uint16_t port /* host/cpu-endian */,
272 __in efx_tunnel_protocol_t protocol)
273 {
274 const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
275 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
276 efsys_lock_state_t state;
277 efx_rc_t rc;
278 unsigned int entry;
279
280 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
281
282 if (protocol >= EFX_TUNNEL_NPROTOS) {
283 rc = EINVAL;
284 goto fail1;
285 }
286
287 if ((encp->enc_tunnel_encapsulations_supported &
288 (1u << protocol)) == 0) {
289 rc = ENOTSUP;
290 goto fail2;
291 }
292
293 EFSYS_LOCK(enp->en_eslp, state);
294
295 rc = efx_tunnel_config_find_udp_tunnel_entry(etcp, port, &entry);
296 if (rc == 0) {
297 rc = EEXIST;
298 goto fail3;
299 }
300
301 if (etcp->etc_udp_entries_num ==
302 encp->enc_tunnel_config_udp_entries_max) {
303 rc = ENOSPC;
304 goto fail4;
305 }
306
307 etcp->etc_udp_entries[etcp->etc_udp_entries_num].etue_port = port;
308 etcp->etc_udp_entries[etcp->etc_udp_entries_num].etue_protocol =
309 protocol;
310
311 etcp->etc_udp_entries_num++;
312
313 EFSYS_UNLOCK(enp->en_eslp, state);
314
315 return (0);
316
317 fail4:
318 EFSYS_PROBE(fail4);
319
320 fail3:
321 EFSYS_PROBE(fail3);
322 EFSYS_UNLOCK(enp->en_eslp, state);
323
324 fail2:
325 EFSYS_PROBE(fail2);
326
327 fail1:
328 EFSYS_PROBE1(fail1, efx_rc_t, rc);
329
330 return (rc);
331 }
332
333 __checkReturn efx_rc_t
efx_tunnel_config_udp_remove(__in efx_nic_t * enp,__in uint16_t port,__in efx_tunnel_protocol_t protocol)334 efx_tunnel_config_udp_remove(
335 __in efx_nic_t *enp,
336 __in uint16_t port /* host/cpu-endian */,
337 __in efx_tunnel_protocol_t protocol)
338 {
339 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
340 efsys_lock_state_t state;
341 unsigned int entry;
342 efx_rc_t rc;
343
344 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
345
346 EFSYS_LOCK(enp->en_eslp, state);
347
348 rc = efx_tunnel_config_find_udp_tunnel_entry(etcp, port, &entry);
349 if (rc != 0)
350 goto fail1;
351
352 if (etcp->etc_udp_entries[entry].etue_protocol != protocol) {
353 rc = EINVAL;
354 goto fail2;
355 }
356
357 EFSYS_ASSERT3U(etcp->etc_udp_entries_num, >, 0);
358 etcp->etc_udp_entries_num--;
359
360 if (entry < etcp->etc_udp_entries_num) {
361 memmove(&etcp->etc_udp_entries[entry],
362 &etcp->etc_udp_entries[entry + 1],
363 (etcp->etc_udp_entries_num - entry) *
364 sizeof (etcp->etc_udp_entries[0]));
365 }
366
367 memset(&etcp->etc_udp_entries[etcp->etc_udp_entries_num], 0,
368 sizeof (etcp->etc_udp_entries[0]));
369
370 EFSYS_UNLOCK(enp->en_eslp, state);
371
372 return (0);
373
374 fail2:
375 EFSYS_PROBE(fail2);
376
377 fail1:
378 EFSYS_PROBE1(fail1, efx_rc_t, rc);
379 EFSYS_UNLOCK(enp->en_eslp, state);
380
381 return (rc);
382 }
383
384 void
efx_tunnel_config_clear(__in efx_nic_t * enp)385 efx_tunnel_config_clear(
386 __in efx_nic_t *enp)
387 {
388 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
389 efsys_lock_state_t state;
390
391 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
392
393 EFSYS_LOCK(enp->en_eslp, state);
394
395 etcp->etc_udp_entries_num = 0;
396 memset(etcp->etc_udp_entries, 0, sizeof (etcp->etc_udp_entries));
397
398 EFSYS_UNLOCK(enp->en_eslp, state);
399 }
400
401 __checkReturn efx_rc_t
efx_tunnel_reconfigure(__in efx_nic_t * enp)402 efx_tunnel_reconfigure(
403 __in efx_nic_t *enp)
404 {
405 const efx_tunnel_ops_t *etop = enp->en_etop;
406 efx_rc_t rc;
407
408 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
409
410 if (etop->eto_reconfigure == NULL) {
411 rc = ENOTSUP;
412 goto fail1;
413 }
414
415 if ((rc = enp->en_etop->eto_reconfigure(enp)) != 0)
416 goto fail2;
417
418 return (0);
419
420 fail2:
421 EFSYS_PROBE(fail2);
422
423 fail1:
424 EFSYS_PROBE1(fail1, efx_rc_t, rc);
425
426 return (rc);
427 }
428
429 #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
430 static __checkReturn boolean_t
ef10_udp_encap_supported(__in efx_nic_t * enp)431 ef10_udp_encap_supported(
432 __in efx_nic_t *enp)
433 {
434 const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
435 uint32_t udp_tunnels_mask = 0;
436
437 udp_tunnels_mask |= (1u << EFX_TUNNEL_PROTOCOL_VXLAN);
438 udp_tunnels_mask |= (1u << EFX_TUNNEL_PROTOCOL_GENEVE);
439
440 return ((encp->enc_tunnel_encapsulations_supported &
441 udp_tunnels_mask) == 0 ? B_FALSE : B_TRUE);
442 }
443
444 static __checkReturn efx_rc_t
ef10_tunnel_reconfigure(__in efx_nic_t * enp)445 ef10_tunnel_reconfigure(
446 __in efx_nic_t *enp)
447 {
448 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
449 efx_rc_t rc;
450 boolean_t resetting;
451 efsys_lock_state_t state;
452 efx_tunnel_cfg_t etc;
453
454 EFSYS_LOCK(enp->en_eslp, state);
455 memcpy(&etc, etcp, sizeof (etc));
456 EFSYS_UNLOCK(enp->en_eslp, state);
457
458 if (ef10_udp_encap_supported(enp) == B_FALSE) {
459 /*
460 * It is OK to apply empty UDP tunnel ports when UDP
461 * tunnel encapsulations are not supported - just nothing
462 * should be done.
463 */
464 if (etc.etc_udp_entries_num == 0)
465 return (0);
466 rc = ENOTSUP;
467 goto fail1;
468 } else {
469 /*
470 * All PCI functions can see a reset upon the
471 * MCDI request completion
472 */
473 rc = efx_mcdi_set_tunnel_encap_udp_ports(enp, &etc, B_FALSE,
474 &resetting);
475 if (rc != 0)
476 goto fail2;
477
478 /*
479 * Although the caller should be able to handle MC reboot,
480 * it might come in handy to report the impending reboot
481 * by returning EAGAIN
482 */
483 return ((resetting) ? EAGAIN : 0);
484 }
485 fail2:
486 EFSYS_PROBE(fail2);
487
488 fail1:
489 EFSYS_PROBE1(fail1, efx_rc_t, rc);
490
491 return (rc);
492 }
493 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
494
495 #endif /* EFSYS_OPT_TUNNEL */
496