common/sfc_efx/base: handle descriptor proxy queue events
[dpdk.git] / drivers / common / sfc_efx / base / rhead_tunnel.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2020 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 */