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 *
sfc_mae_find_switch_domain_by_id(uint16_t switch_domain_id)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
sfc_mae_switch_ports_iterate(uint16_t switch_domain_id,sfc_mae_switch_port_iterator_cb * cb,void * data)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 *
sfc_mae_find_switch_domain_by_hw_switch_id(const struct sfc_hw_switch_id * id)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
sfc_mae_assign_switch_domain(struct sfc_adapter * sa,uint16_t * switch_domain_id)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
sfc_mae_switch_domain_controllers(uint16_t switch_domain_id,const efx_pcie_interface_t ** controllers,size_t * nb_controllers)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
sfc_mae_switch_domain_map_controllers(uint16_t switch_domain_id,efx_pcie_interface_t * controllers,size_t nb_controllers)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
sfc_mae_switch_controller_from_mapping(const efx_pcie_interface_t * controllers,size_t nb_controllers,efx_pcie_interface_t intf,int * controller)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
sfc_mae_switch_domain_get_controller(uint16_t switch_domain_id,efx_pcie_interface_t intf,int * controller)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
sfc_mae_switch_domain_get_intf(uint16_t switch_domain_id,int controller,efx_pcie_interface_t * intf)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 *
sfc_mae_find_switch_port_by_entity(const struct sfc_mae_switch_domain * domain,const efx_mport_sel_t * entity_mportp,enum sfc_mae_switch_port_type type)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
sfc_mae_find_switch_port_id_by_entity(uint16_t switch_domain_id,const efx_mport_sel_t * entity_mportp,enum sfc_mae_switch_port_type type,uint16_t * switch_port_id)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
sfc_mae_assign_switch_port(uint16_t switch_domain_id,const struct sfc_mae_switch_port_request * req,uint16_t * switch_port_id)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
sfc_mae_clear_switch_port(uint16_t switch_domain_id,uint16_t switch_port_id)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 struct sfc_mae_switch_port *port;
493
494 rte_spinlock_lock(&sfc_mae_switch.lock);
495
496 domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
497 if (domain == NULL) {
498 rte_spinlock_unlock(&sfc_mae_switch.lock);
499 return EINVAL;
500 }
501
502 if (domain->mae_admin_port != NULL &&
503 domain->mae_admin_port->id == switch_port_id) {
504 domain->mae_admin_port->data.indep.mae_admin = B_FALSE;
505 domain->mae_admin_port = NULL;
506 }
507
508 TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
509 if (port->id == switch_port_id) {
510 /*
511 * Invalidate the field to prevent wrong
512 * look-ups from flow rule handling path.
513 */
514 port->ethdev_port_id = RTE_MAX_ETHPORTS;
515 break;
516 }
517 }
518
519 rte_spinlock_unlock(&sfc_mae_switch.lock);
520 return 0;
521 }
522
523 /* This function expects to be called only when the lock is held */
524 static int
sfc_mae_find_switch_port_by_ethdev(uint16_t switch_domain_id,uint16_t ethdev_port_id,struct sfc_mae_switch_port ** switch_port)525 sfc_mae_find_switch_port_by_ethdev(uint16_t switch_domain_id,
526 uint16_t ethdev_port_id,
527 struct sfc_mae_switch_port **switch_port)
528 {
529 struct sfc_mae_switch_domain *domain;
530 struct sfc_mae_switch_port *port;
531
532 SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
533
534 if (ethdev_port_id == RTE_MAX_ETHPORTS)
535 return EINVAL;
536
537 domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
538 if (domain == NULL)
539 return EINVAL;
540
541 TAILQ_FOREACH(port, &domain->ports, switch_domain_ports) {
542 if (port->ethdev_port_id == ethdev_port_id) {
543 *switch_port = port;
544 return 0;
545 }
546 }
547
548 return ENOENT;
549 }
550
551 int
sfc_mae_switch_get_ethdev_mport(uint16_t switch_domain_id,uint16_t ethdev_port_id,unsigned int allowed_mae_switch_port_types,efx_mport_sel_t * mport_sel)552 sfc_mae_switch_get_ethdev_mport(uint16_t switch_domain_id,
553 uint16_t ethdev_port_id,
554 unsigned int allowed_mae_switch_port_types,
555 efx_mport_sel_t *mport_sel)
556 {
557 struct sfc_mae_switch_port *port;
558 int rc;
559
560 rte_spinlock_lock(&sfc_mae_switch.lock);
561 rc = sfc_mae_find_switch_port_by_ethdev(switch_domain_id,
562 ethdev_port_id, &port);
563 if (rc != 0)
564 goto unlock;
565
566 if (((1U << port->type) & allowed_mae_switch_port_types) == 0) {
567 rc = ENOTSUP;
568 goto unlock;
569 }
570
571 *mport_sel = port->ethdev_mport;
572
573 unlock:
574 rte_spinlock_unlock(&sfc_mae_switch.lock);
575
576 return rc;
577 }
578
579 int
sfc_mae_switch_get_entity_mport(uint16_t switch_domain_id,uint16_t ethdev_port_id,efx_mport_sel_t * mport_sel)580 sfc_mae_switch_get_entity_mport(uint16_t switch_domain_id,
581 uint16_t ethdev_port_id,
582 efx_mport_sel_t *mport_sel)
583 {
584 static struct sfc_mae_switch_port *port;
585 int rc;
586
587 rte_spinlock_lock(&sfc_mae_switch.lock);
588 rc = sfc_mae_find_switch_port_by_ethdev(switch_domain_id,
589 ethdev_port_id, &port);
590 if (rc != 0)
591 goto unlock;
592
593 if (port->type == SFC_MAE_SWITCH_PORT_INDEPENDENT &&
594 !port->data.indep.mae_admin) {
595 /* See sfc_mae_assign_entity_mport() */
596 rc = ENOTSUP;
597 goto unlock;
598 }
599
600 *mport_sel = port->entity_mport;
601
602 unlock:
603 rte_spinlock_unlock(&sfc_mae_switch.lock);
604
605 return rc;
606 }
607
608 int
sfc_mae_switch_port_id_by_entity(uint16_t switch_domain_id,const efx_mport_sel_t * entity_mportp,enum sfc_mae_switch_port_type type,uint16_t * switch_port_id)609 sfc_mae_switch_port_id_by_entity(uint16_t switch_domain_id,
610 const efx_mport_sel_t *entity_mportp,
611 enum sfc_mae_switch_port_type type,
612 uint16_t *switch_port_id)
613 {
614 int rc;
615
616 rte_spinlock_lock(&sfc_mae_switch.lock);
617 rc = sfc_mae_find_switch_port_id_by_entity(switch_domain_id,
618 entity_mportp, type,
619 switch_port_id);
620 rte_spinlock_unlock(&sfc_mae_switch.lock);
621
622 return rc;
623 }
624
625 static int
sfc_mae_get_switch_domain_admin_locked(uint16_t switch_domain_id,uint16_t * port_id)626 sfc_mae_get_switch_domain_admin_locked(uint16_t switch_domain_id,
627 uint16_t *port_id)
628 {
629 struct sfc_mae_switch_domain *domain;
630
631 SFC_ASSERT(rte_spinlock_is_locked(&sfc_mae_switch.lock));
632
633 domain = sfc_mae_find_switch_domain_by_id(switch_domain_id);
634 if (domain == NULL)
635 return EINVAL;
636
637 if (domain->mae_admin_port != NULL) {
638 *port_id = domain->mae_admin_port->ethdev_port_id;
639 return 0;
640 }
641
642 return ENOENT;
643 }
644
645 int
sfc_mae_get_switch_domain_admin(uint16_t switch_domain_id,uint16_t * port_id)646 sfc_mae_get_switch_domain_admin(uint16_t switch_domain_id,
647 uint16_t *port_id)
648 {
649 int rc;
650
651 rte_spinlock_lock(&sfc_mae_switch.lock);
652 rc = sfc_mae_get_switch_domain_admin_locked(switch_domain_id, port_id);
653 rte_spinlock_unlock(&sfc_mae_switch.lock);
654 return rc;
655 }
656