xref: /dpdk/drivers/net/sfc/sfc_switch.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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 	/** MAE admin port */
97 	struct sfc_mae_switch_port		*mae_admin_port;
98 };
99 
100 TAILQ_HEAD(sfc_mae_switch_domains, sfc_mae_switch_domain);
101 
102 /**
103  * MAE representation of RTE switch infrastructure.
104  *
105  * It is possible that an RTE flow API client tries to insert a rule
106  * referencing an RTE ethdev deployed on top of a different physical
107  * device (it may belong to the same vendor or not). This particular
108  * driver/engine cannot support this and has to turn down such rules.
109  *
110  * Technically, it's HW switch identifier which, if queried for each
111  * RTE ethdev instance, indicates relationship between the instances.
112  * In the meantime, RTE flow API clients also need to somehow figure
113  * out relationship between RTE ethdev instances in advance.
114  *
115  * The concept of RTE switch domains resolves this issue. The driver
116  * maintains a static list of switch domains which is easy to browse,
117  * and each RTE ethdev fills RTE switch parameters in device
118  * information structure which is made available to clients.
119  *
120  * Even if all RTE ethdev instances belonging to a switch domain get
121  * unplugged, the corresponding entry in the switch domain registry
122  * will not be removed because the corresponding HW switch exists
123  * regardless of its ports being plugged to DPDK or kept aside.
124  * If a port gets plugged back to DPDK, the corresponding
125  * RTE ethdev will indicate the same RTE switch domain ID.
126  */
127 struct sfc_mae_switch {
128 	/** A lock to protect the whole structure */
129 	rte_spinlock_t			lock;
130 	/** Switch domain registry */
131 	struct sfc_mae_switch_domains	domains;
132 };
133 
134 static struct sfc_mae_switch sfc_mae_switch = {
135 	.lock = RTE_SPINLOCK_INITIALIZER,
136 	.domains = TAILQ_HEAD_INITIALIZER(sfc_mae_switch.domains),
137 };
138 
139 
140 /* This function expects to be called only when the lock is held */
141 static struct sfc_mae_switch_domain *
142 sfc_mae_find_switch_domain_by_id(uint16_t switch_domain_id)
143 {
144 	struct sfc_mae_switch_domain *domain;
145 
146 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
147 
148 	TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
149 		if (domain->id == switch_domain_id)
150 			return domain;
151 	}
152 
153 	return NULL;
154 }
155 
156 int
157 sfc_mae_switch_ports_iterate(uint16_t switch_domain_id,
158 			     sfc_mae_switch_port_iterator_cb *cb,
159 			     void *data)
160 {
161 	struct sfc_mae_switch_domain *domain;
162 	struct sfc_mae_switch_port *port;
163 
164 	if (cb == NULL)
165 		return EINVAL;
166 
167 	rte_spinlock_lock(&sfc_mae_switch.lock);
168 
169 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
170 	if (domain == NULL) {
171 		rte_spinlock_unlock(&sfc_mae_switch.lock);
172 		return EINVAL;
173 	}
174 
175 	TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
176 		cb(port->type, &port->ethdev_mport, port->ethdev_port_id,
177 		   &port->entity_mport, port->id, &port->data, data);
178 	}
179 
180 	rte_spinlock_unlock(&sfc_mae_switch.lock);
181 	return 0;
182 }
183 
184 /* This function expects to be called only when the lock is held */
185 static struct sfc_mae_switch_domain *
186 sfc_mae_find_switch_domain_by_hw_switch_id(const struct sfc_hw_switch_id *id)
187 {
188 	struct sfc_mae_switch_domain *domain;
189 
190 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
191 
192 	TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
193 		if (sfc_hw_switch_ids_equal(domain->hw_switch_id, id))
194 			return domain;
195 	}
196 
197 	return NULL;
198 }
199 
200 int
201 sfc_mae_assign_switch_domain(struct sfc_adapter *sa,
202 			     uint16_t *switch_domain_id)
203 {
204 	struct sfc_hw_switch_id *hw_switch_id;
205 	struct sfc_mae_switch_domain *domain;
206 	int rc;
207 
208 	rte_spinlock_lock(&sfc_mae_switch.lock);
209 
210 	rc = sfc_hw_switch_id_init(sa, &hw_switch_id);
211 	if (rc != 0)
212 		goto fail_hw_switch_id_init;
213 
214 	domain = sfc_mae_find_switch_domain_by_hw_switch_id(hw_switch_id);
215 	if (domain != NULL) {
216 		sfc_hw_switch_id_fini(sa, hw_switch_id);
217 		goto done;
218 	}
219 
220 	domain = rte_zmalloc("sfc_mae_switch_domain", sizeof(*domain), 0);
221 	if (domain == NULL) {
222 		rc = ENOMEM;
223 		goto fail_mem_alloc;
224 	}
225 
226 	/*
227 	 * This code belongs to driver init path, that is, negation is
228 	 * done at the end of the path by sfc_eth_dev_init(). RTE APIs
229 	 * negate error codes, so drop negation here.
230 	 */
231 	rc = -rte_eth_switch_domain_alloc(&domain->id);
232 	if (rc != 0)
233 		goto fail_domain_alloc;
234 
235 	domain->hw_switch_id = hw_switch_id;
236 
237 	TAILQ_INIT(&domain->ports);
238 
239 	TAILQ_INSERT_TAIL(&sfc_mae_switch.domains, domain, entries);
240 
241 done:
242 	*switch_domain_id = domain->id;
243 
244 	rte_spinlock_unlock(&sfc_mae_switch.lock);
245 
246 	return 0;
247 
248 fail_domain_alloc:
249 	rte_free(domain);
250 
251 fail_mem_alloc:
252 	sfc_hw_switch_id_fini(sa, hw_switch_id);
253 
254 fail_hw_switch_id_init:
255 	rte_spinlock_unlock(&sfc_mae_switch.lock);
256 	return rc;
257 }
258 
259 int
260 sfc_mae_switch_domain_controllers(uint16_t switch_domain_id,
261 				  const efx_pcie_interface_t **controllers,
262 				  size_t *nb_controllers)
263 {
264 	struct sfc_mae_switch_domain *domain;
265 
266 	if (controllers == NULL || nb_controllers == NULL)
267 		return EINVAL;
268 
269 	rte_spinlock_lock(&sfc_mae_switch.lock);
270 
271 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
272 	if (domain == NULL) {
273 		rte_spinlock_unlock(&sfc_mae_switch.lock);
274 		return EINVAL;
275 	}
276 
277 	*controllers = domain->controllers;
278 	*nb_controllers = domain->nb_controllers;
279 
280 	rte_spinlock_unlock(&sfc_mae_switch.lock);
281 	return 0;
282 }
283 
284 int
285 sfc_mae_switch_domain_map_controllers(uint16_t switch_domain_id,
286 				      efx_pcie_interface_t *controllers,
287 				      size_t nb_controllers)
288 {
289 	struct sfc_mae_switch_domain *domain;
290 
291 	rte_spinlock_lock(&sfc_mae_switch.lock);
292 
293 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
294 	if (domain == NULL) {
295 		rte_spinlock_unlock(&sfc_mae_switch.lock);
296 		return EINVAL;
297 	}
298 
299 	/* Controller mapping may be set only once */
300 	if (domain->controllers != NULL) {
301 		rte_spinlock_unlock(&sfc_mae_switch.lock);
302 		return EINVAL;
303 	}
304 
305 	domain->controllers = controllers;
306 	domain->nb_controllers = nb_controllers;
307 
308 	rte_spinlock_unlock(&sfc_mae_switch.lock);
309 	return 0;
310 }
311 
312 int
313 sfc_mae_switch_controller_from_mapping(const efx_pcie_interface_t *controllers,
314 				       size_t nb_controllers,
315 				       efx_pcie_interface_t intf,
316 				       int *controller)
317 {
318 	size_t i;
319 
320 	if (controllers == NULL)
321 		return ENOENT;
322 
323 	for (i = 0; i < nb_controllers; i++) {
324 		if (controllers[i] == intf) {
325 			*controller = i;
326 			return 0;
327 		}
328 	}
329 
330 	return ENOENT;
331 }
332 
333 int
334 sfc_mae_switch_domain_get_controller(uint16_t switch_domain_id,
335 				     efx_pcie_interface_t intf,
336 				     int *controller)
337 {
338 	const efx_pcie_interface_t *controllers;
339 	size_t nb_controllers;
340 	int rc;
341 
342 	rc = sfc_mae_switch_domain_controllers(switch_domain_id, &controllers,
343 					       &nb_controllers);
344 	if (rc != 0)
345 		return rc;
346 
347 	return sfc_mae_switch_controller_from_mapping(controllers,
348 						      nb_controllers,
349 						      intf,
350 						      controller);
351 }
352 
353 int sfc_mae_switch_domain_get_intf(uint16_t switch_domain_id,
354 				   int controller,
355 				   efx_pcie_interface_t *intf)
356 {
357 	const efx_pcie_interface_t *controllers;
358 	size_t nb_controllers;
359 	int rc;
360 
361 	rc = sfc_mae_switch_domain_controllers(switch_domain_id, &controllers,
362 					       &nb_controllers);
363 	if (rc != 0)
364 		return rc;
365 
366 	if (controllers == NULL)
367 		return ENOENT;
368 
369 	if ((size_t)controller > nb_controllers)
370 		return EINVAL;
371 
372 	*intf = controllers[controller];
373 
374 	return 0;
375 }
376 
377 /* This function expects to be called only when the lock is held */
378 static struct sfc_mae_switch_port *
379 sfc_mae_find_switch_port_by_entity(const struct sfc_mae_switch_domain *domain,
380 				   const efx_mport_sel_t *entity_mportp,
381 				   enum sfc_mae_switch_port_type type)
382 {
383 	struct sfc_mae_switch_port *port;
384 
385 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
386 
387 	TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
388 		if (port->entity_mport.sel == entity_mportp->sel &&
389 		    port->type == type)
390 			return port;
391 	}
392 
393 	return NULL;
394 }
395 
396 /* This function expects to be called only when the lock is held */
397 static int
398 sfc_mae_find_switch_port_id_by_entity(uint16_t switch_domain_id,
399 				      const efx_mport_sel_t *entity_mportp,
400 				      enum sfc_mae_switch_port_type type,
401 				      uint16_t *switch_port_id)
402 {
403 	struct sfc_mae_switch_domain *domain;
404 	struct sfc_mae_switch_port *port;
405 
406 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
407 
408 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
409 	if (domain == NULL)
410 		return EINVAL;
411 
412 	port = sfc_mae_find_switch_port_by_entity(domain, entity_mportp, type);
413 	if (port == NULL)
414 		return ENOENT;
415 
416 	*switch_port_id = port->id;
417 	return 0;
418 }
419 
420 int
421 sfc_mae_assign_switch_port(uint16_t switch_domain_id,
422 			   const struct sfc_mae_switch_port_request *req,
423 			   uint16_t *switch_port_id)
424 {
425 	struct sfc_mae_switch_domain *domain;
426 	struct sfc_mae_switch_port *port;
427 	int rc;
428 
429 	rte_spinlock_lock(&sfc_mae_switch.lock);
430 
431 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
432 	if (domain == NULL) {
433 		rc = EINVAL;
434 		goto fail_find_switch_domain_by_id;
435 	}
436 
437 	port = sfc_mae_find_switch_port_by_entity(domain, req->entity_mportp,
438 						  req->type);
439 	if (port != NULL)
440 		goto done;
441 
442 	port = rte_zmalloc("sfc_mae_switch_port", sizeof(*port), 0);
443 	if (port == NULL) {
444 		rc = ENOMEM;
445 		goto fail_mem_alloc;
446 	}
447 
448 	port->entity_mport.sel = req->entity_mportp->sel;
449 	port->type = req->type;
450 
451 	port->id = (domain->nb_ports++);
452 
453 	TAILQ_INSERT_TAIL(&domain->ports, port, switch_domain_ports);
454 
455 done:
456 	port->ethdev_mport = *req->ethdev_mportp;
457 	port->ethdev_port_id = req->ethdev_port_id;
458 
459 	memcpy(&port->data, &req->port_data,
460 	       sizeof(port->data));
461 
462 	switch (req->type) {
463 	case SFC_MAE_SWITCH_PORT_INDEPENDENT:
464 		if (port->data.indep.mae_admin) {
465 			SFC_ASSERT(domain->mae_admin_port == NULL);
466 			domain->mae_admin_port = port;
467 		}
468 		break;
469 	case SFC_MAE_SWITCH_PORT_REPRESENTOR:
470 		break;
471 	default:
472 		SFC_ASSERT(B_FALSE);
473 	}
474 
475 	*switch_port_id = port->id;
476 
477 	rte_spinlock_unlock(&sfc_mae_switch.lock);
478 
479 	return 0;
480 
481 fail_mem_alloc:
482 fail_find_switch_domain_by_id:
483 	rte_spinlock_unlock(&sfc_mae_switch.lock);
484 	return rc;
485 }
486 
487 int
488 sfc_mae_clear_switch_port(uint16_t switch_domain_id,
489 			  uint16_t switch_port_id)
490 {
491 	struct sfc_mae_switch_domain *domain;
492 
493 	rte_spinlock_lock(&sfc_mae_switch.lock);
494 
495 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
496 	if (domain == NULL) {
497 		rte_spinlock_unlock(&sfc_mae_switch.lock);
498 		return EINVAL;
499 	}
500 
501 	if (domain->mae_admin_port != NULL &&
502 	    domain->mae_admin_port->id == switch_port_id) {
503 		domain->mae_admin_port->data.indep.mae_admin = B_FALSE;
504 		domain->mae_admin_port = NULL;
505 	}
506 
507 	rte_spinlock_unlock(&sfc_mae_switch.lock);
508 	return 0;
509 }
510 
511 /* This function expects to be called only when the lock is held */
512 static int
513 sfc_mae_find_switch_port_by_ethdev(uint16_t switch_domain_id,
514 				   uint16_t ethdev_port_id,
515 				   efx_mport_sel_t *mport_sel)
516 {
517 	struct sfc_mae_switch_domain *domain;
518 	struct sfc_mae_switch_port *port;
519 
520 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
521 
522 	if (ethdev_port_id == RTE_MAX_ETHPORTS)
523 		return EINVAL;
524 
525 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
526 	if (domain == NULL)
527 		return EINVAL;
528 
529 	TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
530 		if (port->ethdev_port_id == ethdev_port_id) {
531 			*mport_sel = port->ethdev_mport;
532 			return 0;
533 		}
534 	}
535 
536 	return ENOENT;
537 }
538 
539 int
540 sfc_mae_switch_port_by_ethdev(uint16_t switch_domain_id,
541 			      uint16_t ethdev_port_id,
542 			      efx_mport_sel_t *mport_sel)
543 {
544 	int rc;
545 
546 	rte_spinlock_lock(&sfc_mae_switch.lock);
547 	rc = sfc_mae_find_switch_port_by_ethdev(switch_domain_id,
548 						ethdev_port_id, mport_sel);
549 	rte_spinlock_unlock(&sfc_mae_switch.lock);
550 
551 	return rc;
552 }
553 
554 int
555 sfc_mae_switch_port_id_by_entity(uint16_t switch_domain_id,
556 				 const efx_mport_sel_t *entity_mportp,
557 				 enum sfc_mae_switch_port_type type,
558 				 uint16_t *switch_port_id)
559 {
560 	int rc;
561 
562 	rte_spinlock_lock(&sfc_mae_switch.lock);
563 	rc = sfc_mae_find_switch_port_id_by_entity(switch_domain_id,
564 						   entity_mportp, type,
565 						   switch_port_id);
566 	rte_spinlock_unlock(&sfc_mae_switch.lock);
567 
568 	return rc;
569 }
570 
571 static int
572 sfc_mae_get_switch_domain_admin_locked(uint16_t switch_domain_id,
573 				       uint16_t *port_id)
574 {
575 	struct sfc_mae_switch_domain *domain;
576 
577 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
578 
579 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
580 	if (domain == NULL)
581 		return EINVAL;
582 
583 	if (domain->mae_admin_port != NULL) {
584 		*port_id = domain->mae_admin_port->ethdev_port_id;
585 		return 0;
586 	}
587 
588 	return ENOENT;
589 }
590 
591 int
592 sfc_mae_get_switch_domain_admin(uint16_t switch_domain_id,
593 				uint16_t *port_id)
594 {
595 	int rc;
596 
597 	rte_spinlock_lock(&sfc_mae_switch.lock);
598 	rc = sfc_mae_get_switch_domain_admin_locked(switch_domain_id, port_id);
599 	rte_spinlock_unlock(&sfc_mae_switch.lock);
600 	return rc;
601 }
602