xref: /dpdk/drivers/net/sfc/sfc_switch.c (revision 1fb65e4dae8a5af151bcf8d5bb894a0d3d2c8e52)
11e7fbdf0SIvan Malov /* SPDX-License-Identifier: BSD-3-Clause
21e7fbdf0SIvan Malov  *
31e7fbdf0SIvan Malov  * Copyright(c) 2019-2020 Xilinx, Inc.
41e7fbdf0SIvan Malov  * Copyright(c) 2019 Solarflare Communications Inc.
51e7fbdf0SIvan Malov  *
61e7fbdf0SIvan Malov  * This software was jointly developed between OKTET Labs (under contract
71e7fbdf0SIvan Malov  * for Solarflare) and Solarflare Communications, Inc.
81e7fbdf0SIvan Malov  */
91e7fbdf0SIvan Malov 
101e7fbdf0SIvan Malov #include <stdbool.h>
111e7fbdf0SIvan Malov 
121e7fbdf0SIvan Malov #include <rte_common.h>
131e7fbdf0SIvan Malov #include <rte_spinlock.h>
141e7fbdf0SIvan Malov 
151e7fbdf0SIvan Malov #include "efx.h"
161e7fbdf0SIvan Malov 
171e7fbdf0SIvan Malov #include "sfc.h"
181e7fbdf0SIvan Malov #include "sfc_log.h"
191e7fbdf0SIvan Malov #include "sfc_switch.h"
201e7fbdf0SIvan Malov 
211e7fbdf0SIvan Malov /**
221e7fbdf0SIvan Malov  * Switch port registry entry.
231e7fbdf0SIvan Malov  *
241e7fbdf0SIvan Malov  * Drivers aware of RTE switch domains also have to maintain RTE switch
251e7fbdf0SIvan Malov  * port IDs for RTE ethdev instances they operate. These IDs are supposed
261e7fbdf0SIvan Malov  * to stand for physical interconnect entities, in example, PCIe functions.
271e7fbdf0SIvan Malov  *
281e7fbdf0SIvan Malov  * In terms of MAE, a physical interconnect entity can be referred to using
291e7fbdf0SIvan Malov  * an MPORT selector, that is, a 32-bit value. RTE switch port IDs, in turn,
301e7fbdf0SIvan Malov  * are 16-bit values, so indirect mapping has to be maintained:
311e7fbdf0SIvan Malov  *
321e7fbdf0SIvan Malov  * +--------------------+          +---------------------------------------+
331e7fbdf0SIvan Malov  * | RTE switch port ID |  ------  |         MAE switch port entry         |
341e7fbdf0SIvan Malov  * +--------------------+          |         ---------------------         |
351e7fbdf0SIvan Malov  *                                 |                                       |
361e7fbdf0SIvan Malov  *                                 | Entity (PCIe function) MPORT selector |
371e7fbdf0SIvan Malov  *                                 |                   +                   |
381e7fbdf0SIvan Malov  *                                 |  Port type (independent/representor)  |
391e7fbdf0SIvan Malov  *                                 +---------------------------------------+
401e7fbdf0SIvan Malov  *
411e7fbdf0SIvan Malov  * This mapping comprises a port type to ensure that RTE switch port ID
421e7fbdf0SIvan Malov  * of a represented entity and that of its representor are different in
431e7fbdf0SIvan Malov  * the case when the entity gets plugged into DPDK and not into a guest.
44*1fb65e4dSIvan Malov  *
45*1fb65e4dSIvan Malov  * Entry data also comprises RTE ethdev's own MPORT. This value
46*1fb65e4dSIvan Malov  * coincides with the entity MPORT in the case of independent ports.
47*1fb65e4dSIvan Malov  * In the case of representors, this ID is not a selector and refers
48*1fb65e4dSIvan Malov  * to an allocatable object (that is, it's likely to change on RTE
49*1fb65e4dSIvan Malov  * ethdev replug). Flow API backend must use this value rather
50*1fb65e4dSIvan Malov  * than entity_mport to support flow rule action PORT_ID.
511e7fbdf0SIvan Malov  */
521e7fbdf0SIvan Malov struct sfc_mae_switch_port {
531e7fbdf0SIvan Malov 	TAILQ_ENTRY(sfc_mae_switch_port)	switch_domain_ports;
541e7fbdf0SIvan Malov 
55*1fb65e4dSIvan Malov 	/** RTE ethdev MPORT */
56*1fb65e4dSIvan Malov 	efx_mport_sel_t				ethdev_mport;
57*1fb65e4dSIvan Malov 	/** RTE ethdev port ID */
58*1fb65e4dSIvan Malov 	uint16_t				ethdev_port_id;
59*1fb65e4dSIvan Malov 
601e7fbdf0SIvan Malov 	/** Entity (PCIe function) MPORT selector */
611e7fbdf0SIvan Malov 	efx_mport_sel_t				entity_mport;
621e7fbdf0SIvan Malov 	/** Port type (independent/representor) */
631e7fbdf0SIvan Malov 	enum sfc_mae_switch_port_type		type;
641e7fbdf0SIvan Malov 	/** RTE switch port ID */
651e7fbdf0SIvan Malov 	uint16_t				id;
661e7fbdf0SIvan Malov };
671e7fbdf0SIvan Malov 
681e7fbdf0SIvan Malov TAILQ_HEAD(sfc_mae_switch_ports, sfc_mae_switch_port);
691e7fbdf0SIvan Malov 
701e7fbdf0SIvan Malov /**
711e7fbdf0SIvan Malov  * Switch domain registry entry.
721e7fbdf0SIvan Malov  *
731e7fbdf0SIvan Malov  * Even if an RTE ethdev instance gets unplugged, the corresponding
741e7fbdf0SIvan Malov  * entry in the switch port registry will not be removed because the
751e7fbdf0SIvan Malov  * entity (PCIe function) MPORT is static and cannot change. If this
761e7fbdf0SIvan Malov  * RTE ethdev gets plugged back, the entry will be reused, and
771e7fbdf0SIvan Malov  * RTE switch port ID will be the same.
781e7fbdf0SIvan Malov  */
791e7fbdf0SIvan Malov struct sfc_mae_switch_domain {
801e7fbdf0SIvan Malov 	TAILQ_ENTRY(sfc_mae_switch_domain)	entries;
811e7fbdf0SIvan Malov 
821e7fbdf0SIvan Malov 	/** HW switch ID */
831e7fbdf0SIvan Malov 	struct sfc_hw_switch_id			*hw_switch_id;
841e7fbdf0SIvan Malov 	/** The number of ports in the switch port registry */
851e7fbdf0SIvan Malov 	unsigned int				nb_ports;
861e7fbdf0SIvan Malov 	/** Switch port registry */
871e7fbdf0SIvan Malov 	struct sfc_mae_switch_ports		ports;
881e7fbdf0SIvan Malov 	/** RTE switch domain ID allocated for a group of devices */
891e7fbdf0SIvan Malov 	uint16_t				id;
901e7fbdf0SIvan Malov };
911e7fbdf0SIvan Malov 
921e7fbdf0SIvan Malov TAILQ_HEAD(sfc_mae_switch_domains, sfc_mae_switch_domain);
931e7fbdf0SIvan Malov 
941e7fbdf0SIvan Malov /**
951e7fbdf0SIvan Malov  * MAE representation of RTE switch infrastructure.
961e7fbdf0SIvan Malov  *
971e7fbdf0SIvan Malov  * It is possible that an RTE flow API client tries to insert a rule
981e7fbdf0SIvan Malov  * referencing an RTE ethdev deployed on top of a different physical
991e7fbdf0SIvan Malov  * device (it may belong to the same vendor or not). This particular
1001e7fbdf0SIvan Malov  * driver/engine cannot support this and has to turn down such rules.
1011e7fbdf0SIvan Malov  *
1021e7fbdf0SIvan Malov  * Technically, it's HW switch identifier which, if queried for each
1031e7fbdf0SIvan Malov  * RTE ethdev instance, indicates relationship between the instances.
1041e7fbdf0SIvan Malov  * In the meantime, RTE flow API clients also need to somehow figure
1051e7fbdf0SIvan Malov  * out relationship between RTE ethdev instances in advance.
1061e7fbdf0SIvan Malov  *
1071e7fbdf0SIvan Malov  * The concept of RTE switch domains resolves this issue. The driver
1081e7fbdf0SIvan Malov  * maintains a static list of switch domains which is easy to browse,
1091e7fbdf0SIvan Malov  * and each RTE ethdev fills RTE switch parameters in device
1101e7fbdf0SIvan Malov  * information structure which is made available to clients.
1111e7fbdf0SIvan Malov  *
1121e7fbdf0SIvan Malov  * Even if all RTE ethdev instances belonging to a switch domain get
1131e7fbdf0SIvan Malov  * unplugged, the corresponding entry in the switch domain registry
1141e7fbdf0SIvan Malov  * will not be removed because the corresponding HW switch exists
1151e7fbdf0SIvan Malov  * regardless of its ports being plugged to DPDK or kept aside.
1161e7fbdf0SIvan Malov  * If a port gets plugged back to DPDK, the corresponding
1171e7fbdf0SIvan Malov  * RTE ethdev will indicate the same RTE switch domain ID.
1181e7fbdf0SIvan Malov  */
1191e7fbdf0SIvan Malov struct sfc_mae_switch {
1201e7fbdf0SIvan Malov 	/** A lock to protect the whole structure */
1211e7fbdf0SIvan Malov 	rte_spinlock_t			lock;
1221e7fbdf0SIvan Malov 	/** Switch domain registry */
1231e7fbdf0SIvan Malov 	struct sfc_mae_switch_domains	domains;
1241e7fbdf0SIvan Malov };
1251e7fbdf0SIvan Malov 
1261e7fbdf0SIvan Malov static struct sfc_mae_switch sfc_mae_switch = {
1271e7fbdf0SIvan Malov 	.lock = RTE_SPINLOCK_INITIALIZER,
1281e7fbdf0SIvan Malov 	.domains = TAILQ_HEAD_INITIALIZER(sfc_mae_switch.domains),
1291e7fbdf0SIvan Malov };
1301e7fbdf0SIvan Malov 
1311e7fbdf0SIvan Malov 
1321e7fbdf0SIvan Malov /* This function expects to be called only when the lock is held */
1331e7fbdf0SIvan Malov static struct sfc_mae_switch_domain *
1341e7fbdf0SIvan Malov sfc_mae_find_switch_domain_by_id(uint16_t switch_domain_id)
1351e7fbdf0SIvan Malov {
1361e7fbdf0SIvan Malov 	struct sfc_mae_switch_domain *domain;
1371e7fbdf0SIvan Malov 
1381e7fbdf0SIvan Malov 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
1391e7fbdf0SIvan Malov 
1401e7fbdf0SIvan Malov 	TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
1411e7fbdf0SIvan Malov 		if (domain->id == switch_domain_id)
1421e7fbdf0SIvan Malov 			return domain;
1431e7fbdf0SIvan Malov 	}
1441e7fbdf0SIvan Malov 
1451e7fbdf0SIvan Malov 	return NULL;
1461e7fbdf0SIvan Malov }
1471e7fbdf0SIvan Malov 
1481e7fbdf0SIvan Malov /* This function expects to be called only when the lock is held */
1491e7fbdf0SIvan Malov static struct sfc_mae_switch_domain *
1501e7fbdf0SIvan Malov sfc_mae_find_switch_domain_by_hw_switch_id(const struct sfc_hw_switch_id *id)
1511e7fbdf0SIvan Malov {
1521e7fbdf0SIvan Malov 	struct sfc_mae_switch_domain *domain;
1531e7fbdf0SIvan Malov 
1541e7fbdf0SIvan Malov 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
1551e7fbdf0SIvan Malov 
1561e7fbdf0SIvan Malov 	TAILQ_FOREACH(domain, &sfc_mae_switch.domains, entries) {
1571e7fbdf0SIvan Malov 		if (sfc_hw_switch_ids_equal(domain->hw_switch_id, id))
1581e7fbdf0SIvan Malov 			return domain;
1591e7fbdf0SIvan Malov 	}
1601e7fbdf0SIvan Malov 
1611e7fbdf0SIvan Malov 	return NULL;
1621e7fbdf0SIvan Malov }
1631e7fbdf0SIvan Malov 
1641e7fbdf0SIvan Malov int
1651e7fbdf0SIvan Malov sfc_mae_assign_switch_domain(struct sfc_adapter *sa,
1661e7fbdf0SIvan Malov 			     uint16_t *switch_domain_id)
1671e7fbdf0SIvan Malov {
1681e7fbdf0SIvan Malov 	struct sfc_hw_switch_id *hw_switch_id;
1691e7fbdf0SIvan Malov 	struct sfc_mae_switch_domain *domain;
1701e7fbdf0SIvan Malov 	int rc;
1711e7fbdf0SIvan Malov 
1721e7fbdf0SIvan Malov 	rte_spinlock_lock(&sfc_mae_switch.lock);
1731e7fbdf0SIvan Malov 
1741e7fbdf0SIvan Malov 	rc = sfc_hw_switch_id_init(sa, &hw_switch_id);
1751e7fbdf0SIvan Malov 	if (rc != 0)
1761e7fbdf0SIvan Malov 		goto fail_hw_switch_id_init;
1771e7fbdf0SIvan Malov 
1781e7fbdf0SIvan Malov 	domain = sfc_mae_find_switch_domain_by_hw_switch_id(hw_switch_id);
1791e7fbdf0SIvan Malov 	if (domain != NULL) {
1801e7fbdf0SIvan Malov 		sfc_hw_switch_id_fini(sa, hw_switch_id);
1811e7fbdf0SIvan Malov 		goto done;
1821e7fbdf0SIvan Malov 	}
1831e7fbdf0SIvan Malov 
1841e7fbdf0SIvan Malov 	domain = rte_zmalloc("sfc_mae_switch_domain", sizeof(*domain), 0);
1851e7fbdf0SIvan Malov 	if (domain == NULL) {
1861e7fbdf0SIvan Malov 		rc = ENOMEM;
1871e7fbdf0SIvan Malov 		goto fail_mem_alloc;
1881e7fbdf0SIvan Malov 	}
1891e7fbdf0SIvan Malov 
1901e7fbdf0SIvan Malov 	/*
1911e7fbdf0SIvan Malov 	 * This code belongs to driver init path, that is, negation is
1921e7fbdf0SIvan Malov 	 * done at the end of the path by sfc_eth_dev_init(). RTE APIs
1931e7fbdf0SIvan Malov 	 * negate error codes, so drop negation here.
1941e7fbdf0SIvan Malov 	 */
1951e7fbdf0SIvan Malov 	rc = -rte_eth_switch_domain_alloc(&domain->id);
1961e7fbdf0SIvan Malov 	if (rc != 0)
1971e7fbdf0SIvan Malov 		goto fail_domain_alloc;
1981e7fbdf0SIvan Malov 
1991e7fbdf0SIvan Malov 	domain->hw_switch_id = hw_switch_id;
2001e7fbdf0SIvan Malov 
2011e7fbdf0SIvan Malov 	TAILQ_INIT(&domain->ports);
2021e7fbdf0SIvan Malov 
2031e7fbdf0SIvan Malov 	TAILQ_INSERT_TAIL(&sfc_mae_switch.domains, domain, entries);
2041e7fbdf0SIvan Malov 
2051e7fbdf0SIvan Malov done:
2061e7fbdf0SIvan Malov 	*switch_domain_id = domain->id;
2071e7fbdf0SIvan Malov 
2081e7fbdf0SIvan Malov 	rte_spinlock_unlock(&sfc_mae_switch.lock);
2091e7fbdf0SIvan Malov 
2101e7fbdf0SIvan Malov 	return 0;
2111e7fbdf0SIvan Malov 
2121e7fbdf0SIvan Malov fail_domain_alloc:
2131e7fbdf0SIvan Malov 	rte_free(domain);
2141e7fbdf0SIvan Malov 
2151e7fbdf0SIvan Malov fail_mem_alloc:
2161e7fbdf0SIvan Malov 	sfc_hw_switch_id_fini(sa, hw_switch_id);
2171e7fbdf0SIvan Malov 	rte_spinlock_unlock(&sfc_mae_switch.lock);
2181e7fbdf0SIvan Malov 
2191e7fbdf0SIvan Malov fail_hw_switch_id_init:
2201e7fbdf0SIvan Malov 	return rc;
2211e7fbdf0SIvan Malov }
2221e7fbdf0SIvan Malov 
2231e7fbdf0SIvan Malov /* This function expects to be called only when the lock is held */
2241e7fbdf0SIvan Malov static struct sfc_mae_switch_port *
2251e7fbdf0SIvan Malov sfc_mae_find_switch_port_by_entity(const struct sfc_mae_switch_domain *domain,
2261e7fbdf0SIvan Malov 				   const efx_mport_sel_t *entity_mportp,
2271e7fbdf0SIvan Malov 				   enum sfc_mae_switch_port_type type)
2281e7fbdf0SIvan Malov {
2291e7fbdf0SIvan Malov 	struct sfc_mae_switch_port *port;
2301e7fbdf0SIvan Malov 
2311e7fbdf0SIvan Malov 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
2321e7fbdf0SIvan Malov 
2331e7fbdf0SIvan Malov 	TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
2341e7fbdf0SIvan Malov 		if (port->entity_mport.sel == entity_mportp->sel &&
2351e7fbdf0SIvan Malov 		    port->type == type)
2361e7fbdf0SIvan Malov 			return port;
2371e7fbdf0SIvan Malov 	}
2381e7fbdf0SIvan Malov 
2391e7fbdf0SIvan Malov 	return NULL;
2401e7fbdf0SIvan Malov }
2411e7fbdf0SIvan Malov 
2421e7fbdf0SIvan Malov int
2431e7fbdf0SIvan Malov sfc_mae_assign_switch_port(uint16_t switch_domain_id,
2441e7fbdf0SIvan Malov 			   const struct sfc_mae_switch_port_request *req,
2451e7fbdf0SIvan Malov 			   uint16_t *switch_port_id)
2461e7fbdf0SIvan Malov {
2471e7fbdf0SIvan Malov 	struct sfc_mae_switch_domain *domain;
2481e7fbdf0SIvan Malov 	struct sfc_mae_switch_port *port;
2491e7fbdf0SIvan Malov 	int rc;
2501e7fbdf0SIvan Malov 
2511e7fbdf0SIvan Malov 	rte_spinlock_lock(&sfc_mae_switch.lock);
2521e7fbdf0SIvan Malov 
2531e7fbdf0SIvan Malov 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
2541e7fbdf0SIvan Malov 	if (domain == NULL) {
2551e7fbdf0SIvan Malov 		rc = EINVAL;
2561e7fbdf0SIvan Malov 		goto fail_find_switch_domain_by_id;
2571e7fbdf0SIvan Malov 	}
2581e7fbdf0SIvan Malov 
2591e7fbdf0SIvan Malov 	port = sfc_mae_find_switch_port_by_entity(domain, req->entity_mportp,
2601e7fbdf0SIvan Malov 						  req->type);
2611e7fbdf0SIvan Malov 	if (port != NULL)
2621e7fbdf0SIvan Malov 		goto done;
2631e7fbdf0SIvan Malov 
2641e7fbdf0SIvan Malov 	port = rte_zmalloc("sfc_mae_switch_port", sizeof(*port), 0);
2651e7fbdf0SIvan Malov 	if (port == NULL) {
2661e7fbdf0SIvan Malov 		rc = ENOMEM;
2671e7fbdf0SIvan Malov 		goto fail_mem_alloc;
2681e7fbdf0SIvan Malov 	}
2691e7fbdf0SIvan Malov 
2701e7fbdf0SIvan Malov 	port->entity_mport.sel = req->entity_mportp->sel;
2711e7fbdf0SIvan Malov 	port->type = req->type;
2721e7fbdf0SIvan Malov 
2731e7fbdf0SIvan Malov 	port->id = (domain->nb_ports++);
2741e7fbdf0SIvan Malov 
2751e7fbdf0SIvan Malov 	TAILQ_INSERT_TAIL(&domain->ports, port, switch_domain_ports);
2761e7fbdf0SIvan Malov 
2771e7fbdf0SIvan Malov done:
278*1fb65e4dSIvan Malov 	port->ethdev_mport = *req->ethdev_mportp;
279*1fb65e4dSIvan Malov 	port->ethdev_port_id = req->ethdev_port_id;
280*1fb65e4dSIvan Malov 
2811e7fbdf0SIvan Malov 	*switch_port_id = port->id;
2821e7fbdf0SIvan Malov 
2831e7fbdf0SIvan Malov 	rte_spinlock_unlock(&sfc_mae_switch.lock);
2841e7fbdf0SIvan Malov 
2851e7fbdf0SIvan Malov 	return 0;
2861e7fbdf0SIvan Malov 
2871e7fbdf0SIvan Malov fail_mem_alloc:
2881e7fbdf0SIvan Malov fail_find_switch_domain_by_id:
2891e7fbdf0SIvan Malov 	rte_spinlock_unlock(&sfc_mae_switch.lock);
2901e7fbdf0SIvan Malov 	return rc;
2911e7fbdf0SIvan Malov }
292*1fb65e4dSIvan Malov 
293*1fb65e4dSIvan Malov /* This function expects to be called only when the lock is held */
294*1fb65e4dSIvan Malov static int
295*1fb65e4dSIvan Malov sfc_mae_find_switch_port_by_ethdev(uint16_t switch_domain_id,
296*1fb65e4dSIvan Malov 				   uint16_t ethdev_port_id,
297*1fb65e4dSIvan Malov 				   efx_mport_sel_t *mport_sel)
298*1fb65e4dSIvan Malov {
299*1fb65e4dSIvan Malov 	struct sfc_mae_switch_domain *domain;
300*1fb65e4dSIvan Malov 	struct sfc_mae_switch_port *port;
301*1fb65e4dSIvan Malov 
302*1fb65e4dSIvan Malov 	SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
303*1fb65e4dSIvan Malov 
304*1fb65e4dSIvan Malov 	if (ethdev_port_id == RTE_MAX_ETHPORTS)
305*1fb65e4dSIvan Malov 		return EINVAL;
306*1fb65e4dSIvan Malov 
307*1fb65e4dSIvan Malov 	domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
308*1fb65e4dSIvan Malov 	if (domain == NULL)
309*1fb65e4dSIvan Malov 		return EINVAL;
310*1fb65e4dSIvan Malov 
311*1fb65e4dSIvan Malov 	TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
312*1fb65e4dSIvan Malov 		if (port->ethdev_port_id == ethdev_port_id) {
313*1fb65e4dSIvan Malov 			*mport_sel = port->ethdev_mport;
314*1fb65e4dSIvan Malov 			return 0;
315*1fb65e4dSIvan Malov 		}
316*1fb65e4dSIvan Malov 	}
317*1fb65e4dSIvan Malov 
318*1fb65e4dSIvan Malov 	return ENOENT;
319*1fb65e4dSIvan Malov }
320*1fb65e4dSIvan Malov 
321*1fb65e4dSIvan Malov int
322*1fb65e4dSIvan Malov sfc_mae_switch_port_by_ethdev(uint16_t switch_domain_id,
323*1fb65e4dSIvan Malov 			      uint16_t ethdev_port_id,
324*1fb65e4dSIvan Malov 			      efx_mport_sel_t *mport_sel)
325*1fb65e4dSIvan Malov {
326*1fb65e4dSIvan Malov 	int rc;
327*1fb65e4dSIvan Malov 
328*1fb65e4dSIvan Malov 	rte_spinlock_lock(&sfc_mae_switch.lock);
329*1fb65e4dSIvan Malov 	rc = sfc_mae_find_switch_port_by_ethdev(switch_domain_id,
330*1fb65e4dSIvan Malov 						ethdev_port_id, mport_sel);
331*1fb65e4dSIvan Malov 	rte_spinlock_unlock(&sfc_mae_switch.lock);
332*1fb65e4dSIvan Malov 
333*1fb65e4dSIvan Malov 	return rc;
334*1fb65e4dSIvan Malov }
335