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