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