net/sfc: include controller and port in representor name
[dpdk.git] / drivers / net / sfc / sfc_switch.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2021 Xilinx, Inc.
4  * Copyright(c) 2019 Solarflare Communications Inc.
5  *
6  * This software was jointly developed between OKTET Labs (under contract
7  * for Solarflare) and Solarflare Communications, Inc.
8  */
9
10 #include <stdbool.h>
11
12 #include <rte_common.h>
13 #include <rte_spinlock.h>
14
15 #include "efx.h"
16
17 #include "sfc.h"
18 #include "sfc_log.h"
19 #include "sfc_switch.h"
20
21 /**
22  * Switch port registry entry.
23  *
24  * Drivers aware of RTE switch domains also have to maintain RTE switch
25  * port IDs for RTE ethdev instances they operate. These IDs are supposed
26  * to stand for physical interconnect entities, in example, PCIe functions.
27  *
28  * In terms of MAE, a physical interconnect entity can be referred to using
29  * an MPORT selector, that is, a 32-bit value. RTE switch port IDs, in turn,
30  * are 16-bit values, so indirect mapping has to be maintained:
31  *
32  * +--------------------+          +---------------------------------------+
33  * | RTE switch port ID |  ------  |         MAE switch port entry         |
34  * +--------------------+          |         ---------------------         |
35  *                                 |                                       |
36  *                                 | Entity (PCIe function) MPORT selector |
37  *                                 |                   +                   |
38  *                                 |  Port type (independent/representor)  |
39  *                                 +---------------------------------------+
40  *
41  * This mapping comprises a port type to ensure that RTE switch port ID
42  * of a represented entity and that of its representor are different in
43  * the case when the entity gets plugged into DPDK and not into a guest.
44  *
45  * Entry data also comprises RTE ethdev's own MPORT. This value
46  * coincides with the entity MPORT in the case of independent ports.
47  * In the case of representors, this ID is not a selector and refers
48  * to an allocatable object (that is, it's likely to change on RTE
49  * ethdev replug). Flow API backend must use this value rather
50  * than entity_mport to support flow rule action PORT_ID.
51  */
52 struct sfc_mae_switch_port {
53         TAILQ_ENTRY(sfc_mae_switch_port)        switch_domain_ports;
54
55         /** RTE ethdev MPORT */
56         efx_mport_sel_t                         ethdev_mport;
57         /** RTE ethdev port ID */
58         uint16_t                                ethdev_port_id;
59
60         /** Entity (PCIe function) MPORT selector */
61         efx_mport_sel_t                         entity_mport;
62         /** Port type (independent/representor) */
63         enum sfc_mae_switch_port_type           type;
64         /** RTE switch port ID */
65         uint16_t                                id;
66
67         union sfc_mae_switch_port_data          data;
68 };
69
70 TAILQ_HEAD(sfc_mae_switch_ports, sfc_mae_switch_port);
71
72 /**
73  * Switch domain registry entry.
74  *
75  * Even if an RTE ethdev instance gets unplugged, the corresponding
76  * entry in the switch port registry will not be removed because the
77  * entity (PCIe function) MPORT is static and cannot change. If this
78  * RTE ethdev gets plugged back, the entry will be reused, and
79  * RTE switch port ID will be the same.
80  */
81 struct sfc_mae_switch_domain {
82         TAILQ_ENTRY(sfc_mae_switch_domain)      entries;
83
84         /** HW switch ID */
85         struct sfc_hw_switch_id                 *hw_switch_id;
86         /** The number of ports in the switch port registry */
87         unsigned int                            nb_ports;
88         /** Switch port registry */
89         struct sfc_mae_switch_ports             ports;
90         /** RTE switch domain ID allocated for a group of devices */
91         uint16_t                                id;
92         /** DPDK controller -> EFX interface mapping */
93         efx_pcie_interface_t                    *controllers;
94         /** Number of DPDK controllers and EFX interfaces */
95         size_t                                  nb_controllers;
96 };
97
98 TAILQ_HEAD(sfc_mae_switch_domains, sfc_mae_switch_domain);
99
100 /**
101  * MAE representation of RTE switch infrastructure.
102  *
103  * It is possible that an RTE flow API client tries to insert a rule
104  * referencing an RTE ethdev deployed on top of a different physical
105  * device (it may belong to the same vendor or not). This particular
106  * driver/engine cannot support this and has to turn down such rules.
107  *
108  * Technically, it's HW switch identifier which, if queried for each
109  * RTE ethdev instance, indicates relationship between the instances.
110  * In the meantime, RTE flow API clients also need to somehow figure
111  * out relationship between RTE ethdev instances in advance.
112  *
113  * The concept of RTE switch domains resolves this issue. The driver
114  * maintains a static list of switch domains which is easy to browse,
115  * and each RTE ethdev fills RTE switch parameters in device
116  * information structure which is made available to clients.
117  *
118  * Even if all RTE ethdev instances belonging to a switch domain get
119  * unplugged, the corresponding entry in the switch domain registry
120  * will not be removed because the corresponding HW switch exists
121  * regardless of its ports being plugged to DPDK or kept aside.
122  * If a port gets plugged back to DPDK, the corresponding
123  * RTE ethdev will indicate the same RTE switch domain ID.
124  */
125 struct sfc_mae_switch {
126         /** A lock to protect the whole structure */
127         rte_spinlock_t                  lock;
128         /** Switch domain registry */
129         struct sfc_mae_switch_domains   domains;
130 };
131
132 static struct sfc_mae_switch sfc_mae_switch = {
133         .lock = RTE_SPINLOCK_INITIALIZER,
134         .domains = TAILQ_HEAD_INITIALIZER(sfc_mae_switch.domains),
135 };
136
137
138 /* This function expects to be called only when the lock is held */
139 static struct sfc_mae_switch_domain *
140 sfc_mae_find_switch_domain_by_id(uint16_t switch_domain_id)
141 {
142         struct sfc_mae_switch_domain *domain;
143
144         SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
145
146         TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
147                 if (domain->id == switch_domain_id)
148                         return domain;
149         }
150
151         return NULL;
152 }
153
154 /* This function expects to be called only when the lock is held */
155 static struct sfc_mae_switch_domain *
156 sfc_mae_find_switch_domain_by_hw_switch_id(const struct sfc_hw_switch_id *id)
157 {
158         struct sfc_mae_switch_domain *domain;
159
160         SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
161
162         TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
163                 if (sfc_hw_switch_ids_equal(domain->hw_switch_id, id))
164                         return domain;
165         }
166
167         return NULL;
168 }
169
170 int
171 sfc_mae_assign_switch_domain(struct sfc_adapter *sa,
172                              uint16_t *switch_domain_id)
173 {
174         struct sfc_hw_switch_id *hw_switch_id;
175         struct sfc_mae_switch_domain *domain;
176         int rc;
177
178         rte_spinlock_lock(&sfc_mae_switch.lock);
179
180         rc = sfc_hw_switch_id_init(sa, &hw_switch_id);
181         if (rc != 0)
182                 goto fail_hw_switch_id_init;
183
184         domain = sfc_mae_find_switch_domain_by_hw_switch_id(hw_switch_id);
185         if (domain != NULL) {
186                 sfc_hw_switch_id_fini(sa, hw_switch_id);
187                 goto done;
188         }
189
190         domain = rte_zmalloc("sfc_mae_switch_domain", sizeof(*domain), 0);
191         if (domain == NULL) {
192                 rc = ENOMEM;
193                 goto fail_mem_alloc;
194         }
195
196         /*
197          * This code belongs to driver init path, that is, negation is
198          * done at the end of the path by sfc_eth_dev_init(). RTE APIs
199          * negate error codes, so drop negation here.
200          */
201         rc = -rte_eth_switch_domain_alloc(&domain->id);
202         if (rc != 0)
203                 goto fail_domain_alloc;
204
205         domain->hw_switch_id = hw_switch_id;
206
207         TAILQ_INIT(&domain->ports);
208
209         TAILQ_INSERT_TAIL(&sfc_mae_switch.domains, domain, entries);
210
211 done:
212         *switch_domain_id = domain->id;
213
214         rte_spinlock_unlock(&sfc_mae_switch.lock);
215
216         return 0;
217
218 fail_domain_alloc:
219         rte_free(domain);
220
221 fail_mem_alloc:
222         sfc_hw_switch_id_fini(sa, hw_switch_id);
223
224 fail_hw_switch_id_init:
225         rte_spinlock_unlock(&sfc_mae_switch.lock);
226         return rc;
227 }
228
229 int
230 sfc_mae_switch_domain_controllers(uint16_t switch_domain_id,
231                                   const efx_pcie_interface_t **controllers,
232                                   size_t *nb_controllers)
233 {
234         struct sfc_mae_switch_domain *domain;
235
236         if (controllers == NULL || nb_controllers == NULL)
237                 return EINVAL;
238
239         rte_spinlock_lock(&sfc_mae_switch.lock);
240
241         domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
242         if (domain == NULL) {
243                 rte_spinlock_unlock(&sfc_mae_switch.lock);
244                 return EINVAL;
245         }
246
247         *controllers = domain->controllers;
248         *nb_controllers = domain->nb_controllers;
249
250         rte_spinlock_unlock(&sfc_mae_switch.lock);
251         return 0;
252 }
253
254 int
255 sfc_mae_switch_domain_map_controllers(uint16_t switch_domain_id,
256                                       efx_pcie_interface_t *controllers,
257                                       size_t nb_controllers)
258 {
259         struct sfc_mae_switch_domain *domain;
260
261         rte_spinlock_lock(&sfc_mae_switch.lock);
262
263         domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
264         if (domain == NULL) {
265                 rte_spinlock_unlock(&sfc_mae_switch.lock);
266                 return EINVAL;
267         }
268
269         /* Controller mapping may be set only once */
270         if (domain->controllers != NULL) {
271                 rte_spinlock_unlock(&sfc_mae_switch.lock);
272                 return EINVAL;
273         }
274
275         domain->controllers = controllers;
276         domain->nb_controllers = nb_controllers;
277
278         rte_spinlock_unlock(&sfc_mae_switch.lock);
279         return 0;
280 }
281
282 int
283 sfc_mae_switch_domain_get_controller(uint16_t switch_domain_id,
284                                      efx_pcie_interface_t intf,
285                                      int *controller)
286 {
287         const efx_pcie_interface_t *controllers;
288         size_t nb_controllers;
289         size_t i;
290         int rc;
291
292         rc = sfc_mae_switch_domain_controllers(switch_domain_id, &controllers,
293                                                &nb_controllers);
294         if (rc != 0)
295                 return rc;
296
297         if (controllers == NULL)
298                 return ENOENT;
299
300         for (i = 0; i < nb_controllers; i++) {
301                 if (controllers[i] == intf) {
302                         *controller = i;
303                         return 0;
304                 }
305         }
306
307         return ENOENT;
308 }
309
310 /* This function expects to be called only when the lock is held */
311 static struct sfc_mae_switch_port *
312 sfc_mae_find_switch_port_by_entity(const struct sfc_mae_switch_domain *domain,
313                                    const efx_mport_sel_t *entity_mportp,
314                                    enum sfc_mae_switch_port_type type)
315 {
316         struct sfc_mae_switch_port *port;
317
318         SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
319
320         TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
321                 if (port->entity_mport.sel == entity_mportp->sel &&
322                     port->type == type)
323                         return port;
324         }
325
326         return NULL;
327 }
328
329 int
330 sfc_mae_assign_switch_port(uint16_t switch_domain_id,
331                            const struct sfc_mae_switch_port_request *req,
332                            uint16_t *switch_port_id)
333 {
334         struct sfc_mae_switch_domain *domain;
335         struct sfc_mae_switch_port *port;
336         int rc;
337
338         rte_spinlock_lock(&sfc_mae_switch.lock);
339
340         domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
341         if (domain == NULL) {
342                 rc = EINVAL;
343                 goto fail_find_switch_domain_by_id;
344         }
345
346         port = sfc_mae_find_switch_port_by_entity(domain, req->entity_mportp,
347                                                   req->type);
348         if (port != NULL)
349                 goto done;
350
351         port = rte_zmalloc("sfc_mae_switch_port", sizeof(*port), 0);
352         if (port == NULL) {
353                 rc = ENOMEM;
354                 goto fail_mem_alloc;
355         }
356
357         port->entity_mport.sel = req->entity_mportp->sel;
358         port->type = req->type;
359
360         port->id = (domain->nb_ports++);
361
362         TAILQ_INSERT_TAIL(&domain->ports, port, switch_domain_ports);
363
364 done:
365         port->ethdev_mport = *req->ethdev_mportp;
366         port->ethdev_port_id = req->ethdev_port_id;
367
368         switch (req->type) {
369         case SFC_MAE_SWITCH_PORT_INDEPENDENT:
370                 /* No data */
371                 break;
372         case SFC_MAE_SWITCH_PORT_REPRESENTOR:
373                 memcpy(&port->data.repr, &req->port_data,
374                        sizeof(port->data.repr));
375                 break;
376         default:
377                 SFC_ASSERT(B_FALSE);
378         }
379
380         *switch_port_id = port->id;
381
382         rte_spinlock_unlock(&sfc_mae_switch.lock);
383
384         return 0;
385
386 fail_mem_alloc:
387 fail_find_switch_domain_by_id:
388         rte_spinlock_unlock(&sfc_mae_switch.lock);
389         return rc;
390 }
391
392 /* This function expects to be called only when the lock is held */
393 static int
394 sfc_mae_find_switch_port_by_ethdev(uint16_t switch_domain_id,
395                                    uint16_t ethdev_port_id,
396                                    efx_mport_sel_t *mport_sel)
397 {
398         struct sfc_mae_switch_domain *domain;
399         struct sfc_mae_switch_port *port;
400
401         SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
402
403         if (ethdev_port_id == RTE_MAX_ETHPORTS)
404                 return EINVAL;
405
406         domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
407         if (domain == NULL)
408                 return EINVAL;
409
410         TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
411                 if (port->ethdev_port_id == ethdev_port_id) {
412                         *mport_sel = port->ethdev_mport;
413                         return 0;
414                 }
415         }
416
417         return ENOENT;
418 }
419
420 int
421 sfc_mae_switch_port_by_ethdev(uint16_t switch_domain_id,
422                               uint16_t ethdev_port_id,
423                               efx_mport_sel_t *mport_sel)
424 {
425         int rc;
426
427         rte_spinlock_lock(&sfc_mae_switch.lock);
428         rc = sfc_mae_find_switch_port_by_ethdev(switch_domain_id,
429                                                 ethdev_port_id, mport_sel);
430         rte_spinlock_unlock(&sfc_mae_switch.lock);
431
432         return rc;
433 }