mirror of https://github.com/F-Stack/f-stack.git
344 lines
8.2 KiB
C
344 lines
8.2 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright(c) 2020 Xilinx, Inc.
|
|
*/
|
|
|
|
#include "efx.h"
|
|
#include "efx_impl.h"
|
|
|
|
#if EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL
|
|
|
|
/* Match by Ether-type */
|
|
#define EFX_VNIC_ENCAP_RULE_MATCH_ETHER_TYPE \
|
|
(1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_ETHER_TYPE_LBN)
|
|
/* Match by outer VLAN ID */
|
|
#define EFX_VNIC_ENCAP_RULE_MATCH_OUTER_VID \
|
|
(1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_OUTER_VLAN_LBN)
|
|
/* Match by local IP host address */
|
|
#define EFX_VNIC_ENCAP_RULE_MATCH_LOC_HOST \
|
|
(1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_IP_LBN)
|
|
/* Match by IP transport protocol */
|
|
#define EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO \
|
|
(1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_IP_PROTO_LBN)
|
|
/* Match by local TCP/UDP port */
|
|
#define EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT \
|
|
(1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_PORT_LBN)
|
|
|
|
/*
|
|
* Helper structure to pass parameters to MCDI function to add a VNIC
|
|
* encapsulation rule.
|
|
*/
|
|
typedef struct efx_vnic_encap_rule_spec_s {
|
|
uint32_t evers_mport_selector; /* Host-endian */
|
|
uint32_t evers_match_flags; /* Host-endian */
|
|
uint16_t evers_ether_type; /* Host-endian */
|
|
uint16_t evers_outer_vid; /* Host-endian */
|
|
efx_oword_t evers_loc_host; /* Big-endian */
|
|
uint8_t evers_ip_proto;
|
|
uint16_t evers_loc_port; /* Host-endian */
|
|
efx_tunnel_protocol_t evers_encap_type;
|
|
} efx_vnic_encap_rule_spec_t;
|
|
|
|
static uint32_t
|
|
efx_tunnel_protocol2mae_encap_type(
|
|
__in efx_tunnel_protocol_t proto,
|
|
__out uint32_t *typep)
|
|
{
|
|
efx_rc_t rc;
|
|
|
|
switch (proto) {
|
|
case EFX_TUNNEL_PROTOCOL_NONE:
|
|
*typep = MAE_MCDI_ENCAP_TYPE_NONE;
|
|
break;
|
|
case EFX_TUNNEL_PROTOCOL_VXLAN:
|
|
*typep = MAE_MCDI_ENCAP_TYPE_VXLAN;
|
|
break;
|
|
case EFX_TUNNEL_PROTOCOL_GENEVE:
|
|
*typep = MAE_MCDI_ENCAP_TYPE_GENEVE;
|
|
break;
|
|
case EFX_TUNNEL_PROTOCOL_NVGRE:
|
|
*typep = MAE_MCDI_ENCAP_TYPE_NVGRE;
|
|
break;
|
|
default:
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
return (0);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
efx_mcdi_req_t req;
|
|
EFX_MCDI_DECLARE_BUF(payload,
|
|
MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN,
|
|
MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN);
|
|
uint32_t encap_type;
|
|
efx_rc_t rc;
|
|
|
|
req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_ADD;
|
|
req.emr_in_buf = payload;
|
|
req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN;
|
|
req.emr_out_buf = payload;
|
|
req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN;
|
|
|
|
MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MPORT_SELECTOR,
|
|
spec->evers_mport_selector);
|
|
MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MATCH_FLAGS,
|
|
spec->evers_match_flags);
|
|
|
|
MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_ETHER_TYPE,
|
|
__CPU_TO_BE_16(spec->evers_ether_type));
|
|
MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_OUTER_VLAN_WORD,
|
|
__CPU_TO_BE_16(spec->evers_outer_vid));
|
|
|
|
/*
|
|
* Address is already in network order as well as the MCDI field,
|
|
* so plain copy is used.
|
|
*/
|
|
EFX_STATIC_ASSERT(sizeof (spec->evers_loc_host) ==
|
|
MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN);
|
|
memcpy(MCDI_IN2(req, uint8_t, VNIC_ENCAP_RULE_ADD_IN_DST_IP),
|
|
&spec->evers_loc_host.eo_byte[0],
|
|
MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN);
|
|
|
|
MCDI_IN_SET_BYTE(req, VNIC_ENCAP_RULE_ADD_IN_IP_PROTO,
|
|
spec->evers_ip_proto);
|
|
MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_DST_PORT,
|
|
__CPU_TO_BE_16(spec->evers_loc_port));
|
|
|
|
rc = efx_tunnel_protocol2mae_encap_type(spec->evers_encap_type,
|
|
&encap_type);
|
|
if (rc != 0)
|
|
goto fail1;
|
|
|
|
MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_ENCAP_TYPE, encap_type);
|
|
|
|
efx_mcdi_execute(enp, &req);
|
|
|
|
if (req.emr_rc != 0) {
|
|
rc = req.emr_rc;
|
|
goto fail2;
|
|
}
|
|
|
|
if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN) {
|
|
rc = EMSGSIZE;
|
|
goto fail3;
|
|
}
|
|
|
|
if (handle != NULL)
|
|
*handle = MCDI_OUT_DWORD(req, VNIC_ENCAP_RULE_ADD_OUT_HANDLE);
|
|
|
|
return (0);
|
|
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
static __checkReturn efx_rc_t
|
|
efx_mcdi_vnic_encap_rule_remove(
|
|
__in efx_nic_t *enp,
|
|
__in efx_vnic_encap_rule_handle_t handle)
|
|
|
|
{
|
|
efx_mcdi_req_t req;
|
|
EFX_MCDI_DECLARE_BUF(payload,
|
|
MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN,
|
|
MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN);
|
|
efx_rc_t rc;
|
|
|
|
req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_REMOVE;
|
|
req.emr_in_buf = payload;
|
|
req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN;
|
|
req.emr_out_buf = payload;
|
|
req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN;
|
|
|
|
MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_REMOVE_IN_HANDLE, handle);
|
|
|
|
efx_mcdi_execute(enp, &req);
|
|
|
|
if (req.emr_rc != 0) {
|
|
rc = req.emr_rc;
|
|
goto fail1;
|
|
}
|
|
|
|
if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN) {
|
|
rc = EMSGSIZE;
|
|
goto fail2;
|
|
}
|
|
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
static void
|
|
rhead_vnic_encap_rule_spec_init(
|
|
__in const efx_tunnel_udp_entry_t *etuep,
|
|
__out efx_vnic_encap_rule_spec_t *spec)
|
|
{
|
|
memset(spec, 0, sizeof (*spec));
|
|
|
|
spec->evers_mport_selector = MAE_MPORT_SELECTOR_ASSIGNED;
|
|
spec->evers_match_flags = EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO |
|
|
EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT;
|
|
spec->evers_ip_proto = EFX_IPPROTO_UDP;
|
|
spec->evers_loc_port = etuep->etue_port;
|
|
spec->evers_encap_type = etuep->etue_protocol;
|
|
}
|
|
|
|
static __checkReturn efx_rc_t
|
|
rhead_udp_port_tunnel_add(
|
|
__in efx_nic_t *enp,
|
|
__inout efx_tunnel_udp_entry_t *etuep)
|
|
{
|
|
efx_vnic_encap_rule_spec_t spec;
|
|
|
|
rhead_vnic_encap_rule_spec_init(etuep, &spec);
|
|
return (efx_mcdi_vnic_encap_rule_add(enp, &spec, &etuep->etue_handle));
|
|
}
|
|
|
|
static __checkReturn efx_rc_t
|
|
rhead_udp_port_tunnel_remove(
|
|
__in efx_nic_t *enp,
|
|
__in efx_tunnel_udp_entry_t *etuep)
|
|
{
|
|
return (efx_mcdi_vnic_encap_rule_remove(enp, etuep->etue_handle));
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
rhead_tunnel_reconfigure(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
|
|
efx_rc_t rc;
|
|
efsys_lock_state_t state;
|
|
efx_tunnel_cfg_t etc;
|
|
efx_tunnel_cfg_t added;
|
|
unsigned int i;
|
|
unsigned int j;
|
|
|
|
memset(&added, 0, sizeof(added));
|
|
|
|
/*
|
|
* Make a local copy of UDP tunnel table to release the lock
|
|
* when executing MCDIs.
|
|
*/
|
|
EFSYS_LOCK(enp->en_eslp, state);
|
|
memcpy(&etc, etcp, sizeof (etc));
|
|
EFSYS_UNLOCK(enp->en_eslp, state);
|
|
|
|
for (i = 0; i < etc.etc_udp_entries_num; i++) {
|
|
efx_tunnel_udp_entry_t *etc_entry = &etc.etc_udp_entries[i];
|
|
|
|
if (etc_entry->etue_busy == B_FALSE)
|
|
continue;
|
|
|
|
switch (etc_entry->etue_state) {
|
|
case EFX_TUNNEL_UDP_ENTRY_APPLIED:
|
|
break;
|
|
case EFX_TUNNEL_UDP_ENTRY_ADDED:
|
|
rc = rhead_udp_port_tunnel_add(enp, etc_entry);
|
|
if (rc != 0)
|
|
goto fail1;
|
|
added.etc_udp_entries[added.etc_udp_entries_num] =
|
|
*etc_entry;
|
|
added.etc_udp_entries_num++;
|
|
break;
|
|
case EFX_TUNNEL_UDP_ENTRY_REMOVED:
|
|
rc = rhead_udp_port_tunnel_remove(enp, etc_entry);
|
|
if (rc != 0)
|
|
goto fail2;
|
|
break;
|
|
default:
|
|
EFSYS_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
EFSYS_LOCK(enp->en_eslp, state);
|
|
|
|
/*
|
|
* Adding or removing non-busy entries does not change the
|
|
* order of busy entries. Therefore one linear search iteration
|
|
* suffices.
|
|
*/
|
|
for (i = 0, j = 0; i < etcp->etc_udp_entries_num; i++) {
|
|
efx_tunnel_udp_entry_t *cur_entry = &etcp->etc_udp_entries[i];
|
|
efx_tunnel_udp_entry_t *added_entry = &added.etc_udp_entries[j];
|
|
|
|
if (cur_entry->etue_state == EFX_TUNNEL_UDP_ENTRY_ADDED &&
|
|
cur_entry->etue_port == added_entry->etue_port) {
|
|
cur_entry->etue_handle = added_entry->etue_handle;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
EFSYS_UNLOCK(enp->en_eslp, state);
|
|
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
while (i-- > 0) {
|
|
if (etc.etc_udp_entries[i].etue_busy == B_FALSE)
|
|
continue;
|
|
|
|
switch (etc.etc_udp_entries[i].etue_state) {
|
|
case EFX_TUNNEL_UDP_ENTRY_APPLIED:
|
|
break;
|
|
case EFX_TUNNEL_UDP_ENTRY_ADDED:
|
|
(void) rhead_udp_port_tunnel_remove(enp,
|
|
&etc.etc_udp_entries[i]);
|
|
break;
|
|
case EFX_TUNNEL_UDP_ENTRY_REMOVED:
|
|
(void) rhead_udp_port_tunnel_add(enp,
|
|
&etc.etc_udp_entries[i]);
|
|
break;
|
|
default:
|
|
EFSYS_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
void
|
|
rhead_tunnel_fini(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
(void) efx_tunnel_config_clear(enp);
|
|
(void) efx_tunnel_reconfigure(enp);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL */
|