764 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.5 */6 7#include <linux/if_bridge.h>8#include <net/switchdev.h>9 10#include "sparx5_main_regs.h"11#include "sparx5_main.h"12 13static struct workqueue_struct *sparx5_owq;14 15struct sparx5_switchdev_event_work {16 struct work_struct work;17 struct switchdev_notifier_fdb_info fdb_info;18 struct net_device *dev;19 struct sparx5 *sparx5;20 unsigned long event;21};22 23static int sparx5_port_attr_pre_bridge_flags(struct sparx5_port *port,24 struct switchdev_brport_flags flags)25{26 if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD))27 return -EINVAL;28 29 return 0;30}31 32static void sparx5_port_update_mcast_ip_flood(struct sparx5_port *port, bool flood_flag)33{34 bool should_flood = flood_flag || port->is_mrouter;35 int pgid;36 37 for (pgid = PGID_IPV4_MC_DATA; pgid <= PGID_IPV6_MC_CTRL; pgid++)38 sparx5_pgid_update_mask(port, pgid, should_flood);39}40 41static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,42 struct switchdev_brport_flags flags)43{44 if (flags.mask & BR_MCAST_FLOOD) {45 sparx5_pgid_update_mask(port, PGID_MC_FLOOD, !!(flags.val & BR_MCAST_FLOOD));46 sparx5_port_update_mcast_ip_flood(port, !!(flags.val & BR_MCAST_FLOOD));47 }48 49 if (flags.mask & BR_FLOOD)50 sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD));51 if (flags.mask & BR_BCAST_FLOOD)52 sparx5_pgid_update_mask(port, PGID_BCAST, !!(flags.val & BR_BCAST_FLOOD));53}54 55static void sparx5_attr_stp_state_set(struct sparx5_port *port,56 u8 state)57{58 struct sparx5 *sparx5 = port->sparx5;59 60 if (!test_bit(port->portno, sparx5->bridge_mask)) {61 netdev_err(port->ndev,62 "Controlling non-bridged port %d?\n", port->portno);63 return;64 }65 66 switch (state) {67 case BR_STATE_FORWARDING:68 set_bit(port->portno, sparx5->bridge_fwd_mask);69 fallthrough;70 case BR_STATE_LEARNING:71 set_bit(port->portno, sparx5->bridge_lrn_mask);72 break;73 74 default:75 /* All other states treated as blocking */76 clear_bit(port->portno, sparx5->bridge_fwd_mask);77 clear_bit(port->portno, sparx5->bridge_lrn_mask);78 break;79 }80 81 /* apply the bridge_fwd_mask to all the ports */82 sparx5_update_fwd(sparx5);83}84 85static void sparx5_port_attr_ageing_set(struct sparx5_port *port,86 unsigned long ageing_clock_t)87{88 unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);89 u32 ageing_time = jiffies_to_msecs(ageing_jiffies);90 91 sparx5_set_ageing(port->sparx5, ageing_time);92}93 94static void sparx5_port_attr_mrouter_set(struct sparx5_port *port,95 struct net_device *orig_dev,96 bool enable)97{98 struct sparx5 *sparx5 = port->sparx5;99 struct sparx5_mdb_entry *e;100 bool flood_flag;101 102 if ((enable && port->is_mrouter) || (!enable && !port->is_mrouter))103 return;104 105 /* Add/del mrouter port on all active mdb entries in HW.106 * Don't change entry port mask, since that represents107 * ports that actually joined that group.108 */109 mutex_lock(&sparx5->mdb_lock);110 list_for_each_entry(e, &sparx5->mdb_entries, list) {111 if (!test_bit(port->portno, e->port_mask) &&112 ether_addr_is_ip_mcast(e->addr))113 sparx5_pgid_update_mask(port, e->pgid_idx, enable);114 }115 mutex_unlock(&sparx5->mdb_lock);116 117 /* Enable/disable flooding depending on if port is mrouter port118 * or if mcast flood is enabled.119 */120 port->is_mrouter = enable;121 flood_flag = br_port_flag_is_set(port->ndev, BR_MCAST_FLOOD);122 sparx5_port_update_mcast_ip_flood(port, flood_flag);123}124 125static int sparx5_port_attr_set(struct net_device *dev, const void *ctx,126 const struct switchdev_attr *attr,127 struct netlink_ext_ack *extack)128{129 struct sparx5_port *port = netdev_priv(dev);130 131 switch (attr->id) {132 case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:133 return sparx5_port_attr_pre_bridge_flags(port,134 attr->u.brport_flags);135 case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:136 sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);137 break;138 case SWITCHDEV_ATTR_ID_PORT_STP_STATE:139 sparx5_attr_stp_state_set(port, attr->u.stp_state);140 break;141 case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:142 sparx5_port_attr_ageing_set(port, attr->u.ageing_time);143 break;144 case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:145 /* Used PVID 1 when default_pvid is 0, to avoid146 * collision with non-bridged ports.147 */148 if (port->pvid == 0)149 port->pvid = 1;150 port->vlan_aware = attr->u.vlan_filtering;151 sparx5_vlan_port_apply(port->sparx5, port);152 break;153 case SWITCHDEV_ATTR_ID_PORT_MROUTER:154 sparx5_port_attr_mrouter_set(port,155 attr->orig_dev,156 attr->u.mrouter);157 break;158 default:159 return -EOPNOTSUPP;160 }161 162 return 0;163}164 165static int sparx5_port_bridge_join(struct sparx5_port *port,166 struct net_device *bridge,167 struct netlink_ext_ack *extack)168{169 struct sparx5 *sparx5 = port->sparx5;170 struct net_device *ndev = port->ndev;171 int err;172 173 if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))174 /* First bridged port */175 sparx5->hw_bridge_dev = bridge;176 else177 if (sparx5->hw_bridge_dev != bridge)178 /* This is adding the port to a second bridge, this is179 * unsupported180 */181 return -ENODEV;182 183 set_bit(port->portno, sparx5->bridge_mask);184 185 err = switchdev_bridge_port_offload(ndev, ndev, NULL, NULL, NULL,186 false, extack);187 if (err)188 goto err_switchdev_offload;189 190 /* Remove standalone port entry */191 sparx5_mact_forget(sparx5, ndev->dev_addr, 0);192 193 /* Port enters in bridge mode therefore don't need to copy to CPU194 * frames for multicast in case the bridge is not requesting them195 */196 __dev_mc_unsync(ndev, sparx5_mc_unsync);197 198 return 0;199 200err_switchdev_offload:201 clear_bit(port->portno, sparx5->bridge_mask);202 return err;203}204 205static void sparx5_port_bridge_leave(struct sparx5_port *port,206 struct net_device *bridge)207{208 struct sparx5 *sparx5 = port->sparx5;209 210 switchdev_bridge_port_unoffload(port->ndev, NULL, NULL, NULL);211 212 clear_bit(port->portno, sparx5->bridge_mask);213 if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))214 sparx5->hw_bridge_dev = NULL;215 216 /* Clear bridge vlan settings before updating the port settings */217 port->vlan_aware = 0;218 port->pvid = NULL_VID;219 port->vid = NULL_VID;220 221 /* Forward frames to CPU */222 sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, 0);223 224 /* Port enters in host more therefore restore mc list */225 __dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);226}227 228static int sparx5_port_changeupper(struct net_device *dev,229 struct netdev_notifier_changeupper_info *info)230{231 struct sparx5_port *port = netdev_priv(dev);232 struct netlink_ext_ack *extack;233 int err = 0;234 235 extack = netdev_notifier_info_to_extack(&info->info);236 237 if (netif_is_bridge_master(info->upper_dev)) {238 if (info->linking)239 err = sparx5_port_bridge_join(port, info->upper_dev,240 extack);241 else242 sparx5_port_bridge_leave(port, info->upper_dev);243 244 sparx5_vlan_port_apply(port->sparx5, port);245 }246 247 return err;248}249 250static int sparx5_port_add_addr(struct net_device *dev, bool up)251{252 struct sparx5_port *port = netdev_priv(dev);253 struct sparx5 *sparx5 = port->sparx5;254 u16 vid = port->pvid;255 256 if (up)257 sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);258 else259 sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);260 261 return 0;262}263 264static int sparx5_netdevice_port_event(struct net_device *dev,265 struct notifier_block *nb,266 unsigned long event, void *ptr)267{268 int err = 0;269 270 if (!sparx5_netdevice_check(dev))271 return 0;272 273 switch (event) {274 case NETDEV_CHANGEUPPER:275 err = sparx5_port_changeupper(dev, ptr);276 break;277 case NETDEV_PRE_UP:278 err = sparx5_port_add_addr(dev, true);279 break;280 case NETDEV_DOWN:281 err = sparx5_port_add_addr(dev, false);282 break;283 }284 285 return err;286}287 288static int sparx5_netdevice_event(struct notifier_block *nb,289 unsigned long event, void *ptr)290{291 struct net_device *dev = netdev_notifier_info_to_dev(ptr);292 int ret = 0;293 294 ret = sparx5_netdevice_port_event(dev, nb, event, ptr);295 296 return notifier_from_errno(ret);297}298 299static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)300{301 struct sparx5_switchdev_event_work *switchdev_work =302 container_of(work, struct sparx5_switchdev_event_work, work);303 struct net_device *dev = switchdev_work->dev;304 struct switchdev_notifier_fdb_info *fdb_info;305 struct sparx5_port *port;306 struct sparx5 *sparx5;307 bool host_addr;308 u16 vid;309 310 rtnl_lock();311 if (!sparx5_netdevice_check(dev)) {312 host_addr = true;313 sparx5 = switchdev_work->sparx5;314 } else {315 host_addr = false;316 sparx5 = switchdev_work->sparx5;317 port = netdev_priv(dev);318 }319 320 fdb_info = &switchdev_work->fdb_info;321 322 /* Used PVID 1 when default_pvid is 0, to avoid323 * collision with non-bridged ports.324 */325 if (fdb_info->vid == 0)326 vid = 1;327 else328 vid = fdb_info->vid;329 330 switch (switchdev_work->event) {331 case SWITCHDEV_FDB_ADD_TO_DEVICE:332 if (host_addr)333 sparx5_add_mact_entry(sparx5, dev, PGID_CPU,334 fdb_info->addr, vid);335 else336 sparx5_add_mact_entry(sparx5, port->ndev, port->portno,337 fdb_info->addr, vid);338 break;339 case SWITCHDEV_FDB_DEL_TO_DEVICE:340 sparx5_del_mact_entry(sparx5, fdb_info->addr, vid);341 break;342 }343 344 rtnl_unlock();345 kfree(switchdev_work->fdb_info.addr);346 kfree(switchdev_work);347 dev_put(dev);348}349 350static void sparx5_schedule_work(struct work_struct *work)351{352 queue_work(sparx5_owq, work);353}354 355static int sparx5_switchdev_event(struct notifier_block *nb,356 unsigned long event, void *ptr)357{358 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);359 struct sparx5_switchdev_event_work *switchdev_work;360 struct switchdev_notifier_fdb_info *fdb_info;361 struct switchdev_notifier_info *info = ptr;362 struct sparx5 *spx5;363 int err;364 365 spx5 = container_of(nb, struct sparx5, switchdev_nb);366 367 switch (event) {368 case SWITCHDEV_PORT_ATTR_SET:369 err = switchdev_handle_port_attr_set(dev, ptr,370 sparx5_netdevice_check,371 sparx5_port_attr_set);372 return notifier_from_errno(err);373 case SWITCHDEV_FDB_ADD_TO_DEVICE:374 fallthrough;375 case SWITCHDEV_FDB_DEL_TO_DEVICE:376 switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);377 if (!switchdev_work)378 return NOTIFY_BAD;379 380 switchdev_work->dev = dev;381 switchdev_work->event = event;382 switchdev_work->sparx5 = spx5;383 384 fdb_info = container_of(info,385 struct switchdev_notifier_fdb_info,386 info);387 INIT_WORK(&switchdev_work->work,388 sparx5_switchdev_bridge_fdb_event_work);389 memcpy(&switchdev_work->fdb_info, ptr,390 sizeof(switchdev_work->fdb_info));391 switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);392 if (!switchdev_work->fdb_info.addr)393 goto err_addr_alloc;394 395 ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,396 fdb_info->addr);397 dev_hold(dev);398 399 sparx5_schedule_work(&switchdev_work->work);400 break;401 }402 403 return NOTIFY_DONE;404err_addr_alloc:405 kfree(switchdev_work);406 return NOTIFY_BAD;407}408 409static int sparx5_handle_port_vlan_add(struct net_device *dev,410 struct notifier_block *nb,411 const struct switchdev_obj_port_vlan *v)412{413 struct sparx5_port *port = netdev_priv(dev);414 415 if (netif_is_bridge_master(dev)) {416 struct sparx5 *sparx5 =417 container_of(nb, struct sparx5,418 switchdev_blocking_nb);419 420 /* Flood broadcast to CPU */421 sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,422 v->vid);423 return 0;424 }425 426 if (!sparx5_netdevice_check(dev))427 return -EOPNOTSUPP;428 429 return sparx5_vlan_vid_add(port, v->vid,430 v->flags & BRIDGE_VLAN_INFO_PVID,431 v->flags & BRIDGE_VLAN_INFO_UNTAGGED);432}433 434static int sparx5_alloc_mdb_entry(struct sparx5 *sparx5,435 const unsigned char *addr,436 u16 vid,437 struct sparx5_mdb_entry **entry_out)438{439 struct sparx5_mdb_entry *entry;440 u16 pgid_idx;441 int err;442 443 entry = kzalloc(sizeof(*entry), GFP_KERNEL);444 if (!entry)445 return -ENOMEM;446 447 err = sparx5_pgid_alloc_mcast(sparx5, &pgid_idx);448 if (err) {449 kfree(entry);450 return err;451 }452 453 memcpy(entry->addr, addr, ETH_ALEN);454 entry->vid = vid;455 entry->pgid_idx = pgid_idx;456 457 mutex_lock(&sparx5->mdb_lock);458 list_add_tail(&entry->list, &sparx5->mdb_entries);459 mutex_unlock(&sparx5->mdb_lock);460 461 *entry_out = entry;462 return 0;463}464 465static void sparx5_free_mdb_entry(struct sparx5 *sparx5,466 const unsigned char *addr,467 u16 vid)468{469 struct sparx5_mdb_entry *entry, *tmp;470 471 mutex_lock(&sparx5->mdb_lock);472 list_for_each_entry_safe(entry, tmp, &sparx5->mdb_entries, list) {473 if ((vid == 0 || entry->vid == vid) &&474 ether_addr_equal(addr, entry->addr)) {475 list_del(&entry->list);476 477 sparx5_pgid_free(sparx5, entry->pgid_idx);478 kfree(entry);479 goto out;480 }481 }482 483out:484 mutex_unlock(&sparx5->mdb_lock);485}486 487static struct sparx5_mdb_entry *sparx5_mdb_get_entry(struct sparx5 *sparx5,488 const unsigned char *addr,489 u16 vid)490{491 struct sparx5_mdb_entry *e, *found = NULL;492 493 mutex_lock(&sparx5->mdb_lock);494 list_for_each_entry(e, &sparx5->mdb_entries, list) {495 if (ether_addr_equal(e->addr, addr) && e->vid == vid) {496 found = e;497 goto out;498 }499 }500 501out:502 mutex_unlock(&sparx5->mdb_lock);503 return found;504}505 506static void sparx5_cpu_copy_ena(struct sparx5 *spx5, u16 pgid, bool enable)507{508 spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(enable),509 ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,510 ANA_AC_PGID_MISC_CFG(pgid));511}512 513static int sparx5_handle_port_mdb_add(struct net_device *dev,514 struct notifier_block *nb,515 const struct switchdev_obj_port_mdb *v)516{517 struct sparx5_port *port = netdev_priv(dev);518 struct sparx5 *spx5 = port->sparx5;519 struct sparx5_mdb_entry *entry;520 bool is_host, is_new;521 int err, i;522 u16 vid;523 524 if (!sparx5_netdevice_check(dev))525 return -EOPNOTSUPP;526 527 is_host = netif_is_bridge_master(v->obj.orig_dev);528 529 /* When VLAN unaware the vlan value is not parsed and we receive vid 0.530 * Fall back to bridge vid 1.531 */532 if (!br_vlan_enabled(spx5->hw_bridge_dev))533 vid = 1;534 else535 vid = v->vid;536 537 is_new = false;538 entry = sparx5_mdb_get_entry(spx5, v->addr, vid);539 if (!entry) {540 err = sparx5_alloc_mdb_entry(spx5, v->addr, vid, &entry);541 is_new = true;542 if (err)543 return err;544 }545 546 mutex_lock(&spx5->mdb_lock);547 548 /* Add any mrouter ports to the new entry */549 if (is_new && ether_addr_is_ip_mcast(v->addr))550 for (i = 0; i < SPX5_PORTS; i++)551 if (spx5->ports[i] && spx5->ports[i]->is_mrouter)552 sparx5_pgid_update_mask(spx5->ports[i],553 entry->pgid_idx,554 true);555 556 if (is_host && !entry->cpu_copy) {557 sparx5_cpu_copy_ena(spx5, entry->pgid_idx, true);558 entry->cpu_copy = true;559 } else if (!is_host) {560 sparx5_pgid_update_mask(port, entry->pgid_idx, true);561 set_bit(port->portno, entry->port_mask);562 }563 mutex_unlock(&spx5->mdb_lock);564 565 sparx5_mact_learn(spx5, entry->pgid_idx, entry->addr, entry->vid);566 567 return 0;568}569 570static int sparx5_handle_port_mdb_del(struct net_device *dev,571 struct notifier_block *nb,572 const struct switchdev_obj_port_mdb *v)573{574 struct sparx5_port *port = netdev_priv(dev);575 struct sparx5 *spx5 = port->sparx5;576 struct sparx5_mdb_entry *entry;577 bool is_host;578 u16 vid;579 580 if (!sparx5_netdevice_check(dev))581 return -EOPNOTSUPP;582 583 is_host = netif_is_bridge_master(v->obj.orig_dev);584 585 if (!br_vlan_enabled(spx5->hw_bridge_dev))586 vid = 1;587 else588 vid = v->vid;589 590 entry = sparx5_mdb_get_entry(spx5, v->addr, vid);591 if (!entry)592 return 0;593 594 mutex_lock(&spx5->mdb_lock);595 if (is_host && entry->cpu_copy) {596 sparx5_cpu_copy_ena(spx5, entry->pgid_idx, false);597 entry->cpu_copy = false;598 } else if (!is_host) {599 clear_bit(port->portno, entry->port_mask);600 601 /* Port not mrouter port or addr is L2 mcast, remove port from mask. */602 if (!port->is_mrouter || !ether_addr_is_ip_mcast(v->addr))603 sparx5_pgid_update_mask(port, entry->pgid_idx, false);604 }605 mutex_unlock(&spx5->mdb_lock);606 607 if (bitmap_empty(entry->port_mask, SPX5_PORTS) && !entry->cpu_copy) {608 /* Clear pgid in case mrouter ports exists609 * that are not part of the group.610 */611 sparx5_pgid_clear(spx5, entry->pgid_idx);612 sparx5_mact_forget(spx5, entry->addr, entry->vid);613 sparx5_free_mdb_entry(spx5, entry->addr, entry->vid);614 }615 return 0;616}617 618static int sparx5_handle_port_obj_add(struct net_device *dev,619 struct notifier_block *nb,620 struct switchdev_notifier_port_obj_info *info)621{622 const struct switchdev_obj *obj = info->obj;623 int err;624 625 switch (obj->id) {626 case SWITCHDEV_OBJ_ID_PORT_VLAN:627 err = sparx5_handle_port_vlan_add(dev, nb,628 SWITCHDEV_OBJ_PORT_VLAN(obj));629 break;630 case SWITCHDEV_OBJ_ID_PORT_MDB:631 case SWITCHDEV_OBJ_ID_HOST_MDB:632 err = sparx5_handle_port_mdb_add(dev, nb,633 SWITCHDEV_OBJ_PORT_MDB(obj));634 break;635 default:636 err = -EOPNOTSUPP;637 break;638 }639 640 info->handled = true;641 return err;642}643 644static int sparx5_handle_port_vlan_del(struct net_device *dev,645 struct notifier_block *nb,646 u16 vid)647{648 struct sparx5_port *port = netdev_priv(dev);649 int ret;650 651 /* Master bridge? */652 if (netif_is_bridge_master(dev)) {653 struct sparx5 *sparx5 =654 container_of(nb, struct sparx5,655 switchdev_blocking_nb);656 657 sparx5_mact_forget(sparx5, dev->broadcast, vid);658 return 0;659 }660 661 if (!sparx5_netdevice_check(dev))662 return -EOPNOTSUPP;663 664 ret = sparx5_vlan_vid_del(port, vid);665 if (ret)666 return ret;667 668 return 0;669}670 671static int sparx5_handle_port_obj_del(struct net_device *dev,672 struct notifier_block *nb,673 struct switchdev_notifier_port_obj_info *info)674{675 const struct switchdev_obj *obj = info->obj;676 int err;677 678 switch (obj->id) {679 case SWITCHDEV_OBJ_ID_PORT_VLAN:680 err = sparx5_handle_port_vlan_del(dev, nb,681 SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);682 break;683 case SWITCHDEV_OBJ_ID_PORT_MDB:684 case SWITCHDEV_OBJ_ID_HOST_MDB:685 err = sparx5_handle_port_mdb_del(dev, nb,686 SWITCHDEV_OBJ_PORT_MDB(obj));687 break;688 default:689 err = -EOPNOTSUPP;690 break;691 }692 693 info->handled = true;694 return err;695}696 697static int sparx5_switchdev_blocking_event(struct notifier_block *nb,698 unsigned long event,699 void *ptr)700{701 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);702 int err;703 704 switch (event) {705 case SWITCHDEV_PORT_OBJ_ADD:706 err = sparx5_handle_port_obj_add(dev, nb, ptr);707 return notifier_from_errno(err);708 case SWITCHDEV_PORT_OBJ_DEL:709 err = sparx5_handle_port_obj_del(dev, nb, ptr);710 return notifier_from_errno(err);711 case SWITCHDEV_PORT_ATTR_SET:712 err = switchdev_handle_port_attr_set(dev, ptr,713 sparx5_netdevice_check,714 sparx5_port_attr_set);715 return notifier_from_errno(err);716 }717 718 return NOTIFY_DONE;719}720 721int sparx5_register_notifier_blocks(struct sparx5 *s5)722{723 int err;724 725 s5->netdevice_nb.notifier_call = sparx5_netdevice_event;726 err = register_netdevice_notifier(&s5->netdevice_nb);727 if (err)728 return err;729 730 s5->switchdev_nb.notifier_call = sparx5_switchdev_event;731 err = register_switchdev_notifier(&s5->switchdev_nb);732 if (err)733 goto err_switchdev_nb;734 735 s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;736 err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);737 if (err)738 goto err_switchdev_blocking_nb;739 740 sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);741 if (!sparx5_owq) {742 err = -ENOMEM;743 goto err_switchdev_blocking_nb;744 }745 746 return 0;747 748err_switchdev_blocking_nb:749 unregister_switchdev_notifier(&s5->switchdev_nb);750err_switchdev_nb:751 unregister_netdevice_notifier(&s5->netdevice_nb);752 753 return err;754}755 756void sparx5_unregister_notifier_blocks(struct sparx5 *s5)757{758 destroy_workqueue(sparx5_owq);759 760 unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);761 unregister_switchdev_notifier(&s5->switchdev_nb);762 unregister_netdevice_notifier(&s5->netdevice_nb);763}764