cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sparx5_switchdev.c (16464B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/* Microchip Sparx5 Switch driver
      3 *
      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_attr_bridge_flags(struct sparx5_port *port,
     33					  struct switchdev_brport_flags flags)
     34{
     35	int pgid;
     36
     37	if (flags.mask & BR_MCAST_FLOOD)
     38		for (pgid = PGID_MC_FLOOD; pgid <= PGID_IPV6_MC_CTRL; pgid++)
     39			sparx5_pgid_update_mask(port, pgid, !!(flags.val & BR_MCAST_FLOOD));
     40	if (flags.mask & BR_FLOOD)
     41		sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD));
     42	if (flags.mask & BR_BCAST_FLOOD)
     43		sparx5_pgid_update_mask(port, PGID_BCAST, !!(flags.val & BR_BCAST_FLOOD));
     44}
     45
     46static void sparx5_attr_stp_state_set(struct sparx5_port *port,
     47				      u8 state)
     48{
     49	struct sparx5 *sparx5 = port->sparx5;
     50
     51	if (!test_bit(port->portno, sparx5->bridge_mask)) {
     52		netdev_err(port->ndev,
     53			   "Controlling non-bridged port %d?\n", port->portno);
     54		return;
     55	}
     56
     57	switch (state) {
     58	case BR_STATE_FORWARDING:
     59		set_bit(port->portno, sparx5->bridge_fwd_mask);
     60		fallthrough;
     61	case BR_STATE_LEARNING:
     62		set_bit(port->portno, sparx5->bridge_lrn_mask);
     63		break;
     64
     65	default:
     66		/* All other states treated as blocking */
     67		clear_bit(port->portno, sparx5->bridge_fwd_mask);
     68		clear_bit(port->portno, sparx5->bridge_lrn_mask);
     69		break;
     70	}
     71
     72	/* apply the bridge_fwd_mask to all the ports */
     73	sparx5_update_fwd(sparx5);
     74}
     75
     76static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
     77					unsigned long ageing_clock_t)
     78{
     79	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
     80	u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
     81
     82	sparx5_set_ageing(port->sparx5, ageing_time);
     83}
     84
     85static int sparx5_port_attr_set(struct net_device *dev, const void *ctx,
     86				const struct switchdev_attr *attr,
     87				struct netlink_ext_ack *extack)
     88{
     89	struct sparx5_port *port = netdev_priv(dev);
     90
     91	switch (attr->id) {
     92	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
     93		return sparx5_port_attr_pre_bridge_flags(port,
     94							 attr->u.brport_flags);
     95	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
     96		sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);
     97		break;
     98	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
     99		sparx5_attr_stp_state_set(port, attr->u.stp_state);
    100		break;
    101	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
    102		sparx5_port_attr_ageing_set(port, attr->u.ageing_time);
    103		break;
    104	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
    105		/* Used PVID 1 when default_pvid is 0, to avoid
    106		 * collision with non-bridged ports.
    107		 */
    108		if (port->pvid == 0)
    109			port->pvid = 1;
    110		port->vlan_aware = attr->u.vlan_filtering;
    111		sparx5_vlan_port_apply(port->sparx5, port);
    112		break;
    113	default:
    114		return -EOPNOTSUPP;
    115	}
    116
    117	return 0;
    118}
    119
    120static int sparx5_port_bridge_join(struct sparx5_port *port,
    121				   struct net_device *bridge,
    122				   struct netlink_ext_ack *extack)
    123{
    124	struct sparx5 *sparx5 = port->sparx5;
    125	struct net_device *ndev = port->ndev;
    126	int err;
    127
    128	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
    129		/* First bridged port */
    130		sparx5->hw_bridge_dev = bridge;
    131	else
    132		if (sparx5->hw_bridge_dev != bridge)
    133			/* This is adding the port to a second bridge, this is
    134			 * unsupported
    135			 */
    136			return -ENODEV;
    137
    138	set_bit(port->portno, sparx5->bridge_mask);
    139
    140	err = switchdev_bridge_port_offload(ndev, ndev, NULL, NULL, NULL,
    141					    false, extack);
    142	if (err)
    143		goto err_switchdev_offload;
    144
    145	/* Remove standalone port entry */
    146	sparx5_mact_forget(sparx5, ndev->dev_addr, 0);
    147
    148	/* Port enters in bridge mode therefor don't need to copy to CPU
    149	 * frames for multicast in case the bridge is not requesting them
    150	 */
    151	__dev_mc_unsync(ndev, sparx5_mc_unsync);
    152
    153	return 0;
    154
    155err_switchdev_offload:
    156	clear_bit(port->portno, sparx5->bridge_mask);
    157	return err;
    158}
    159
    160static void sparx5_port_bridge_leave(struct sparx5_port *port,
    161				     struct net_device *bridge)
    162{
    163	struct sparx5 *sparx5 = port->sparx5;
    164
    165	switchdev_bridge_port_unoffload(port->ndev, NULL, NULL, NULL);
    166
    167	clear_bit(port->portno, sparx5->bridge_mask);
    168	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
    169		sparx5->hw_bridge_dev = NULL;
    170
    171	/* Clear bridge vlan settings before updating the port settings */
    172	port->vlan_aware = 0;
    173	port->pvid = NULL_VID;
    174	port->vid = NULL_VID;
    175
    176	/* Forward frames to CPU */
    177	sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, 0);
    178
    179	/* Port enters in host more therefore restore mc list */
    180	__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
    181}
    182
    183static int sparx5_port_changeupper(struct net_device *dev,
    184				   struct netdev_notifier_changeupper_info *info)
    185{
    186	struct sparx5_port *port = netdev_priv(dev);
    187	struct netlink_ext_ack *extack;
    188	int err = 0;
    189
    190	extack = netdev_notifier_info_to_extack(&info->info);
    191
    192	if (netif_is_bridge_master(info->upper_dev)) {
    193		if (info->linking)
    194			err = sparx5_port_bridge_join(port, info->upper_dev,
    195						      extack);
    196		else
    197			sparx5_port_bridge_leave(port, info->upper_dev);
    198
    199		sparx5_vlan_port_apply(port->sparx5, port);
    200	}
    201
    202	return err;
    203}
    204
    205static int sparx5_port_add_addr(struct net_device *dev, bool up)
    206{
    207	struct sparx5_port *port = netdev_priv(dev);
    208	struct sparx5 *sparx5 = port->sparx5;
    209	u16 vid = port->pvid;
    210
    211	if (up)
    212		sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
    213	else
    214		sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
    215
    216	return 0;
    217}
    218
    219static int sparx5_netdevice_port_event(struct net_device *dev,
    220				       struct notifier_block *nb,
    221				       unsigned long event, void *ptr)
    222{
    223	int err = 0;
    224
    225	if (!sparx5_netdevice_check(dev))
    226		return 0;
    227
    228	switch (event) {
    229	case NETDEV_CHANGEUPPER:
    230		err = sparx5_port_changeupper(dev, ptr);
    231		break;
    232	case NETDEV_PRE_UP:
    233		err = sparx5_port_add_addr(dev, true);
    234		break;
    235	case NETDEV_DOWN:
    236		err = sparx5_port_add_addr(dev, false);
    237		break;
    238	}
    239
    240	return err;
    241}
    242
    243static int sparx5_netdevice_event(struct notifier_block *nb,
    244				  unsigned long event, void *ptr)
    245{
    246	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
    247	int ret = 0;
    248
    249	ret = sparx5_netdevice_port_event(dev, nb, event, ptr);
    250
    251	return notifier_from_errno(ret);
    252}
    253
    254static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
    255{
    256	struct sparx5_switchdev_event_work *switchdev_work =
    257		container_of(work, struct sparx5_switchdev_event_work, work);
    258	struct net_device *dev = switchdev_work->dev;
    259	struct switchdev_notifier_fdb_info *fdb_info;
    260	struct sparx5_port *port;
    261	struct sparx5 *sparx5;
    262	bool host_addr;
    263	u16 vid;
    264
    265	rtnl_lock();
    266	if (!sparx5_netdevice_check(dev)) {
    267		host_addr = true;
    268		sparx5 = switchdev_work->sparx5;
    269	} else {
    270		host_addr = false;
    271		sparx5 = switchdev_work->sparx5;
    272		port = netdev_priv(dev);
    273	}
    274
    275	fdb_info = &switchdev_work->fdb_info;
    276
    277	/* Used PVID 1 when default_pvid is 0, to avoid
    278	 * collision with non-bridged ports.
    279	 */
    280	if (fdb_info->vid == 0)
    281		vid = 1;
    282	else
    283		vid = fdb_info->vid;
    284
    285	switch (switchdev_work->event) {
    286	case SWITCHDEV_FDB_ADD_TO_DEVICE:
    287		if (host_addr)
    288			sparx5_add_mact_entry(sparx5, dev, PGID_CPU,
    289					      fdb_info->addr, vid);
    290		else
    291			sparx5_add_mact_entry(sparx5, port->ndev, port->portno,
    292					      fdb_info->addr, vid);
    293		break;
    294	case SWITCHDEV_FDB_DEL_TO_DEVICE:
    295		sparx5_del_mact_entry(sparx5, fdb_info->addr, vid);
    296		break;
    297	}
    298
    299	rtnl_unlock();
    300	kfree(switchdev_work->fdb_info.addr);
    301	kfree(switchdev_work);
    302	dev_put(dev);
    303}
    304
    305static void sparx5_schedule_work(struct work_struct *work)
    306{
    307	queue_work(sparx5_owq, work);
    308}
    309
    310static int sparx5_switchdev_event(struct notifier_block *nb,
    311				  unsigned long event, void *ptr)
    312{
    313	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
    314	struct sparx5_switchdev_event_work *switchdev_work;
    315	struct switchdev_notifier_fdb_info *fdb_info;
    316	struct switchdev_notifier_info *info = ptr;
    317	struct sparx5 *spx5;
    318	int err;
    319
    320	spx5 = container_of(nb, struct sparx5, switchdev_nb);
    321
    322	switch (event) {
    323	case SWITCHDEV_PORT_ATTR_SET:
    324		err = switchdev_handle_port_attr_set(dev, ptr,
    325						     sparx5_netdevice_check,
    326						     sparx5_port_attr_set);
    327		return notifier_from_errno(err);
    328	case SWITCHDEV_FDB_ADD_TO_DEVICE:
    329		fallthrough;
    330	case SWITCHDEV_FDB_DEL_TO_DEVICE:
    331		switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
    332		if (!switchdev_work)
    333			return NOTIFY_BAD;
    334
    335		switchdev_work->dev = dev;
    336		switchdev_work->event = event;
    337		switchdev_work->sparx5 = spx5;
    338
    339		fdb_info = container_of(info,
    340					struct switchdev_notifier_fdb_info,
    341					info);
    342		INIT_WORK(&switchdev_work->work,
    343			  sparx5_switchdev_bridge_fdb_event_work);
    344		memcpy(&switchdev_work->fdb_info, ptr,
    345		       sizeof(switchdev_work->fdb_info));
    346		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
    347		if (!switchdev_work->fdb_info.addr)
    348			goto err_addr_alloc;
    349
    350		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
    351				fdb_info->addr);
    352		dev_hold(dev);
    353
    354		sparx5_schedule_work(&switchdev_work->work);
    355		break;
    356	}
    357
    358	return NOTIFY_DONE;
    359err_addr_alloc:
    360	kfree(switchdev_work);
    361	return NOTIFY_BAD;
    362}
    363
    364static int sparx5_handle_port_vlan_add(struct net_device *dev,
    365				       struct notifier_block *nb,
    366				       const struct switchdev_obj_port_vlan *v)
    367{
    368	struct sparx5_port *port = netdev_priv(dev);
    369
    370	if (netif_is_bridge_master(dev)) {
    371		struct sparx5 *sparx5 =
    372			container_of(nb, struct sparx5,
    373				     switchdev_blocking_nb);
    374
    375		/* Flood broadcast to CPU */
    376		sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
    377				  v->vid);
    378		return 0;
    379	}
    380
    381	if (!sparx5_netdevice_check(dev))
    382		return -EOPNOTSUPP;
    383
    384	return sparx5_vlan_vid_add(port, v->vid,
    385				  v->flags & BRIDGE_VLAN_INFO_PVID,
    386				  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
    387}
    388
    389static int sparx5_handle_port_mdb_add(struct net_device *dev,
    390				      struct notifier_block *nb,
    391				      const struct switchdev_obj_port_mdb *v)
    392{
    393	struct sparx5_port *port = netdev_priv(dev);
    394	struct sparx5 *spx5 = port->sparx5;
    395	u16 pgid_idx, vid;
    396	u32 mact_entry;
    397	int res, err;
    398
    399	if (!sparx5_netdevice_check(dev))
    400		return -EOPNOTSUPP;
    401
    402	if (netif_is_bridge_master(v->obj.orig_dev)) {
    403		sparx5_mact_learn(spx5, PGID_CPU, v->addr, v->vid);
    404		return 0;
    405	}
    406
    407	/* When VLAN unaware the vlan value is not parsed and we receive vid 0.
    408	 * Fall back to bridge vid 1.
    409	 */
    410	if (!br_vlan_enabled(spx5->hw_bridge_dev))
    411		vid = 1;
    412	else
    413		vid = v->vid;
    414
    415	res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
    416
    417	if (res == 0) {
    418		pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
    419
    420		/* MC_IDX starts after the port masks in the PGID table */
    421		pgid_idx += SPX5_PORTS;
    422		sparx5_pgid_update_mask(port, pgid_idx, true);
    423	} else {
    424		err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx);
    425		if (err) {
    426			netdev_warn(dev, "multicast pgid table full\n");
    427			return err;
    428		}
    429		sparx5_pgid_update_mask(port, pgid_idx, true);
    430		err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid);
    431		if (err) {
    432			netdev_warn(dev, "could not learn mac address %pM\n", v->addr);
    433			sparx5_pgid_update_mask(port, pgid_idx, false);
    434			return err;
    435		}
    436	}
    437
    438	return 0;
    439}
    440
    441static int sparx5_mdb_del_entry(struct net_device *dev,
    442				struct sparx5 *spx5,
    443				const unsigned char mac[ETH_ALEN],
    444				const u16 vid,
    445				u16 pgid_idx)
    446{
    447	int err;
    448
    449	err = sparx5_mact_forget(spx5, mac, vid);
    450	if (err) {
    451		netdev_warn(dev, "could not forget mac address %pM", mac);
    452		return err;
    453	}
    454	err = sparx5_pgid_free(spx5, pgid_idx);
    455	if (err) {
    456		netdev_err(dev, "attempted to free already freed pgid\n");
    457		return err;
    458	}
    459	return 0;
    460}
    461
    462static int sparx5_handle_port_mdb_del(struct net_device *dev,
    463				      struct notifier_block *nb,
    464				      const struct switchdev_obj_port_mdb *v)
    465{
    466	struct sparx5_port *port = netdev_priv(dev);
    467	struct sparx5 *spx5 = port->sparx5;
    468	u16 pgid_idx, vid;
    469	u32 mact_entry, res, pgid_entry[3];
    470	int err;
    471
    472	if (!sparx5_netdevice_check(dev))
    473		return -EOPNOTSUPP;
    474
    475	if (netif_is_bridge_master(v->obj.orig_dev)) {
    476		sparx5_mact_forget(spx5, v->addr, v->vid);
    477		return 0;
    478	}
    479
    480	if (!br_vlan_enabled(spx5->hw_bridge_dev))
    481		vid = 1;
    482	else
    483		vid = v->vid;
    484
    485	res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
    486
    487	if (res == 0) {
    488		pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
    489
    490		/* MC_IDX starts after the port masks in the PGID table */
    491		pgid_idx += SPX5_PORTS;
    492		sparx5_pgid_update_mask(port, pgid_idx, false);
    493
    494		sparx5_pgid_read_mask(spx5, pgid_idx, pgid_entry);
    495		if (bitmap_empty((unsigned long *)pgid_entry, SPX5_PORTS)) {
    496			/* No ports are in MC group. Remove entry */
    497			err = sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx);
    498			if (err)
    499				return err;
    500		}
    501	}
    502
    503	return 0;
    504}
    505
    506static int sparx5_handle_port_obj_add(struct net_device *dev,
    507				      struct notifier_block *nb,
    508				      struct switchdev_notifier_port_obj_info *info)
    509{
    510	const struct switchdev_obj *obj = info->obj;
    511	int err;
    512
    513	switch (obj->id) {
    514	case SWITCHDEV_OBJ_ID_PORT_VLAN:
    515		err = sparx5_handle_port_vlan_add(dev, nb,
    516						  SWITCHDEV_OBJ_PORT_VLAN(obj));
    517		break;
    518	case SWITCHDEV_OBJ_ID_PORT_MDB:
    519	case SWITCHDEV_OBJ_ID_HOST_MDB:
    520		err = sparx5_handle_port_mdb_add(dev, nb,
    521						 SWITCHDEV_OBJ_PORT_MDB(obj));
    522		break;
    523	default:
    524		err = -EOPNOTSUPP;
    525		break;
    526	}
    527
    528	info->handled = true;
    529	return err;
    530}
    531
    532static int sparx5_handle_port_vlan_del(struct net_device *dev,
    533				       struct notifier_block *nb,
    534				       u16 vid)
    535{
    536	struct sparx5_port *port = netdev_priv(dev);
    537	int ret;
    538
    539	/* Master bridge? */
    540	if (netif_is_bridge_master(dev)) {
    541		struct sparx5 *sparx5 =
    542			container_of(nb, struct sparx5,
    543				     switchdev_blocking_nb);
    544
    545		sparx5_mact_forget(sparx5, dev->broadcast, vid);
    546		return 0;
    547	}
    548
    549	if (!sparx5_netdevice_check(dev))
    550		return -EOPNOTSUPP;
    551
    552	ret = sparx5_vlan_vid_del(port, vid);
    553	if (ret)
    554		return ret;
    555
    556	return 0;
    557}
    558
    559static int sparx5_handle_port_obj_del(struct net_device *dev,
    560				      struct notifier_block *nb,
    561				      struct switchdev_notifier_port_obj_info *info)
    562{
    563	const struct switchdev_obj *obj = info->obj;
    564	int err;
    565
    566	switch (obj->id) {
    567	case SWITCHDEV_OBJ_ID_PORT_VLAN:
    568		err = sparx5_handle_port_vlan_del(dev, nb,
    569						  SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
    570		break;
    571	case SWITCHDEV_OBJ_ID_PORT_MDB:
    572	case SWITCHDEV_OBJ_ID_HOST_MDB:
    573		err = sparx5_handle_port_mdb_del(dev, nb,
    574						 SWITCHDEV_OBJ_PORT_MDB(obj));
    575		break;
    576	default:
    577		err = -EOPNOTSUPP;
    578		break;
    579	}
    580
    581	info->handled = true;
    582	return err;
    583}
    584
    585static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
    586					   unsigned long event,
    587					   void *ptr)
    588{
    589	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
    590	int err;
    591
    592	switch (event) {
    593	case SWITCHDEV_PORT_OBJ_ADD:
    594		err = sparx5_handle_port_obj_add(dev, nb, ptr);
    595		return notifier_from_errno(err);
    596	case SWITCHDEV_PORT_OBJ_DEL:
    597		err = sparx5_handle_port_obj_del(dev, nb, ptr);
    598		return notifier_from_errno(err);
    599	case SWITCHDEV_PORT_ATTR_SET:
    600		err = switchdev_handle_port_attr_set(dev, ptr,
    601						     sparx5_netdevice_check,
    602						     sparx5_port_attr_set);
    603		return notifier_from_errno(err);
    604	}
    605
    606	return NOTIFY_DONE;
    607}
    608
    609int sparx5_register_notifier_blocks(struct sparx5 *s5)
    610{
    611	int err;
    612
    613	s5->netdevice_nb.notifier_call = sparx5_netdevice_event;
    614	err = register_netdevice_notifier(&s5->netdevice_nb);
    615	if (err)
    616		return err;
    617
    618	s5->switchdev_nb.notifier_call = sparx5_switchdev_event;
    619	err = register_switchdev_notifier(&s5->switchdev_nb);
    620	if (err)
    621		goto err_switchdev_nb;
    622
    623	s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;
    624	err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
    625	if (err)
    626		goto err_switchdev_blocking_nb;
    627
    628	sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
    629	if (!sparx5_owq) {
    630		err = -ENOMEM;
    631		goto err_switchdev_blocking_nb;
    632	}
    633
    634	return 0;
    635
    636err_switchdev_blocking_nb:
    637	unregister_switchdev_notifier(&s5->switchdev_nb);
    638err_switchdev_nb:
    639	unregister_netdevice_notifier(&s5->netdevice_nb);
    640
    641	return err;
    642}
    643
    644void sparx5_unregister_notifier_blocks(struct sparx5 *s5)
    645{
    646	destroy_workqueue(sparx5_owq);
    647
    648	unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
    649	unregister_switchdev_notifier(&s5->switchdev_nb);
    650	unregister_netdevice_notifier(&s5->netdevice_nb);
    651}