18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/types.h>
68c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
118c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
128c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
138c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
148c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
158c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
168c2ecf20Sopenharmony_ci#include <linux/netlink.h>
178c2ecf20Sopenharmony_ci#include <net/switchdev.h>
188c2ecf20Sopenharmony_ci#include <net/vxlan.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "spectrum_span.h"
218c2ecf20Sopenharmony_ci#include "spectrum_switchdev.h"
228c2ecf20Sopenharmony_ci#include "spectrum.h"
238c2ecf20Sopenharmony_ci#include "core.h"
248c2ecf20Sopenharmony_ci#include "reg.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_ops;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge {
298c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
308c2ecf20Sopenharmony_ci	struct {
318c2ecf20Sopenharmony_ci		struct delayed_work dw;
328c2ecf20Sopenharmony_ci#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
338c2ecf20Sopenharmony_ci		unsigned int interval; /* ms */
348c2ecf20Sopenharmony_ci	} fdb_notify;
358c2ecf20Sopenharmony_ci#define MLXSW_SP_MIN_AGEING_TIME 10
368c2ecf20Sopenharmony_ci#define MLXSW_SP_MAX_AGEING_TIME 1000000
378c2ecf20Sopenharmony_ci#define MLXSW_SP_DEFAULT_AGEING_TIME 300
388c2ecf20Sopenharmony_ci	u32 ageing_time;
398c2ecf20Sopenharmony_ci	bool vlan_enabled_exists;
408c2ecf20Sopenharmony_ci	struct list_head bridges_list;
418c2ecf20Sopenharmony_ci	DECLARE_BITMAP(mids_bitmap, MLXSW_SP_MID_MAX);
428c2ecf20Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *bridge_8021q_ops;
438c2ecf20Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *bridge_8021d_ops;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_device {
478c2ecf20Sopenharmony_ci	struct net_device *dev;
488c2ecf20Sopenharmony_ci	struct list_head list;
498c2ecf20Sopenharmony_ci	struct list_head ports_list;
508c2ecf20Sopenharmony_ci	struct list_head mids_list;
518c2ecf20Sopenharmony_ci	u8 vlan_enabled:1,
528c2ecf20Sopenharmony_ci	   multicast_enabled:1,
538c2ecf20Sopenharmony_ci	   mrouter:1;
548c2ecf20Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *ops;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_port {
588c2ecf20Sopenharmony_ci	struct net_device *dev;
598c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
608c2ecf20Sopenharmony_ci	struct list_head list;
618c2ecf20Sopenharmony_ci	struct list_head vlans_list;
628c2ecf20Sopenharmony_ci	unsigned int ref_count;
638c2ecf20Sopenharmony_ci	u8 stp_state;
648c2ecf20Sopenharmony_ci	unsigned long flags;
658c2ecf20Sopenharmony_ci	bool mrouter;
668c2ecf20Sopenharmony_ci	bool lagged;
678c2ecf20Sopenharmony_ci	union {
688c2ecf20Sopenharmony_ci		u16 lag_id;
698c2ecf20Sopenharmony_ci		u16 system_port;
708c2ecf20Sopenharmony_ci	};
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_vlan {
748c2ecf20Sopenharmony_ci	struct list_head list;
758c2ecf20Sopenharmony_ci	struct list_head port_vlan_list;
768c2ecf20Sopenharmony_ci	u16 vid;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_ops {
808c2ecf20Sopenharmony_ci	int (*port_join)(struct mlxsw_sp_bridge_device *bridge_device,
818c2ecf20Sopenharmony_ci			 struct mlxsw_sp_bridge_port *bridge_port,
828c2ecf20Sopenharmony_ci			 struct mlxsw_sp_port *mlxsw_sp_port,
838c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack);
848c2ecf20Sopenharmony_ci	void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device,
858c2ecf20Sopenharmony_ci			   struct mlxsw_sp_bridge_port *bridge_port,
868c2ecf20Sopenharmony_ci			   struct mlxsw_sp_port *mlxsw_sp_port);
878c2ecf20Sopenharmony_ci	int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device,
888c2ecf20Sopenharmony_ci			  const struct net_device *vxlan_dev, u16 vid,
898c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack);
908c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *
918c2ecf20Sopenharmony_ci		(*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
928c2ecf20Sopenharmony_ci			   u16 vid, struct netlink_ext_ack *extack);
938c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *
948c2ecf20Sopenharmony_ci		(*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device,
958c2ecf20Sopenharmony_ci			      u16 vid);
968c2ecf20Sopenharmony_ci	u16 (*fid_vid)(struct mlxsw_sp_bridge_device *bridge_device,
978c2ecf20Sopenharmony_ci		       const struct mlxsw_sp_fid *fid);
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int
1018c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp,
1028c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
1038c2ecf20Sopenharmony_ci			       u16 fid_index);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void
1068c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port,
1078c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void
1108c2ecf20Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp_port *mlxsw_sp_port,
1118c2ecf20Sopenharmony_ci				   struct mlxsw_sp_bridge_device
1128c2ecf20Sopenharmony_ci				   *bridge_device);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void
1158c2ecf20Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
1168c2ecf20Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
1178c2ecf20Sopenharmony_ci				 bool add);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
1208c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_find(const struct mlxsw_sp_bridge *bridge,
1218c2ecf20Sopenharmony_ci			    const struct net_device *br_dev)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_device, &bridge->bridges_list, list)
1268c2ecf20Sopenharmony_ci		if (bridge_device->dev == br_dev)
1278c2ecf20Sopenharmony_ci			return bridge_device;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return NULL;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cibool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp,
1338c2ecf20Sopenharmony_ci					 const struct net_device *br_dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return !!mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int mlxsw_sp_bridge_device_upper_rif_destroy(struct net_device *dev,
1398c2ecf20Sopenharmony_ci						    struct netdev_nested_priv *priv)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv->data;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev);
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp,
1488c2ecf20Sopenharmony_ci						struct net_device *dev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct netdev_nested_priv priv = {
1518c2ecf20Sopenharmony_ci		.data = (void *)mlxsw_sp,
1528c2ecf20Sopenharmony_ci	};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev);
1558c2ecf20Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev,
1568c2ecf20Sopenharmony_ci				      mlxsw_sp_bridge_device_upper_rif_destroy,
1578c2ecf20Sopenharmony_ci				      &priv);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge,
1618c2ecf20Sopenharmony_ci					     struct net_device *br_dev,
1628c2ecf20Sopenharmony_ci					     struct netlink_ext_ack *extack)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct net_device *dev, *stop_dev;
1658c2ecf20Sopenharmony_ci	struct list_head *iter;
1668c2ecf20Sopenharmony_ci	int err;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
1698c2ecf20Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev)) {
1708c2ecf20Sopenharmony_ci			err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp,
1718c2ecf20Sopenharmony_ci							 br_dev, dev, 0,
1728c2ecf20Sopenharmony_ci							 extack);
1738c2ecf20Sopenharmony_ci			if (err) {
1748c2ecf20Sopenharmony_ci				stop_dev = dev;
1758c2ecf20Sopenharmony_ci				goto err_vxlan_join;
1768c2ecf20Sopenharmony_ci			}
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cierr_vxlan_join:
1838c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
1848c2ecf20Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev)) {
1858c2ecf20Sopenharmony_ci			if (stop_dev == dev)
1868c2ecf20Sopenharmony_ci				break;
1878c2ecf20Sopenharmony_ci			mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci	return err;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge,
1948c2ecf20Sopenharmony_ci					      struct net_device *br_dev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct net_device *dev;
1978c2ecf20Sopenharmony_ci	struct list_head *iter;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
2008c2ecf20Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev))
2018c2ecf20Sopenharmony_ci			mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
2068c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge,
2078c2ecf20Sopenharmony_ci			      struct net_device *br_dev,
2088c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct device *dev = bridge->mlxsw_sp->bus_info->dev;
2118c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
2128c2ecf20Sopenharmony_ci	bool vlan_enabled = br_vlan_enabled(br_dev);
2138c2ecf20Sopenharmony_ci	int err;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (vlan_enabled && bridge->vlan_enabled_exists) {
2168c2ecf20Sopenharmony_ci		dev_err(dev, "Only one VLAN-aware bridge is supported\n");
2178c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported");
2188c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	bridge_device = kzalloc(sizeof(*bridge_device), GFP_KERNEL);
2228c2ecf20Sopenharmony_ci	if (!bridge_device)
2238c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	bridge_device->dev = br_dev;
2268c2ecf20Sopenharmony_ci	bridge_device->vlan_enabled = vlan_enabled;
2278c2ecf20Sopenharmony_ci	bridge_device->multicast_enabled = br_multicast_enabled(br_dev);
2288c2ecf20Sopenharmony_ci	bridge_device->mrouter = br_multicast_router(br_dev);
2298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bridge_device->ports_list);
2308c2ecf20Sopenharmony_ci	if (vlan_enabled) {
2318c2ecf20Sopenharmony_ci		bridge->vlan_enabled_exists = true;
2328c2ecf20Sopenharmony_ci		bridge_device->ops = bridge->bridge_8021q_ops;
2338c2ecf20Sopenharmony_ci	} else {
2348c2ecf20Sopenharmony_ci		bridge_device->ops = bridge->bridge_8021d_ops;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bridge_device->mids_list);
2378c2ecf20Sopenharmony_ci	list_add(&bridge_device->list, &bridge->bridges_list);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* It is possible we already have VXLAN devices enslaved to the bridge.
2408c2ecf20Sopenharmony_ci	 * In which case, we need to replay their configuration as if they were
2418c2ecf20Sopenharmony_ci	 * just now enslaved to the bridge.
2428c2ecf20Sopenharmony_ci	 */
2438c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack);
2448c2ecf20Sopenharmony_ci	if (err)
2458c2ecf20Sopenharmony_ci		goto err_vxlan_init;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return bridge_device;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cierr_vxlan_init:
2508c2ecf20Sopenharmony_ci	list_del(&bridge_device->list);
2518c2ecf20Sopenharmony_ci	if (bridge_device->vlan_enabled)
2528c2ecf20Sopenharmony_ci		bridge->vlan_enabled_exists = false;
2538c2ecf20Sopenharmony_ci	kfree(bridge_device);
2548c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void
2588c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge,
2598c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_device *bridge_device)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev);
2628c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp,
2638c2ecf20Sopenharmony_ci					    bridge_device->dev);
2648c2ecf20Sopenharmony_ci	list_del(&bridge_device->list);
2658c2ecf20Sopenharmony_ci	if (bridge_device->vlan_enabled)
2668c2ecf20Sopenharmony_ci		bridge->vlan_enabled_exists = false;
2678c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bridge_device->ports_list));
2688c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bridge_device->mids_list));
2698c2ecf20Sopenharmony_ci	kfree(bridge_device);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
2738c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge,
2748c2ecf20Sopenharmony_ci			   struct net_device *br_dev,
2758c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev);
2808c2ecf20Sopenharmony_ci	if (bridge_device)
2818c2ecf20Sopenharmony_ci		return bridge_device;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return mlxsw_sp_bridge_device_create(bridge, br_dev, extack);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic void
2878c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_put(struct mlxsw_sp_bridge *bridge,
2888c2ecf20Sopenharmony_ci			   struct mlxsw_sp_bridge_device *bridge_device)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	if (list_empty(&bridge_device->ports_list))
2918c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_device_destroy(bridge, bridge_device);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
2958c2ecf20Sopenharmony_ci__mlxsw_sp_bridge_port_find(const struct mlxsw_sp_bridge_device *bridge_device,
2968c2ecf20Sopenharmony_ci			    const struct net_device *brport_dev)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
3018c2ecf20Sopenharmony_ci		if (bridge_port->dev == brport_dev)
3028c2ecf20Sopenharmony_ci			return bridge_port;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return NULL;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_port *
3098c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_find(struct mlxsw_sp_bridge *bridge,
3108c2ecf20Sopenharmony_ci			  struct net_device *brport_dev)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev);
3138c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (!br_dev)
3168c2ecf20Sopenharmony_ci		return NULL;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev);
3198c2ecf20Sopenharmony_ci	if (!bridge_device)
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return __mlxsw_sp_bridge_port_find(bridge_device, brport_dev);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
3268c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device,
3278c2ecf20Sopenharmony_ci			    struct net_device *brport_dev)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
3308c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	bridge_port = kzalloc(sizeof(*bridge_port), GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!bridge_port)
3348c2ecf20Sopenharmony_ci		return NULL;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(brport_dev);
3378c2ecf20Sopenharmony_ci	bridge_port->lagged = mlxsw_sp_port->lagged;
3388c2ecf20Sopenharmony_ci	if (bridge_port->lagged)
3398c2ecf20Sopenharmony_ci		bridge_port->lag_id = mlxsw_sp_port->lag_id;
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		bridge_port->system_port = mlxsw_sp_port->local_port;
3428c2ecf20Sopenharmony_ci	bridge_port->dev = brport_dev;
3438c2ecf20Sopenharmony_ci	bridge_port->bridge_device = bridge_device;
3448c2ecf20Sopenharmony_ci	bridge_port->stp_state = BR_STATE_DISABLED;
3458c2ecf20Sopenharmony_ci	bridge_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC |
3468c2ecf20Sopenharmony_ci			     BR_MCAST_FLOOD;
3478c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bridge_port->vlans_list);
3488c2ecf20Sopenharmony_ci	list_add(&bridge_port->list, &bridge_device->ports_list);
3498c2ecf20Sopenharmony_ci	bridge_port->ref_count = 1;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return bridge_port;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void
3558c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	list_del(&bridge_port->list);
3588c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bridge_port->vlans_list));
3598c2ecf20Sopenharmony_ci	kfree(bridge_port);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
3638c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
3648c2ecf20Sopenharmony_ci			 struct net_device *brport_dev,
3658c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev);
3688c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
3698c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
3708c2ecf20Sopenharmony_ci	int err;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(bridge, brport_dev);
3738c2ecf20Sopenharmony_ci	if (bridge_port) {
3748c2ecf20Sopenharmony_ci		bridge_port->ref_count++;
3758c2ecf20Sopenharmony_ci		return bridge_port;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack);
3798c2ecf20Sopenharmony_ci	if (IS_ERR(bridge_device))
3808c2ecf20Sopenharmony_ci		return ERR_CAST(bridge_device);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev);
3838c2ecf20Sopenharmony_ci	if (!bridge_port) {
3848c2ecf20Sopenharmony_ci		err = -ENOMEM;
3858c2ecf20Sopenharmony_ci		goto err_bridge_port_create;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return bridge_port;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cierr_bridge_port_create:
3918c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_device_put(bridge, bridge_device);
3928c2ecf20Sopenharmony_ci	return ERR_PTR(err);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge,
3968c2ecf20Sopenharmony_ci				     struct mlxsw_sp_bridge_port *bridge_port)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (--bridge_port->ref_count != 0)
4018c2ecf20Sopenharmony_ci		return;
4028c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
4038c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_port_destroy(bridge_port);
4048c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_device_put(bridge, bridge_device);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port_vlan *
4088c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_find_by_bridge(struct mlxsw_sp_port *mlxsw_sp_port,
4098c2ecf20Sopenharmony_ci				  const struct mlxsw_sp_bridge_device *
4108c2ecf20Sopenharmony_ci				  bridge_device,
4118c2ecf20Sopenharmony_ci				  u16 vid)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
4168c2ecf20Sopenharmony_ci			    list) {
4178c2ecf20Sopenharmony_ci		if (!mlxsw_sp_port_vlan->bridge_port)
4188c2ecf20Sopenharmony_ci			continue;
4198c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_vlan->bridge_port->bridge_device !=
4208c2ecf20Sopenharmony_ci		    bridge_device)
4218c2ecf20Sopenharmony_ci			continue;
4228c2ecf20Sopenharmony_ci		if (bridge_device->vlan_enabled &&
4238c2ecf20Sopenharmony_ci		    mlxsw_sp_port_vlan->vid != vid)
4248c2ecf20Sopenharmony_ci			continue;
4258c2ecf20Sopenharmony_ci		return mlxsw_sp_port_vlan;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return NULL;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port_vlan*
4328c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_find_by_fid(struct mlxsw_sp_port *mlxsw_sp_port,
4338c2ecf20Sopenharmony_ci			       u16 fid_index)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
4388c2ecf20Sopenharmony_ci			    list) {
4398c2ecf20Sopenharmony_ci		struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		if (fid && mlxsw_sp_fid_index(fid) == fid_index)
4428c2ecf20Sopenharmony_ci			return mlxsw_sp_port_vlan;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return NULL;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
4498c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_find(const struct mlxsw_sp_bridge_port *bridge_port,
4508c2ecf20Sopenharmony_ci			  u16 vid)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
4558c2ecf20Sopenharmony_ci		if (bridge_vlan->vid == vid)
4568c2ecf20Sopenharmony_ci			return bridge_vlan;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return NULL;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
4638c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_create(struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	bridge_vlan = kzalloc(sizeof(*bridge_vlan), GFP_KERNEL);
4688c2ecf20Sopenharmony_ci	if (!bridge_vlan)
4698c2ecf20Sopenharmony_ci		return NULL;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bridge_vlan->port_vlan_list);
4728c2ecf20Sopenharmony_ci	bridge_vlan->vid = vid;
4738c2ecf20Sopenharmony_ci	list_add(&bridge_vlan->list, &bridge_port->vlans_list);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return bridge_vlan;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void
4798c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_destroy(struct mlxsw_sp_bridge_vlan *bridge_vlan)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	list_del(&bridge_vlan->list);
4828c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bridge_vlan->port_vlan_list));
4838c2ecf20Sopenharmony_ci	kfree(bridge_vlan);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
4878c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_get(struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
4928c2ecf20Sopenharmony_ci	if (bridge_vlan)
4938c2ecf20Sopenharmony_ci		return bridge_vlan;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return mlxsw_sp_bridge_vlan_create(bridge_port, vid);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_vlan_put(struct mlxsw_sp_bridge_vlan *bridge_vlan)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	if (list_empty(&bridge_vlan->port_vlan_list))
5018c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_vlan_destroy(bridge_vlan);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic int
5058c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
5068c2ecf20Sopenharmony_ci				  struct mlxsw_sp_bridge_vlan *bridge_vlan,
5078c2ecf20Sopenharmony_ci				  u8 state)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
5128c2ecf20Sopenharmony_ci			    bridge_vlan_node) {
5138c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
5148c2ecf20Sopenharmony_ci			continue;
5158c2ecf20Sopenharmony_ci		return mlxsw_sp_port_vid_stp_set(mlxsw_sp_port,
5168c2ecf20Sopenharmony_ci						 bridge_vlan->vid, state);
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return 0;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
5238c2ecf20Sopenharmony_ci					    struct switchdev_trans *trans,
5248c2ecf20Sopenharmony_ci					    struct net_device *orig_dev,
5258c2ecf20Sopenharmony_ci					    u8 state)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
5288c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
5298c2ecf20Sopenharmony_ci	int err;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
5328c2ecf20Sopenharmony_ci		return 0;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
5358c2ecf20Sopenharmony_ci	 * operation is executed due to it being deferred.
5368c2ecf20Sopenharmony_ci	 */
5378c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
5388c2ecf20Sopenharmony_ci						orig_dev);
5398c2ecf20Sopenharmony_ci	if (!bridge_port)
5408c2ecf20Sopenharmony_ci		return 0;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
5438c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port,
5448c2ecf20Sopenharmony_ci							bridge_vlan, state);
5458c2ecf20Sopenharmony_ci		if (err)
5468c2ecf20Sopenharmony_ci			goto err_port_bridge_vlan_stp_set;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	bridge_port->stp_state = state;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cierr_port_bridge_vlan_stp_set:
5548c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
5558c2ecf20Sopenharmony_ci					     &bridge_port->vlans_list, list)
5568c2ecf20Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, bridge_vlan,
5578c2ecf20Sopenharmony_ci						  bridge_port->stp_state);
5588c2ecf20Sopenharmony_ci	return err;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int
5628c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
5638c2ecf20Sopenharmony_ci				    struct mlxsw_sp_bridge_vlan *bridge_vlan,
5648c2ecf20Sopenharmony_ci				    enum mlxsw_sp_flood_type packet_type,
5658c2ecf20Sopenharmony_ci				    bool member)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
5708c2ecf20Sopenharmony_ci			    bridge_vlan_node) {
5718c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
5728c2ecf20Sopenharmony_ci			continue;
5738c2ecf20Sopenharmony_ci		return mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid,
5748c2ecf20Sopenharmony_ci					      packet_type,
5758c2ecf20Sopenharmony_ci					      mlxsw_sp_port->local_port,
5768c2ecf20Sopenharmony_ci					      member);
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return 0;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic int
5838c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
5848c2ecf20Sopenharmony_ci				     struct mlxsw_sp_bridge_port *bridge_port,
5858c2ecf20Sopenharmony_ci				     enum mlxsw_sp_flood_type packet_type,
5868c2ecf20Sopenharmony_ci				     bool member)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
5898c2ecf20Sopenharmony_ci	int err;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
5928c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port,
5938c2ecf20Sopenharmony_ci							  bridge_vlan,
5948c2ecf20Sopenharmony_ci							  packet_type,
5958c2ecf20Sopenharmony_ci							  member);
5968c2ecf20Sopenharmony_ci		if (err)
5978c2ecf20Sopenharmony_ci			goto err_port_bridge_vlan_flood_set;
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return 0;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cierr_port_bridge_vlan_flood_set:
6038c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
6048c2ecf20Sopenharmony_ci					     &bridge_port->vlans_list, list)
6058c2ecf20Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, bridge_vlan,
6068c2ecf20Sopenharmony_ci						    packet_type, !member);
6078c2ecf20Sopenharmony_ci	return err;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int
6118c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
6128c2ecf20Sopenharmony_ci				       struct mlxsw_sp_bridge_vlan *bridge_vlan,
6138c2ecf20Sopenharmony_ci				       bool set)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
6168c2ecf20Sopenharmony_ci	u16 vid = bridge_vlan->vid;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
6198c2ecf20Sopenharmony_ci			    bridge_vlan_node) {
6208c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
6218c2ecf20Sopenharmony_ci			continue;
6228c2ecf20Sopenharmony_ci		return mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set);
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic int
6298c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
6308c2ecf20Sopenharmony_ci				  struct mlxsw_sp_bridge_port *bridge_port,
6318c2ecf20Sopenharmony_ci				  bool set)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
6348c2ecf20Sopenharmony_ci	int err;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
6378c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port,
6388c2ecf20Sopenharmony_ci							     bridge_vlan, set);
6398c2ecf20Sopenharmony_ci		if (err)
6408c2ecf20Sopenharmony_ci			goto err_port_bridge_vlan_learning_set;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cierr_port_bridge_vlan_learning_set:
6468c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
6478c2ecf20Sopenharmony_ci					     &bridge_port->vlans_list, list)
6488c2ecf20Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port,
6498c2ecf20Sopenharmony_ci						       bridge_vlan, !set);
6508c2ecf20Sopenharmony_ci	return err;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port
6548c2ecf20Sopenharmony_ci					       *mlxsw_sp_port,
6558c2ecf20Sopenharmony_ci					       struct switchdev_trans *trans,
6568c2ecf20Sopenharmony_ci					       unsigned long brport_flags)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
6598c2ecf20Sopenharmony_ci		return -EINVAL;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return 0;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
6658c2ecf20Sopenharmony_ci					   struct switchdev_trans *trans,
6668c2ecf20Sopenharmony_ci					   struct net_device *orig_dev,
6678c2ecf20Sopenharmony_ci					   unsigned long brport_flags)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
6708c2ecf20Sopenharmony_ci	int err;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
6738c2ecf20Sopenharmony_ci		return 0;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
6768c2ecf20Sopenharmony_ci						orig_dev);
6778c2ecf20Sopenharmony_ci	if (!bridge_port)
6788c2ecf20Sopenharmony_ci		return 0;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
6818c2ecf20Sopenharmony_ci						   MLXSW_SP_FLOOD_TYPE_UC,
6828c2ecf20Sopenharmony_ci						   brport_flags & BR_FLOOD);
6838c2ecf20Sopenharmony_ci	if (err)
6848c2ecf20Sopenharmony_ci		return err;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, bridge_port,
6878c2ecf20Sopenharmony_ci						brport_flags & BR_LEARNING);
6888c2ecf20Sopenharmony_ci	if (err)
6898c2ecf20Sopenharmony_ci		return err;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (bridge_port->bridge_device->multicast_enabled)
6928c2ecf20Sopenharmony_ci		goto out;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
6958c2ecf20Sopenharmony_ci						   MLXSW_SP_FLOOD_TYPE_MC,
6968c2ecf20Sopenharmony_ci						   brport_flags &
6978c2ecf20Sopenharmony_ci						   BR_MCAST_FLOOD);
6988c2ecf20Sopenharmony_ci	if (err)
6998c2ecf20Sopenharmony_ci		return err;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ciout:
7028c2ecf20Sopenharmony_ci	memcpy(&bridge_port->flags, &brport_flags, sizeof(brport_flags));
7038c2ecf20Sopenharmony_ci	return 0;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	char sfdat_pl[MLXSW_REG_SFDAT_LEN];
7098c2ecf20Sopenharmony_ci	int err;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time);
7128c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl);
7138c2ecf20Sopenharmony_ci	if (err)
7148c2ecf20Sopenharmony_ci		return err;
7158c2ecf20Sopenharmony_ci	mlxsw_sp->bridge->ageing_time = ageing_time;
7168c2ecf20Sopenharmony_ci	return 0;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
7208c2ecf20Sopenharmony_ci					    struct switchdev_trans *trans,
7218c2ecf20Sopenharmony_ci					    unsigned long ageing_clock_t)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7248c2ecf20Sopenharmony_ci	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
7258c2ecf20Sopenharmony_ci	u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans)) {
7288c2ecf20Sopenharmony_ci		if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
7298c2ecf20Sopenharmony_ci		    ageing_time > MLXSW_SP_MAX_AGEING_TIME)
7308c2ecf20Sopenharmony_ci			return -ERANGE;
7318c2ecf20Sopenharmony_ci		else
7328c2ecf20Sopenharmony_ci			return 0;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
7398c2ecf20Sopenharmony_ci					  struct switchdev_trans *trans,
7408c2ecf20Sopenharmony_ci					  struct net_device *orig_dev,
7418c2ecf20Sopenharmony_ci					  bool vlan_enabled)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7448c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (!switchdev_trans_ph_prepare(trans))
7478c2ecf20Sopenharmony_ci		return 0;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
7508c2ecf20Sopenharmony_ci	if (WARN_ON(!bridge_device))
7518c2ecf20Sopenharmony_ci		return -EINVAL;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (bridge_device->vlan_enabled == vlan_enabled)
7548c2ecf20Sopenharmony_ci		return 0;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	netdev_err(bridge_device->dev, "VLAN filtering can't be changed for existing bridge\n");
7578c2ecf20Sopenharmony_ci	return -EINVAL;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
7618c2ecf20Sopenharmony_ci					  struct switchdev_trans *trans,
7628c2ecf20Sopenharmony_ci					  struct net_device *orig_dev,
7638c2ecf20Sopenharmony_ci					  bool is_port_mrouter)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
7668c2ecf20Sopenharmony_ci	int err;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
7698c2ecf20Sopenharmony_ci		return 0;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
7728c2ecf20Sopenharmony_ci						orig_dev);
7738c2ecf20Sopenharmony_ci	if (!bridge_port)
7748c2ecf20Sopenharmony_ci		return 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!bridge_port->bridge_device->multicast_enabled)
7778c2ecf20Sopenharmony_ci		goto out;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
7808c2ecf20Sopenharmony_ci						   MLXSW_SP_FLOOD_TYPE_MC,
7818c2ecf20Sopenharmony_ci						   is_port_mrouter);
7828c2ecf20Sopenharmony_ci	if (err)
7838c2ecf20Sopenharmony_ci		return err;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port,
7868c2ecf20Sopenharmony_ci					 is_port_mrouter);
7878c2ecf20Sopenharmony_ciout:
7888c2ecf20Sopenharmony_ci	bridge_port->mrouter = is_port_mrouter;
7898c2ecf20Sopenharmony_ci	return 0;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	const struct mlxsw_sp_bridge_device *bridge_device;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
7978c2ecf20Sopenharmony_ci	return bridge_device->multicast_enabled ? bridge_port->mrouter :
7988c2ecf20Sopenharmony_ci					bridge_port->flags & BR_MCAST_FLOOD;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
8028c2ecf20Sopenharmony_ci					 struct switchdev_trans *trans,
8038c2ecf20Sopenharmony_ci					 struct net_device *orig_dev,
8048c2ecf20Sopenharmony_ci					 bool mc_disabled)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
8078c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
8088c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
8098c2ecf20Sopenharmony_ci	int err;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
8128c2ecf20Sopenharmony_ci		return 0;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
8158c2ecf20Sopenharmony_ci	 * operation is executed due to it being deferred.
8168c2ecf20Sopenharmony_ci	 */
8178c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
8188c2ecf20Sopenharmony_ci	if (!bridge_device)
8198c2ecf20Sopenharmony_ci		return 0;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (bridge_device->multicast_enabled != !mc_disabled) {
8228c2ecf20Sopenharmony_ci		bridge_device->multicast_enabled = !mc_disabled;
8238c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp_port,
8248c2ecf20Sopenharmony_ci						   bridge_device);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
8288c2ecf20Sopenharmony_ci		enum mlxsw_sp_flood_type packet_type = MLXSW_SP_FLOOD_TYPE_MC;
8298c2ecf20Sopenharmony_ci		bool member = mlxsw_sp_mc_flood(bridge_port);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port,
8328c2ecf20Sopenharmony_ci							   bridge_port,
8338c2ecf20Sopenharmony_ci							   packet_type, member);
8348c2ecf20Sopenharmony_ci		if (err)
8358c2ecf20Sopenharmony_ci			return err;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	bridge_device->multicast_enabled = !mc_disabled;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic int mlxsw_sp_smid_router_port_set(struct mlxsw_sp *mlxsw_sp,
8448c2ecf20Sopenharmony_ci					 u16 mid_idx, bool add)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	char *smid_pl;
8478c2ecf20Sopenharmony_ci	int err;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
8508c2ecf20Sopenharmony_ci	if (!smid_pl)
8518c2ecf20Sopenharmony_ci		return -ENOMEM;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	mlxsw_reg_smid_pack(smid_pl, mid_idx,
8548c2ecf20Sopenharmony_ci			    mlxsw_sp_router_port(mlxsw_sp), add);
8558c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
8568c2ecf20Sopenharmony_ci	kfree(smid_pl);
8578c2ecf20Sopenharmony_ci	return err;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic void
8618c2ecf20Sopenharmony_cimlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp,
8628c2ecf20Sopenharmony_ci				   struct mlxsw_sp_bridge_device *bridge_device,
8638c2ecf20Sopenharmony_ci				   bool add)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	list_for_each_entry(mid, &bridge_device->mids_list, list)
8688c2ecf20Sopenharmony_ci		mlxsw_sp_smid_router_port_set(mlxsw_sp, mid->mid, add);
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int
8728c2ecf20Sopenharmony_cimlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
8738c2ecf20Sopenharmony_ci				  struct switchdev_trans *trans,
8748c2ecf20Sopenharmony_ci				  struct net_device *orig_dev,
8758c2ecf20Sopenharmony_ci				  bool is_mrouter)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
8788c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
8818c2ecf20Sopenharmony_ci		return 0;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
8848c2ecf20Sopenharmony_ci	 * operation is executed due to it being deferred.
8858c2ecf20Sopenharmony_ci	 */
8868c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
8878c2ecf20Sopenharmony_ci	if (!bridge_device)
8888c2ecf20Sopenharmony_ci		return 0;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (bridge_device->mrouter != is_mrouter)
8918c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_mrouter_update_mdb(mlxsw_sp, bridge_device,
8928c2ecf20Sopenharmony_ci						   is_mrouter);
8938c2ecf20Sopenharmony_ci	bridge_device->mrouter = is_mrouter;
8948c2ecf20Sopenharmony_ci	return 0;
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_set(struct net_device *dev,
8988c2ecf20Sopenharmony_ci				  const struct switchdev_attr *attr,
8998c2ecf20Sopenharmony_ci				  struct switchdev_trans *trans)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
9028c2ecf20Sopenharmony_ci	int err;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	switch (attr->id) {
9058c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
9068c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
9078c2ecf20Sopenharmony_ci						       attr->orig_dev,
9088c2ecf20Sopenharmony_ci						       attr->u.stp_state);
9098c2ecf20Sopenharmony_ci		break;
9108c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
9118c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port,
9128c2ecf20Sopenharmony_ci							  trans,
9138c2ecf20Sopenharmony_ci							  attr->u.brport_flags);
9148c2ecf20Sopenharmony_ci		break;
9158c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
9168c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans,
9178c2ecf20Sopenharmony_ci						      attr->orig_dev,
9188c2ecf20Sopenharmony_ci						      attr->u.brport_flags);
9198c2ecf20Sopenharmony_ci		break;
9208c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
9218c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
9228c2ecf20Sopenharmony_ci						       attr->u.ageing_time);
9238c2ecf20Sopenharmony_ci		break;
9248c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
9258c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
9268c2ecf20Sopenharmony_ci						     attr->orig_dev,
9278c2ecf20Sopenharmony_ci						     attr->u.vlan_filtering);
9288c2ecf20Sopenharmony_ci		break;
9298c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_MROUTER:
9308c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, trans,
9318c2ecf20Sopenharmony_ci						     attr->orig_dev,
9328c2ecf20Sopenharmony_ci						     attr->u.mrouter);
9338c2ecf20Sopenharmony_ci		break;
9348c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
9358c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans,
9368c2ecf20Sopenharmony_ci						    attr->orig_dev,
9378c2ecf20Sopenharmony_ci						    attr->u.mc_disabled);
9388c2ecf20Sopenharmony_ci		break;
9398c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
9408c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, trans,
9418c2ecf20Sopenharmony_ci							attr->orig_dev,
9428c2ecf20Sopenharmony_ci							attr->u.mrouter);
9438c2ecf20Sopenharmony_ci		break;
9448c2ecf20Sopenharmony_ci	default:
9458c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
9468c2ecf20Sopenharmony_ci		break;
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_commit(trans))
9508c2ecf20Sopenharmony_ci		mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	return err;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_cistatic int
9568c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
9578c2ecf20Sopenharmony_ci			    struct mlxsw_sp_bridge_port *bridge_port,
9588c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
9618c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
9628c2ecf20Sopenharmony_ci	u8 local_port = mlxsw_sp_port->local_port;
9638c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
9648c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
9658c2ecf20Sopenharmony_ci	int err;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
9688c2ecf20Sopenharmony_ci	fid = bridge_device->ops->fid_get(bridge_device, vid, extack);
9698c2ecf20Sopenharmony_ci	if (IS_ERR(fid))
9708c2ecf20Sopenharmony_ci		return PTR_ERR(fid);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port,
9738c2ecf20Sopenharmony_ci				     bridge_port->flags & BR_FLOOD);
9748c2ecf20Sopenharmony_ci	if (err)
9758c2ecf20Sopenharmony_ci		goto err_fid_uc_flood_set;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port,
9788c2ecf20Sopenharmony_ci				     mlxsw_sp_mc_flood(bridge_port));
9798c2ecf20Sopenharmony_ci	if (err)
9808c2ecf20Sopenharmony_ci		goto err_fid_mc_flood_set;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port,
9838c2ecf20Sopenharmony_ci				     true);
9848c2ecf20Sopenharmony_ci	if (err)
9858c2ecf20Sopenharmony_ci		goto err_fid_bc_flood_set;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
9888c2ecf20Sopenharmony_ci	if (err)
9898c2ecf20Sopenharmony_ci		goto err_fid_port_vid_map;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->fid = fid;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return 0;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cierr_fid_port_vid_map:
9968c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false);
9978c2ecf20Sopenharmony_cierr_fid_bc_flood_set:
9988c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false);
9998c2ecf20Sopenharmony_cierr_fid_mc_flood_set:
10008c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false);
10018c2ecf20Sopenharmony_cierr_fid_uc_flood_set:
10028c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
10038c2ecf20Sopenharmony_ci	return err;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic void
10078c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_fid_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
10108c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
10118c2ecf20Sopenharmony_ci	u8 local_port = mlxsw_sp_port->local_port;
10128c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->fid = NULL;
10158c2ecf20Sopenharmony_ci	mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
10168c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false);
10178c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false);
10188c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false);
10198c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic u16
10238c2ecf20Sopenharmony_cimlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port,
10248c2ecf20Sopenharmony_ci			     u16 vid, bool is_pvid)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	if (is_pvid)
10278c2ecf20Sopenharmony_ci		return vid;
10288c2ecf20Sopenharmony_ci	else if (mlxsw_sp_port->pvid == vid)
10298c2ecf20Sopenharmony_ci		return 0;	/* Dis-allow untagged packets */
10308c2ecf20Sopenharmony_ci	else
10318c2ecf20Sopenharmony_ci		return mlxsw_sp_port->pvid;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_cistatic int
10358c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
10368c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
10378c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
10408c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
10418c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
10428c2ecf20Sopenharmony_ci	int err;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	/* No need to continue if only VLAN flags were changed */
10458c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_vlan->bridge_port)
10468c2ecf20Sopenharmony_ci		return 0;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
10498c2ecf20Sopenharmony_ci					  extack);
10508c2ecf20Sopenharmony_ci	if (err)
10518c2ecf20Sopenharmony_ci		return err;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid,
10548c2ecf20Sopenharmony_ci					     bridge_port->flags & BR_LEARNING);
10558c2ecf20Sopenharmony_ci	if (err)
10568c2ecf20Sopenharmony_ci		goto err_port_vid_learning_set;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
10598c2ecf20Sopenharmony_ci					bridge_port->stp_state);
10608c2ecf20Sopenharmony_ci	if (err)
10618c2ecf20Sopenharmony_ci		goto err_port_vid_stp_set;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_get(bridge_port, vid);
10648c2ecf20Sopenharmony_ci	if (!bridge_vlan) {
10658c2ecf20Sopenharmony_ci		err = -ENOMEM;
10668c2ecf20Sopenharmony_ci		goto err_bridge_vlan_get;
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	list_add(&mlxsw_sp_port_vlan->bridge_vlan_node,
10708c2ecf20Sopenharmony_ci		 &bridge_vlan->port_vlan_list);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge,
10738c2ecf20Sopenharmony_ci				 bridge_port->dev, extack);
10748c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->bridge_port = bridge_port;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	return 0;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cierr_bridge_vlan_get:
10798c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED);
10808c2ecf20Sopenharmony_cierr_port_vid_stp_set:
10818c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
10828c2ecf20Sopenharmony_cierr_port_vid_learning_set:
10838c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan);
10848c2ecf20Sopenharmony_ci	return err;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_civoid
10888c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
10918c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
10928c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
10938c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
10948c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
10958c2ecf20Sopenharmony_ci	bool last_port, last_vlan;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021Q &&
10988c2ecf20Sopenharmony_ci		    mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021D))
10998c2ecf20Sopenharmony_ci		return;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
11028c2ecf20Sopenharmony_ci	last_vlan = list_is_singular(&bridge_port->vlans_list);
11038c2ecf20Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
11048c2ecf20Sopenharmony_ci	last_port = list_is_singular(&bridge_vlan->port_vlan_list);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	list_del(&mlxsw_sp_port_vlan->bridge_vlan_node);
11078c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_vlan_put(bridge_vlan);
11088c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED);
11098c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
11108c2ecf20Sopenharmony_ci	if (last_port)
11118c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_port_fdb_flush(mlxsw_sp_port->mlxsw_sp,
11128c2ecf20Sopenharmony_ci					       bridge_port,
11138c2ecf20Sopenharmony_ci					       mlxsw_sp_fid_index(fid));
11148c2ecf20Sopenharmony_ci	if (last_vlan)
11158c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp_port->mlxsw_sp->bridge, bridge_port);
11208c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->bridge_port = NULL;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic int
11248c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
11258c2ecf20Sopenharmony_ci			      struct mlxsw_sp_bridge_port *bridge_port,
11268c2ecf20Sopenharmony_ci			      u16 vid, bool is_untagged, bool is_pvid,
11278c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
11308c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
11318c2ecf20Sopenharmony_ci	u16 old_pvid = mlxsw_sp_port->pvid;
11328c2ecf20Sopenharmony_ci	int err;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	/* The only valid scenario in which a port-vlan already exists, is if
11358c2ecf20Sopenharmony_ci	 * the VLAN flags were changed and the port-vlan is associated with the
11368c2ecf20Sopenharmony_ci	 * correct bridge port
11378c2ecf20Sopenharmony_ci	 */
11388c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
11398c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_vlan &&
11408c2ecf20Sopenharmony_ci	    mlxsw_sp_port_vlan->bridge_port != bridge_port)
11418c2ecf20Sopenharmony_ci		return -EEXIST;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
11448c2ecf20Sopenharmony_ci		mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
11458c2ecf20Sopenharmony_ci							       vid);
11468c2ecf20Sopenharmony_ci		if (IS_ERR(mlxsw_sp_port_vlan))
11478c2ecf20Sopenharmony_ci			return PTR_ERR(mlxsw_sp_port_vlan);
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
11518c2ecf20Sopenharmony_ci				     is_untagged);
11528c2ecf20Sopenharmony_ci	if (err)
11538c2ecf20Sopenharmony_ci		goto err_port_vlan_set;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
11568c2ecf20Sopenharmony_ci	if (err)
11578c2ecf20Sopenharmony_ci		goto err_port_pvid_set;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
11608c2ecf20Sopenharmony_ci					     extack);
11618c2ecf20Sopenharmony_ci	if (err)
11628c2ecf20Sopenharmony_ci		goto err_port_vlan_bridge_join;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	return 0;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cierr_port_vlan_bridge_join:
11678c2ecf20Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
11688c2ecf20Sopenharmony_cierr_port_pvid_set:
11698c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
11708c2ecf20Sopenharmony_cierr_port_vlan_set:
11718c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
11728c2ecf20Sopenharmony_ci	return err;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_cistatic int
11768c2ecf20Sopenharmony_cimlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
11778c2ecf20Sopenharmony_ci				const struct net_device *br_dev,
11788c2ecf20Sopenharmony_ci				const struct switchdev_obj_port_vlan *vlan)
11798c2ecf20Sopenharmony_ci{
11808c2ecf20Sopenharmony_ci	u16 pvid;
11818c2ecf20Sopenharmony_ci	u16 vid;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev);
11848c2ecf20Sopenharmony_ci	if (!pvid)
11858c2ecf20Sopenharmony_ci		return 0;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
11888c2ecf20Sopenharmony_ci		if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
11898c2ecf20Sopenharmony_ci			if (vid != pvid) {
11908c2ecf20Sopenharmony_ci				netdev_err(br_dev, "Can't change PVID, it's used by router interface\n");
11918c2ecf20Sopenharmony_ci				return -EBUSY;
11928c2ecf20Sopenharmony_ci			}
11938c2ecf20Sopenharmony_ci		} else {
11948c2ecf20Sopenharmony_ci			if (vid == pvid) {
11958c2ecf20Sopenharmony_ci				netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n");
11968c2ecf20Sopenharmony_ci				return -EBUSY;
11978c2ecf20Sopenharmony_ci			}
11988c2ecf20Sopenharmony_ci		}
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return 0;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
12058c2ecf20Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan,
12068c2ecf20Sopenharmony_ci				   struct switchdev_trans *trans,
12078c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
12108c2ecf20Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
12118c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
12128c2ecf20Sopenharmony_ci	struct net_device *orig_dev = vlan->obj.orig_dev;
12138c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
12148c2ecf20Sopenharmony_ci	u16 vid;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(orig_dev)) {
12178c2ecf20Sopenharmony_ci		int err = 0;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci		if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) &&
12208c2ecf20Sopenharmony_ci		    br_vlan_enabled(orig_dev) &&
12218c2ecf20Sopenharmony_ci		    switchdev_trans_ph_prepare(trans))
12228c2ecf20Sopenharmony_ci			err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp,
12238c2ecf20Sopenharmony_ci							      orig_dev, vlan);
12248c2ecf20Sopenharmony_ci		if (!err)
12258c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
12268c2ecf20Sopenharmony_ci		return err;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_commit(trans))
12308c2ecf20Sopenharmony_ci		return 0;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
12338c2ecf20Sopenharmony_ci	if (WARN_ON(!bridge_port))
12348c2ecf20Sopenharmony_ci		return -EINVAL;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	if (!bridge_port->bridge_device->vlan_enabled)
12378c2ecf20Sopenharmony_ci		return 0;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
12408c2ecf20Sopenharmony_ci		int err;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
12438c2ecf20Sopenharmony_ci						    vid, flag_untagged,
12448c2ecf20Sopenharmony_ci						    flag_pvid, extack);
12458c2ecf20Sopenharmony_ci		if (err)
12468c2ecf20Sopenharmony_ci			return err;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	return 0;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	return lagged ? MLXSW_REG_SFDF_FLUSH_PER_LAG_AND_FID :
12558c2ecf20Sopenharmony_ci			MLXSW_REG_SFDF_FLUSH_PER_PORT_AND_FID;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic int
12598c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp,
12608c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
12618c2ecf20Sopenharmony_ci			       u16 fid_index)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	bool lagged = bridge_port->lagged;
12648c2ecf20Sopenharmony_ci	char sfdf_pl[MLXSW_REG_SFDF_LEN];
12658c2ecf20Sopenharmony_ci	u16 system_port;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	system_port = lagged ? bridge_port->lag_id : bridge_port->system_port;
12688c2ecf20Sopenharmony_ci	mlxsw_reg_sfdf_pack(sfdf_pl, mlxsw_sp_fdb_flush_type(lagged));
12698c2ecf20Sopenharmony_ci	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
12708c2ecf20Sopenharmony_ci	mlxsw_reg_sfdf_port_fid_system_port_set(sfdf_pl, system_port);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
12768c2ecf20Sopenharmony_ci{
12778c2ecf20Sopenharmony_ci	return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
12788c2ecf20Sopenharmony_ci			 MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG;
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
12848c2ecf20Sopenharmony_ci			MLXSW_REG_SFD_OP_WRITE_REMOVE;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp,
12888c2ecf20Sopenharmony_ci					  const char *mac, u16 fid,
12898c2ecf20Sopenharmony_ci					  enum mlxsw_sp_l3proto proto,
12908c2ecf20Sopenharmony_ci					  const union mlxsw_sp_l3addr *addr,
12918c2ecf20Sopenharmony_ci					  bool adding, bool dynamic)
12928c2ecf20Sopenharmony_ci{
12938c2ecf20Sopenharmony_ci	enum mlxsw_reg_sfd_uc_tunnel_protocol sfd_proto;
12948c2ecf20Sopenharmony_ci	char *sfd_pl;
12958c2ecf20Sopenharmony_ci	u8 num_rec;
12968c2ecf20Sopenharmony_ci	u32 uip;
12978c2ecf20Sopenharmony_ci	int err;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	switch (proto) {
13008c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
13018c2ecf20Sopenharmony_ci		uip = be32_to_cpu(addr->addr4);
13028c2ecf20Sopenharmony_ci		sfd_proto = MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV4;
13038c2ecf20Sopenharmony_ci		break;
13048c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
13058c2ecf20Sopenharmony_ci	default:
13068c2ecf20Sopenharmony_ci		WARN_ON(1);
13078c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
13118c2ecf20Sopenharmony_ci	if (!sfd_pl)
13128c2ecf20Sopenharmony_ci		return -ENOMEM;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
13158c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_uc_tunnel_pack(sfd_pl, 0,
13168c2ecf20Sopenharmony_ci				     mlxsw_sp_sfd_rec_policy(dynamic), mac, fid,
13178c2ecf20Sopenharmony_ci				     MLXSW_REG_SFD_REC_ACTION_NOP, uip,
13188c2ecf20Sopenharmony_ci				     sfd_proto);
13198c2ecf20Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
13208c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
13218c2ecf20Sopenharmony_ci	if (err)
13228c2ecf20Sopenharmony_ci		goto out;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
13258c2ecf20Sopenharmony_ci		err = -EBUSY;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ciout:
13288c2ecf20Sopenharmony_ci	kfree(sfd_pl);
13298c2ecf20Sopenharmony_ci	return err;
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
13338c2ecf20Sopenharmony_ci				     const char *mac, u16 fid, bool adding,
13348c2ecf20Sopenharmony_ci				     enum mlxsw_reg_sfd_rec_action action,
13358c2ecf20Sopenharmony_ci				     enum mlxsw_reg_sfd_rec_policy policy)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	char *sfd_pl;
13388c2ecf20Sopenharmony_ci	u8 num_rec;
13398c2ecf20Sopenharmony_ci	int err;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
13428c2ecf20Sopenharmony_ci	if (!sfd_pl)
13438c2ecf20Sopenharmony_ci		return -ENOMEM;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
13468c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, mac, fid, action, local_port);
13478c2ecf20Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
13488c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
13498c2ecf20Sopenharmony_ci	if (err)
13508c2ecf20Sopenharmony_ci		goto out;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
13538c2ecf20Sopenharmony_ci		err = -EBUSY;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ciout:
13568c2ecf20Sopenharmony_ci	kfree(sfd_pl);
13578c2ecf20Sopenharmony_ci	return err;
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
13618c2ecf20Sopenharmony_ci				   const char *mac, u16 fid, bool adding,
13628c2ecf20Sopenharmony_ci				   bool dynamic)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, adding,
13658c2ecf20Sopenharmony_ci					 MLXSW_REG_SFD_REC_ACTION_NOP,
13668c2ecf20Sopenharmony_ci					 mlxsw_sp_sfd_rec_policy(dynamic));
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ciint mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
13708c2ecf20Sopenharmony_ci			bool adding)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 0, mac, fid, adding,
13738c2ecf20Sopenharmony_ci					 MLXSW_REG_SFD_REC_ACTION_FORWARD_IP_ROUTER,
13748c2ecf20Sopenharmony_ci					 MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY);
13758c2ecf20Sopenharmony_ci}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
13788c2ecf20Sopenharmony_ci				       const char *mac, u16 fid, u16 lag_vid,
13798c2ecf20Sopenharmony_ci				       bool adding, bool dynamic)
13808c2ecf20Sopenharmony_ci{
13818c2ecf20Sopenharmony_ci	char *sfd_pl;
13828c2ecf20Sopenharmony_ci	u8 num_rec;
13838c2ecf20Sopenharmony_ci	int err;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
13868c2ecf20Sopenharmony_ci	if (!sfd_pl)
13878c2ecf20Sopenharmony_ci		return -ENOMEM;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
13908c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
13918c2ecf20Sopenharmony_ci				  mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
13928c2ecf20Sopenharmony_ci				  lag_vid, lag_id);
13938c2ecf20Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
13948c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
13958c2ecf20Sopenharmony_ci	if (err)
13968c2ecf20Sopenharmony_ci		goto out;
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
13998c2ecf20Sopenharmony_ci		err = -EBUSY;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ciout:
14028c2ecf20Sopenharmony_ci	kfree(sfd_pl);
14038c2ecf20Sopenharmony_ci	return err;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_cistatic int
14078c2ecf20Sopenharmony_cimlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port,
14088c2ecf20Sopenharmony_ci		      struct switchdev_notifier_fdb_info *fdb_info, bool adding)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
14118c2ecf20Sopenharmony_ci	struct net_device *orig_dev = fdb_info->info.dev;
14128c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
14138c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
14148c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
14158c2ecf20Sopenharmony_ci	u16 fid_index, vid;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
14188c2ecf20Sopenharmony_ci	if (!bridge_port)
14198c2ecf20Sopenharmony_ci		return -EINVAL;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
14228c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
14238c2ecf20Sopenharmony_ci							       bridge_device,
14248c2ecf20Sopenharmony_ci							       fdb_info->vid);
14258c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
14268c2ecf20Sopenharmony_ci		return 0;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
14298c2ecf20Sopenharmony_ci	vid = mlxsw_sp_port_vlan->vid;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (!bridge_port->lagged)
14328c2ecf20Sopenharmony_ci		return mlxsw_sp_port_fdb_uc_op(mlxsw_sp,
14338c2ecf20Sopenharmony_ci					       bridge_port->system_port,
14348c2ecf20Sopenharmony_ci					       fdb_info->addr, fid_index,
14358c2ecf20Sopenharmony_ci					       adding, false);
14368c2ecf20Sopenharmony_ci	else
14378c2ecf20Sopenharmony_ci		return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp,
14388c2ecf20Sopenharmony_ci						   bridge_port->lag_id,
14398c2ecf20Sopenharmony_ci						   fdb_info->addr, fid_index,
14408c2ecf20Sopenharmony_ci						   vid, adding, false);
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
14448c2ecf20Sopenharmony_ci				u16 fid, u16 mid_idx, bool adding)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	char *sfd_pl;
14478c2ecf20Sopenharmony_ci	u8 num_rec;
14488c2ecf20Sopenharmony_ci	int err;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
14518c2ecf20Sopenharmony_ci	if (!sfd_pl)
14528c2ecf20Sopenharmony_ci		return -ENOMEM;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
14558c2ecf20Sopenharmony_ci	mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
14568c2ecf20Sopenharmony_ci			      MLXSW_REG_SFD_REC_ACTION_NOP, mid_idx);
14578c2ecf20Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
14588c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
14598c2ecf20Sopenharmony_ci	if (err)
14608c2ecf20Sopenharmony_ci		goto out;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
14638c2ecf20Sopenharmony_ci		err = -EBUSY;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ciout:
14668c2ecf20Sopenharmony_ci	kfree(sfd_pl);
14678c2ecf20Sopenharmony_ci	return err;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx,
14718c2ecf20Sopenharmony_ci					 long *ports_bitmap,
14728c2ecf20Sopenharmony_ci					 bool set_router_port)
14738c2ecf20Sopenharmony_ci{
14748c2ecf20Sopenharmony_ci	char *smid_pl;
14758c2ecf20Sopenharmony_ci	int err, i;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
14788c2ecf20Sopenharmony_ci	if (!smid_pl)
14798c2ecf20Sopenharmony_ci		return -ENOMEM;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	mlxsw_reg_smid_pack(smid_pl, mid_idx, 0, false);
14828c2ecf20Sopenharmony_ci	for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) {
14838c2ecf20Sopenharmony_ci		if (mlxsw_sp->ports[i])
14848c2ecf20Sopenharmony_ci			mlxsw_reg_smid_port_mask_set(smid_pl, i, 1);
14858c2ecf20Sopenharmony_ci	}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	mlxsw_reg_smid_port_mask_set(smid_pl,
14888c2ecf20Sopenharmony_ci				     mlxsw_sp_router_port(mlxsw_sp), 1);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	for_each_set_bit(i, ports_bitmap, mlxsw_core_max_ports(mlxsw_sp->core))
14918c2ecf20Sopenharmony_ci		mlxsw_reg_smid_port_set(smid_pl, i, 1);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	mlxsw_reg_smid_port_set(smid_pl, mlxsw_sp_router_port(mlxsw_sp),
14948c2ecf20Sopenharmony_ci				set_router_port);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
14978c2ecf20Sopenharmony_ci	kfree(smid_pl);
14988c2ecf20Sopenharmony_ci	return err;
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port,
15028c2ecf20Sopenharmony_ci				  u16 mid_idx, bool add)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
15058c2ecf20Sopenharmony_ci	char *smid_pl;
15068c2ecf20Sopenharmony_ci	int err;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
15098c2ecf20Sopenharmony_ci	if (!smid_pl)
15108c2ecf20Sopenharmony_ci		return -ENOMEM;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	mlxsw_reg_smid_pack(smid_pl, mid_idx, mlxsw_sp_port->local_port, add);
15138c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
15148c2ecf20Sopenharmony_ci	kfree(smid_pl);
15158c2ecf20Sopenharmony_ci	return err;
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_cistatic struct
15198c2ecf20Sopenharmony_cimlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device,
15208c2ecf20Sopenharmony_ci				const unsigned char *addr,
15218c2ecf20Sopenharmony_ci				u16 fid)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	list_for_each_entry(mid, &bridge_device->mids_list, list) {
15268c2ecf20Sopenharmony_ci		if (ether_addr_equal(mid->addr, addr) && mid->fid == fid)
15278c2ecf20Sopenharmony_ci			return mid;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci	return NULL;
15308c2ecf20Sopenharmony_ci}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_cistatic void
15338c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp,
15348c2ecf20Sopenharmony_ci				      struct mlxsw_sp_bridge_port *bridge_port,
15358c2ecf20Sopenharmony_ci				      unsigned long *ports_bitmap)
15368c2ecf20Sopenharmony_ci{
15378c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
15388c2ecf20Sopenharmony_ci	u64 max_lag_members, i;
15398c2ecf20Sopenharmony_ci	int lag_id;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	if (!bridge_port->lagged) {
15428c2ecf20Sopenharmony_ci		set_bit(bridge_port->system_port, ports_bitmap);
15438c2ecf20Sopenharmony_ci	} else {
15448c2ecf20Sopenharmony_ci		max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
15458c2ecf20Sopenharmony_ci						     MAX_LAG_MEMBERS);
15468c2ecf20Sopenharmony_ci		lag_id = bridge_port->lag_id;
15478c2ecf20Sopenharmony_ci		for (i = 0; i < max_lag_members; i++) {
15488c2ecf20Sopenharmony_ci			mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp,
15498c2ecf20Sopenharmony_ci								 lag_id, i);
15508c2ecf20Sopenharmony_ci			if (mlxsw_sp_port)
15518c2ecf20Sopenharmony_ci				set_bit(mlxsw_sp_port->local_port,
15528c2ecf20Sopenharmony_ci					ports_bitmap);
15538c2ecf20Sopenharmony_ci		}
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_cistatic void
15588c2ecf20Sopenharmony_cimlxsw_sp_mc_get_mrouters_bitmap(unsigned long *flood_bitmap,
15598c2ecf20Sopenharmony_ci				struct mlxsw_sp_bridge_device *bridge_device,
15608c2ecf20Sopenharmony_ci				struct mlxsw_sp *mlxsw_sp)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
15658c2ecf20Sopenharmony_ci		if (bridge_port->mrouter) {
15668c2ecf20Sopenharmony_ci			mlxsw_sp_bridge_port_get_ports_bitmap(mlxsw_sp,
15678c2ecf20Sopenharmony_ci							      bridge_port,
15688c2ecf20Sopenharmony_ci							      flood_bitmap);
15698c2ecf20Sopenharmony_ci		}
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic bool
15748c2ecf20Sopenharmony_cimlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp,
15758c2ecf20Sopenharmony_ci			    struct mlxsw_sp_mid *mid,
15768c2ecf20Sopenharmony_ci			    struct mlxsw_sp_bridge_device *bridge_device)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	long *flood_bitmap;
15798c2ecf20Sopenharmony_ci	int num_of_ports;
15808c2ecf20Sopenharmony_ci	int alloc_size;
15818c2ecf20Sopenharmony_ci	u16 mid_idx;
15828c2ecf20Sopenharmony_ci	int err;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	mid_idx = find_first_zero_bit(mlxsw_sp->bridge->mids_bitmap,
15858c2ecf20Sopenharmony_ci				      MLXSW_SP_MID_MAX);
15868c2ecf20Sopenharmony_ci	if (mid_idx == MLXSW_SP_MID_MAX)
15878c2ecf20Sopenharmony_ci		return false;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	num_of_ports = mlxsw_core_max_ports(mlxsw_sp->core);
15908c2ecf20Sopenharmony_ci	alloc_size = sizeof(long) * BITS_TO_LONGS(num_of_ports);
15918c2ecf20Sopenharmony_ci	flood_bitmap = kzalloc(alloc_size, GFP_KERNEL);
15928c2ecf20Sopenharmony_ci	if (!flood_bitmap)
15938c2ecf20Sopenharmony_ci		return false;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	bitmap_copy(flood_bitmap,  mid->ports_in_mid, num_of_ports);
15968c2ecf20Sopenharmony_ci	mlxsw_sp_mc_get_mrouters_bitmap(flood_bitmap, bridge_device, mlxsw_sp);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	mid->mid = mid_idx;
15998c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, flood_bitmap,
16008c2ecf20Sopenharmony_ci					    bridge_device->mrouter);
16018c2ecf20Sopenharmony_ci	kfree(flood_bitmap);
16028c2ecf20Sopenharmony_ci	if (err)
16038c2ecf20Sopenharmony_ci		return false;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid_idx,
16068c2ecf20Sopenharmony_ci				   true);
16078c2ecf20Sopenharmony_ci	if (err)
16088c2ecf20Sopenharmony_ci		return false;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap);
16118c2ecf20Sopenharmony_ci	mid->in_hw = true;
16128c2ecf20Sopenharmony_ci	return true;
16138c2ecf20Sopenharmony_ci}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_cistatic int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp,
16168c2ecf20Sopenharmony_ci					struct mlxsw_sp_mid *mid)
16178c2ecf20Sopenharmony_ci{
16188c2ecf20Sopenharmony_ci	if (!mid->in_hw)
16198c2ecf20Sopenharmony_ci		return 0;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	clear_bit(mid->mid, mlxsw_sp->bridge->mids_bitmap);
16228c2ecf20Sopenharmony_ci	mid->in_hw = false;
16238c2ecf20Sopenharmony_ci	return mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid->mid,
16248c2ecf20Sopenharmony_ci				    false);
16258c2ecf20Sopenharmony_ci}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_cistatic struct
16288c2ecf20Sopenharmony_cimlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp,
16298c2ecf20Sopenharmony_ci				  struct mlxsw_sp_bridge_device *bridge_device,
16308c2ecf20Sopenharmony_ci				  const unsigned char *addr,
16318c2ecf20Sopenharmony_ci				  u16 fid)
16328c2ecf20Sopenharmony_ci{
16338c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	mid = kzalloc(sizeof(*mid), GFP_KERNEL);
16368c2ecf20Sopenharmony_ci	if (!mid)
16378c2ecf20Sopenharmony_ci		return NULL;
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	mid->ports_in_mid = bitmap_zalloc(mlxsw_core_max_ports(mlxsw_sp->core),
16408c2ecf20Sopenharmony_ci					  GFP_KERNEL);
16418c2ecf20Sopenharmony_ci	if (!mid->ports_in_mid)
16428c2ecf20Sopenharmony_ci		goto err_ports_in_mid_alloc;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	ether_addr_copy(mid->addr, addr);
16458c2ecf20Sopenharmony_ci	mid->fid = fid;
16468c2ecf20Sopenharmony_ci	mid->in_hw = false;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if (!bridge_device->multicast_enabled)
16498c2ecf20Sopenharmony_ci		goto out;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	if (!mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, bridge_device))
16528c2ecf20Sopenharmony_ci		goto err_write_mdb_entry;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ciout:
16558c2ecf20Sopenharmony_ci	list_add_tail(&mid->list, &bridge_device->mids_list);
16568c2ecf20Sopenharmony_ci	return mid;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_cierr_write_mdb_entry:
16598c2ecf20Sopenharmony_ci	bitmap_free(mid->ports_in_mid);
16608c2ecf20Sopenharmony_cierr_ports_in_mid_alloc:
16618c2ecf20Sopenharmony_ci	kfree(mid);
16628c2ecf20Sopenharmony_ci	return NULL;
16638c2ecf20Sopenharmony_ci}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port,
16668c2ecf20Sopenharmony_ci					 struct mlxsw_sp_mid *mid)
16678c2ecf20Sopenharmony_ci{
16688c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
16698c2ecf20Sopenharmony_ci	int err = 0;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	clear_bit(mlxsw_sp_port->local_port, mid->ports_in_mid);
16728c2ecf20Sopenharmony_ci	if (bitmap_empty(mid->ports_in_mid,
16738c2ecf20Sopenharmony_ci			 mlxsw_core_max_ports(mlxsw_sp->core))) {
16748c2ecf20Sopenharmony_ci		err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid);
16758c2ecf20Sopenharmony_ci		list_del(&mid->list);
16768c2ecf20Sopenharmony_ci		bitmap_free(mid->ports_in_mid);
16778c2ecf20Sopenharmony_ci		kfree(mid);
16788c2ecf20Sopenharmony_ci	}
16798c2ecf20Sopenharmony_ci	return err;
16808c2ecf20Sopenharmony_ci}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
16838c2ecf20Sopenharmony_ci				 const struct switchdev_obj_port_mdb *mdb,
16848c2ecf20Sopenharmony_ci				 struct switchdev_trans *trans)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
16878c2ecf20Sopenharmony_ci	struct net_device *orig_dev = mdb->obj.orig_dev;
16888c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
16898c2ecf20Sopenharmony_ci	struct net_device *dev = mlxsw_sp_port->dev;
16908c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
16918c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
16928c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
16938c2ecf20Sopenharmony_ci	u16 fid_index;
16948c2ecf20Sopenharmony_ci	int err = 0;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_commit(trans))
16978c2ecf20Sopenharmony_ci		return 0;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
17008c2ecf20Sopenharmony_ci	if (!bridge_port)
17018c2ecf20Sopenharmony_ci		return 0;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
17048c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
17058c2ecf20Sopenharmony_ci							       bridge_device,
17068c2ecf20Sopenharmony_ci							       mdb->vid);
17078c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
17088c2ecf20Sopenharmony_ci		return 0;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index);
17138c2ecf20Sopenharmony_ci	if (!mid) {
17148c2ecf20Sopenharmony_ci		mid = __mlxsw_sp_mc_alloc(mlxsw_sp, bridge_device, mdb->addr,
17158c2ecf20Sopenharmony_ci					  fid_index);
17168c2ecf20Sopenharmony_ci		if (!mid) {
17178c2ecf20Sopenharmony_ci			netdev_err(dev, "Unable to allocate MC group\n");
17188c2ecf20Sopenharmony_ci			return -ENOMEM;
17198c2ecf20Sopenharmony_ci		}
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci	set_bit(mlxsw_sp_port->local_port, mid->ports_in_mid);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	if (!bridge_device->multicast_enabled)
17248c2ecf20Sopenharmony_ci		return 0;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	if (bridge_port->mrouter)
17278c2ecf20Sopenharmony_ci		return 0;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true);
17308c2ecf20Sopenharmony_ci	if (err) {
17318c2ecf20Sopenharmony_ci		netdev_err(dev, "Unable to set SMID\n");
17328c2ecf20Sopenharmony_ci		goto err_out;
17338c2ecf20Sopenharmony_ci	}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	return 0;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_cierr_out:
17388c2ecf20Sopenharmony_ci	mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid);
17398c2ecf20Sopenharmony_ci	return err;
17408c2ecf20Sopenharmony_ci}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_cistatic void
17438c2ecf20Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp_port *mlxsw_sp_port,
17448c2ecf20Sopenharmony_ci				   struct mlxsw_sp_bridge_device
17458c2ecf20Sopenharmony_ci				   *bridge_device)
17468c2ecf20Sopenharmony_ci{
17478c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
17488c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
17498c2ecf20Sopenharmony_ci	bool mc_enabled;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	mc_enabled = bridge_device->multicast_enabled;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	list_for_each_entry(mid, &bridge_device->mids_list, list) {
17548c2ecf20Sopenharmony_ci		if (mc_enabled)
17558c2ecf20Sopenharmony_ci			mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid,
17568c2ecf20Sopenharmony_ci						    bridge_device);
17578c2ecf20Sopenharmony_ci		else
17588c2ecf20Sopenharmony_ci			mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid);
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci}
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_cistatic void
17638c2ecf20Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
17648c2ecf20Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
17658c2ecf20Sopenharmony_ci				 bool add)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
17688c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	list_for_each_entry(mid, &bridge_device->mids_list, list) {
17738c2ecf20Sopenharmony_ci		if (!test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid))
17748c2ecf20Sopenharmony_ci			mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, add);
17758c2ecf20Sopenharmony_ci	}
17768c2ecf20Sopenharmony_ci}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev,
17798c2ecf20Sopenharmony_ci				 const struct switchdev_obj *obj,
17808c2ecf20Sopenharmony_ci				 struct switchdev_trans *trans,
17818c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
17828c2ecf20Sopenharmony_ci{
17838c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
17848c2ecf20Sopenharmony_ci	const struct switchdev_obj_port_vlan *vlan;
17858c2ecf20Sopenharmony_ci	int err = 0;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	switch (obj->id) {
17888c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
17898c2ecf20Sopenharmony_ci		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
17908c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans,
17918c2ecf20Sopenharmony_ci					      extack);
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci		if (switchdev_trans_ph_prepare(trans)) {
17948c2ecf20Sopenharmony_ci			/* The event is emitted before the changes are actually
17958c2ecf20Sopenharmony_ci			 * applied to the bridge. Therefore schedule the respin
17968c2ecf20Sopenharmony_ci			 * call for later, so that the respin logic sees the
17978c2ecf20Sopenharmony_ci			 * updated bridge state.
17988c2ecf20Sopenharmony_ci			 */
17998c2ecf20Sopenharmony_ci			mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
18008c2ecf20Sopenharmony_ci		}
18018c2ecf20Sopenharmony_ci		break;
18028c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
18038c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
18048c2ecf20Sopenharmony_ci					    SWITCHDEV_OBJ_PORT_MDB(obj),
18058c2ecf20Sopenharmony_ci					    trans);
18068c2ecf20Sopenharmony_ci		break;
18078c2ecf20Sopenharmony_ci	default:
18088c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
18098c2ecf20Sopenharmony_ci		break;
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	return err;
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_cistatic void
18168c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port,
18178c2ecf20Sopenharmony_ci			      struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
18188c2ecf20Sopenharmony_ci{
18198c2ecf20Sopenharmony_ci	u16 pvid = mlxsw_sp_port->pvid == vid ? 0 : mlxsw_sp_port->pvid;
18208c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
18238c2ecf20Sopenharmony_ci	if (WARN_ON(!mlxsw_sp_port_vlan))
18248c2ecf20Sopenharmony_ci		return;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
18278c2ecf20Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
18288c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
18298c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
18308c2ecf20Sopenharmony_ci}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
18338c2ecf20Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
18368c2ecf20Sopenharmony_ci	struct net_device *orig_dev = vlan->obj.orig_dev;
18378c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
18388c2ecf20Sopenharmony_ci	u16 vid;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(orig_dev))
18418c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
18448c2ecf20Sopenharmony_ci	if (WARN_ON(!bridge_port))
18458c2ecf20Sopenharmony_ci		return -EINVAL;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	if (!bridge_port->bridge_device->vlan_enabled)
18488c2ecf20Sopenharmony_ci		return 0;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
18518c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vid);
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	return 0;
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_cistatic int
18578c2ecf20Sopenharmony_ci__mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
18588c2ecf20Sopenharmony_ci			struct mlxsw_sp_bridge_port *bridge_port,
18598c2ecf20Sopenharmony_ci			struct mlxsw_sp_mid *mid)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct net_device *dev = mlxsw_sp_port->dev;
18628c2ecf20Sopenharmony_ci	int err;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (bridge_port->bridge_device->multicast_enabled &&
18658c2ecf20Sopenharmony_ci	    !bridge_port->mrouter) {
18668c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false);
18678c2ecf20Sopenharmony_ci		if (err)
18688c2ecf20Sopenharmony_ci			netdev_err(dev, "Unable to remove port from SMID\n");
18698c2ecf20Sopenharmony_ci	}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid);
18728c2ecf20Sopenharmony_ci	if (err)
18738c2ecf20Sopenharmony_ci		netdev_err(dev, "Unable to remove MC SFD\n");
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	return err;
18768c2ecf20Sopenharmony_ci}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
18798c2ecf20Sopenharmony_ci				 const struct switchdev_obj_port_mdb *mdb)
18808c2ecf20Sopenharmony_ci{
18818c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
18828c2ecf20Sopenharmony_ci	struct net_device *orig_dev = mdb->obj.orig_dev;
18838c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
18848c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
18858c2ecf20Sopenharmony_ci	struct net_device *dev = mlxsw_sp_port->dev;
18868c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
18878c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid;
18888c2ecf20Sopenharmony_ci	u16 fid_index;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
18918c2ecf20Sopenharmony_ci	if (!bridge_port)
18928c2ecf20Sopenharmony_ci		return 0;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
18958c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
18968c2ecf20Sopenharmony_ci							       bridge_device,
18978c2ecf20Sopenharmony_ci							       mdb->vid);
18988c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
18998c2ecf20Sopenharmony_ci		return 0;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index);
19048c2ecf20Sopenharmony_ci	if (!mid) {
19058c2ecf20Sopenharmony_ci		netdev_err(dev, "Unable to remove port from MC DB\n");
19068c2ecf20Sopenharmony_ci		return -EINVAL;
19078c2ecf20Sopenharmony_ci	}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	return __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mid);
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic void
19138c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port,
19148c2ecf20Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port)
19158c2ecf20Sopenharmony_ci{
19168c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
19178c2ecf20Sopenharmony_ci	struct mlxsw_sp_mid *mid, *tmp;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mid, tmp, &bridge_device->mids_list, list) {
19228c2ecf20Sopenharmony_ci		if (test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid)) {
19238c2ecf20Sopenharmony_ci			__mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port,
19248c2ecf20Sopenharmony_ci						mid);
19258c2ecf20Sopenharmony_ci		} else if (bridge_device->multicast_enabled &&
19268c2ecf20Sopenharmony_ci			   bridge_port->mrouter) {
19278c2ecf20Sopenharmony_ci			mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false);
19288c2ecf20Sopenharmony_ci		}
19298c2ecf20Sopenharmony_ci	}
19308c2ecf20Sopenharmony_ci}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev,
19338c2ecf20Sopenharmony_ci				 const struct switchdev_obj *obj)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
19368c2ecf20Sopenharmony_ci	int err = 0;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	switch (obj->id) {
19398c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
19408c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
19418c2ecf20Sopenharmony_ci					      SWITCHDEV_OBJ_PORT_VLAN(obj));
19428c2ecf20Sopenharmony_ci		break;
19438c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
19448c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
19458c2ecf20Sopenharmony_ci					    SWITCHDEV_OBJ_PORT_MDB(obj));
19468c2ecf20Sopenharmony_ci		break;
19478c2ecf20Sopenharmony_ci	default:
19488c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
19498c2ecf20Sopenharmony_ci		break;
19508c2ecf20Sopenharmony_ci	}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	return err;
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
19588c2ecf20Sopenharmony_ci						   u16 lag_id)
19598c2ecf20Sopenharmony_ci{
19608c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
19618c2ecf20Sopenharmony_ci	u64 max_lag_members;
19628c2ecf20Sopenharmony_ci	int i;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
19658c2ecf20Sopenharmony_ci					     MAX_LAG_MEMBERS);
19668c2ecf20Sopenharmony_ci	for (i = 0; i < max_lag_members; i++) {
19678c2ecf20Sopenharmony_ci		mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
19688c2ecf20Sopenharmony_ci		if (mlxsw_sp_port)
19698c2ecf20Sopenharmony_ci			return mlxsw_sp_port;
19708c2ecf20Sopenharmony_ci	}
19718c2ecf20Sopenharmony_ci	return NULL;
19728c2ecf20Sopenharmony_ci}
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_cistatic int
19758c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
19768c2ecf20Sopenharmony_ci				struct mlxsw_sp_bridge_port *bridge_port,
19778c2ecf20Sopenharmony_ci				struct mlxsw_sp_port *mlxsw_sp_port,
19788c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
19798c2ecf20Sopenharmony_ci{
19808c2ecf20Sopenharmony_ci	if (is_vlan_dev(bridge_port->dev)) {
19818c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge");
19828c2ecf20Sopenharmony_ci		return -EINVAL;
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	/* Port is no longer usable as a router interface */
19868c2ecf20Sopenharmony_ci	if (mlxsw_sp_port->default_vlan->fid)
19878c2ecf20Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	return 0;
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_cistatic void
19938c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
19948c2ecf20Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
19958c2ecf20Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port)
19968c2ecf20Sopenharmony_ci{
19978c2ecf20Sopenharmony_ci	/* Make sure untagged frames are allowed to ingress */
19988c2ecf20Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
19998c2ecf20Sopenharmony_ci}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_cistatic int
20028c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
20038c2ecf20Sopenharmony_ci				 const struct net_device *vxlan_dev, u16 vid,
20048c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
20058c2ecf20Sopenharmony_ci{
20068c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
20078c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
20088c2ecf20Sopenharmony_ci	struct mlxsw_sp_nve_params params = {
20098c2ecf20Sopenharmony_ci		.type = MLXSW_SP_NVE_TYPE_VXLAN,
20108c2ecf20Sopenharmony_ci		.vni = vxlan->cfg.vni,
20118c2ecf20Sopenharmony_ci		.dev = vxlan_dev,
20128c2ecf20Sopenharmony_ci	};
20138c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
20148c2ecf20Sopenharmony_ci	int err;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	/* If the VLAN is 0, we need to find the VLAN that is configured as
20178c2ecf20Sopenharmony_ci	 * PVID and egress untagged on the bridge port of the VxLAN device.
20188c2ecf20Sopenharmony_ci	 * It is possible no such VLAN exists
20198c2ecf20Sopenharmony_ci	 */
20208c2ecf20Sopenharmony_ci	if (!vid) {
20218c2ecf20Sopenharmony_ci		err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid);
20228c2ecf20Sopenharmony_ci		if (err || !vid)
20238c2ecf20Sopenharmony_ci			return err;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
20278c2ecf20Sopenharmony_ci	if (IS_ERR(fid)) {
20288c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID");
20298c2ecf20Sopenharmony_ci		return PTR_ERR(fid);
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	if (mlxsw_sp_fid_vni_is_set(fid)) {
20338c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID");
20348c2ecf20Sopenharmony_ci		err = -EINVAL;
20358c2ecf20Sopenharmony_ci		goto err_vni_exists;
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
20398c2ecf20Sopenharmony_ci	if (err)
20408c2ecf20Sopenharmony_ci		goto err_nve_fid_enable;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	return 0;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_cierr_nve_fid_enable:
20458c2ecf20Sopenharmony_cierr_vni_exists:
20468c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
20478c2ecf20Sopenharmony_ci	return err;
20488c2ecf20Sopenharmony_ci}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_cistatic struct net_device *
20518c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid)
20528c2ecf20Sopenharmony_ci{
20538c2ecf20Sopenharmony_ci	struct net_device *dev;
20548c2ecf20Sopenharmony_ci	struct list_head *iter;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
20578c2ecf20Sopenharmony_ci		u16 pvid;
20588c2ecf20Sopenharmony_ci		int err;
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci		if (!netif_is_vxlan(dev))
20618c2ecf20Sopenharmony_ci			continue;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci		err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
20648c2ecf20Sopenharmony_ci		if (err || pvid != vid)
20658c2ecf20Sopenharmony_ci			continue;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci		return dev;
20688c2ecf20Sopenharmony_ci	}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	return NULL;
20718c2ecf20Sopenharmony_ci}
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
20748c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
20758c2ecf20Sopenharmony_ci			      u16 vid, struct netlink_ext_ack *extack)
20768c2ecf20Sopenharmony_ci{
20778c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
20838c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
20848c2ecf20Sopenharmony_ci				 u16 vid)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
20898c2ecf20Sopenharmony_ci}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_cistatic u16
20928c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_vid(struct mlxsw_sp_bridge_device *bridge_device,
20938c2ecf20Sopenharmony_ci			      const struct mlxsw_sp_fid *fid)
20948c2ecf20Sopenharmony_ci{
20958c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021q_vid(fid);
20968c2ecf20Sopenharmony_ci}
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = {
20998c2ecf20Sopenharmony_ci	.port_join	= mlxsw_sp_bridge_8021q_port_join,
21008c2ecf20Sopenharmony_ci	.port_leave	= mlxsw_sp_bridge_8021q_port_leave,
21018c2ecf20Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021q_vxlan_join,
21028c2ecf20Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021q_fid_get,
21038c2ecf20Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021q_fid_lookup,
21048c2ecf20Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021q_fid_vid,
21058c2ecf20Sopenharmony_ci};
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_cistatic bool
21088c2ecf20Sopenharmony_cimlxsw_sp_port_is_br_member(const struct mlxsw_sp_port *mlxsw_sp_port,
21098c2ecf20Sopenharmony_ci			   const struct net_device *br_dev)
21108c2ecf20Sopenharmony_ci{
21118c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
21148c2ecf20Sopenharmony_ci			    list) {
21158c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_vlan->bridge_port &&
21168c2ecf20Sopenharmony_ci		    mlxsw_sp_port_vlan->bridge_port->bridge_device->dev ==
21178c2ecf20Sopenharmony_ci		    br_dev)
21188c2ecf20Sopenharmony_ci			return true;
21198c2ecf20Sopenharmony_ci	}
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci	return false;
21228c2ecf20Sopenharmony_ci}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cistatic int
21258c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
21268c2ecf20Sopenharmony_ci				struct mlxsw_sp_bridge_port *bridge_port,
21278c2ecf20Sopenharmony_ci				struct mlxsw_sp_port *mlxsw_sp_port,
21288c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
21298c2ecf20Sopenharmony_ci{
21308c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
21318c2ecf20Sopenharmony_ci	struct net_device *dev = bridge_port->dev;
21328c2ecf20Sopenharmony_ci	u16 vid;
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
21358c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
21368c2ecf20Sopenharmony_ci	if (WARN_ON(!mlxsw_sp_port_vlan))
21378c2ecf20Sopenharmony_ci		return -EINVAL;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_is_br_member(mlxsw_sp_port, bridge_device->dev)) {
21408c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not bridge VLAN uppers of the same port");
21418c2ecf20Sopenharmony_ci		return -EINVAL;
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	/* Port is no longer usable as a router interface */
21458c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_vlan->fid)
21468c2ecf20Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
21498c2ecf20Sopenharmony_ci					      extack);
21508c2ecf20Sopenharmony_ci}
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_cistatic void
21538c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
21548c2ecf20Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
21558c2ecf20Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port)
21568c2ecf20Sopenharmony_ci{
21578c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
21588c2ecf20Sopenharmony_ci	struct net_device *dev = bridge_port->dev;
21598c2ecf20Sopenharmony_ci	u16 vid;
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci	vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
21628c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
21638c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port)
21648c2ecf20Sopenharmony_ci		return;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
21678c2ecf20Sopenharmony_ci}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_cistatic int
21708c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
21718c2ecf20Sopenharmony_ci				 const struct net_device *vxlan_dev, u16 vid,
21728c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
21738c2ecf20Sopenharmony_ci{
21748c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
21758c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
21768c2ecf20Sopenharmony_ci	struct mlxsw_sp_nve_params params = {
21778c2ecf20Sopenharmony_ci		.type = MLXSW_SP_NVE_TYPE_VXLAN,
21788c2ecf20Sopenharmony_ci		.vni = vxlan->cfg.vni,
21798c2ecf20Sopenharmony_ci		.dev = vxlan_dev,
21808c2ecf20Sopenharmony_ci	};
21818c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
21828c2ecf20Sopenharmony_ci	int err;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
21858c2ecf20Sopenharmony_ci	if (IS_ERR(fid)) {
21868c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID");
21878c2ecf20Sopenharmony_ci		return -EINVAL;
21888c2ecf20Sopenharmony_ci	}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci	if (mlxsw_sp_fid_vni_is_set(fid)) {
21918c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID");
21928c2ecf20Sopenharmony_ci		err = -EINVAL;
21938c2ecf20Sopenharmony_ci		goto err_vni_exists;
21948c2ecf20Sopenharmony_ci	}
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
21978c2ecf20Sopenharmony_ci	if (err)
21988c2ecf20Sopenharmony_ci		goto err_nve_fid_enable;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	return 0;
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_cierr_nve_fid_enable:
22038c2ecf20Sopenharmony_cierr_vni_exists:
22048c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
22058c2ecf20Sopenharmony_ci	return err;
22068c2ecf20Sopenharmony_ci}
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
22098c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
22108c2ecf20Sopenharmony_ci			      u16 vid, struct netlink_ext_ack *extack)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
22188c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
22198c2ecf20Sopenharmony_ci				 u16 vid)
22208c2ecf20Sopenharmony_ci{
22218c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	/* The only valid VLAN for a VLAN-unaware bridge is 0 */
22248c2ecf20Sopenharmony_ci	if (vid)
22258c2ecf20Sopenharmony_ci		return NULL;
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
22288c2ecf20Sopenharmony_ci}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_cistatic u16
22318c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_vid(struct mlxsw_sp_bridge_device *bridge_device,
22328c2ecf20Sopenharmony_ci			      const struct mlxsw_sp_fid *fid)
22338c2ecf20Sopenharmony_ci{
22348c2ecf20Sopenharmony_ci	return 0;
22358c2ecf20Sopenharmony_ci}
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = {
22388c2ecf20Sopenharmony_ci	.port_join	= mlxsw_sp_bridge_8021d_port_join,
22398c2ecf20Sopenharmony_ci	.port_leave	= mlxsw_sp_bridge_8021d_port_leave,
22408c2ecf20Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021d_vxlan_join,
22418c2ecf20Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021d_fid_get,
22428c2ecf20Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021d_fid_lookup,
22438c2ecf20Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021d_fid_vid,
22448c2ecf20Sopenharmony_ci};
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ciint mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
22478c2ecf20Sopenharmony_ci			      struct net_device *brport_dev,
22488c2ecf20Sopenharmony_ci			      struct net_device *br_dev,
22498c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
22508c2ecf20Sopenharmony_ci{
22518c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
22528c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
22538c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
22548c2ecf20Sopenharmony_ci	int err;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev,
22578c2ecf20Sopenharmony_ci					       extack);
22588c2ecf20Sopenharmony_ci	if (IS_ERR(bridge_port))
22598c2ecf20Sopenharmony_ci		return PTR_ERR(bridge_port);
22608c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	err = bridge_device->ops->port_join(bridge_device, bridge_port,
22638c2ecf20Sopenharmony_ci					    mlxsw_sp_port, extack);
22648c2ecf20Sopenharmony_ci	if (err)
22658c2ecf20Sopenharmony_ci		goto err_port_join;
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	return 0;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_cierr_port_join:
22708c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
22718c2ecf20Sopenharmony_ci	return err;
22728c2ecf20Sopenharmony_ci}
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_civoid mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
22758c2ecf20Sopenharmony_ci				struct net_device *brport_dev,
22768c2ecf20Sopenharmony_ci				struct net_device *br_dev)
22778c2ecf20Sopenharmony_ci{
22788c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
22798c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
22808c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
22838c2ecf20Sopenharmony_ci	if (!bridge_device)
22848c2ecf20Sopenharmony_ci		return;
22858c2ecf20Sopenharmony_ci	bridge_port = __mlxsw_sp_bridge_port_find(bridge_device, brport_dev);
22868c2ecf20Sopenharmony_ci	if (!bridge_port)
22878c2ecf20Sopenharmony_ci		return;
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	bridge_device->ops->port_leave(bridge_device, bridge_port,
22908c2ecf20Sopenharmony_ci				       mlxsw_sp_port);
22918c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
22928c2ecf20Sopenharmony_ci}
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ciint mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
22958c2ecf20Sopenharmony_ci			       const struct net_device *br_dev,
22968c2ecf20Sopenharmony_ci			       const struct net_device *vxlan_dev, u16 vid,
22978c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
22988c2ecf20Sopenharmony_ci{
22998c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
23028c2ecf20Sopenharmony_ci	if (WARN_ON(!bridge_device))
23038c2ecf20Sopenharmony_ci		return -EINVAL;
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid,
23068c2ecf20Sopenharmony_ci					      extack);
23078c2ecf20Sopenharmony_ci}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_civoid mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
23108c2ecf20Sopenharmony_ci				 const struct net_device *vxlan_dev)
23118c2ecf20Sopenharmony_ci{
23128c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
23138c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	/* If the VxLAN device is down, then the FID does not have a VNI */
23168c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
23178c2ecf20Sopenharmony_ci	if (!fid)
23188c2ecf20Sopenharmony_ci		return;
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci	mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
23218c2ecf20Sopenharmony_ci	/* Drop both the reference we just took during lookup and the reference
23228c2ecf20Sopenharmony_ci	 * the VXLAN device took.
23238c2ecf20Sopenharmony_ci	 */
23248c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
23258c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
23268c2ecf20Sopenharmony_ci}
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_cistatic void
23298c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
23308c2ecf20Sopenharmony_ci				      enum mlxsw_sp_l3proto *proto,
23318c2ecf20Sopenharmony_ci				      union mlxsw_sp_l3addr *addr)
23328c2ecf20Sopenharmony_ci{
23338c2ecf20Sopenharmony_ci	if (vxlan_addr->sa.sa_family == AF_INET) {
23348c2ecf20Sopenharmony_ci		addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
23358c2ecf20Sopenharmony_ci		*proto = MLXSW_SP_L3_PROTO_IPV4;
23368c2ecf20Sopenharmony_ci	} else {
23378c2ecf20Sopenharmony_ci		addr->addr6 = vxlan_addr->sin6.sin6_addr;
23388c2ecf20Sopenharmony_ci		*proto = MLXSW_SP_L3_PROTO_IPV6;
23398c2ecf20Sopenharmony_ci	}
23408c2ecf20Sopenharmony_ci}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_cistatic void
23438c2ecf20Sopenharmony_cimlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto,
23448c2ecf20Sopenharmony_ci				      const union mlxsw_sp_l3addr *addr,
23458c2ecf20Sopenharmony_ci				      union vxlan_addr *vxlan_addr)
23468c2ecf20Sopenharmony_ci{
23478c2ecf20Sopenharmony_ci	switch (proto) {
23488c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
23498c2ecf20Sopenharmony_ci		vxlan_addr->sa.sa_family = AF_INET;
23508c2ecf20Sopenharmony_ci		vxlan_addr->sin.sin_addr.s_addr = addr->addr4;
23518c2ecf20Sopenharmony_ci		break;
23528c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
23538c2ecf20Sopenharmony_ci		vxlan_addr->sa.sa_family = AF_INET6;
23548c2ecf20Sopenharmony_ci		vxlan_addr->sin6.sin6_addr = addr->addr6;
23558c2ecf20Sopenharmony_ci		break;
23568c2ecf20Sopenharmony_ci	}
23578c2ecf20Sopenharmony_ci}
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
23608c2ecf20Sopenharmony_ci					      const char *mac,
23618c2ecf20Sopenharmony_ci					      enum mlxsw_sp_l3proto proto,
23628c2ecf20Sopenharmony_ci					      union mlxsw_sp_l3addr *addr,
23638c2ecf20Sopenharmony_ci					      __be32 vni, bool adding)
23648c2ecf20Sopenharmony_ci{
23658c2ecf20Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info info;
23668c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(dev);
23678c2ecf20Sopenharmony_ci	enum switchdev_notifier_type type;
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
23708c2ecf20Sopenharmony_ci			SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
23718c2ecf20Sopenharmony_ci	mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
23728c2ecf20Sopenharmony_ci	info.remote_port = vxlan->cfg.dst_port;
23738c2ecf20Sopenharmony_ci	info.remote_vni = vni;
23748c2ecf20Sopenharmony_ci	info.remote_ifindex = 0;
23758c2ecf20Sopenharmony_ci	ether_addr_copy(info.eth_addr, mac);
23768c2ecf20Sopenharmony_ci	info.vni = vni;
23778c2ecf20Sopenharmony_ci	info.offloaded = adding;
23788c2ecf20Sopenharmony_ci	call_switchdev_notifiers(type, dev, &info.info, NULL);
23798c2ecf20Sopenharmony_ci}
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev,
23828c2ecf20Sopenharmony_ci					    const char *mac,
23838c2ecf20Sopenharmony_ci					    enum mlxsw_sp_l3proto proto,
23848c2ecf20Sopenharmony_ci					    union mlxsw_sp_l3addr *addr,
23858c2ecf20Sopenharmony_ci					    __be32 vni,
23868c2ecf20Sopenharmony_ci					    bool adding)
23878c2ecf20Sopenharmony_ci{
23888c2ecf20Sopenharmony_ci	if (netif_is_vxlan(dev))
23898c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni,
23908c2ecf20Sopenharmony_ci						  adding);
23918c2ecf20Sopenharmony_ci}
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_cistatic void
23948c2ecf20Sopenharmony_cimlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type,
23958c2ecf20Sopenharmony_ci			    const char *mac, u16 vid,
23968c2ecf20Sopenharmony_ci			    struct net_device *dev, bool offloaded)
23978c2ecf20Sopenharmony_ci{
23988c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info info;
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	info.addr = mac;
24018c2ecf20Sopenharmony_ci	info.vid = vid;
24028c2ecf20Sopenharmony_ci	info.offloaded = offloaded;
24038c2ecf20Sopenharmony_ci	call_switchdev_notifiers(type, dev, &info.info, NULL);
24048c2ecf20Sopenharmony_ci}
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
24078c2ecf20Sopenharmony_ci					    char *sfn_pl, int rec_index,
24088c2ecf20Sopenharmony_ci					    bool adding)
24098c2ecf20Sopenharmony_ci{
24108c2ecf20Sopenharmony_ci	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
24118c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
24128c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
24138c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
24148c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
24158c2ecf20Sopenharmony_ci	enum switchdev_notifier_type type;
24168c2ecf20Sopenharmony_ci	char mac[ETH_ALEN];
24178c2ecf20Sopenharmony_ci	u8 local_port;
24188c2ecf20Sopenharmony_ci	u16 vid, fid;
24198c2ecf20Sopenharmony_ci	bool do_notification = true;
24208c2ecf20Sopenharmony_ci	int err;
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(local_port >= max_ports))
24258c2ecf20Sopenharmony_ci		return;
24268c2ecf20Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp->ports[local_port];
24278c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port) {
24288c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
24298c2ecf20Sopenharmony_ci		goto just_remove;
24308c2ecf20Sopenharmony_ci	}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid))
24338c2ecf20Sopenharmony_ci		goto just_remove;
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid);
24368c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
24378c2ecf20Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n");
24388c2ecf20Sopenharmony_ci		goto just_remove;
24398c2ecf20Sopenharmony_ci	}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
24428c2ecf20Sopenharmony_ci	if (!bridge_port) {
24438c2ecf20Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n");
24448c2ecf20Sopenharmony_ci		goto just_remove;
24458c2ecf20Sopenharmony_ci	}
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
24488c2ecf20Sopenharmony_ci	vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_cido_fdb_op:
24518c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
24528c2ecf20Sopenharmony_ci				      adding, true);
24538c2ecf20Sopenharmony_ci	if (err) {
24548c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
24558c2ecf20Sopenharmony_ci		return;
24568c2ecf20Sopenharmony_ci	}
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	if (!do_notification)
24598c2ecf20Sopenharmony_ci		return;
24608c2ecf20Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
24618c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding);
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	return;
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_cijust_remove:
24668c2ecf20Sopenharmony_ci	adding = false;
24678c2ecf20Sopenharmony_ci	do_notification = false;
24688c2ecf20Sopenharmony_ci	goto do_fdb_op;
24698c2ecf20Sopenharmony_ci}
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
24728c2ecf20Sopenharmony_ci						char *sfn_pl, int rec_index,
24738c2ecf20Sopenharmony_ci						bool adding)
24748c2ecf20Sopenharmony_ci{
24758c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
24768c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
24778c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
24788c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
24798c2ecf20Sopenharmony_ci	enum switchdev_notifier_type type;
24808c2ecf20Sopenharmony_ci	char mac[ETH_ALEN];
24818c2ecf20Sopenharmony_ci	u16 lag_vid = 0;
24828c2ecf20Sopenharmony_ci	u16 lag_id;
24838c2ecf20Sopenharmony_ci	u16 vid, fid;
24848c2ecf20Sopenharmony_ci	bool do_notification = true;
24858c2ecf20Sopenharmony_ci	int err;
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id);
24888c2ecf20Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
24898c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port) {
24908c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
24918c2ecf20Sopenharmony_ci		goto just_remove;
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid))
24958c2ecf20Sopenharmony_ci		goto just_remove;
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid);
24988c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
24998c2ecf20Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n");
25008c2ecf20Sopenharmony_ci		goto just_remove;
25018c2ecf20Sopenharmony_ci	}
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
25048c2ecf20Sopenharmony_ci	if (!bridge_port) {
25058c2ecf20Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n");
25068c2ecf20Sopenharmony_ci		goto just_remove;
25078c2ecf20Sopenharmony_ci	}
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
25108c2ecf20Sopenharmony_ci	vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
25118c2ecf20Sopenharmony_ci	lag_vid = mlxsw_sp_fid_lag_vid_valid(mlxsw_sp_port_vlan->fid) ?
25128c2ecf20Sopenharmony_ci		  mlxsw_sp_port_vlan->vid : 0;
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_cido_fdb_op:
25158c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
25168c2ecf20Sopenharmony_ci					  adding, true);
25178c2ecf20Sopenharmony_ci	if (err) {
25188c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
25198c2ecf20Sopenharmony_ci		return;
25208c2ecf20Sopenharmony_ci	}
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	if (!do_notification)
25238c2ecf20Sopenharmony_ci		return;
25248c2ecf20Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
25258c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding);
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	return;
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_cijust_remove:
25308c2ecf20Sopenharmony_ci	adding = false;
25318c2ecf20Sopenharmony_ci	do_notification = false;
25328c2ecf20Sopenharmony_ci	goto do_fdb_op;
25338c2ecf20Sopenharmony_ci}
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_cistatic int
25368c2ecf20Sopenharmony_ci__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
25378c2ecf20Sopenharmony_ci					    const struct mlxsw_sp_fid *fid,
25388c2ecf20Sopenharmony_ci					    bool adding,
25398c2ecf20Sopenharmony_ci					    struct net_device **nve_dev,
25408c2ecf20Sopenharmony_ci					    u16 *p_vid, __be32 *p_vni)
25418c2ecf20Sopenharmony_ci{
25428c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
25438c2ecf20Sopenharmony_ci	struct net_device *br_dev, *dev;
25448c2ecf20Sopenharmony_ci	int nve_ifindex;
25458c2ecf20Sopenharmony_ci	int err;
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex);
25488c2ecf20Sopenharmony_ci	if (err)
25498c2ecf20Sopenharmony_ci		return err;
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_vni(fid, p_vni);
25528c2ecf20Sopenharmony_ci	if (err)
25538c2ecf20Sopenharmony_ci		return err;
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
25568c2ecf20Sopenharmony_ci	if (!dev)
25578c2ecf20Sopenharmony_ci		return -EINVAL;
25588c2ecf20Sopenharmony_ci	*nve_dev = dev;
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	if (!netif_running(dev))
25618c2ecf20Sopenharmony_ci		return -EINVAL;
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	if (adding && !br_port_flag_is_set(dev, BR_LEARNING))
25648c2ecf20Sopenharmony_ci		return -EINVAL;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	if (adding && netif_is_vxlan(dev)) {
25678c2ecf20Sopenharmony_ci		struct vxlan_dev *vxlan = netdev_priv(dev);
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci		if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
25708c2ecf20Sopenharmony_ci			return -EINVAL;
25718c2ecf20Sopenharmony_ci	}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
25748c2ecf20Sopenharmony_ci	if (!br_dev)
25758c2ecf20Sopenharmony_ci		return -EINVAL;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
25788c2ecf20Sopenharmony_ci	if (!bridge_device)
25798c2ecf20Sopenharmony_ci		return -EINVAL;
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	*p_vid = bridge_device->ops->fid_vid(bridge_device, fid);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	return 0;
25848c2ecf20Sopenharmony_ci}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
25878c2ecf20Sopenharmony_ci						      char *sfn_pl,
25888c2ecf20Sopenharmony_ci						      int rec_index,
25898c2ecf20Sopenharmony_ci						      bool adding)
25908c2ecf20Sopenharmony_ci{
25918c2ecf20Sopenharmony_ci	enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto;
25928c2ecf20Sopenharmony_ci	enum switchdev_notifier_type type;
25938c2ecf20Sopenharmony_ci	struct net_device *nve_dev;
25948c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr addr;
25958c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
25968c2ecf20Sopenharmony_ci	char mac[ETH_ALEN];
25978c2ecf20Sopenharmony_ci	u16 fid_index, vid;
25988c2ecf20Sopenharmony_ci	__be32 vni;
25998c2ecf20Sopenharmony_ci	u32 uip;
26008c2ecf20Sopenharmony_ci	int err;
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index,
26038c2ecf20Sopenharmony_ci				       &uip, &sfn_proto);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index);
26068c2ecf20Sopenharmony_ci	if (!fid)
26078c2ecf20Sopenharmony_ci		goto err_fid_lookup;
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip,
26108c2ecf20Sopenharmony_ci					      (enum mlxsw_sp_l3proto) sfn_proto,
26118c2ecf20Sopenharmony_ci					      &addr);
26128c2ecf20Sopenharmony_ci	if (err)
26138c2ecf20Sopenharmony_ci		goto err_ip_resolve;
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding,
26168c2ecf20Sopenharmony_ci							  &nve_dev, &vid, &vni);
26178c2ecf20Sopenharmony_ci	if (err)
26188c2ecf20Sopenharmony_ci		goto err_fdb_process;
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
26218c2ecf20Sopenharmony_ci					     (enum mlxsw_sp_l3proto) sfn_proto,
26228c2ecf20Sopenharmony_ci					     &addr, adding, true);
26238c2ecf20Sopenharmony_ci	if (err)
26248c2ecf20Sopenharmony_ci		goto err_fdb_op;
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac,
26278c2ecf20Sopenharmony_ci					(enum mlxsw_sp_l3proto) sfn_proto,
26288c2ecf20Sopenharmony_ci					&addr, vni, adding);
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE :
26318c2ecf20Sopenharmony_ci			SWITCHDEV_FDB_DEL_TO_BRIDGE;
26328c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding);
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	return;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_cierr_fdb_op:
26398c2ecf20Sopenharmony_cierr_fdb_process:
26408c2ecf20Sopenharmony_cierr_ip_resolve:
26418c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
26428c2ecf20Sopenharmony_cierr_fid_lookup:
26438c2ecf20Sopenharmony_ci	/* Remove an FDB entry in case we cannot process it. Otherwise the
26448c2ecf20Sopenharmony_ci	 * device will keep sending the same notification over and over again.
26458c2ecf20Sopenharmony_ci	 */
26468c2ecf20Sopenharmony_ci	mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
26478c2ecf20Sopenharmony_ci				       (enum mlxsw_sp_l3proto) sfn_proto, &addr,
26488c2ecf20Sopenharmony_ci				       false, true);
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
26528c2ecf20Sopenharmony_ci					    char *sfn_pl, int rec_index)
26538c2ecf20Sopenharmony_ci{
26548c2ecf20Sopenharmony_ci	switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) {
26558c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC:
26568c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
26578c2ecf20Sopenharmony_ci						rec_index, true);
26588c2ecf20Sopenharmony_ci		break;
26598c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC:
26608c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
26618c2ecf20Sopenharmony_ci						rec_index, false);
26628c2ecf20Sopenharmony_ci		break;
26638c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
26648c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
26658c2ecf20Sopenharmony_ci						    rec_index, true);
26668c2ecf20Sopenharmony_ci		break;
26678c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
26688c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
26698c2ecf20Sopenharmony_ci						    rec_index, false);
26708c2ecf20Sopenharmony_ci		break;
26718c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL:
26728c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
26738c2ecf20Sopenharmony_ci							  rec_index, true);
26748c2ecf20Sopenharmony_ci		break;
26758c2ecf20Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL:
26768c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
26778c2ecf20Sopenharmony_ci							  rec_index, false);
26788c2ecf20Sopenharmony_ci		break;
26798c2ecf20Sopenharmony_ci	}
26808c2ecf20Sopenharmony_ci}
26818c2ecf20Sopenharmony_ci
26828c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp,
26838c2ecf20Sopenharmony_ci					      bool no_delay)
26848c2ecf20Sopenharmony_ci{
26858c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
26868c2ecf20Sopenharmony_ci	unsigned int interval = no_delay ? 0 : bridge->fdb_notify.interval;
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci	mlxsw_core_schedule_dw(&bridge->fdb_notify.dw,
26898c2ecf20Sopenharmony_ci			       msecs_to_jiffies(interval));
26908c2ecf20Sopenharmony_ci}
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci#define MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION 10
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work(struct work_struct *work)
26958c2ecf20Sopenharmony_ci{
26968c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge *bridge;
26978c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
26988c2ecf20Sopenharmony_ci	char *sfn_pl;
26998c2ecf20Sopenharmony_ci	int queries;
27008c2ecf20Sopenharmony_ci	u8 num_rec;
27018c2ecf20Sopenharmony_ci	int i;
27028c2ecf20Sopenharmony_ci	int err;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL);
27058c2ecf20Sopenharmony_ci	if (!sfn_pl)
27068c2ecf20Sopenharmony_ci		return;
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	bridge = container_of(work, struct mlxsw_sp_bridge, fdb_notify.dw.work);
27098c2ecf20Sopenharmony_ci	mlxsw_sp = bridge->mlxsw_sp;
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	rtnl_lock();
27128c2ecf20Sopenharmony_ci	queries = MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION;
27138c2ecf20Sopenharmony_ci	while (queries > 0) {
27148c2ecf20Sopenharmony_ci		mlxsw_reg_sfn_pack(sfn_pl);
27158c2ecf20Sopenharmony_ci		err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
27168c2ecf20Sopenharmony_ci		if (err) {
27178c2ecf20Sopenharmony_ci			dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
27188c2ecf20Sopenharmony_ci			goto out;
27198c2ecf20Sopenharmony_ci		}
27208c2ecf20Sopenharmony_ci		num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
27218c2ecf20Sopenharmony_ci		for (i = 0; i < num_rec; i++)
27228c2ecf20Sopenharmony_ci			mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
27238c2ecf20Sopenharmony_ci		if (num_rec != MLXSW_REG_SFN_REC_MAX_COUNT)
27248c2ecf20Sopenharmony_ci			goto out;
27258c2ecf20Sopenharmony_ci		queries--;
27268c2ecf20Sopenharmony_ci	}
27278c2ecf20Sopenharmony_ci
27288c2ecf20Sopenharmony_ciout:
27298c2ecf20Sopenharmony_ci	rtnl_unlock();
27308c2ecf20Sopenharmony_ci	kfree(sfn_pl);
27318c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, !queries);
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_cistruct mlxsw_sp_switchdev_event_work {
27358c2ecf20Sopenharmony_ci	struct work_struct work;
27368c2ecf20Sopenharmony_ci	union {
27378c2ecf20Sopenharmony_ci		struct switchdev_notifier_fdb_info fdb_info;
27388c2ecf20Sopenharmony_ci		struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info;
27398c2ecf20Sopenharmony_ci	};
27408c2ecf20Sopenharmony_ci	struct net_device *dev;
27418c2ecf20Sopenharmony_ci	unsigned long event;
27428c2ecf20Sopenharmony_ci};
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_cistatic void
27458c2ecf20Sopenharmony_cimlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
27468c2ecf20Sopenharmony_ci					  struct mlxsw_sp_switchdev_event_work *
27478c2ecf20Sopenharmony_ci					  switchdev_work,
27488c2ecf20Sopenharmony_ci					  struct mlxsw_sp_fid *fid, __be32 vni)
27498c2ecf20Sopenharmony_ci{
27508c2ecf20Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info;
27518c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
27528c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
27538c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
27548c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr addr;
27558c2ecf20Sopenharmony_ci	int err;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	fdb_info = &switchdev_work->fdb_info;
27588c2ecf20Sopenharmony_ci	err = vxlan_fdb_find_uc(dev, fdb_info->addr, vni, &vxlan_fdb_info);
27598c2ecf20Sopenharmony_ci	if (err)
27608c2ecf20Sopenharmony_ci		return;
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info.remote_ip,
27638c2ecf20Sopenharmony_ci					      &proto, &addr);
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	switch (switchdev_work->event) {
27668c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
27678c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp,
27688c2ecf20Sopenharmony_ci						     vxlan_fdb_info.eth_addr,
27698c2ecf20Sopenharmony_ci						     mlxsw_sp_fid_index(fid),
27708c2ecf20Sopenharmony_ci						     proto, &addr, true, false);
27718c2ecf20Sopenharmony_ci		if (err)
27728c2ecf20Sopenharmony_ci			return;
27738c2ecf20Sopenharmony_ci		vxlan_fdb_info.offloaded = true;
27748c2ecf20Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
27758c2ecf20Sopenharmony_ci					 &vxlan_fdb_info.info, NULL);
27768c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
27778c2ecf20Sopenharmony_ci					    vxlan_fdb_info.eth_addr,
27788c2ecf20Sopenharmony_ci					    fdb_info->vid, dev, true);
27798c2ecf20Sopenharmony_ci		break;
27808c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
27818c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp,
27828c2ecf20Sopenharmony_ci						     vxlan_fdb_info.eth_addr,
27838c2ecf20Sopenharmony_ci						     mlxsw_sp_fid_index(fid),
27848c2ecf20Sopenharmony_ci						     proto, &addr, false,
27858c2ecf20Sopenharmony_ci						     false);
27868c2ecf20Sopenharmony_ci		vxlan_fdb_info.offloaded = false;
27878c2ecf20Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
27888c2ecf20Sopenharmony_ci					 &vxlan_fdb_info.info, NULL);
27898c2ecf20Sopenharmony_ci		break;
27908c2ecf20Sopenharmony_ci	}
27918c2ecf20Sopenharmony_ci}
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_cistatic void
27948c2ecf20Sopenharmony_cimlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work *
27958c2ecf20Sopenharmony_ci					switchdev_work)
27968c2ecf20Sopenharmony_ci{
27978c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
27988c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
27998c2ecf20Sopenharmony_ci	struct net_device *br_dev;
28008c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
28018c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
28028c2ecf20Sopenharmony_ci	__be32 vni;
28038c2ecf20Sopenharmony_ci	int err;
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	if (switchdev_work->event != SWITCHDEV_FDB_ADD_TO_DEVICE &&
28068c2ecf20Sopenharmony_ci	    switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE)
28078c2ecf20Sopenharmony_ci		return;
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE &&
28108c2ecf20Sopenharmony_ci	    !switchdev_work->fdb_info.added_by_user)
28118c2ecf20Sopenharmony_ci		return;
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci	if (!netif_running(dev))
28148c2ecf20Sopenharmony_ci		return;
28158c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
28168c2ecf20Sopenharmony_ci	if (!br_dev)
28178c2ecf20Sopenharmony_ci		return;
28188c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
28198c2ecf20Sopenharmony_ci		return;
28208c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
28218c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
28228c2ecf20Sopenharmony_ci		return;
28238c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
28248c2ecf20Sopenharmony_ci	if (!bridge_device)
28258c2ecf20Sopenharmony_ci		return;
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_ci	fid = bridge_device->ops->fid_lookup(bridge_device,
28288c2ecf20Sopenharmony_ci					     switchdev_work->fdb_info.vid);
28298c2ecf20Sopenharmony_ci	if (!fid)
28308c2ecf20Sopenharmony_ci		return;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_vni(fid, &vni);
28338c2ecf20Sopenharmony_ci	if (err)
28348c2ecf20Sopenharmony_ci		goto out;
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	mlxsw_sp_switchdev_bridge_vxlan_fdb_event(mlxsw_sp, switchdev_work, fid,
28378c2ecf20Sopenharmony_ci						  vni);
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ciout:
28408c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
28418c2ecf20Sopenharmony_ci}
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_cistatic void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work)
28448c2ecf20Sopenharmony_ci{
28458c2ecf20Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work =
28468c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_switchdev_event_work, work);
28478c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
28488c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
28498c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
28508c2ecf20Sopenharmony_ci	int err;
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci	rtnl_lock();
28538c2ecf20Sopenharmony_ci	if (netif_is_vxlan(dev)) {
28548c2ecf20Sopenharmony_ci		mlxsw_sp_switchdev_bridge_nve_fdb_event(switchdev_work);
28558c2ecf20Sopenharmony_ci		goto out;
28568c2ecf20Sopenharmony_ci	}
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev);
28598c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port)
28608c2ecf20Sopenharmony_ci		goto out;
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci	switch (switchdev_work->event) {
28638c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
28648c2ecf20Sopenharmony_ci		fdb_info = &switchdev_work->fdb_info;
28658c2ecf20Sopenharmony_ci		if (!fdb_info->added_by_user)
28668c2ecf20Sopenharmony_ci			break;
28678c2ecf20Sopenharmony_ci		err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true);
28688c2ecf20Sopenharmony_ci		if (err)
28698c2ecf20Sopenharmony_ci			break;
28708c2ecf20Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
28718c2ecf20Sopenharmony_ci					    fdb_info->addr,
28728c2ecf20Sopenharmony_ci					    fdb_info->vid, dev, true);
28738c2ecf20Sopenharmony_ci		break;
28748c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
28758c2ecf20Sopenharmony_ci		fdb_info = &switchdev_work->fdb_info;
28768c2ecf20Sopenharmony_ci		mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
28778c2ecf20Sopenharmony_ci		break;
28788c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
28798c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
28808c2ecf20Sopenharmony_ci		/* These events are only used to potentially update an existing
28818c2ecf20Sopenharmony_ci		 * SPAN mirror.
28828c2ecf20Sopenharmony_ci		 */
28838c2ecf20Sopenharmony_ci		break;
28848c2ecf20Sopenharmony_ci	}
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
28878c2ecf20Sopenharmony_ci
28888c2ecf20Sopenharmony_ciout:
28898c2ecf20Sopenharmony_ci	rtnl_unlock();
28908c2ecf20Sopenharmony_ci	kfree(switchdev_work->fdb_info.addr);
28918c2ecf20Sopenharmony_ci	kfree(switchdev_work);
28928c2ecf20Sopenharmony_ci	dev_put(dev);
28938c2ecf20Sopenharmony_ci}
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_cistatic void
28968c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp,
28978c2ecf20Sopenharmony_ci				 struct mlxsw_sp_switchdev_event_work *
28988c2ecf20Sopenharmony_ci				 switchdev_work)
28998c2ecf20Sopenharmony_ci{
29008c2ecf20Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
29018c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
29028c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
29038c2ecf20Sopenharmony_ci	u8 all_zeros_mac[ETH_ALEN] = { 0 };
29048c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
29058c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr addr;
29068c2ecf20Sopenharmony_ci	struct net_device *br_dev;
29078c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
29088c2ecf20Sopenharmony_ci	u16 vid;
29098c2ecf20Sopenharmony_ci	int err;
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	vxlan_fdb_info = &switchdev_work->vxlan_fdb_info;
29128c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
29158c2ecf20Sopenharmony_ci	if (!bridge_device)
29168c2ecf20Sopenharmony_ci		return;
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni);
29198c2ecf20Sopenharmony_ci	if (!fid)
29208c2ecf20Sopenharmony_ci		return;
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip,
29238c2ecf20Sopenharmony_ci					      &proto, &addr);
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_ci	if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) {
29268c2ecf20Sopenharmony_ci		err = mlxsw_sp_nve_flood_ip_add(mlxsw_sp, fid, proto, &addr);
29278c2ecf20Sopenharmony_ci		if (err) {
29288c2ecf20Sopenharmony_ci			mlxsw_sp_fid_put(fid);
29298c2ecf20Sopenharmony_ci			return;
29308c2ecf20Sopenharmony_ci		}
29318c2ecf20Sopenharmony_ci		vxlan_fdb_info->offloaded = true;
29328c2ecf20Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
29338c2ecf20Sopenharmony_ci					 &vxlan_fdb_info->info, NULL);
29348c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
29358c2ecf20Sopenharmony_ci		return;
29368c2ecf20Sopenharmony_ci	}
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	/* The device has a single FDB table, whereas Linux has two - one
29398c2ecf20Sopenharmony_ci	 * in the bridge driver and another in the VxLAN driver. We only
29408c2ecf20Sopenharmony_ci	 * program an entry to the device if the MAC points to the VxLAN
29418c2ecf20Sopenharmony_ci	 * device in the bridge's FDB table
29428c2ecf20Sopenharmony_ci	 */
29438c2ecf20Sopenharmony_ci	vid = bridge_device->ops->fid_vid(bridge_device, fid);
29448c2ecf20Sopenharmony_ci	if (br_fdb_find_port(br_dev, vxlan_fdb_info->eth_addr, vid) != dev)
29458c2ecf20Sopenharmony_ci		goto err_br_fdb_find;
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr,
29488c2ecf20Sopenharmony_ci					     mlxsw_sp_fid_index(fid), proto,
29498c2ecf20Sopenharmony_ci					     &addr, true, false);
29508c2ecf20Sopenharmony_ci	if (err)
29518c2ecf20Sopenharmony_ci		goto err_fdb_tunnel_uc_op;
29528c2ecf20Sopenharmony_ci	vxlan_fdb_info->offloaded = true;
29538c2ecf20Sopenharmony_ci	call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
29548c2ecf20Sopenharmony_ci				 &vxlan_fdb_info->info, NULL);
29558c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
29568c2ecf20Sopenharmony_ci				    vxlan_fdb_info->eth_addr, vid, dev, true);
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	return;
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_cierr_fdb_tunnel_uc_op:
29638c2ecf20Sopenharmony_cierr_br_fdb_find:
29648c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
29658c2ecf20Sopenharmony_ci}
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_cistatic void
29688c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp,
29698c2ecf20Sopenharmony_ci				 struct mlxsw_sp_switchdev_event_work *
29708c2ecf20Sopenharmony_ci				 switchdev_work)
29718c2ecf20Sopenharmony_ci{
29728c2ecf20Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
29738c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
29748c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
29758c2ecf20Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(dev);
29768c2ecf20Sopenharmony_ci	u8 all_zeros_mac[ETH_ALEN] = { 0 };
29778c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
29788c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr addr;
29798c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
29808c2ecf20Sopenharmony_ci	u16 vid;
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	vxlan_fdb_info = &switchdev_work->vxlan_fdb_info;
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
29858c2ecf20Sopenharmony_ci	if (!bridge_device)
29868c2ecf20Sopenharmony_ci		return;
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni);
29898c2ecf20Sopenharmony_ci	if (!fid)
29908c2ecf20Sopenharmony_ci		return;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip,
29938c2ecf20Sopenharmony_ci					      &proto, &addr);
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) {
29968c2ecf20Sopenharmony_ci		mlxsw_sp_nve_flood_ip_del(mlxsw_sp, fid, proto, &addr);
29978c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
29988c2ecf20Sopenharmony_ci		return;
29998c2ecf20Sopenharmony_ci	}
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr,
30028c2ecf20Sopenharmony_ci				       mlxsw_sp_fid_index(fid), proto, &addr,
30038c2ecf20Sopenharmony_ci				       false, false);
30048c2ecf20Sopenharmony_ci	vid = bridge_device->ops->fid_vid(bridge_device, fid);
30058c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
30068c2ecf20Sopenharmony_ci				    vxlan_fdb_info->eth_addr, vid, dev, false);
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
30098c2ecf20Sopenharmony_ci}
30108c2ecf20Sopenharmony_ci
30118c2ecf20Sopenharmony_cistatic void mlxsw_sp_switchdev_vxlan_fdb_event_work(struct work_struct *work)
30128c2ecf20Sopenharmony_ci{
30138c2ecf20Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work =
30148c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_switchdev_event_work, work);
30158c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
30168c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
30178c2ecf20Sopenharmony_ci	struct net_device *br_dev;
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_ci	rtnl_lock();
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ci	if (!netif_running(dev))
30228c2ecf20Sopenharmony_ci		goto out;
30238c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
30248c2ecf20Sopenharmony_ci	if (!br_dev)
30258c2ecf20Sopenharmony_ci		goto out;
30268c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
30278c2ecf20Sopenharmony_ci		goto out;
30288c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
30298c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
30308c2ecf20Sopenharmony_ci		goto out;
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_ci	switch (switchdev_work->event) {
30338c2ecf20Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE:
30348c2ecf20Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_fdb_add(mlxsw_sp, switchdev_work);
30358c2ecf20Sopenharmony_ci		break;
30368c2ecf20Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE:
30378c2ecf20Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_fdb_del(mlxsw_sp, switchdev_work);
30388c2ecf20Sopenharmony_ci		break;
30398c2ecf20Sopenharmony_ci	}
30408c2ecf20Sopenharmony_ci
30418c2ecf20Sopenharmony_ciout:
30428c2ecf20Sopenharmony_ci	rtnl_unlock();
30438c2ecf20Sopenharmony_ci	kfree(switchdev_work);
30448c2ecf20Sopenharmony_ci	dev_put(dev);
30458c2ecf20Sopenharmony_ci}
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_cistatic int
30488c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
30498c2ecf20Sopenharmony_ci				      switchdev_work,
30508c2ecf20Sopenharmony_ci				      struct switchdev_notifier_info *info)
30518c2ecf20Sopenharmony_ci{
30528c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
30538c2ecf20Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
30548c2ecf20Sopenharmony_ci	struct vxlan_config *cfg = &vxlan->cfg;
30558c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	extack = switchdev_notifier_info_to_extack(info);
30588c2ecf20Sopenharmony_ci	vxlan_fdb_info = container_of(info,
30598c2ecf20Sopenharmony_ci				      struct switchdev_notifier_vxlan_fdb_info,
30608c2ecf20Sopenharmony_ci				      info);
30618c2ecf20Sopenharmony_ci
30628c2ecf20Sopenharmony_ci	if (vxlan_fdb_info->remote_port != cfg->dst_port) {
30638c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported");
30648c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
30658c2ecf20Sopenharmony_ci	}
30668c2ecf20Sopenharmony_ci	if (vxlan_fdb_info->remote_vni != cfg->vni ||
30678c2ecf20Sopenharmony_ci	    vxlan_fdb_info->vni != cfg->vni) {
30688c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported");
30698c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
30708c2ecf20Sopenharmony_ci	}
30718c2ecf20Sopenharmony_ci	if (vxlan_fdb_info->remote_ifindex) {
30728c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported");
30738c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
30748c2ecf20Sopenharmony_ci	}
30758c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) {
30768c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported");
30778c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
30788c2ecf20Sopenharmony_ci	}
30798c2ecf20Sopenharmony_ci	if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) {
30808c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported");
30818c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
30828c2ecf20Sopenharmony_ci	}
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	switchdev_work->vxlan_fdb_info = *vxlan_fdb_info;
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	return 0;
30878c2ecf20Sopenharmony_ci}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci/* Called under rcu_read_lock() */
30908c2ecf20Sopenharmony_cistatic int mlxsw_sp_switchdev_event(struct notifier_block *unused,
30918c2ecf20Sopenharmony_ci				    unsigned long event, void *ptr)
30928c2ecf20Sopenharmony_ci{
30938c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
30948c2ecf20Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work;
30958c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
30968c2ecf20Sopenharmony_ci	struct switchdev_notifier_info *info = ptr;
30978c2ecf20Sopenharmony_ci	struct net_device *br_dev;
30988c2ecf20Sopenharmony_ci	int err;
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci	if (event == SWITCHDEV_PORT_ATTR_SET) {
31018c2ecf20Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
31028c2ecf20Sopenharmony_ci						     mlxsw_sp_port_dev_check,
31038c2ecf20Sopenharmony_ci						     mlxsw_sp_port_attr_set);
31048c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
31058c2ecf20Sopenharmony_ci	}
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci	/* Tunnel devices are not our uppers, so check their master instead */
31088c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get_rcu(dev);
31098c2ecf20Sopenharmony_ci	if (!br_dev)
31108c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31118c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
31128c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31138c2ecf20Sopenharmony_ci	if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev))
31148c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
31178c2ecf20Sopenharmony_ci	if (!switchdev_work)
31188c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
31198c2ecf20Sopenharmony_ci
31208c2ecf20Sopenharmony_ci	switchdev_work->dev = dev;
31218c2ecf20Sopenharmony_ci	switchdev_work->event = event;
31228c2ecf20Sopenharmony_ci
31238c2ecf20Sopenharmony_ci	switch (event) {
31248c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
31258c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
31268c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
31278c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
31288c2ecf20Sopenharmony_ci		fdb_info = container_of(info,
31298c2ecf20Sopenharmony_ci					struct switchdev_notifier_fdb_info,
31308c2ecf20Sopenharmony_ci					info);
31318c2ecf20Sopenharmony_ci		INIT_WORK(&switchdev_work->work,
31328c2ecf20Sopenharmony_ci			  mlxsw_sp_switchdev_bridge_fdb_event_work);
31338c2ecf20Sopenharmony_ci		memcpy(&switchdev_work->fdb_info, ptr,
31348c2ecf20Sopenharmony_ci		       sizeof(switchdev_work->fdb_info));
31358c2ecf20Sopenharmony_ci		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
31368c2ecf20Sopenharmony_ci		if (!switchdev_work->fdb_info.addr)
31378c2ecf20Sopenharmony_ci			goto err_addr_alloc;
31388c2ecf20Sopenharmony_ci		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
31398c2ecf20Sopenharmony_ci				fdb_info->addr);
31408c2ecf20Sopenharmony_ci		/* Take a reference on the device. This can be either
31418c2ecf20Sopenharmony_ci		 * upper device containig mlxsw_sp_port or just a
31428c2ecf20Sopenharmony_ci		 * mlxsw_sp_port
31438c2ecf20Sopenharmony_ci		 */
31448c2ecf20Sopenharmony_ci		dev_hold(dev);
31458c2ecf20Sopenharmony_ci		break;
31468c2ecf20Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE:
31478c2ecf20Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE:
31488c2ecf20Sopenharmony_ci		INIT_WORK(&switchdev_work->work,
31498c2ecf20Sopenharmony_ci			  mlxsw_sp_switchdev_vxlan_fdb_event_work);
31508c2ecf20Sopenharmony_ci		err = mlxsw_sp_switchdev_vxlan_work_prepare(switchdev_work,
31518c2ecf20Sopenharmony_ci							    info);
31528c2ecf20Sopenharmony_ci		if (err)
31538c2ecf20Sopenharmony_ci			goto err_vxlan_work_prepare;
31548c2ecf20Sopenharmony_ci		dev_hold(dev);
31558c2ecf20Sopenharmony_ci		break;
31568c2ecf20Sopenharmony_ci	default:
31578c2ecf20Sopenharmony_ci		kfree(switchdev_work);
31588c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31598c2ecf20Sopenharmony_ci	}
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci	mlxsw_core_schedule_work(&switchdev_work->work);
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_cierr_vxlan_work_prepare:
31668c2ecf20Sopenharmony_cierr_addr_alloc:
31678c2ecf20Sopenharmony_ci	kfree(switchdev_work);
31688c2ecf20Sopenharmony_ci	return NOTIFY_BAD;
31698c2ecf20Sopenharmony_ci}
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_cistruct notifier_block mlxsw_sp_switchdev_notifier = {
31728c2ecf20Sopenharmony_ci	.notifier_call = mlxsw_sp_switchdev_event,
31738c2ecf20Sopenharmony_ci};
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_cistatic int
31768c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
31778c2ecf20Sopenharmony_ci				  struct mlxsw_sp_bridge_device *bridge_device,
31788c2ecf20Sopenharmony_ci				  const struct net_device *vxlan_dev, u16 vid,
31798c2ecf20Sopenharmony_ci				  bool flag_untagged, bool flag_pvid,
31808c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
31818c2ecf20Sopenharmony_ci{
31828c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
31838c2ecf20Sopenharmony_ci	__be32 vni = vxlan->cfg.vni;
31848c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
31858c2ecf20Sopenharmony_ci	u16 old_vid;
31868c2ecf20Sopenharmony_ci	int err;
31878c2ecf20Sopenharmony_ci
31888c2ecf20Sopenharmony_ci	/* We cannot have the same VLAN as PVID and egress untagged on multiple
31898c2ecf20Sopenharmony_ci	 * VxLAN devices. Note that we get this notification before the VLAN is
31908c2ecf20Sopenharmony_ci	 * actually added to the bridge's database, so it is not possible for
31918c2ecf20Sopenharmony_ci	 * the lookup function to return 'vxlan_dev'
31928c2ecf20Sopenharmony_ci	 */
31938c2ecf20Sopenharmony_ci	if (flag_untagged && flag_pvid &&
31948c2ecf20Sopenharmony_ci	    mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid)) {
31958c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VLAN already mapped to a different VNI");
31968c2ecf20Sopenharmony_ci		return -EINVAL;
31978c2ecf20Sopenharmony_ci	}
31988c2ecf20Sopenharmony_ci
31998c2ecf20Sopenharmony_ci	if (!netif_running(vxlan_dev))
32008c2ecf20Sopenharmony_ci		return 0;
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	/* First case: FID is not associated with this VNI, but the new VLAN
32038c2ecf20Sopenharmony_ci	 * is both PVID and egress untagged. Need to enable NVE on the FID, if
32048c2ecf20Sopenharmony_ci	 * it exists
32058c2ecf20Sopenharmony_ci	 */
32068c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
32078c2ecf20Sopenharmony_ci	if (!fid) {
32088c2ecf20Sopenharmony_ci		if (!flag_untagged || !flag_pvid)
32098c2ecf20Sopenharmony_ci			return 0;
32108c2ecf20Sopenharmony_ci		return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device,
32118c2ecf20Sopenharmony_ci							vxlan_dev, vid, extack);
32128c2ecf20Sopenharmony_ci	}
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci	/* Second case: FID is associated with the VNI and the VLAN associated
32158c2ecf20Sopenharmony_ci	 * with the FID is the same as the notified VLAN. This means the flags
32168c2ecf20Sopenharmony_ci	 * (PVID / egress untagged) were toggled and that NVE should be
32178c2ecf20Sopenharmony_ci	 * disabled on the FID
32188c2ecf20Sopenharmony_ci	 */
32198c2ecf20Sopenharmony_ci	old_vid = mlxsw_sp_fid_8021q_vid(fid);
32208c2ecf20Sopenharmony_ci	if (vid == old_vid) {
32218c2ecf20Sopenharmony_ci		if (WARN_ON(flag_untagged && flag_pvid)) {
32228c2ecf20Sopenharmony_ci			mlxsw_sp_fid_put(fid);
32238c2ecf20Sopenharmony_ci			return -EINVAL;
32248c2ecf20Sopenharmony_ci		}
32258c2ecf20Sopenharmony_ci		mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
32268c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
32278c2ecf20Sopenharmony_ci		return 0;
32288c2ecf20Sopenharmony_ci	}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci	/* Third case: A new VLAN was configured on the VxLAN device, but this
32318c2ecf20Sopenharmony_ci	 * VLAN is not PVID, so there is nothing to do.
32328c2ecf20Sopenharmony_ci	 */
32338c2ecf20Sopenharmony_ci	if (!flag_pvid) {
32348c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
32358c2ecf20Sopenharmony_ci		return 0;
32368c2ecf20Sopenharmony_ci	}
32378c2ecf20Sopenharmony_ci
32388c2ecf20Sopenharmony_ci	/* Fourth case: Thew new VLAN is PVID, which means the VLAN currently
32398c2ecf20Sopenharmony_ci	 * mapped to the VNI should be unmapped
32408c2ecf20Sopenharmony_ci	 */
32418c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
32428c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_ci	/* Fifth case: The new VLAN is also egress untagged, which means the
32458c2ecf20Sopenharmony_ci	 * VLAN needs to be mapped to the VNI
32468c2ecf20Sopenharmony_ci	 */
32478c2ecf20Sopenharmony_ci	if (!flag_untagged)
32488c2ecf20Sopenharmony_ci		return 0;
32498c2ecf20Sopenharmony_ci
32508c2ecf20Sopenharmony_ci	err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
32518c2ecf20Sopenharmony_ci					       extack);
32528c2ecf20Sopenharmony_ci	if (err)
32538c2ecf20Sopenharmony_ci		goto err_vxlan_join;
32548c2ecf20Sopenharmony_ci
32558c2ecf20Sopenharmony_ci	return 0;
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_cierr_vxlan_join:
32588c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, old_vid,
32598c2ecf20Sopenharmony_ci					 NULL);
32608c2ecf20Sopenharmony_ci	return err;
32618c2ecf20Sopenharmony_ci}
32628c2ecf20Sopenharmony_ci
32638c2ecf20Sopenharmony_cistatic void
32648c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
32658c2ecf20Sopenharmony_ci				  struct mlxsw_sp_bridge_device *bridge_device,
32668c2ecf20Sopenharmony_ci				  const struct net_device *vxlan_dev, u16 vid)
32678c2ecf20Sopenharmony_ci{
32688c2ecf20Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
32698c2ecf20Sopenharmony_ci	__be32 vni = vxlan->cfg.vni;
32708c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
32718c2ecf20Sopenharmony_ci
32728c2ecf20Sopenharmony_ci	if (!netif_running(vxlan_dev))
32738c2ecf20Sopenharmony_ci		return;
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
32768c2ecf20Sopenharmony_ci	if (!fid)
32778c2ecf20Sopenharmony_ci		return;
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ci	/* A different VLAN than the one mapped to the VNI is deleted */
32808c2ecf20Sopenharmony_ci	if (mlxsw_sp_fid_8021q_vid(fid) != vid)
32818c2ecf20Sopenharmony_ci		goto out;
32828c2ecf20Sopenharmony_ci
32838c2ecf20Sopenharmony_ci	mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ciout:
32868c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
32878c2ecf20Sopenharmony_ci}
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_cistatic int
32908c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
32918c2ecf20Sopenharmony_ci				   struct switchdev_notifier_port_obj_info *
32928c2ecf20Sopenharmony_ci				   port_obj_info)
32938c2ecf20Sopenharmony_ci{
32948c2ecf20Sopenharmony_ci	struct switchdev_obj_port_vlan *vlan =
32958c2ecf20Sopenharmony_ci		SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
32968c2ecf20Sopenharmony_ci	bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
32978c2ecf20Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
32988c2ecf20Sopenharmony_ci	struct switchdev_trans *trans = port_obj_info->trans;
32998c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
33008c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
33018c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
33028c2ecf20Sopenharmony_ci	struct net_device *br_dev;
33038c2ecf20Sopenharmony_ci	u16 vid;
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
33068c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(vxlan_dev);
33078c2ecf20Sopenharmony_ci	if (!br_dev)
33088c2ecf20Sopenharmony_ci		return 0;
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
33118c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
33128c2ecf20Sopenharmony_ci		return 0;
33138c2ecf20Sopenharmony_ci
33148c2ecf20Sopenharmony_ci	port_obj_info->handled = true;
33158c2ecf20Sopenharmony_ci
33168c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_commit(trans))
33178c2ecf20Sopenharmony_ci		return 0;
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
33208c2ecf20Sopenharmony_ci	if (!bridge_device)
33218c2ecf20Sopenharmony_ci		return -EINVAL;
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	if (!bridge_device->vlan_enabled)
33248c2ecf20Sopenharmony_ci		return 0;
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
33278c2ecf20Sopenharmony_ci		int err;
33288c2ecf20Sopenharmony_ci
33298c2ecf20Sopenharmony_ci		err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
33308c2ecf20Sopenharmony_ci							vxlan_dev, vid,
33318c2ecf20Sopenharmony_ci							flag_untagged,
33328c2ecf20Sopenharmony_ci							flag_pvid, extack);
33338c2ecf20Sopenharmony_ci		if (err)
33348c2ecf20Sopenharmony_ci			return err;
33358c2ecf20Sopenharmony_ci	}
33368c2ecf20Sopenharmony_ci
33378c2ecf20Sopenharmony_ci	return 0;
33388c2ecf20Sopenharmony_ci}
33398c2ecf20Sopenharmony_ci
33408c2ecf20Sopenharmony_cistatic void
33418c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
33428c2ecf20Sopenharmony_ci				   struct switchdev_notifier_port_obj_info *
33438c2ecf20Sopenharmony_ci				   port_obj_info)
33448c2ecf20Sopenharmony_ci{
33458c2ecf20Sopenharmony_ci	struct switchdev_obj_port_vlan *vlan =
33468c2ecf20Sopenharmony_ci		SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
33478c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
33488c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
33498c2ecf20Sopenharmony_ci	struct net_device *br_dev;
33508c2ecf20Sopenharmony_ci	u16 vid;
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(vxlan_dev);
33538c2ecf20Sopenharmony_ci	if (!br_dev)
33548c2ecf20Sopenharmony_ci		return;
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
33578c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
33588c2ecf20Sopenharmony_ci		return;
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	port_obj_info->handled = true;
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
33638c2ecf20Sopenharmony_ci	if (!bridge_device)
33648c2ecf20Sopenharmony_ci		return;
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_ci	if (!bridge_device->vlan_enabled)
33678c2ecf20Sopenharmony_ci		return;
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
33708c2ecf20Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device,
33718c2ecf20Sopenharmony_ci						  vxlan_dev, vid);
33728c2ecf20Sopenharmony_ci}
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_cistatic int
33758c2ecf20Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev,
33768c2ecf20Sopenharmony_ci					struct switchdev_notifier_port_obj_info *
33778c2ecf20Sopenharmony_ci					port_obj_info)
33788c2ecf20Sopenharmony_ci{
33798c2ecf20Sopenharmony_ci	int err = 0;
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_ci	switch (port_obj_info->obj->id) {
33828c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
33838c2ecf20Sopenharmony_ci		err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev,
33848c2ecf20Sopenharmony_ci							 port_obj_info);
33858c2ecf20Sopenharmony_ci		break;
33868c2ecf20Sopenharmony_ci	default:
33878c2ecf20Sopenharmony_ci		break;
33888c2ecf20Sopenharmony_ci	}
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_ci	return err;
33918c2ecf20Sopenharmony_ci}
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_cistatic void
33948c2ecf20Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev,
33958c2ecf20Sopenharmony_ci					struct switchdev_notifier_port_obj_info *
33968c2ecf20Sopenharmony_ci					port_obj_info)
33978c2ecf20Sopenharmony_ci{
33988c2ecf20Sopenharmony_ci	switch (port_obj_info->obj->id) {
33998c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
34008c2ecf20Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info);
34018c2ecf20Sopenharmony_ci		break;
34028c2ecf20Sopenharmony_ci	default:
34038c2ecf20Sopenharmony_ci		break;
34048c2ecf20Sopenharmony_ci	}
34058c2ecf20Sopenharmony_ci}
34068c2ecf20Sopenharmony_ci
34078c2ecf20Sopenharmony_cistatic int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
34088c2ecf20Sopenharmony_ci					     unsigned long event, void *ptr)
34098c2ecf20Sopenharmony_ci{
34108c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
34118c2ecf20Sopenharmony_ci	int err = 0;
34128c2ecf20Sopenharmony_ci
34138c2ecf20Sopenharmony_ci	switch (event) {
34148c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_ADD:
34158c2ecf20Sopenharmony_ci		if (netif_is_vxlan(dev))
34168c2ecf20Sopenharmony_ci			err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr);
34178c2ecf20Sopenharmony_ci		else
34188c2ecf20Sopenharmony_ci			err = switchdev_handle_port_obj_add(dev, ptr,
34198c2ecf20Sopenharmony_ci							mlxsw_sp_port_dev_check,
34208c2ecf20Sopenharmony_ci							mlxsw_sp_port_obj_add);
34218c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
34228c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_DEL:
34238c2ecf20Sopenharmony_ci		if (netif_is_vxlan(dev))
34248c2ecf20Sopenharmony_ci			mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr);
34258c2ecf20Sopenharmony_ci		else
34268c2ecf20Sopenharmony_ci			err = switchdev_handle_port_obj_del(dev, ptr,
34278c2ecf20Sopenharmony_ci							mlxsw_sp_port_dev_check,
34288c2ecf20Sopenharmony_ci							mlxsw_sp_port_obj_del);
34298c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
34308c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_ATTR_SET:
34318c2ecf20Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
34328c2ecf20Sopenharmony_ci						     mlxsw_sp_port_dev_check,
34338c2ecf20Sopenharmony_ci						     mlxsw_sp_port_attr_set);
34348c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
34358c2ecf20Sopenharmony_ci	}
34368c2ecf20Sopenharmony_ci
34378c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
34388c2ecf20Sopenharmony_ci}
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_cistatic struct notifier_block mlxsw_sp_switchdev_blocking_notifier = {
34418c2ecf20Sopenharmony_ci	.notifier_call = mlxsw_sp_switchdev_blocking_event,
34428c2ecf20Sopenharmony_ci};
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_ciu8
34458c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
34468c2ecf20Sopenharmony_ci{
34478c2ecf20Sopenharmony_ci	return bridge_port->stp_state;
34488c2ecf20Sopenharmony_ci}
34498c2ecf20Sopenharmony_ci
34508c2ecf20Sopenharmony_cistatic int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
34518c2ecf20Sopenharmony_ci{
34528c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
34538c2ecf20Sopenharmony_ci	struct notifier_block *nb;
34548c2ecf20Sopenharmony_ci	int err;
34558c2ecf20Sopenharmony_ci
34568c2ecf20Sopenharmony_ci	err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
34578c2ecf20Sopenharmony_ci	if (err) {
34588c2ecf20Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
34598c2ecf20Sopenharmony_ci		return err;
34608c2ecf20Sopenharmony_ci	}
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_ci	err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
34638c2ecf20Sopenharmony_ci	if (err) {
34648c2ecf20Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n");
34658c2ecf20Sopenharmony_ci		return err;
34668c2ecf20Sopenharmony_ci	}
34678c2ecf20Sopenharmony_ci
34688c2ecf20Sopenharmony_ci	nb = &mlxsw_sp_switchdev_blocking_notifier;
34698c2ecf20Sopenharmony_ci	err = register_switchdev_blocking_notifier(nb);
34708c2ecf20Sopenharmony_ci	if (err) {
34718c2ecf20Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n");
34728c2ecf20Sopenharmony_ci		goto err_register_switchdev_blocking_notifier;
34738c2ecf20Sopenharmony_ci	}
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
34768c2ecf20Sopenharmony_ci	bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
34778c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, false);
34788c2ecf20Sopenharmony_ci	return 0;
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_cierr_register_switchdev_blocking_notifier:
34818c2ecf20Sopenharmony_ci	unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
34828c2ecf20Sopenharmony_ci	return err;
34838c2ecf20Sopenharmony_ci}
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
34868c2ecf20Sopenharmony_ci{
34878c2ecf20Sopenharmony_ci	struct notifier_block *nb;
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
34908c2ecf20Sopenharmony_ci
34918c2ecf20Sopenharmony_ci	nb = &mlxsw_sp_switchdev_blocking_notifier;
34928c2ecf20Sopenharmony_ci	unregister_switchdev_blocking_notifier(nb);
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci	unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
34958c2ecf20Sopenharmony_ci}
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ciint mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
34988c2ecf20Sopenharmony_ci{
34998c2ecf20Sopenharmony_ci	struct mlxsw_sp_bridge *bridge;
35008c2ecf20Sopenharmony_ci
35018c2ecf20Sopenharmony_ci	bridge = kzalloc(sizeof(*mlxsw_sp->bridge), GFP_KERNEL);
35028c2ecf20Sopenharmony_ci	if (!bridge)
35038c2ecf20Sopenharmony_ci		return -ENOMEM;
35048c2ecf20Sopenharmony_ci	mlxsw_sp->bridge = bridge;
35058c2ecf20Sopenharmony_ci	bridge->mlxsw_sp = mlxsw_sp;
35068c2ecf20Sopenharmony_ci
35078c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp->bridge->bridges_list);
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	bridge->bridge_8021q_ops = &mlxsw_sp_bridge_8021q_ops;
35108c2ecf20Sopenharmony_ci	bridge->bridge_8021d_ops = &mlxsw_sp_bridge_8021d_ops;
35118c2ecf20Sopenharmony_ci
35128c2ecf20Sopenharmony_ci	return mlxsw_sp_fdb_init(mlxsw_sp);
35138c2ecf20Sopenharmony_ci}
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_civoid mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp)
35168c2ecf20Sopenharmony_ci{
35178c2ecf20Sopenharmony_ci	mlxsw_sp_fdb_fini(mlxsw_sp);
35188c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&mlxsw_sp->bridge->bridges_list));
35198c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->bridge);
35208c2ecf20Sopenharmony_ci}
35218c2ecf20Sopenharmony_ci
3522