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