1 /* SPDX-License-Identifier: BSD-3-Clause 2 * 3 * Copyright(c) 2021 Xilinx, Inc. 4 */ 5 6 #include "efx.h" 7 #include "efx_impl.h" 8 9 #if EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL 10 11 /* Match by Ether-type */ 12 #define EFX_VNIC_ENCAP_RULE_MATCH_ETHER_TYPE \ 13 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_ETHER_TYPE_LBN) 14 /* Match by outer VLAN ID */ 15 #define EFX_VNIC_ENCAP_RULE_MATCH_OUTER_VID \ 16 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_OUTER_VLAN_LBN) 17 /* Match by local IP host address */ 18 #define EFX_VNIC_ENCAP_RULE_MATCH_LOC_HOST \ 19 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_IP_LBN) 20 /* Match by IP transport protocol */ 21 #define EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO \ 22 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_IP_PROTO_LBN) 23 /* Match by local TCP/UDP port */ 24 #define EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT \ 25 (1u << MC_CMD_VNIC_ENCAP_RULE_ADD_IN_MATCH_DST_PORT_LBN) 26 27 /* 28 * Helper structure to pass parameters to MCDI function to add a VNIC 29 * encapsulation rule. 30 */ 31 typedef struct efx_vnic_encap_rule_spec_s { 32 uint32_t evers_mport_selector; /* Host-endian */ 33 uint32_t evers_match_flags; /* Host-endian */ 34 uint16_t evers_ether_type; /* Host-endian */ 35 uint16_t evers_outer_vid; /* Host-endian */ 36 efx_oword_t evers_loc_host; /* Big-endian */ 37 uint8_t evers_ip_proto; 38 uint16_t evers_loc_port; /* Host-endian */ 39 efx_tunnel_protocol_t evers_encap_type; 40 } efx_vnic_encap_rule_spec_t; 41 42 static uint32_t 43 efx_tunnel_protocol2mae_encap_type( 44 __in efx_tunnel_protocol_t proto, 45 __out uint32_t *typep) 46 { 47 efx_rc_t rc; 48 49 switch (proto) { 50 case EFX_TUNNEL_PROTOCOL_NONE: 51 *typep = MAE_MCDI_ENCAP_TYPE_NONE; 52 break; 53 case EFX_TUNNEL_PROTOCOL_VXLAN: 54 *typep = MAE_MCDI_ENCAP_TYPE_VXLAN; 55 break; 56 case EFX_TUNNEL_PROTOCOL_GENEVE: 57 *typep = MAE_MCDI_ENCAP_TYPE_GENEVE; 58 break; 59 case EFX_TUNNEL_PROTOCOL_NVGRE: 60 *typep = MAE_MCDI_ENCAP_TYPE_NVGRE; 61 break; 62 default: 63 rc = EINVAL; 64 goto fail1; 65 } 66 67 return (0); 68 69 fail1: 70 EFSYS_PROBE1(fail1, efx_rc_t, rc); 71 72 return (rc); 73 } 74 75 static __checkReturn efx_rc_t 76 efx_mcdi_vnic_encap_rule_add( 77 __in efx_nic_t *enp, 78 __in const efx_vnic_encap_rule_spec_t *spec, 79 __out efx_vnic_encap_rule_handle_t *handle) 80 81 { 82 efx_mcdi_req_t req; 83 EFX_MCDI_DECLARE_BUF(payload, 84 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN, 85 MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN); 86 uint32_t encap_type; 87 efx_rc_t rc; 88 89 req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_ADD; 90 req.emr_in_buf = payload; 91 req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_ADD_IN_LEN; 92 req.emr_out_buf = payload; 93 req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN; 94 95 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MPORT_SELECTOR, 96 spec->evers_mport_selector); 97 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_MATCH_FLAGS, 98 spec->evers_match_flags); 99 100 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_ETHER_TYPE, 101 __CPU_TO_BE_16(spec->evers_ether_type)); 102 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_OUTER_VLAN_WORD, 103 __CPU_TO_BE_16(spec->evers_outer_vid)); 104 105 /* 106 * Address is already in network order as well as the MCDI field, 107 * so plain copy is used. 108 */ 109 EFX_STATIC_ASSERT(sizeof (spec->evers_loc_host) == 110 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN); 111 memcpy(MCDI_IN2(req, uint8_t, VNIC_ENCAP_RULE_ADD_IN_DST_IP), 112 &spec->evers_loc_host.eo_byte[0], 113 MC_CMD_VNIC_ENCAP_RULE_ADD_IN_DST_IP_LEN); 114 115 MCDI_IN_SET_BYTE(req, VNIC_ENCAP_RULE_ADD_IN_IP_PROTO, 116 spec->evers_ip_proto); 117 MCDI_IN_SET_WORD_NATIVE(req, VNIC_ENCAP_RULE_ADD_IN_DST_PORT, 118 __CPU_TO_BE_16(spec->evers_loc_port)); 119 120 rc = efx_tunnel_protocol2mae_encap_type(spec->evers_encap_type, 121 &encap_type); 122 if (rc != 0) 123 goto fail1; 124 125 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_ADD_IN_ENCAP_TYPE, encap_type); 126 127 efx_mcdi_execute(enp, &req); 128 129 if (req.emr_rc != 0) { 130 rc = req.emr_rc; 131 goto fail2; 132 } 133 134 if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_ADD_OUT_LEN) { 135 rc = EMSGSIZE; 136 goto fail3; 137 } 138 139 if (handle != NULL) 140 *handle = MCDI_OUT_DWORD(req, VNIC_ENCAP_RULE_ADD_OUT_HANDLE); 141 142 return (0); 143 144 fail3: 145 EFSYS_PROBE(fail3); 146 147 fail2: 148 EFSYS_PROBE(fail2); 149 150 fail1: 151 EFSYS_PROBE1(fail1, efx_rc_t, rc); 152 153 return (rc); 154 } 155 156 static __checkReturn efx_rc_t 157 efx_mcdi_vnic_encap_rule_remove( 158 __in efx_nic_t *enp, 159 __in efx_vnic_encap_rule_handle_t handle) 160 161 { 162 efx_mcdi_req_t req; 163 EFX_MCDI_DECLARE_BUF(payload, 164 MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN, 165 MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN); 166 efx_rc_t rc; 167 168 req.emr_cmd = MC_CMD_VNIC_ENCAP_RULE_REMOVE; 169 req.emr_in_buf = payload; 170 req.emr_in_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_IN_LEN; 171 req.emr_out_buf = payload; 172 req.emr_out_length = MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN; 173 174 MCDI_IN_SET_DWORD(req, VNIC_ENCAP_RULE_REMOVE_IN_HANDLE, handle); 175 176 efx_mcdi_execute(enp, &req); 177 178 if (req.emr_rc != 0) { 179 rc = req.emr_rc; 180 goto fail1; 181 } 182 183 if (req.emr_out_length_used != MC_CMD_VNIC_ENCAP_RULE_REMOVE_OUT_LEN) { 184 rc = EMSGSIZE; 185 goto fail2; 186 } 187 188 return (0); 189 190 fail2: 191 EFSYS_PROBE(fail2); 192 193 fail1: 194 EFSYS_PROBE1(fail1, efx_rc_t, rc); 195 196 return (rc); 197 } 198 199 static void 200 rhead_vnic_encap_rule_spec_init( 201 __in const efx_tunnel_udp_entry_t *etuep, 202 __out efx_vnic_encap_rule_spec_t *spec) 203 { 204 memset(spec, 0, sizeof (*spec)); 205 206 spec->evers_mport_selector = MAE_MPORT_SELECTOR_ASSIGNED; 207 spec->evers_match_flags = EFX_VNIC_ENCAP_RULE_MATCH_IP_PROTO | 208 EFX_VNIC_ENCAP_RULE_MATCH_LOC_PORT; 209 spec->evers_ip_proto = EFX_IPPROTO_UDP; 210 spec->evers_loc_port = etuep->etue_port; 211 spec->evers_encap_type = etuep->etue_protocol; 212 } 213 214 static __checkReturn efx_rc_t 215 rhead_udp_port_tunnel_add( 216 __in efx_nic_t *enp, 217 __inout efx_tunnel_udp_entry_t *etuep) 218 { 219 efx_vnic_encap_rule_spec_t spec; 220 221 rhead_vnic_encap_rule_spec_init(etuep, &spec); 222 return (efx_mcdi_vnic_encap_rule_add(enp, &spec, &etuep->etue_handle)); 223 } 224 225 static __checkReturn efx_rc_t 226 rhead_udp_port_tunnel_remove( 227 __in efx_nic_t *enp, 228 __in efx_tunnel_udp_entry_t *etuep) 229 { 230 return (efx_mcdi_vnic_encap_rule_remove(enp, etuep->etue_handle)); 231 } 232 233 __checkReturn efx_rc_t 234 rhead_tunnel_reconfigure( 235 __in efx_nic_t *enp) 236 { 237 efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg; 238 efx_rc_t rc; 239 efsys_lock_state_t state; 240 efx_tunnel_cfg_t etc; 241 efx_tunnel_cfg_t added; 242 unsigned int i; 243 unsigned int j; 244 245 memset(&added, 0, sizeof(added)); 246 247 /* 248 * Make a local copy of UDP tunnel table to release the lock 249 * when executing MCDIs. 250 */ 251 EFSYS_LOCK(enp->en_eslp, state); 252 memcpy(&etc, etcp, sizeof (etc)); 253 EFSYS_UNLOCK(enp->en_eslp, state); 254 255 for (i = 0; i < etc.etc_udp_entries_num; i++) { 256 efx_tunnel_udp_entry_t *etc_entry = &etc.etc_udp_entries[i]; 257 258 if (etc_entry->etue_busy == B_FALSE) 259 continue; 260 261 switch (etc_entry->etue_state) { 262 case EFX_TUNNEL_UDP_ENTRY_APPLIED: 263 break; 264 case EFX_TUNNEL_UDP_ENTRY_ADDED: 265 rc = rhead_udp_port_tunnel_add(enp, etc_entry); 266 if (rc != 0) 267 goto fail1; 268 added.etc_udp_entries[added.etc_udp_entries_num] = 269 *etc_entry; 270 added.etc_udp_entries_num++; 271 break; 272 case EFX_TUNNEL_UDP_ENTRY_REMOVED: 273 rc = rhead_udp_port_tunnel_remove(enp, etc_entry); 274 if (rc != 0) 275 goto fail2; 276 break; 277 default: 278 EFSYS_ASSERT(0); 279 break; 280 } 281 } 282 283 EFSYS_LOCK(enp->en_eslp, state); 284 285 /* 286 * Adding or removing non-busy entries does not change the 287 * order of busy entries. Therefore one linear search iteration 288 * suffices. 289 */ 290 for (i = 0, j = 0; i < etcp->etc_udp_entries_num; i++) { 291 efx_tunnel_udp_entry_t *cur_entry = &etcp->etc_udp_entries[i]; 292 efx_tunnel_udp_entry_t *added_entry = &added.etc_udp_entries[j]; 293 294 if (cur_entry->etue_state == EFX_TUNNEL_UDP_ENTRY_ADDED && 295 cur_entry->etue_port == added_entry->etue_port) { 296 cur_entry->etue_handle = added_entry->etue_handle; 297 j++; 298 } 299 } 300 301 EFSYS_UNLOCK(enp->en_eslp, state); 302 303 return (0); 304 305 fail2: 306 EFSYS_PROBE(fail2); 307 308 fail1: 309 EFSYS_PROBE1(fail1, efx_rc_t, rc); 310 311 while (i-- > 0) { 312 if (etc.etc_udp_entries[i].etue_busy == B_FALSE) 313 continue; 314 315 switch (etc.etc_udp_entries[i].etue_state) { 316 case EFX_TUNNEL_UDP_ENTRY_APPLIED: 317 break; 318 case EFX_TUNNEL_UDP_ENTRY_ADDED: 319 (void) rhead_udp_port_tunnel_remove(enp, 320 &etc.etc_udp_entries[i]); 321 break; 322 case EFX_TUNNEL_UDP_ENTRY_REMOVED: 323 (void) rhead_udp_port_tunnel_add(enp, 324 &etc.etc_udp_entries[i]); 325 break; 326 default: 327 EFSYS_ASSERT(0); 328 break; 329 } 330 } 331 332 return (rc); 333 } 334 335 void 336 rhead_tunnel_fini( 337 __in efx_nic_t *enp) 338 { 339 (void) efx_tunnel_config_clear(enp); 340 (void) efx_tunnel_reconfigure(enp); 341 } 342 343 #endif /* EFSYS_OPT_RIVERHEAD && EFSYS_OPT_TUNNEL */ 344