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 } 434