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
40static 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
47static	__checkReturn	boolean_t
48ef10_udp_encap_supported(
49	__in		efx_nic_t *enp);
50
51static	__checkReturn	efx_rc_t
52ef10_tunnel_reconfigure(
53	__in		efx_nic_t *enp);
54
55static 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
61static	__checkReturn		efx_rc_t
62efx_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
148fail3:
149	EFSYS_PROBE(fail3);
150
151fail2:
152	EFSYS_PROBE(fail2);
153
154fail1:
155	EFSYS_PROBE1(fail1, efx_rc_t, rc);
156
157	return (rc);
158}
159
160	__checkReturn	efx_rc_t
161efx_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
214fail1:
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
224efx_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
248static	__checkReturn	efx_rc_t
249efx_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
269efx_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
317fail4:
318	EFSYS_PROBE(fail4);
319
320fail3:
321	EFSYS_PROBE(fail3);
322	EFSYS_UNLOCK(enp->en_eslp, state);
323
324fail2:
325	EFSYS_PROBE(fail2);
326
327fail1:
328	EFSYS_PROBE1(fail1, efx_rc_t, rc);
329
330	return (rc);
331}
332
333	__checkReturn	efx_rc_t
334efx_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
374fail2:
375	EFSYS_PROBE(fail2);
376
377fail1:
378	EFSYS_PROBE1(fail1, efx_rc_t, rc);
379	EFSYS_UNLOCK(enp->en_eslp, state);
380
381	return (rc);
382}
383
384			void
385efx_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
402efx_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
420fail2:
421	EFSYS_PROBE(fail2);
422
423fail1:
424	EFSYS_PROBE1(fail1, efx_rc_t, rc);
425
426	return (rc);
427}
428
429#if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
430static	__checkReturn		boolean_t
431ef10_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
444static	__checkReturn	efx_rc_t
445ef10_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	}
485fail2:
486	EFSYS_PROBE(fail2);
487
488fail1:
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