xref: /dpdk/drivers/common/sfc_efx/base/efx_tunnel.c (revision 672386c1e9e1f64f7aa3b1360ad22dc737ea8d72)
15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko  *
3*672386c1SAndrew Rybchenko  * Copyright(c) 2019-2021 Xilinx, Inc.
45e111ed8SAndrew Rybchenko  * Copyright(c) 2007-2019 Solarflare Communications Inc.
55e111ed8SAndrew Rybchenko  */
65e111ed8SAndrew Rybchenko 
75e111ed8SAndrew Rybchenko #include "efx.h"
85e111ed8SAndrew Rybchenko #include "efx_impl.h"
95e111ed8SAndrew Rybchenko 
1072e9af05SIgor Romanov /*
1172e9af05SIgor Romanov  * State diagram of the UDP tunnel table entries
1272e9af05SIgor Romanov  * (efx_tunnel_udp_entry_state_t and busy flag):
1372e9af05SIgor Romanov  *
1472e9af05SIgor Romanov  *                             +---------+
1572e9af05SIgor Romanov  *                    +--------| APPLIED |<-------+
1672e9af05SIgor Romanov  *                    |        +---------+        |
1772e9af05SIgor Romanov  *                    |                           |
1872e9af05SIgor Romanov  *                    |                efx_tunnel_reconfigure (end)
1972e9af05SIgor Romanov  *   efx_tunnel_config_udp_remove                 |
2072e9af05SIgor Romanov  *                    |                    +------------+
2172e9af05SIgor Romanov  *                    v                    | BUSY ADDED |
2272e9af05SIgor Romanov  *               +---------+               +------------+
2372e9af05SIgor Romanov  *               | REMOVED |                      ^
2472e9af05SIgor Romanov  *               +---------+                      |
2572e9af05SIgor Romanov  *                    |               efx_tunnel_reconfigure (begin)
2672e9af05SIgor Romanov  *  efx_tunnel_reconfigure (begin)                |
2772e9af05SIgor Romanov  *                    |                           |
2872e9af05SIgor Romanov  *                    v                     +-----------+
2972e9af05SIgor Romanov  *            +--------------+              |   ADDED   |<---------+
3072e9af05SIgor Romanov  *            | BUSY REMOVED |              +-----------+          |
3172e9af05SIgor Romanov  *            +--------------+                    |                |
3272e9af05SIgor Romanov  *                    |              efx_tunnel_config_udp_remove  |
3372e9af05SIgor Romanov  *  efx_tunnel_reconfigure (end)                  |                |
3472e9af05SIgor Romanov  *                    |                           |                |
3572e9af05SIgor Romanov  *                    |        +---------+        |                |
3672e9af05SIgor Romanov  *                    |        |+-------+|        |                |
3772e9af05SIgor Romanov  *                    +------->|| empty ||<-------+                |
3872e9af05SIgor Romanov  *                             |+-------+|                         |
3972e9af05SIgor Romanov  *                             +---------+        efx_tunnel_config_udp_add
4072e9af05SIgor Romanov  *                                  |                              |
4172e9af05SIgor Romanov  *                                  +------------------------------+
4272e9af05SIgor Romanov  *
4372e9af05SIgor Romanov  * Note that there is no BUSY APPLIED state since removing an applied entry
4472e9af05SIgor Romanov  * should not be blocked by ongoing reconfiguration in another thread -
4572e9af05SIgor Romanov  * reconfiguration will remove only busy entries.
4672e9af05SIgor Romanov  */
475e111ed8SAndrew Rybchenko 
485e111ed8SAndrew Rybchenko #if EFSYS_OPT_TUNNEL
495e111ed8SAndrew Rybchenko 
505e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
515e111ed8SAndrew Rybchenko static	__checkReturn	boolean_t
525e111ed8SAndrew Rybchenko ef10_udp_encap_supported(
535e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp);
545e111ed8SAndrew Rybchenko 
555e111ed8SAndrew Rybchenko static	__checkReturn	efx_rc_t
565e111ed8SAndrew Rybchenko ef10_tunnel_reconfigure(
575e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp);
585e111ed8SAndrew Rybchenko 
594dda992fSIgor Romanov static			void
604dda992fSIgor Romanov ef10_tunnel_fini(
614dda992fSIgor Romanov 	__in		efx_nic_t *enp);
62d874d2a1SIgor Romanov #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
634dda992fSIgor Romanov 
64d874d2a1SIgor Romanov #if EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON
65d874d2a1SIgor Romanov static const efx_tunnel_ops_t	__efx_tunnel_dummy_ops = {
66d874d2a1SIgor Romanov 	NULL,	/* eto_reconfigure */
67d874d2a1SIgor Romanov 	NULL,	/* eto_fini */
68d874d2a1SIgor Romanov };
69d874d2a1SIgor Romanov #endif /* EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON */
70d874d2a1SIgor Romanov 
71d874d2a1SIgor Romanov #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
725e111ed8SAndrew Rybchenko static const efx_tunnel_ops_t	__efx_tunnel_ef10_ops = {
735e111ed8SAndrew Rybchenko 	ef10_tunnel_reconfigure,	/* eto_reconfigure */
744dda992fSIgor Romanov 	ef10_tunnel_fini,		/* eto_fini */
755e111ed8SAndrew Rybchenko };
765e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
775e111ed8SAndrew Rybchenko 
78d874d2a1SIgor Romanov #if EFSYS_OPT_RIVERHEAD
79d874d2a1SIgor Romanov static const efx_tunnel_ops_t	__efx_tunnel_rhead_ops = {
80d874d2a1SIgor Romanov 	rhead_tunnel_reconfigure,	/* eto_reconfigure */
81d874d2a1SIgor Romanov 	rhead_tunnel_fini,		/* eto_fini */
82d874d2a1SIgor Romanov };
83d874d2a1SIgor Romanov #endif /* EFSYS_OPT_RIVERHEAD */
84d874d2a1SIgor Romanov 
8572e9af05SIgor Romanov /* Indicates that an entry is to be set */
8672e9af05SIgor Romanov static	__checkReturn		boolean_t
ef10_entry_staged(__in efx_tunnel_udp_entry_t * entry)8772e9af05SIgor Romanov ef10_entry_staged(
8872e9af05SIgor Romanov 	__in			efx_tunnel_udp_entry_t *entry)
8972e9af05SIgor Romanov {
9072e9af05SIgor Romanov 	switch (entry->etue_state) {
9172e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_ADDED:
9272e9af05SIgor Romanov 		return (entry->etue_busy);
9372e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_REMOVED:
9472e9af05SIgor Romanov 		return (!entry->etue_busy);
9572e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_APPLIED:
9672e9af05SIgor Romanov 		return (B_TRUE);
9772e9af05SIgor Romanov 	default:
9872e9af05SIgor Romanov 		EFSYS_ASSERT(0);
9972e9af05SIgor Romanov 		return (B_FALSE);
10072e9af05SIgor Romanov 	}
10172e9af05SIgor Romanov }
10272e9af05SIgor Romanov 
1035e111ed8SAndrew Rybchenko 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)1045e111ed8SAndrew Rybchenko efx_mcdi_set_tunnel_encap_udp_ports(
1055e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
1065e111ed8SAndrew Rybchenko 	__in			efx_tunnel_cfg_t *etcp,
1075e111ed8SAndrew Rybchenko 	__in			boolean_t unloading,
1085e111ed8SAndrew Rybchenko 	__out			boolean_t *resetting)
1095e111ed8SAndrew Rybchenko {
1105e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
1115e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload,
1125e111ed8SAndrew Rybchenko 		MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX,
1135e111ed8SAndrew Rybchenko 		MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN);
1145e111ed8SAndrew Rybchenko 	efx_word_t flags;
1155e111ed8SAndrew Rybchenko 	efx_rc_t rc;
1165e111ed8SAndrew Rybchenko 	unsigned int i;
1175e111ed8SAndrew Rybchenko 	unsigned int entries_num;
11872e9af05SIgor Romanov 	unsigned int entry;
1195e111ed8SAndrew Rybchenko 
1205e111ed8SAndrew Rybchenko 	entries_num = 0;
12172e9af05SIgor Romanov 	if (etcp != NULL) {
12272e9af05SIgor Romanov 		for (i = 0; i < etcp->etc_udp_entries_num; i++) {
12372e9af05SIgor Romanov 			if (ef10_entry_staged(&etcp->etc_udp_entries[i]) !=
12472e9af05SIgor Romanov 			    B_FALSE) {
12572e9af05SIgor Romanov 				entries_num++;
12672e9af05SIgor Romanov 			}
12772e9af05SIgor Romanov 		}
12872e9af05SIgor Romanov 	}
1295e111ed8SAndrew Rybchenko 
1305e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS;
1315e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
1325e111ed8SAndrew Rybchenko 	req.emr_in_length =
1335e111ed8SAndrew Rybchenko 	    MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(entries_num);
1345e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
1355e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN;
1365e111ed8SAndrew Rybchenko 
1375e111ed8SAndrew Rybchenko 	EFX_POPULATE_WORD_1(flags,
1385e111ed8SAndrew Rybchenko 	    MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING,
1395e111ed8SAndrew Rybchenko 	    (unloading == B_TRUE) ? 1 : 0);
1405e111ed8SAndrew Rybchenko 	MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS,
1415e111ed8SAndrew Rybchenko 	    EFX_WORD_FIELD(flags, EFX_WORD_0));
1425e111ed8SAndrew Rybchenko 
1435e111ed8SAndrew Rybchenko 	MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES,
1445e111ed8SAndrew Rybchenko 	    entries_num);
1455e111ed8SAndrew Rybchenko 
14672e9af05SIgor Romanov 	for (i = 0, entry = 0; entry < entries_num; ++entry, ++i) {
1475e111ed8SAndrew Rybchenko 		uint16_t mcdi_udp_protocol;
1485e111ed8SAndrew Rybchenko 
14972e9af05SIgor Romanov 		while (ef10_entry_staged(&etcp->etc_udp_entries[i]) == B_FALSE)
15072e9af05SIgor Romanov 			i++;
15172e9af05SIgor Romanov 
1525e111ed8SAndrew Rybchenko 		switch (etcp->etc_udp_entries[i].etue_protocol) {
1535e111ed8SAndrew Rybchenko 		case EFX_TUNNEL_PROTOCOL_VXLAN:
1545e111ed8SAndrew Rybchenko 			mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN;
1555e111ed8SAndrew Rybchenko 			break;
1565e111ed8SAndrew Rybchenko 		case EFX_TUNNEL_PROTOCOL_GENEVE:
1575e111ed8SAndrew Rybchenko 			mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE;
1585e111ed8SAndrew Rybchenko 			break;
1595e111ed8SAndrew Rybchenko 		default:
1605e111ed8SAndrew Rybchenko 			rc = EINVAL;
1615e111ed8SAndrew Rybchenko 			goto fail1;
1625e111ed8SAndrew Rybchenko 		}
1635e111ed8SAndrew Rybchenko 
1645e111ed8SAndrew Rybchenko 		/*
1655e111ed8SAndrew Rybchenko 		 * UDP port is MCDI native little-endian in the request
1665e111ed8SAndrew Rybchenko 		 * and EFX_POPULATE_DWORD cares about conversion from
1675e111ed8SAndrew Rybchenko 		 * host/CPU byte order to little-endian.
1685e111ed8SAndrew Rybchenko 		 */
1695e111ed8SAndrew Rybchenko 		EFX_STATIC_ASSERT(sizeof (efx_dword_t) ==
1705e111ed8SAndrew Rybchenko 		    TUNNEL_ENCAP_UDP_PORT_ENTRY_LEN);
1715e111ed8SAndrew Rybchenko 		EFX_POPULATE_DWORD_2(
1725e111ed8SAndrew Rybchenko 		    MCDI_IN2(req, efx_dword_t,
17372e9af05SIgor Romanov 			SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES)[entry],
1745e111ed8SAndrew Rybchenko 		    TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT,
1755e111ed8SAndrew Rybchenko 		    etcp->etc_udp_entries[i].etue_port,
1765e111ed8SAndrew Rybchenko 		    TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL,
1775e111ed8SAndrew Rybchenko 		    mcdi_udp_protocol);
1785e111ed8SAndrew Rybchenko 	}
1795e111ed8SAndrew Rybchenko 
1805e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
1815e111ed8SAndrew Rybchenko 
1825e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
1835e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
1845e111ed8SAndrew Rybchenko 		goto fail2;
1855e111ed8SAndrew Rybchenko 	}
1865e111ed8SAndrew Rybchenko 
1875e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used !=
1885e111ed8SAndrew Rybchenko 	    MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN) {
1895e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
1905e111ed8SAndrew Rybchenko 		goto fail3;
1915e111ed8SAndrew Rybchenko 	}
1925e111ed8SAndrew Rybchenko 
1935e111ed8SAndrew Rybchenko 	*resetting = MCDI_OUT_WORD_FIELD(req,
1945e111ed8SAndrew Rybchenko 	    SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS,
1955e111ed8SAndrew Rybchenko 	    SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING);
1965e111ed8SAndrew Rybchenko 
1975e111ed8SAndrew Rybchenko 	return (0);
1985e111ed8SAndrew Rybchenko 
1995e111ed8SAndrew Rybchenko fail3:
2005e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
2015e111ed8SAndrew Rybchenko 
2025e111ed8SAndrew Rybchenko fail2:
2035e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
2045e111ed8SAndrew Rybchenko 
2055e111ed8SAndrew Rybchenko fail1:
2065e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2075e111ed8SAndrew Rybchenko 
2085e111ed8SAndrew Rybchenko 	return (rc);
2095e111ed8SAndrew Rybchenko }
2105e111ed8SAndrew Rybchenko 
2115e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_tunnel_init(__in efx_nic_t * enp)2125e111ed8SAndrew Rybchenko efx_tunnel_init(
2135e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
2145e111ed8SAndrew Rybchenko {
2155e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
2165e111ed8SAndrew Rybchenko 	const efx_tunnel_ops_t *etop;
2175e111ed8SAndrew Rybchenko 	efx_rc_t rc;
2185e111ed8SAndrew Rybchenko 
2195e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
2205e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2215e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TUNNEL));
2225e111ed8SAndrew Rybchenko 
2235e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(EFX_TUNNEL_MAXNENTRIES ==
2245e111ed8SAndrew Rybchenko 	    MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM);
2255e111ed8SAndrew Rybchenko 
2265e111ed8SAndrew Rybchenko 	switch (enp->en_family) {
2275e111ed8SAndrew Rybchenko #if EFSYS_OPT_SIENA
2285e111ed8SAndrew Rybchenko 	case EFX_FAMILY_SIENA:
2295e111ed8SAndrew Rybchenko 		etop = &__efx_tunnel_dummy_ops;
2305e111ed8SAndrew Rybchenko 		break;
2315e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_SIENA */
2325e111ed8SAndrew Rybchenko 
2335e111ed8SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON
2345e111ed8SAndrew Rybchenko 	case EFX_FAMILY_HUNTINGTON:
2355e111ed8SAndrew Rybchenko 		etop = &__efx_tunnel_dummy_ops;
2365e111ed8SAndrew Rybchenko 		break;
2375e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_HUNTINGTON */
2385e111ed8SAndrew Rybchenko 
2395e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD
2405e111ed8SAndrew Rybchenko 	case EFX_FAMILY_MEDFORD:
2415e111ed8SAndrew Rybchenko 		etop = &__efx_tunnel_ef10_ops;
2425e111ed8SAndrew Rybchenko 		break;
2435e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD */
2445e111ed8SAndrew Rybchenko 
2455e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2
2465e111ed8SAndrew Rybchenko 	case EFX_FAMILY_MEDFORD2:
2475e111ed8SAndrew Rybchenko 		etop = &__efx_tunnel_ef10_ops;
2485e111ed8SAndrew Rybchenko 		break;
2495e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD2 */
2505e111ed8SAndrew Rybchenko 
251a33217d5SAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD
252a33217d5SAndrew Rybchenko 	case EFX_FAMILY_RIVERHEAD:
253d874d2a1SIgor Romanov 		etop = &__efx_tunnel_rhead_ops;
254a33217d5SAndrew Rybchenko 		break;
255a33217d5SAndrew Rybchenko #endif /* EFSYS_OPT_RIVERHEAD */
256a33217d5SAndrew Rybchenko 
2575e111ed8SAndrew Rybchenko 	default:
2585e111ed8SAndrew Rybchenko 		EFSYS_ASSERT(0);
2595e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
2605e111ed8SAndrew Rybchenko 		goto fail1;
2615e111ed8SAndrew Rybchenko 	}
2625e111ed8SAndrew Rybchenko 
2635e111ed8SAndrew Rybchenko 	memset(etcp->etc_udp_entries, 0, sizeof (etcp->etc_udp_entries));
2645e111ed8SAndrew Rybchenko 	etcp->etc_udp_entries_num = 0;
2655e111ed8SAndrew Rybchenko 
2665e111ed8SAndrew Rybchenko 	enp->en_etop = etop;
2675e111ed8SAndrew Rybchenko 	enp->en_mod_flags |= EFX_MOD_TUNNEL;
2685e111ed8SAndrew Rybchenko 
2695e111ed8SAndrew Rybchenko 	return (0);
2705e111ed8SAndrew Rybchenko 
2715e111ed8SAndrew Rybchenko fail1:
2725e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2735e111ed8SAndrew Rybchenko 
2745e111ed8SAndrew Rybchenko 	enp->en_etop = NULL;
2755e111ed8SAndrew Rybchenko 	enp->en_mod_flags &= ~EFX_MOD_TUNNEL;
2765e111ed8SAndrew Rybchenko 
2775e111ed8SAndrew Rybchenko 	return (rc);
2785e111ed8SAndrew Rybchenko }
2795e111ed8SAndrew Rybchenko 
2805e111ed8SAndrew Rybchenko 			void
efx_tunnel_fini(__in efx_nic_t * enp)2815e111ed8SAndrew Rybchenko efx_tunnel_fini(
2825e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
2835e111ed8SAndrew Rybchenko {
2845e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
2855e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2865e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
2875e111ed8SAndrew Rybchenko 
2884dda992fSIgor Romanov 	if (enp->en_etop->eto_fini != NULL)
2894dda992fSIgor Romanov 		enp->en_etop->eto_fini(enp);
2905e111ed8SAndrew Rybchenko 
2915e111ed8SAndrew Rybchenko 	enp->en_etop = NULL;
2925e111ed8SAndrew Rybchenko 	enp->en_mod_flags &= ~EFX_MOD_TUNNEL;
2935e111ed8SAndrew Rybchenko }
2945e111ed8SAndrew Rybchenko 
2955e111ed8SAndrew Rybchenko 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)2965e111ed8SAndrew Rybchenko efx_tunnel_config_find_udp_tunnel_entry(
2975e111ed8SAndrew Rybchenko 	__in		efx_tunnel_cfg_t *etcp,
2985e111ed8SAndrew Rybchenko 	__in		uint16_t port,
2995e111ed8SAndrew Rybchenko 	__out		unsigned int *entryp)
3005e111ed8SAndrew Rybchenko {
3015e111ed8SAndrew Rybchenko 	unsigned int i;
3025e111ed8SAndrew Rybchenko 
3035e111ed8SAndrew Rybchenko 	for (i = 0; i < etcp->etc_udp_entries_num; ++i) {
3045e111ed8SAndrew Rybchenko 		efx_tunnel_udp_entry_t *p = &etcp->etc_udp_entries[i];
3055e111ed8SAndrew Rybchenko 
30672e9af05SIgor Romanov 		if (p->etue_port == port &&
30772e9af05SIgor Romanov 		    p->etue_state != EFX_TUNNEL_UDP_ENTRY_REMOVED) {
3085e111ed8SAndrew Rybchenko 			*entryp = i;
3095e111ed8SAndrew Rybchenko 			return (0);
3105e111ed8SAndrew Rybchenko 		}
3115e111ed8SAndrew Rybchenko 	}
3125e111ed8SAndrew Rybchenko 
3135e111ed8SAndrew Rybchenko 	return (ENOENT);
3145e111ed8SAndrew Rybchenko }
3155e111ed8SAndrew Rybchenko 
3165e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_tunnel_config_udp_add(__in efx_nic_t * enp,__in uint16_t port,__in efx_tunnel_protocol_t protocol)3175e111ed8SAndrew Rybchenko efx_tunnel_config_udp_add(
3185e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
3195e111ed8SAndrew Rybchenko 	__in		uint16_t port /* host/cpu-endian */,
3205e111ed8SAndrew Rybchenko 	__in		efx_tunnel_protocol_t protocol)
3215e111ed8SAndrew Rybchenko {
3225e111ed8SAndrew Rybchenko 	const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
3235e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
3245e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
3255e111ed8SAndrew Rybchenko 	efx_rc_t rc;
3265e111ed8SAndrew Rybchenko 	unsigned int entry;
3275e111ed8SAndrew Rybchenko 
3285e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
3295e111ed8SAndrew Rybchenko 
3305e111ed8SAndrew Rybchenko 	if (protocol >= EFX_TUNNEL_NPROTOS) {
3315e111ed8SAndrew Rybchenko 		rc = EINVAL;
3325e111ed8SAndrew Rybchenko 		goto fail1;
3335e111ed8SAndrew Rybchenko 	}
3345e111ed8SAndrew Rybchenko 
3355e111ed8SAndrew Rybchenko 	if ((encp->enc_tunnel_encapsulations_supported &
3365e111ed8SAndrew Rybchenko 	    (1u << protocol)) == 0) {
3375e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
3385e111ed8SAndrew Rybchenko 		goto fail2;
3395e111ed8SAndrew Rybchenko 	}
3405e111ed8SAndrew Rybchenko 
3415e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
3425e111ed8SAndrew Rybchenko 
3435e111ed8SAndrew Rybchenko 	rc = efx_tunnel_config_find_udp_tunnel_entry(etcp, port, &entry);
3445e111ed8SAndrew Rybchenko 	if (rc == 0) {
3455e111ed8SAndrew Rybchenko 		rc = EEXIST;
3465e111ed8SAndrew Rybchenko 		goto fail3;
3475e111ed8SAndrew Rybchenko 	}
3485e111ed8SAndrew Rybchenko 
3495e111ed8SAndrew Rybchenko 	if (etcp->etc_udp_entries_num ==
3505e111ed8SAndrew Rybchenko 	    encp->enc_tunnel_config_udp_entries_max) {
3515e111ed8SAndrew Rybchenko 		rc = ENOSPC;
3525e111ed8SAndrew Rybchenko 		goto fail4;
3535e111ed8SAndrew Rybchenko 	}
3545e111ed8SAndrew Rybchenko 
3555e111ed8SAndrew Rybchenko 	etcp->etc_udp_entries[etcp->etc_udp_entries_num].etue_port = port;
3565e111ed8SAndrew Rybchenko 	etcp->etc_udp_entries[etcp->etc_udp_entries_num].etue_protocol =
3575e111ed8SAndrew Rybchenko 	    protocol;
35872e9af05SIgor Romanov 	etcp->etc_udp_entries[etcp->etc_udp_entries_num].etue_state =
35972e9af05SIgor Romanov 	    EFX_TUNNEL_UDP_ENTRY_ADDED;
3605e111ed8SAndrew Rybchenko 
3615e111ed8SAndrew Rybchenko 	etcp->etc_udp_entries_num++;
3625e111ed8SAndrew Rybchenko 
3635e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
3645e111ed8SAndrew Rybchenko 
3655e111ed8SAndrew Rybchenko 	return (0);
3665e111ed8SAndrew Rybchenko 
3675e111ed8SAndrew Rybchenko fail4:
3685e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail4);
3695e111ed8SAndrew Rybchenko 
3705e111ed8SAndrew Rybchenko fail3:
3715e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
3725e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
3735e111ed8SAndrew Rybchenko 
3745e111ed8SAndrew Rybchenko fail2:
3755e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
3765e111ed8SAndrew Rybchenko 
3775e111ed8SAndrew Rybchenko fail1:
3785e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
3795e111ed8SAndrew Rybchenko 
3805e111ed8SAndrew Rybchenko 	return (rc);
3815e111ed8SAndrew Rybchenko }
3825e111ed8SAndrew Rybchenko 
38372e9af05SIgor Romanov /*
38472e9af05SIgor Romanov  * Returns the index of the entry after the deleted one,
38572e9af05SIgor Romanov  * or one past the last entry.
38672e9af05SIgor Romanov  */
38772e9af05SIgor Romanov static			unsigned int
efx_tunnel_config_udp_do_remove(__in efx_tunnel_cfg_t * etcp,__in unsigned int entry)38872e9af05SIgor Romanov efx_tunnel_config_udp_do_remove(
38972e9af05SIgor Romanov 	__in		efx_tunnel_cfg_t *etcp,
39072e9af05SIgor Romanov 	__in		unsigned int entry)
39172e9af05SIgor Romanov {
39272e9af05SIgor Romanov 	EFSYS_ASSERT3U(etcp->etc_udp_entries_num, >, 0);
39372e9af05SIgor Romanov 	etcp->etc_udp_entries_num--;
39472e9af05SIgor Romanov 
39572e9af05SIgor Romanov 	if (entry < etcp->etc_udp_entries_num) {
39672e9af05SIgor Romanov 		memmove(&etcp->etc_udp_entries[entry],
39772e9af05SIgor Romanov 		    &etcp->etc_udp_entries[entry + 1],
39872e9af05SIgor Romanov 		    (etcp->etc_udp_entries_num - entry) *
39972e9af05SIgor Romanov 		    sizeof (etcp->etc_udp_entries[0]));
40072e9af05SIgor Romanov 	}
40172e9af05SIgor Romanov 
40272e9af05SIgor Romanov 	memset(&etcp->etc_udp_entries[etcp->etc_udp_entries_num], 0,
40372e9af05SIgor Romanov 	    sizeof (etcp->etc_udp_entries[0]));
40472e9af05SIgor Romanov 
40572e9af05SIgor Romanov 	return (entry);
40672e9af05SIgor Romanov }
40772e9af05SIgor Romanov 
40872e9af05SIgor Romanov /*
40972e9af05SIgor Romanov  * Returns the index of the entry after the specified one,
41072e9af05SIgor Romanov  * or one past the last entry. The index is correct whether
41172e9af05SIgor Romanov  * the specified entry was removed or not.
41272e9af05SIgor Romanov  */
41372e9af05SIgor Romanov static			unsigned int
efx_tunnel_config_udp_remove_prepare(__in efx_tunnel_cfg_t * etcp,__in unsigned int entry)41472e9af05SIgor Romanov efx_tunnel_config_udp_remove_prepare(
41572e9af05SIgor Romanov 	__in		efx_tunnel_cfg_t *etcp,
41672e9af05SIgor Romanov 	__in		unsigned int entry)
41772e9af05SIgor Romanov {
41872e9af05SIgor Romanov 	unsigned int next = entry + 1;
41972e9af05SIgor Romanov 
42072e9af05SIgor Romanov 	switch (etcp->etc_udp_entries[entry].etue_state) {
42172e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_ADDED:
42272e9af05SIgor Romanov 		next = efx_tunnel_config_udp_do_remove(etcp, entry);
42372e9af05SIgor Romanov 		break;
42472e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_REMOVED:
42572e9af05SIgor Romanov 		break;
42672e9af05SIgor Romanov 	case EFX_TUNNEL_UDP_ENTRY_APPLIED:
42772e9af05SIgor Romanov 		etcp->etc_udp_entries[entry].etue_state =
42872e9af05SIgor Romanov 		    EFX_TUNNEL_UDP_ENTRY_REMOVED;
42972e9af05SIgor Romanov 		break;
43072e9af05SIgor Romanov 	default:
43172e9af05SIgor Romanov 		EFSYS_ASSERT(0);
43272e9af05SIgor Romanov 		break;
43372e9af05SIgor Romanov 	}
43472e9af05SIgor Romanov 
43572e9af05SIgor Romanov 	return (next);
43672e9af05SIgor Romanov }
43772e9af05SIgor Romanov 
4385e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_tunnel_config_udp_remove(__in efx_nic_t * enp,__in uint16_t port,__in efx_tunnel_protocol_t protocol)4395e111ed8SAndrew Rybchenko efx_tunnel_config_udp_remove(
4405e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
4415e111ed8SAndrew Rybchenko 	__in		uint16_t port /* host/cpu-endian */,
4425e111ed8SAndrew Rybchenko 	__in		efx_tunnel_protocol_t protocol)
4435e111ed8SAndrew Rybchenko {
4445e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
4455e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
4465e111ed8SAndrew Rybchenko 	unsigned int entry;
4475e111ed8SAndrew Rybchenko 	efx_rc_t rc;
4485e111ed8SAndrew Rybchenko 
4495e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
4505e111ed8SAndrew Rybchenko 
4515e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
4525e111ed8SAndrew Rybchenko 
4535e111ed8SAndrew Rybchenko 	rc = efx_tunnel_config_find_udp_tunnel_entry(etcp, port, &entry);
4545e111ed8SAndrew Rybchenko 	if (rc != 0)
4555e111ed8SAndrew Rybchenko 		goto fail1;
4565e111ed8SAndrew Rybchenko 
45772e9af05SIgor Romanov 	if (etcp->etc_udp_entries[entry].etue_busy != B_FALSE) {
45872e9af05SIgor Romanov 		rc = EBUSY;
4595e111ed8SAndrew Rybchenko 		goto fail2;
4605e111ed8SAndrew Rybchenko 	}
4615e111ed8SAndrew Rybchenko 
46272e9af05SIgor Romanov 	if (etcp->etc_udp_entries[entry].etue_protocol != protocol) {
46372e9af05SIgor Romanov 		rc = EINVAL;
46472e9af05SIgor Romanov 		goto fail3;
4655e111ed8SAndrew Rybchenko 	}
4665e111ed8SAndrew Rybchenko 
46772e9af05SIgor Romanov 	(void) efx_tunnel_config_udp_remove_prepare(etcp, entry);
4685e111ed8SAndrew Rybchenko 
4695e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
4705e111ed8SAndrew Rybchenko 
4715e111ed8SAndrew Rybchenko 	return (0);
4725e111ed8SAndrew Rybchenko 
47372e9af05SIgor Romanov fail3:
47472e9af05SIgor Romanov 	EFSYS_PROBE(fail3);
47572e9af05SIgor Romanov 
4765e111ed8SAndrew Rybchenko fail2:
4775e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
4785e111ed8SAndrew Rybchenko 
4795e111ed8SAndrew Rybchenko fail1:
4805e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
4815e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
4825e111ed8SAndrew Rybchenko 
4835e111ed8SAndrew Rybchenko 	return (rc);
4845e111ed8SAndrew Rybchenko }
4855e111ed8SAndrew Rybchenko 
48672e9af05SIgor Romanov static			boolean_t
efx_tunnel_table_all_available(__in efx_tunnel_cfg_t * etcp)48772e9af05SIgor Romanov efx_tunnel_table_all_available(
48872e9af05SIgor Romanov 	__in			efx_tunnel_cfg_t *etcp)
48972e9af05SIgor Romanov {
49072e9af05SIgor Romanov 	unsigned int i;
49172e9af05SIgor Romanov 
49272e9af05SIgor Romanov 	for (i = 0; i < etcp->etc_udp_entries_num; i++) {
49372e9af05SIgor Romanov 		if (etcp->etc_udp_entries[i].etue_busy != B_FALSE)
49472e9af05SIgor Romanov 			return (B_FALSE);
49572e9af05SIgor Romanov 	}
49672e9af05SIgor Romanov 
49772e9af05SIgor Romanov 	return (B_TRUE);
49872e9af05SIgor Romanov }
49972e9af05SIgor Romanov 
50072e9af05SIgor Romanov 	__checkReturn	efx_rc_t
efx_tunnel_config_clear(__in efx_nic_t * enp)5015e111ed8SAndrew Rybchenko efx_tunnel_config_clear(
5025e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp)
5035e111ed8SAndrew Rybchenko {
5045e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
5055e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
50672e9af05SIgor Romanov 	unsigned int i;
50772e9af05SIgor Romanov 	efx_rc_t rc;
5085e111ed8SAndrew Rybchenko 
5095e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
5105e111ed8SAndrew Rybchenko 
5115e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
5125e111ed8SAndrew Rybchenko 
51372e9af05SIgor Romanov 	if (efx_tunnel_table_all_available(etcp) == B_FALSE) {
51472e9af05SIgor Romanov 		rc = EBUSY;
51572e9af05SIgor Romanov 		goto fail1;
51672e9af05SIgor Romanov 	}
51772e9af05SIgor Romanov 
51872e9af05SIgor Romanov 	i = 0;
51972e9af05SIgor Romanov 	while (i < etcp->etc_udp_entries_num)
52072e9af05SIgor Romanov 		i = efx_tunnel_config_udp_remove_prepare(etcp, i);
5215e111ed8SAndrew Rybchenko 
5225e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
52372e9af05SIgor Romanov 
52472e9af05SIgor Romanov 	return (0);
52572e9af05SIgor Romanov 
52672e9af05SIgor Romanov fail1:
52772e9af05SIgor Romanov 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
52872e9af05SIgor Romanov 	EFSYS_UNLOCK(enp->en_eslp, state);
52972e9af05SIgor Romanov 
53072e9af05SIgor Romanov 	return (rc);
5315e111ed8SAndrew Rybchenko }
5325e111ed8SAndrew Rybchenko 
5335e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_tunnel_reconfigure(__in efx_nic_t * enp)5345e111ed8SAndrew Rybchenko efx_tunnel_reconfigure(
5355e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
5365e111ed8SAndrew Rybchenko {
5375e111ed8SAndrew Rybchenko 	const efx_tunnel_ops_t *etop = enp->en_etop;
53872e9af05SIgor Romanov 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
53972e9af05SIgor Romanov 	efx_tunnel_udp_entry_t *entry;
54072e9af05SIgor Romanov 	boolean_t locked = B_FALSE;
54172e9af05SIgor Romanov 	efsys_lock_state_t state;
54272e9af05SIgor Romanov 	boolean_t resetting;
54372e9af05SIgor Romanov 	unsigned int i;
5445e111ed8SAndrew Rybchenko 	efx_rc_t rc;
5455e111ed8SAndrew Rybchenko 
5465e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
5475e111ed8SAndrew Rybchenko 
5485e111ed8SAndrew Rybchenko 	if (etop->eto_reconfigure == NULL) {
5495e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
5505e111ed8SAndrew Rybchenko 		goto fail1;
5515e111ed8SAndrew Rybchenko 	}
5525e111ed8SAndrew Rybchenko 
55372e9af05SIgor Romanov 	EFSYS_LOCK(enp->en_eslp, state);
55472e9af05SIgor Romanov 	locked = B_TRUE;
5555e111ed8SAndrew Rybchenko 
55672e9af05SIgor Romanov 	if (efx_tunnel_table_all_available(etcp) == B_FALSE) {
55772e9af05SIgor Romanov 		rc = EBUSY;
55872e9af05SIgor Romanov 		goto fail2;
55972e9af05SIgor Romanov 	}
56072e9af05SIgor Romanov 
56172e9af05SIgor Romanov 	for (i = 0; i < etcp->etc_udp_entries_num; i++) {
56272e9af05SIgor Romanov 		entry = &etcp->etc_udp_entries[i];
56372e9af05SIgor Romanov 		if (entry->etue_state != EFX_TUNNEL_UDP_ENTRY_APPLIED)
56472e9af05SIgor Romanov 			entry->etue_busy = B_TRUE;
56572e9af05SIgor Romanov 	}
56672e9af05SIgor Romanov 
56772e9af05SIgor Romanov 	EFSYS_UNLOCK(enp->en_eslp, state);
56872e9af05SIgor Romanov 	locked = B_FALSE;
56972e9af05SIgor Romanov 
57072e9af05SIgor Romanov 	rc = enp->en_etop->eto_reconfigure(enp);
57172e9af05SIgor Romanov 	if (rc != 0 && rc != EAGAIN)
57272e9af05SIgor Romanov 		goto fail3;
57372e9af05SIgor Romanov 
57472e9af05SIgor Romanov 	resetting = (rc == EAGAIN) ? B_TRUE : B_FALSE;
57572e9af05SIgor Romanov 
57672e9af05SIgor Romanov 	EFSYS_LOCK(enp->en_eslp, state);
57772e9af05SIgor Romanov 	locked = B_TRUE;
57872e9af05SIgor Romanov 
57972e9af05SIgor Romanov 	/*
58072e9af05SIgor Romanov 	 * Delete entries marked for removal since they are no longer
58172e9af05SIgor Romanov 	 * needed after successful NIC-specific reconfiguration.
58272e9af05SIgor Romanov 	 * Added entries become applied because they are installed in
58372e9af05SIgor Romanov 	 * the hardware.
58472e9af05SIgor Romanov 	 */
58572e9af05SIgor Romanov 
58672e9af05SIgor Romanov 	i = 0;
58772e9af05SIgor Romanov 	while (i < etcp->etc_udp_entries_num) {
58872e9af05SIgor Romanov 		unsigned int next = i + 1;
58972e9af05SIgor Romanov 
59072e9af05SIgor Romanov 		entry = &etcp->etc_udp_entries[i];
59172e9af05SIgor Romanov 		if (entry->etue_busy != B_FALSE) {
59272e9af05SIgor Romanov 			entry->etue_busy = B_FALSE;
59372e9af05SIgor Romanov 
59472e9af05SIgor Romanov 			switch (entry->etue_state) {
59572e9af05SIgor Romanov 			case EFX_TUNNEL_UDP_ENTRY_APPLIED:
59672e9af05SIgor Romanov 				break;
59772e9af05SIgor Romanov 			case EFX_TUNNEL_UDP_ENTRY_ADDED:
59872e9af05SIgor Romanov 				entry->etue_state =
59972e9af05SIgor Romanov 				    EFX_TUNNEL_UDP_ENTRY_APPLIED;
60072e9af05SIgor Romanov 				break;
60172e9af05SIgor Romanov 			case EFX_TUNNEL_UDP_ENTRY_REMOVED:
60272e9af05SIgor Romanov 				next = efx_tunnel_config_udp_do_remove(etcp, i);
60372e9af05SIgor Romanov 				break;
60472e9af05SIgor Romanov 			default:
60572e9af05SIgor Romanov 				EFSYS_ASSERT(0);
60672e9af05SIgor Romanov 				break;
60772e9af05SIgor Romanov 			}
60872e9af05SIgor Romanov 		}
60972e9af05SIgor Romanov 
61072e9af05SIgor Romanov 		i = next;
61172e9af05SIgor Romanov 	}
61272e9af05SIgor Romanov 
61372e9af05SIgor Romanov 	EFSYS_UNLOCK(enp->en_eslp, state);
61472e9af05SIgor Romanov 	locked = B_FALSE;
61572e9af05SIgor Romanov 
61672e9af05SIgor Romanov 	return ((resetting == B_FALSE) ? 0 : EAGAIN);
61772e9af05SIgor Romanov 
61872e9af05SIgor Romanov fail3:
61972e9af05SIgor Romanov 	EFSYS_PROBE(fail3);
62072e9af05SIgor Romanov 
62172e9af05SIgor Romanov 	EFSYS_ASSERT(locked == B_FALSE);
62272e9af05SIgor Romanov 	EFSYS_LOCK(enp->en_eslp, state);
62372e9af05SIgor Romanov 
62472e9af05SIgor Romanov 	for (i = 0; i < etcp->etc_udp_entries_num; i++)
62572e9af05SIgor Romanov 		etcp->etc_udp_entries[i].etue_busy = B_FALSE;
62672e9af05SIgor Romanov 
62772e9af05SIgor Romanov 	EFSYS_UNLOCK(enp->en_eslp, state);
6285e111ed8SAndrew Rybchenko 
6295e111ed8SAndrew Rybchenko fail2:
6305e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
6315e111ed8SAndrew Rybchenko 
6325e111ed8SAndrew Rybchenko fail1:
6335e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
63472e9af05SIgor Romanov 	if (locked)
63572e9af05SIgor Romanov 		EFSYS_UNLOCK(enp->en_eslp, state);
6365e111ed8SAndrew Rybchenko 
6375e111ed8SAndrew Rybchenko 	return (rc);
6385e111ed8SAndrew Rybchenko }
6395e111ed8SAndrew Rybchenko 
6405e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
6415e111ed8SAndrew Rybchenko static	__checkReturn		boolean_t
ef10_udp_encap_supported(__in efx_nic_t * enp)6425e111ed8SAndrew Rybchenko ef10_udp_encap_supported(
6435e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
6445e111ed8SAndrew Rybchenko {
6455e111ed8SAndrew Rybchenko 	const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
6465e111ed8SAndrew Rybchenko 	uint32_t udp_tunnels_mask = 0;
6475e111ed8SAndrew Rybchenko 
6485e111ed8SAndrew Rybchenko 	udp_tunnels_mask |= (1u << EFX_TUNNEL_PROTOCOL_VXLAN);
6495e111ed8SAndrew Rybchenko 	udp_tunnels_mask |= (1u << EFX_TUNNEL_PROTOCOL_GENEVE);
6505e111ed8SAndrew Rybchenko 
6515e111ed8SAndrew Rybchenko 	return ((encp->enc_tunnel_encapsulations_supported &
6525e111ed8SAndrew Rybchenko 	    udp_tunnels_mask) == 0 ? B_FALSE : B_TRUE);
6535e111ed8SAndrew Rybchenko }
6545e111ed8SAndrew Rybchenko 
6555e111ed8SAndrew Rybchenko static	__checkReturn	efx_rc_t
ef10_tunnel_reconfigure(__in efx_nic_t * enp)6565e111ed8SAndrew Rybchenko ef10_tunnel_reconfigure(
6575e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
6585e111ed8SAndrew Rybchenko {
6595e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
6605e111ed8SAndrew Rybchenko 	efx_rc_t rc;
6615e111ed8SAndrew Rybchenko 	boolean_t resetting = B_FALSE;
6625e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
6635e111ed8SAndrew Rybchenko 	efx_tunnel_cfg_t etc;
6645e111ed8SAndrew Rybchenko 
6655e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
6665e111ed8SAndrew Rybchenko 	memcpy(&etc, etcp, sizeof (etc));
6675e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
6685e111ed8SAndrew Rybchenko 
6695e111ed8SAndrew Rybchenko 	if (ef10_udp_encap_supported(enp) == B_FALSE) {
6705e111ed8SAndrew Rybchenko 		/*
6715e111ed8SAndrew Rybchenko 		 * It is OK to apply empty UDP tunnel ports when UDP
6725e111ed8SAndrew Rybchenko 		 * tunnel encapsulations are not supported - just nothing
6735e111ed8SAndrew Rybchenko 		 * should be done.
6745e111ed8SAndrew Rybchenko 		 */
6755e111ed8SAndrew Rybchenko 		if (etc.etc_udp_entries_num == 0)
6765e111ed8SAndrew Rybchenko 			return (0);
6775e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
6785e111ed8SAndrew Rybchenko 		goto fail1;
6795e111ed8SAndrew Rybchenko 	} else {
6805e111ed8SAndrew Rybchenko 		/*
6815e111ed8SAndrew Rybchenko 		 * All PCI functions can see a reset upon the
6825e111ed8SAndrew Rybchenko 		 * MCDI request completion
6835e111ed8SAndrew Rybchenko 		 */
6845e111ed8SAndrew Rybchenko 		rc = efx_mcdi_set_tunnel_encap_udp_ports(enp, &etc, B_FALSE,
6855e111ed8SAndrew Rybchenko 		    &resetting);
6865e111ed8SAndrew Rybchenko 		if (rc != 0) {
6875e111ed8SAndrew Rybchenko 			/*
6885e111ed8SAndrew Rybchenko 			 * Do not fail if the access is denied when no
6895e111ed8SAndrew Rybchenko 			 * tunnel encap UDP ports are configured.
6905e111ed8SAndrew Rybchenko 			 */
6915e111ed8SAndrew Rybchenko 			if (rc != EACCES || etc.etc_udp_entries_num != 0)
6925e111ed8SAndrew Rybchenko 				goto fail2;
6935e111ed8SAndrew Rybchenko 		}
6945e111ed8SAndrew Rybchenko 
6955e111ed8SAndrew Rybchenko 		/*
6965e111ed8SAndrew Rybchenko 		 * Although the caller should be able to handle MC reboot,
6975e111ed8SAndrew Rybchenko 		 * it might come in handy to report the impending reboot
6985e111ed8SAndrew Rybchenko 		 * by returning EAGAIN
6995e111ed8SAndrew Rybchenko 		 */
7005e111ed8SAndrew Rybchenko 		return ((resetting) ? EAGAIN : 0);
7015e111ed8SAndrew Rybchenko 	}
7025e111ed8SAndrew Rybchenko fail2:
7035e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
7045e111ed8SAndrew Rybchenko 
7055e111ed8SAndrew Rybchenko fail1:
7065e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
7075e111ed8SAndrew Rybchenko 
7085e111ed8SAndrew Rybchenko 	return (rc);
7095e111ed8SAndrew Rybchenko }
7104dda992fSIgor Romanov 
7114dda992fSIgor Romanov static			void
ef10_tunnel_fini(__in efx_nic_t * enp)7124dda992fSIgor Romanov ef10_tunnel_fini(
7134dda992fSIgor Romanov 	__in		efx_nic_t *enp)
7144dda992fSIgor Romanov {
7154dda992fSIgor Romanov 	boolean_t resetting;
7164dda992fSIgor Romanov 
7174dda992fSIgor Romanov 	if (ef10_udp_encap_supported(enp) != B_FALSE) {
7184dda992fSIgor Romanov 		/*
7194dda992fSIgor Romanov 		 * The UNLOADING flag allows the MC to suppress the datapath
7204dda992fSIgor Romanov 		 * reset if it was set on the last call to
7214dda992fSIgor Romanov 		 * MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS by all functions
7224dda992fSIgor Romanov 		 */
7234dda992fSIgor Romanov 		(void) efx_mcdi_set_tunnel_encap_udp_ports(enp, NULL, B_TRUE,
7244dda992fSIgor Romanov 		    &resetting);
7254dda992fSIgor Romanov 	}
7264dda992fSIgor Romanov }
7275e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
7285e111ed8SAndrew Rybchenko 
7295e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_TUNNEL */
730