common/sfc_efx/base: introduce UDP tunnel destruct operation
[dpdk.git] / drivers / common / sfc_efx / base / efx_tunnel.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2020 Xilinx, Inc.
4  * Copyright(c) 2007-2019 Solarflare Communications Inc.
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 || EFSYS_OPT_RIVERHEAD
14 static const efx_tunnel_ops_t   __efx_tunnel_dummy_ops = {
15         NULL,   /* eto_reconfigure */
16         NULL,   /* eto_fini */
17 };
18 #endif /* EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_RIVERHEAD */
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                  void
30 ef10_tunnel_fini(
31         __in            efx_nic_t *enp);
32
33 static const efx_tunnel_ops_t   __efx_tunnel_ef10_ops = {
34         ef10_tunnel_reconfigure,        /* eto_reconfigure */
35         ef10_tunnel_fini,               /* eto_fini */
36 };
37 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
38
39 static  __checkReturn           efx_rc_t
40 efx_mcdi_set_tunnel_encap_udp_ports(
41         __in                    efx_nic_t *enp,
42         __in                    efx_tunnel_cfg_t *etcp,
43         __in                    boolean_t unloading,
44         __out                   boolean_t *resetting)
45 {
46         efx_mcdi_req_t req;
47         EFX_MCDI_DECLARE_BUF(payload,
48                 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX,
49                 MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN);
50         efx_word_t flags;
51         efx_rc_t rc;
52         unsigned int i;
53         unsigned int entries_num;
54
55         if (etcp == NULL)
56                 entries_num = 0;
57         else
58                 entries_num = etcp->etc_udp_entries_num;
59
60         req.emr_cmd = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS;
61         req.emr_in_buf = payload;
62         req.emr_in_length =
63             MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(entries_num);
64         req.emr_out_buf = payload;
65         req.emr_out_length = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN;
66
67         EFX_POPULATE_WORD_1(flags,
68             MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING,
69             (unloading == B_TRUE) ? 1 : 0);
70         MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS,
71             EFX_WORD_FIELD(flags, EFX_WORD_0));
72
73         MCDI_IN_SET_WORD(req, SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES,
74             entries_num);
75
76         for (i = 0; i < entries_num; ++i) {
77                 uint16_t mcdi_udp_protocol;
78
79                 switch (etcp->etc_udp_entries[i].etue_protocol) {
80                 case EFX_TUNNEL_PROTOCOL_VXLAN:
81                         mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN;
82                         break;
83                 case EFX_TUNNEL_PROTOCOL_GENEVE:
84                         mcdi_udp_protocol = TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE;
85                         break;
86                 default:
87                         rc = EINVAL;
88                         goto fail1;
89                 }
90
91                 /*
92                  * UDP port is MCDI native little-endian in the request
93                  * and EFX_POPULATE_DWORD cares about conversion from
94                  * host/CPU byte order to little-endian.
95                  */
96                 EFX_STATIC_ASSERT(sizeof (efx_dword_t) ==
97                     TUNNEL_ENCAP_UDP_PORT_ENTRY_LEN);
98                 EFX_POPULATE_DWORD_2(
99                     MCDI_IN2(req, efx_dword_t,
100                         SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES)[i],
101                     TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT,
102                     etcp->etc_udp_entries[i].etue_port,
103                     TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL,
104                     mcdi_udp_protocol);
105         }
106
107         efx_mcdi_execute(enp, &req);
108
109         if (req.emr_rc != 0) {
110                 rc = req.emr_rc;
111                 goto fail2;
112         }
113
114         if (req.emr_out_length_used !=
115             MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN) {
116                 rc = EMSGSIZE;
117                 goto fail3;
118         }
119
120         *resetting = MCDI_OUT_WORD_FIELD(req,
121             SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS,
122             SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING);
123
124         return (0);
125
126 fail3:
127         EFSYS_PROBE(fail3);
128
129 fail2:
130         EFSYS_PROBE(fail2);
131
132 fail1:
133         EFSYS_PROBE1(fail1, efx_rc_t, rc);
134
135         return (rc);
136 }
137
138         __checkReturn   efx_rc_t
139 efx_tunnel_init(
140         __in            efx_nic_t *enp)
141 {
142         efx_tunnel_cfg_t *etcp = &enp->en_tunnel_cfg;
143         const efx_tunnel_ops_t *etop;
144         efx_rc_t rc;
145
146         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
147         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
148         EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TUNNEL));
149
150         EFX_STATIC_ASSERT(EFX_TUNNEL_MAXNENTRIES ==
151             MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM);
152
153         switch (enp->en_family) {
154 #if EFSYS_OPT_SIENA
155         case EFX_FAMILY_SIENA:
156                 etop = &__efx_tunnel_dummy_ops;
157                 break;
158 #endif /* EFSYS_OPT_SIENA */
159
160 #if EFSYS_OPT_HUNTINGTON
161         case EFX_FAMILY_HUNTINGTON:
162                 etop = &__efx_tunnel_dummy_ops;
163                 break;
164 #endif /* EFSYS_OPT_HUNTINGTON */
165
166 #if EFSYS_OPT_MEDFORD
167         case EFX_FAMILY_MEDFORD:
168                 etop = &__efx_tunnel_ef10_ops;
169                 break;
170 #endif /* EFSYS_OPT_MEDFORD */
171
172 #if EFSYS_OPT_MEDFORD2
173         case EFX_FAMILY_MEDFORD2:
174                 etop = &__efx_tunnel_ef10_ops;
175                 break;
176 #endif /* EFSYS_OPT_MEDFORD2 */
177
178 #if EFSYS_OPT_RIVERHEAD
179         case EFX_FAMILY_RIVERHEAD:
180                 etop = &__efx_tunnel_dummy_ops;
181                 break;
182 #endif /* EFSYS_OPT_RIVERHEAD */
183
184         default:
185                 EFSYS_ASSERT(0);
186                 rc = ENOTSUP;
187                 goto fail1;
188         }
189
190         memset(etcp->etc_udp_entries, 0, sizeof (etcp->etc_udp_entries));
191         etcp->etc_udp_entries_num = 0;
192
193         enp->en_etop = etop;
194         enp->en_mod_flags |= EFX_MOD_TUNNEL;
195
196         return (0);
197
198 fail1:
199         EFSYS_PROBE1(fail1, efx_rc_t, rc);
200
201         enp->en_etop = NULL;
202         enp->en_mod_flags &= ~EFX_MOD_TUNNEL;
203
204         return (rc);
205 }
206
207                         void
208 efx_tunnel_fini(
209         __in            efx_nic_t *enp)
210 {
211         EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
212         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
213         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_TUNNEL);
214
215         if (enp->en_etop->eto_fini != NULL)
216                 enp->en_etop->eto_fini(enp);
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 = B_FALSE;
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                         /*
451                          * Do not fail if the access is denied when no
452                          * tunnel encap UDP ports are configured.
453                          */
454                         if (rc != EACCES || etc.etc_udp_entries_num != 0)
455                                 goto fail2;
456                 }
457
458                 /*
459                  * Although the caller should be able to handle MC reboot,
460                  * it might come in handy to report the impending reboot
461                  * by returning EAGAIN
462                  */
463                 return ((resetting) ? EAGAIN : 0);
464         }
465 fail2:
466         EFSYS_PROBE(fail2);
467
468 fail1:
469         EFSYS_PROBE1(fail1, efx_rc_t, rc);
470
471         return (rc);
472 }
473
474 static                  void
475 ef10_tunnel_fini(
476         __in            efx_nic_t *enp)
477 {
478         boolean_t resetting;
479
480         if (ef10_udp_encap_supported(enp) != B_FALSE) {
481                 /*
482                  * The UNLOADING flag allows the MC to suppress the datapath
483                  * reset if it was set on the last call to
484                  * MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS by all functions
485                  */
486                 (void) efx_mcdi_set_tunnel_encap_udp_ports(enp, NULL, B_TRUE,
487                     &resetting);
488         }
489 }
490 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
491
492 #endif /* EFSYS_OPT_TUNNEL */