162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/netdevice.h>
762306a36Sopenharmony_ci#include <linux/etherdevice.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <linux/if_vlan.h>
1262306a36Sopenharmony_ci#include <linux/if_bridge.h>
1362306a36Sopenharmony_ci#include <linux/workqueue.h>
1462306a36Sopenharmony_ci#include <linux/jiffies.h>
1562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1662306a36Sopenharmony_ci#include <linux/netlink.h>
1762306a36Sopenharmony_ci#include <net/switchdev.h>
1862306a36Sopenharmony_ci#include <net/vxlan.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "spectrum_span.h"
2162306a36Sopenharmony_ci#include "spectrum_switchdev.h"
2262306a36Sopenharmony_ci#include "spectrum.h"
2362306a36Sopenharmony_ci#include "core.h"
2462306a36Sopenharmony_ci#include "reg.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct mlxsw_sp_bridge_ops;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct mlxsw_sp_bridge {
2962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
3062306a36Sopenharmony_ci	struct {
3162306a36Sopenharmony_ci		struct delayed_work dw;
3262306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
3362306a36Sopenharmony_ci		unsigned int interval; /* ms */
3462306a36Sopenharmony_ci	} fdb_notify;
3562306a36Sopenharmony_ci#define MLXSW_SP_MIN_AGEING_TIME 10
3662306a36Sopenharmony_ci#define MLXSW_SP_MAX_AGEING_TIME 1000000
3762306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_AGEING_TIME 300
3862306a36Sopenharmony_ci	u32 ageing_time;
3962306a36Sopenharmony_ci	bool vlan_enabled_exists;
4062306a36Sopenharmony_ci	struct list_head bridges_list;
4162306a36Sopenharmony_ci	DECLARE_BITMAP(mids_bitmap, MLXSW_SP_MID_MAX);
4262306a36Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *bridge_8021q_ops;
4362306a36Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *bridge_8021d_ops;
4462306a36Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *bridge_8021ad_ops;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct mlxsw_sp_bridge_device {
4862306a36Sopenharmony_ci	struct net_device *dev;
4962306a36Sopenharmony_ci	struct list_head list;
5062306a36Sopenharmony_ci	struct list_head ports_list;
5162306a36Sopenharmony_ci	struct list_head mdb_list;
5262306a36Sopenharmony_ci	struct rhashtable mdb_ht;
5362306a36Sopenharmony_ci	u8 vlan_enabled:1,
5462306a36Sopenharmony_ci	   multicast_enabled:1,
5562306a36Sopenharmony_ci	   mrouter:1;
5662306a36Sopenharmony_ci	const struct mlxsw_sp_bridge_ops *ops;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct mlxsw_sp_bridge_port {
6062306a36Sopenharmony_ci	struct net_device *dev;
6162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
6262306a36Sopenharmony_ci	struct list_head list;
6362306a36Sopenharmony_ci	struct list_head vlans_list;
6462306a36Sopenharmony_ci	unsigned int ref_count;
6562306a36Sopenharmony_ci	u8 stp_state;
6662306a36Sopenharmony_ci	unsigned long flags;
6762306a36Sopenharmony_ci	bool mrouter;
6862306a36Sopenharmony_ci	bool lagged;
6962306a36Sopenharmony_ci	union {
7062306a36Sopenharmony_ci		u16 lag_id;
7162306a36Sopenharmony_ci		u16 system_port;
7262306a36Sopenharmony_ci	};
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct mlxsw_sp_bridge_vlan {
7662306a36Sopenharmony_ci	struct list_head list;
7762306a36Sopenharmony_ci	struct list_head port_vlan_list;
7862306a36Sopenharmony_ci	u16 vid;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct mlxsw_sp_bridge_ops {
8262306a36Sopenharmony_ci	int (*port_join)(struct mlxsw_sp_bridge_device *bridge_device,
8362306a36Sopenharmony_ci			 struct mlxsw_sp_bridge_port *bridge_port,
8462306a36Sopenharmony_ci			 struct mlxsw_sp_port *mlxsw_sp_port,
8562306a36Sopenharmony_ci			 struct netlink_ext_ack *extack);
8662306a36Sopenharmony_ci	void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device,
8762306a36Sopenharmony_ci			   struct mlxsw_sp_bridge_port *bridge_port,
8862306a36Sopenharmony_ci			   struct mlxsw_sp_port *mlxsw_sp_port);
8962306a36Sopenharmony_ci	int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device,
9062306a36Sopenharmony_ci			  const struct net_device *vxlan_dev, u16 vid,
9162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack);
9262306a36Sopenharmony_ci	struct mlxsw_sp_fid *
9362306a36Sopenharmony_ci		(*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
9462306a36Sopenharmony_ci			   u16 vid, struct netlink_ext_ack *extack);
9562306a36Sopenharmony_ci	struct mlxsw_sp_fid *
9662306a36Sopenharmony_ci		(*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device,
9762306a36Sopenharmony_ci			      u16 vid);
9862306a36Sopenharmony_ci	u16 (*fid_vid)(struct mlxsw_sp_bridge_device *bridge_device,
9962306a36Sopenharmony_ci		       const struct mlxsw_sp_fid *fid);
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct mlxsw_sp_switchdev_ops {
10362306a36Sopenharmony_ci	void (*init)(struct mlxsw_sp *mlxsw_sp);
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry_key {
10762306a36Sopenharmony_ci	unsigned char addr[ETH_ALEN];
10862306a36Sopenharmony_ci	u16 fid;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry {
11262306a36Sopenharmony_ci	struct list_head list;
11362306a36Sopenharmony_ci	struct rhash_head ht_node;
11462306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_key key;
11562306a36Sopenharmony_ci	u16 mid;
11662306a36Sopenharmony_ci	struct list_head ports_list;
11762306a36Sopenharmony_ci	u16 ports_count;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry_port {
12162306a36Sopenharmony_ci	struct list_head list; /* Member of 'ports_list'. */
12262306a36Sopenharmony_ci	u16 local_port;
12362306a36Sopenharmony_ci	refcount_t refcount;
12462306a36Sopenharmony_ci	bool mrouter;
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_mdb_ht_params = {
12862306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_mdb_entry, key),
12962306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_mdb_entry, ht_node),
13062306a36Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_mdb_entry_key),
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int
13462306a36Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp,
13562306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
13662306a36Sopenharmony_ci			       u16 fid_index);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void
13962306a36Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port,
14062306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
14162306a36Sopenharmony_ci			       u16 fid_index);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int
14462306a36Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp,
14562306a36Sopenharmony_ci				   struct mlxsw_sp_bridge_device
14662306a36Sopenharmony_ci				   *bridge_device, bool mc_enabled);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void
14962306a36Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
15062306a36Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
15162306a36Sopenharmony_ci				 bool add);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
15462306a36Sopenharmony_cimlxsw_sp_bridge_device_find(const struct mlxsw_sp_bridge *bridge,
15562306a36Sopenharmony_ci			    const struct net_device *br_dev)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	list_for_each_entry(bridge_device, &bridge->bridges_list, list)
16062306a36Sopenharmony_ci		if (bridge_device->dev == br_dev)
16162306a36Sopenharmony_ci			return bridge_device;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return NULL;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cibool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp,
16762306a36Sopenharmony_ci					 const struct net_device *br_dev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return !!mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int mlxsw_sp_bridge_device_upper_rif_destroy(struct net_device *dev,
17362306a36Sopenharmony_ci						    struct netdev_nested_priv *priv)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv->data;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev);
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp,
18262306a36Sopenharmony_ci						struct net_device *dev)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
18562306a36Sopenharmony_ci		.data = (void *)mlxsw_sp,
18662306a36Sopenharmony_ci	};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev);
18962306a36Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev,
19062306a36Sopenharmony_ci				      mlxsw_sp_bridge_device_upper_rif_destroy,
19162306a36Sopenharmony_ci				      &priv);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge,
19562306a36Sopenharmony_ci					     struct net_device *br_dev,
19662306a36Sopenharmony_ci					     struct netlink_ext_ack *extack)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct net_device *dev, *stop_dev;
19962306a36Sopenharmony_ci	struct list_head *iter;
20062306a36Sopenharmony_ci	int err;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
20362306a36Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev)) {
20462306a36Sopenharmony_ci			err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp,
20562306a36Sopenharmony_ci							 br_dev, dev, 0,
20662306a36Sopenharmony_ci							 extack);
20762306a36Sopenharmony_ci			if (err) {
20862306a36Sopenharmony_ci				stop_dev = dev;
20962306a36Sopenharmony_ci				goto err_vxlan_join;
21062306a36Sopenharmony_ci			}
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cierr_vxlan_join:
21762306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
21862306a36Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev)) {
21962306a36Sopenharmony_ci			if (stop_dev == dev)
22062306a36Sopenharmony_ci				break;
22162306a36Sopenharmony_ci			mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	return err;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge,
22862306a36Sopenharmony_ci					      struct net_device *br_dev)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct net_device *dev;
23162306a36Sopenharmony_ci	struct list_head *iter;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
23462306a36Sopenharmony_ci		if (netif_is_vxlan(dev) && netif_running(dev))
23562306a36Sopenharmony_ci			mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp,
24062306a36Sopenharmony_ci					      bool no_delay)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
24362306a36Sopenharmony_ci	unsigned int interval = no_delay ? 0 : bridge->fdb_notify.interval;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	mlxsw_core_schedule_dw(&bridge->fdb_notify.dw,
24662306a36Sopenharmony_ci			       msecs_to_jiffies(interval));
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
25062306a36Sopenharmony_cimlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge,
25162306a36Sopenharmony_ci			      struct net_device *br_dev,
25262306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct device *dev = bridge->mlxsw_sp->bus_info->dev;
25562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
25662306a36Sopenharmony_ci	bool vlan_enabled = br_vlan_enabled(br_dev);
25762306a36Sopenharmony_ci	int err;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (vlan_enabled && bridge->vlan_enabled_exists) {
26062306a36Sopenharmony_ci		dev_err(dev, "Only one VLAN-aware bridge is supported\n");
26162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported");
26262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	bridge_device = kzalloc(sizeof(*bridge_device), GFP_KERNEL);
26662306a36Sopenharmony_ci	if (!bridge_device)
26762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	err = rhashtable_init(&bridge_device->mdb_ht, &mlxsw_sp_mdb_ht_params);
27062306a36Sopenharmony_ci	if (err)
27162306a36Sopenharmony_ci		goto err_mdb_rhashtable_init;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	bridge_device->dev = br_dev;
27462306a36Sopenharmony_ci	bridge_device->vlan_enabled = vlan_enabled;
27562306a36Sopenharmony_ci	bridge_device->multicast_enabled = br_multicast_enabled(br_dev);
27662306a36Sopenharmony_ci	bridge_device->mrouter = br_multicast_router(br_dev);
27762306a36Sopenharmony_ci	INIT_LIST_HEAD(&bridge_device->ports_list);
27862306a36Sopenharmony_ci	if (vlan_enabled) {
27962306a36Sopenharmony_ci		u16 proto;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		bridge->vlan_enabled_exists = true;
28262306a36Sopenharmony_ci		br_vlan_get_proto(br_dev, &proto);
28362306a36Sopenharmony_ci		if (proto == ETH_P_8021AD)
28462306a36Sopenharmony_ci			bridge_device->ops = bridge->bridge_8021ad_ops;
28562306a36Sopenharmony_ci		else
28662306a36Sopenharmony_ci			bridge_device->ops = bridge->bridge_8021q_ops;
28762306a36Sopenharmony_ci	} else {
28862306a36Sopenharmony_ci		bridge_device->ops = bridge->bridge_8021d_ops;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	INIT_LIST_HEAD(&bridge_device->mdb_list);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (list_empty(&bridge->bridges_list))
29362306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_work_schedule(bridge->mlxsw_sp, false);
29462306a36Sopenharmony_ci	list_add(&bridge_device->list, &bridge->bridges_list);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* It is possible we already have VXLAN devices enslaved to the bridge.
29762306a36Sopenharmony_ci	 * In which case, we need to replay their configuration as if they were
29862306a36Sopenharmony_ci	 * just now enslaved to the bridge.
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack);
30162306a36Sopenharmony_ci	if (err)
30262306a36Sopenharmony_ci		goto err_vxlan_init;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return bridge_device;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cierr_vxlan_init:
30762306a36Sopenharmony_ci	list_del(&bridge_device->list);
30862306a36Sopenharmony_ci	if (bridge_device->vlan_enabled)
30962306a36Sopenharmony_ci		bridge->vlan_enabled_exists = false;
31062306a36Sopenharmony_ci	rhashtable_destroy(&bridge_device->mdb_ht);
31162306a36Sopenharmony_cierr_mdb_rhashtable_init:
31262306a36Sopenharmony_ci	kfree(bridge_device);
31362306a36Sopenharmony_ci	return ERR_PTR(err);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic void
31762306a36Sopenharmony_cimlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge,
31862306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_device *bridge_device)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev);
32162306a36Sopenharmony_ci	mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp,
32262306a36Sopenharmony_ci					    bridge_device->dev);
32362306a36Sopenharmony_ci	list_del(&bridge_device->list);
32462306a36Sopenharmony_ci	if (list_empty(&bridge->bridges_list))
32562306a36Sopenharmony_ci		cancel_delayed_work(&bridge->fdb_notify.dw);
32662306a36Sopenharmony_ci	if (bridge_device->vlan_enabled)
32762306a36Sopenharmony_ci		bridge->vlan_enabled_exists = false;
32862306a36Sopenharmony_ci	WARN_ON(!list_empty(&bridge_device->ports_list));
32962306a36Sopenharmony_ci	WARN_ON(!list_empty(&bridge_device->mdb_list));
33062306a36Sopenharmony_ci	rhashtable_destroy(&bridge_device->mdb_ht);
33162306a36Sopenharmony_ci	kfree(bridge_device);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device *
33562306a36Sopenharmony_cimlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge,
33662306a36Sopenharmony_ci			   struct net_device *br_dev,
33762306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev);
34262306a36Sopenharmony_ci	if (bridge_device)
34362306a36Sopenharmony_ci		return bridge_device;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return mlxsw_sp_bridge_device_create(bridge, br_dev, extack);
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void
34962306a36Sopenharmony_cimlxsw_sp_bridge_device_put(struct mlxsw_sp_bridge *bridge,
35062306a36Sopenharmony_ci			   struct mlxsw_sp_bridge_device *bridge_device)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	if (list_empty(&bridge_device->ports_list))
35362306a36Sopenharmony_ci		mlxsw_sp_bridge_device_destroy(bridge, bridge_device);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
35762306a36Sopenharmony_ci__mlxsw_sp_bridge_port_find(const struct mlxsw_sp_bridge_device *bridge_device,
35862306a36Sopenharmony_ci			    const struct net_device *brport_dev)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
36362306a36Sopenharmony_ci		if (bridge_port->dev == brport_dev)
36462306a36Sopenharmony_ci			return bridge_port;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return NULL;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistruct mlxsw_sp_bridge_port *
37162306a36Sopenharmony_cimlxsw_sp_bridge_port_find(struct mlxsw_sp_bridge *bridge,
37262306a36Sopenharmony_ci			  struct net_device *brport_dev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev);
37562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!br_dev)
37862306a36Sopenharmony_ci		return NULL;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev);
38162306a36Sopenharmony_ci	if (!bridge_device)
38262306a36Sopenharmony_ci		return NULL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return __mlxsw_sp_bridge_port_find(bridge_device, brport_dev);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx,
38862306a36Sopenharmony_ci				 const struct switchdev_obj *obj,
38962306a36Sopenharmony_ci				 struct netlink_ext_ack *extack);
39062306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx,
39162306a36Sopenharmony_ci				 const struct switchdev_obj *obj);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistruct mlxsw_sp_bridge_port_replay_switchdev_objs {
39462306a36Sopenharmony_ci	struct net_device *brport_dev;
39562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
39662306a36Sopenharmony_ci	int done;
39762306a36Sopenharmony_ci};
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int
40062306a36Sopenharmony_cimlxsw_sp_bridge_port_replay_switchdev_objs(struct notifier_block *nb,
40162306a36Sopenharmony_ci					   unsigned long event, void *ptr)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
40462306a36Sopenharmony_ci	struct switchdev_notifier_port_obj_info *port_obj_info = ptr;
40562306a36Sopenharmony_ci	struct netlink_ext_ack *extack = port_obj_info->info.extack;
40662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port_replay_switchdev_objs *rso;
40762306a36Sopenharmony_ci	int err = 0;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	rso = (void *)port_obj_info->info.ctx;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (event != SWITCHDEV_PORT_OBJ_ADD ||
41262306a36Sopenharmony_ci	    dev != rso->brport_dev)
41362306a36Sopenharmony_ci		goto out;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* When a port is joining the bridge through a LAG, there likely are
41662306a36Sopenharmony_ci	 * VLANs configured on that LAG already. The replay will thus attempt to
41762306a36Sopenharmony_ci	 * have the given port-vlans join the corresponding FIDs. But the LAG
41862306a36Sopenharmony_ci	 * netdevice has already called the ndo_vlan_rx_add_vid NDO for its VLAN
41962306a36Sopenharmony_ci	 * memberships, back before CHANGEUPPER was distributed and netdevice
42062306a36Sopenharmony_ci	 * master set. So now before propagating the VLAN events further, we
42162306a36Sopenharmony_ci	 * first need to kill the corresponding VID at the mlxsw_sp_port.
42262306a36Sopenharmony_ci	 *
42362306a36Sopenharmony_ci	 * Note that this doesn't need to be rolled back on failure -- if the
42462306a36Sopenharmony_ci	 * replay fails, the enslavement is off, and the VIDs would be killed by
42562306a36Sopenharmony_ci	 * LAG anyway as part of its rollback.
42662306a36Sopenharmony_ci	 */
42762306a36Sopenharmony_ci	if (port_obj_info->obj->id == SWITCHDEV_OBJ_ID_PORT_VLAN) {
42862306a36Sopenharmony_ci		u16 vid = SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj)->vid;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		err = mlxsw_sp_port_kill_vid(rso->mlxsw_sp_port->dev, 0, vid);
43162306a36Sopenharmony_ci		if (err)
43262306a36Sopenharmony_ci			goto out;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	++rso->done;
43662306a36Sopenharmony_ci	err = mlxsw_sp_port_obj_add(rso->mlxsw_sp_port->dev, NULL,
43762306a36Sopenharmony_ci				    port_obj_info->obj, extack);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ciout:
44062306a36Sopenharmony_ci	return notifier_from_errno(err);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_bridge_port_replay_switchdev_objs_nb = {
44462306a36Sopenharmony_ci	.notifier_call = mlxsw_sp_bridge_port_replay_switchdev_objs,
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int
44862306a36Sopenharmony_cimlxsw_sp_bridge_port_unreplay_switchdev_objs(struct notifier_block *nb,
44962306a36Sopenharmony_ci					     unsigned long event, void *ptr)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
45262306a36Sopenharmony_ci	struct switchdev_notifier_port_obj_info *port_obj_info = ptr;
45362306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port_replay_switchdev_objs *rso;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	rso = (void *)port_obj_info->info.ctx;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (event != SWITCHDEV_PORT_OBJ_ADD ||
45862306a36Sopenharmony_ci	    dev != rso->brport_dev)
45962306a36Sopenharmony_ci		return NOTIFY_DONE;
46062306a36Sopenharmony_ci	if (!rso->done--)
46162306a36Sopenharmony_ci		return NOTIFY_STOP;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	mlxsw_sp_port_obj_del(rso->mlxsw_sp_port->dev, NULL,
46462306a36Sopenharmony_ci			      port_obj_info->obj);
46562306a36Sopenharmony_ci	return NOTIFY_DONE;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_bridge_port_unreplay_switchdev_objs_nb = {
46962306a36Sopenharmony_ci	.notifier_call = mlxsw_sp_bridge_port_unreplay_switchdev_objs,
47062306a36Sopenharmony_ci};
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
47362306a36Sopenharmony_cimlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device,
47462306a36Sopenharmony_ci			    struct net_device *brport_dev,
47562306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
47862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
47962306a36Sopenharmony_ci	int err;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	bridge_port = kzalloc(sizeof(*bridge_port), GFP_KERNEL);
48262306a36Sopenharmony_ci	if (!bridge_port)
48362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(brport_dev);
48662306a36Sopenharmony_ci	bridge_port->lagged = mlxsw_sp_port->lagged;
48762306a36Sopenharmony_ci	if (bridge_port->lagged)
48862306a36Sopenharmony_ci		bridge_port->lag_id = mlxsw_sp_port->lag_id;
48962306a36Sopenharmony_ci	else
49062306a36Sopenharmony_ci		bridge_port->system_port = mlxsw_sp_port->local_port;
49162306a36Sopenharmony_ci	bridge_port->dev = brport_dev;
49262306a36Sopenharmony_ci	bridge_port->bridge_device = bridge_device;
49362306a36Sopenharmony_ci	bridge_port->stp_state = br_port_get_stp_state(brport_dev);
49462306a36Sopenharmony_ci	bridge_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC |
49562306a36Sopenharmony_ci			     BR_MCAST_FLOOD;
49662306a36Sopenharmony_ci	INIT_LIST_HEAD(&bridge_port->vlans_list);
49762306a36Sopenharmony_ci	list_add(&bridge_port->list, &bridge_device->ports_list);
49862306a36Sopenharmony_ci	bridge_port->ref_count = 1;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	err = switchdev_bridge_port_offload(brport_dev, mlxsw_sp_port->dev,
50162306a36Sopenharmony_ci					    NULL, NULL, NULL, false, extack);
50262306a36Sopenharmony_ci	if (err)
50362306a36Sopenharmony_ci		goto err_switchdev_offload;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return bridge_port;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cierr_switchdev_offload:
50862306a36Sopenharmony_ci	list_del(&bridge_port->list);
50962306a36Sopenharmony_ci	kfree(bridge_port);
51062306a36Sopenharmony_ci	return ERR_PTR(err);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void
51462306a36Sopenharmony_cimlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	switchdev_bridge_port_unoffload(bridge_port->dev, NULL, NULL, NULL);
51762306a36Sopenharmony_ci	list_del(&bridge_port->list);
51862306a36Sopenharmony_ci	WARN_ON(!list_empty(&bridge_port->vlans_list));
51962306a36Sopenharmony_ci	kfree(bridge_port);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port *
52362306a36Sopenharmony_cimlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
52462306a36Sopenharmony_ci			 struct net_device *brport_dev,
52562306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev);
52862306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
52962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
53062306a36Sopenharmony_ci	int err;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(bridge, brport_dev);
53362306a36Sopenharmony_ci	if (bridge_port) {
53462306a36Sopenharmony_ci		bridge_port->ref_count++;
53562306a36Sopenharmony_ci		return bridge_port;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack);
53962306a36Sopenharmony_ci	if (IS_ERR(bridge_device))
54062306a36Sopenharmony_ci		return ERR_CAST(bridge_device);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev,
54362306a36Sopenharmony_ci						  extack);
54462306a36Sopenharmony_ci	if (IS_ERR(bridge_port)) {
54562306a36Sopenharmony_ci		err = PTR_ERR(bridge_port);
54662306a36Sopenharmony_ci		goto err_bridge_port_create;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return bridge_port;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cierr_bridge_port_create:
55262306a36Sopenharmony_ci	mlxsw_sp_bridge_device_put(bridge, bridge_device);
55362306a36Sopenharmony_ci	return ERR_PTR(err);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge,
55762306a36Sopenharmony_ci				     struct mlxsw_sp_bridge_port *bridge_port)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (--bridge_port->ref_count != 0)
56262306a36Sopenharmony_ci		return;
56362306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
56462306a36Sopenharmony_ci	mlxsw_sp_bridge_port_destroy(bridge_port);
56562306a36Sopenharmony_ci	mlxsw_sp_bridge_device_put(bridge, bridge_device);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic struct mlxsw_sp_port_vlan *
56962306a36Sopenharmony_cimlxsw_sp_port_vlan_find_by_bridge(struct mlxsw_sp_port *mlxsw_sp_port,
57062306a36Sopenharmony_ci				  const struct mlxsw_sp_bridge_device *
57162306a36Sopenharmony_ci				  bridge_device,
57262306a36Sopenharmony_ci				  u16 vid)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
57762306a36Sopenharmony_ci			    list) {
57862306a36Sopenharmony_ci		if (!mlxsw_sp_port_vlan->bridge_port)
57962306a36Sopenharmony_ci			continue;
58062306a36Sopenharmony_ci		if (mlxsw_sp_port_vlan->bridge_port->bridge_device !=
58162306a36Sopenharmony_ci		    bridge_device)
58262306a36Sopenharmony_ci			continue;
58362306a36Sopenharmony_ci		if (bridge_device->vlan_enabled &&
58462306a36Sopenharmony_ci		    mlxsw_sp_port_vlan->vid != vid)
58562306a36Sopenharmony_ci			continue;
58662306a36Sopenharmony_ci		return mlxsw_sp_port_vlan;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return NULL;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic struct mlxsw_sp_port_vlan*
59362306a36Sopenharmony_cimlxsw_sp_port_vlan_find_by_fid(struct mlxsw_sp_port *mlxsw_sp_port,
59462306a36Sopenharmony_ci			       u16 fid_index)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
59962306a36Sopenharmony_ci			    list) {
60062306a36Sopenharmony_ci		struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (fid && mlxsw_sp_fid_index(fid) == fid_index)
60362306a36Sopenharmony_ci			return mlxsw_sp_port_vlan;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return NULL;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
61062306a36Sopenharmony_cimlxsw_sp_bridge_vlan_find(const struct mlxsw_sp_bridge_port *bridge_port,
61162306a36Sopenharmony_ci			  u16 vid)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
61662306a36Sopenharmony_ci		if (bridge_vlan->vid == vid)
61762306a36Sopenharmony_ci			return bridge_vlan;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return NULL;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
62462306a36Sopenharmony_cimlxsw_sp_bridge_vlan_create(struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	bridge_vlan = kzalloc(sizeof(*bridge_vlan), GFP_KERNEL);
62962306a36Sopenharmony_ci	if (!bridge_vlan)
63062306a36Sopenharmony_ci		return NULL;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	INIT_LIST_HEAD(&bridge_vlan->port_vlan_list);
63362306a36Sopenharmony_ci	bridge_vlan->vid = vid;
63462306a36Sopenharmony_ci	list_add(&bridge_vlan->list, &bridge_port->vlans_list);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return bridge_vlan;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void
64062306a36Sopenharmony_cimlxsw_sp_bridge_vlan_destroy(struct mlxsw_sp_bridge_vlan *bridge_vlan)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	list_del(&bridge_vlan->list);
64362306a36Sopenharmony_ci	WARN_ON(!list_empty(&bridge_vlan->port_vlan_list));
64462306a36Sopenharmony_ci	kfree(bridge_vlan);
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan *
64862306a36Sopenharmony_cimlxsw_sp_bridge_vlan_get(struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
65362306a36Sopenharmony_ci	if (bridge_vlan)
65462306a36Sopenharmony_ci		return bridge_vlan;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	return mlxsw_sp_bridge_vlan_create(bridge_port, vid);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void mlxsw_sp_bridge_vlan_put(struct mlxsw_sp_bridge_vlan *bridge_vlan)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	if (list_empty(&bridge_vlan->port_vlan_list))
66262306a36Sopenharmony_ci		mlxsw_sp_bridge_vlan_destroy(bridge_vlan);
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int
66662306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
66762306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_vlan *bridge_vlan,
66862306a36Sopenharmony_ci				  u8 state)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
67362306a36Sopenharmony_ci			    bridge_vlan_node) {
67462306a36Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
67562306a36Sopenharmony_ci			continue;
67662306a36Sopenharmony_ci		return mlxsw_sp_port_vid_stp_set(mlxsw_sp_port,
67762306a36Sopenharmony_ci						 bridge_vlan->vid, state);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
68462306a36Sopenharmony_ci					    struct net_device *orig_dev,
68562306a36Sopenharmony_ci					    u8 state)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
68862306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
68962306a36Sopenharmony_ci	int err;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
69262306a36Sopenharmony_ci	 * operation is executed due to it being deferred.
69362306a36Sopenharmony_ci	 */
69462306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
69562306a36Sopenharmony_ci						orig_dev);
69662306a36Sopenharmony_ci	if (!bridge_port)
69762306a36Sopenharmony_ci		return 0;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
70062306a36Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port,
70162306a36Sopenharmony_ci							bridge_vlan, state);
70262306a36Sopenharmony_ci		if (err)
70362306a36Sopenharmony_ci			goto err_port_bridge_vlan_stp_set;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	bridge_port->stp_state = state;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cierr_port_bridge_vlan_stp_set:
71162306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
71262306a36Sopenharmony_ci					     &bridge_port->vlans_list, list)
71362306a36Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, bridge_vlan,
71462306a36Sopenharmony_ci						  bridge_port->stp_state);
71562306a36Sopenharmony_ci	return err;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic int
71962306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
72062306a36Sopenharmony_ci				    struct mlxsw_sp_bridge_vlan *bridge_vlan,
72162306a36Sopenharmony_ci				    enum mlxsw_sp_flood_type packet_type,
72262306a36Sopenharmony_ci				    bool member)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
72762306a36Sopenharmony_ci			    bridge_vlan_node) {
72862306a36Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
72962306a36Sopenharmony_ci			continue;
73062306a36Sopenharmony_ci		return mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid,
73162306a36Sopenharmony_ci					      packet_type,
73262306a36Sopenharmony_ci					      mlxsw_sp_port->local_port,
73362306a36Sopenharmony_ci					      member);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int
74062306a36Sopenharmony_cimlxsw_sp_bridge_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
74162306a36Sopenharmony_ci				     struct mlxsw_sp_bridge_port *bridge_port,
74262306a36Sopenharmony_ci				     enum mlxsw_sp_flood_type packet_type,
74362306a36Sopenharmony_ci				     bool member)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
74662306a36Sopenharmony_ci	int err;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
74962306a36Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port,
75062306a36Sopenharmony_ci							  bridge_vlan,
75162306a36Sopenharmony_ci							  packet_type,
75262306a36Sopenharmony_ci							  member);
75362306a36Sopenharmony_ci		if (err)
75462306a36Sopenharmony_ci			goto err_port_bridge_vlan_flood_set;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cierr_port_bridge_vlan_flood_set:
76062306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
76162306a36Sopenharmony_ci					     &bridge_port->vlans_list, list)
76262306a36Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, bridge_vlan,
76362306a36Sopenharmony_ci						    packet_type, !member);
76462306a36Sopenharmony_ci	return err;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int
76862306a36Sopenharmony_cimlxsw_sp_bridge_vlans_flood_set(struct mlxsw_sp_bridge_vlan *bridge_vlan,
76962306a36Sopenharmony_ci				enum mlxsw_sp_flood_type packet_type,
77062306a36Sopenharmony_ci				bool member)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
77362306a36Sopenharmony_ci	int err;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
77662306a36Sopenharmony_ci			    bridge_vlan_node) {
77762306a36Sopenharmony_ci		u16 local_port = mlxsw_sp_port_vlan->mlxsw_sp_port->local_port;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		err = mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid,
78062306a36Sopenharmony_ci					     packet_type, local_port, member);
78162306a36Sopenharmony_ci		if (err)
78262306a36Sopenharmony_ci			goto err_fid_flood_set;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cierr_fid_flood_set:
78862306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(mlxsw_sp_port_vlan,
78962306a36Sopenharmony_ci					     &bridge_vlan->port_vlan_list,
79062306a36Sopenharmony_ci					     list) {
79162306a36Sopenharmony_ci		u16 local_port = mlxsw_sp_port_vlan->mlxsw_sp_port->local_port;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid, packet_type,
79462306a36Sopenharmony_ci				       local_port, !member);
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return err;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int
80162306a36Sopenharmony_cimlxsw_sp_bridge_ports_flood_table_set(struct mlxsw_sp_bridge_port *bridge_port,
80262306a36Sopenharmony_ci				      enum mlxsw_sp_flood_type packet_type,
80362306a36Sopenharmony_ci				      bool member)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
80662306a36Sopenharmony_ci	int err;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
80962306a36Sopenharmony_ci		err = mlxsw_sp_bridge_vlans_flood_set(bridge_vlan, packet_type,
81062306a36Sopenharmony_ci						      member);
81162306a36Sopenharmony_ci		if (err)
81262306a36Sopenharmony_ci			goto err_bridge_vlans_flood_set;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return 0;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cierr_bridge_vlans_flood_set:
81862306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
81962306a36Sopenharmony_ci					     &bridge_port->vlans_list, list)
82062306a36Sopenharmony_ci		mlxsw_sp_bridge_vlans_flood_set(bridge_vlan, packet_type,
82162306a36Sopenharmony_ci						!member);
82262306a36Sopenharmony_ci	return err;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int
82662306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
82762306a36Sopenharmony_ci				       struct mlxsw_sp_bridge_vlan *bridge_vlan,
82862306a36Sopenharmony_ci				       bool set)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
83162306a36Sopenharmony_ci	u16 vid = bridge_vlan->vid;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list,
83462306a36Sopenharmony_ci			    bridge_vlan_node) {
83562306a36Sopenharmony_ci		if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port)
83662306a36Sopenharmony_ci			continue;
83762306a36Sopenharmony_ci		return mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set);
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return 0;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic int
84462306a36Sopenharmony_cimlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
84562306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_port *bridge_port,
84662306a36Sopenharmony_ci				  bool set)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
84962306a36Sopenharmony_ci	int err;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
85262306a36Sopenharmony_ci		err = mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port,
85362306a36Sopenharmony_ci							     bridge_vlan, set);
85462306a36Sopenharmony_ci		if (err)
85562306a36Sopenharmony_ci			goto err_port_bridge_vlan_learning_set;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return 0;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cierr_port_bridge_vlan_learning_set:
86162306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_vlan,
86262306a36Sopenharmony_ci					     &bridge_port->vlans_list, list)
86362306a36Sopenharmony_ci		mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port,
86462306a36Sopenharmony_ci						       bridge_vlan, !set);
86562306a36Sopenharmony_ci	return err;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic int
86962306a36Sopenharmony_cimlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
87062306a36Sopenharmony_ci				    const struct net_device *orig_dev,
87162306a36Sopenharmony_ci				    struct switchdev_brport_flags flags,
87262306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
87562306a36Sopenharmony_ci			   BR_PORT_LOCKED | BR_PORT_MAB)) {
87662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge port flag");
87762306a36Sopenharmony_ci		return -EINVAL;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if ((flags.mask & BR_PORT_LOCKED) && is_vlan_dev(orig_dev)) {
88162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a VLAN upper");
88262306a36Sopenharmony_ci		return -EINVAL;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if ((flags.mask & BR_PORT_LOCKED) && vlan_uses_dev(orig_dev)) {
88662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a bridge port that has VLAN uppers");
88762306a36Sopenharmony_ci		return -EINVAL;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
89462306a36Sopenharmony_ci					   struct net_device *orig_dev,
89562306a36Sopenharmony_ci					   struct switchdev_brport_flags flags)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
89862306a36Sopenharmony_ci	int err;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
90162306a36Sopenharmony_ci						orig_dev);
90262306a36Sopenharmony_ci	if (!bridge_port)
90362306a36Sopenharmony_ci		return 0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (flags.mask & BR_FLOOD) {
90662306a36Sopenharmony_ci		err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port,
90762306a36Sopenharmony_ci							   bridge_port,
90862306a36Sopenharmony_ci							   MLXSW_SP_FLOOD_TYPE_UC,
90962306a36Sopenharmony_ci							   flags.val & BR_FLOOD);
91062306a36Sopenharmony_ci		if (err)
91162306a36Sopenharmony_ci			return err;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (flags.mask & BR_LEARNING) {
91562306a36Sopenharmony_ci		err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port,
91662306a36Sopenharmony_ci							bridge_port,
91762306a36Sopenharmony_ci							flags.val & BR_LEARNING);
91862306a36Sopenharmony_ci		if (err)
91962306a36Sopenharmony_ci			return err;
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (flags.mask & BR_PORT_LOCKED) {
92362306a36Sopenharmony_ci		err = mlxsw_sp_port_security_set(mlxsw_sp_port,
92462306a36Sopenharmony_ci						 flags.val & BR_PORT_LOCKED);
92562306a36Sopenharmony_ci		if (err)
92662306a36Sopenharmony_ci			return err;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (bridge_port->bridge_device->multicast_enabled)
93062306a36Sopenharmony_ci		goto out;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (flags.mask & BR_MCAST_FLOOD) {
93362306a36Sopenharmony_ci		err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port,
93462306a36Sopenharmony_ci							   bridge_port,
93562306a36Sopenharmony_ci							   MLXSW_SP_FLOOD_TYPE_MC,
93662306a36Sopenharmony_ci							   flags.val & BR_MCAST_FLOOD);
93762306a36Sopenharmony_ci		if (err)
93862306a36Sopenharmony_ci			return err;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ciout:
94262306a36Sopenharmony_ci	memcpy(&bridge_port->flags, &flags.val, sizeof(flags.val));
94362306a36Sopenharmony_ci	return 0;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	char sfdat_pl[MLXSW_REG_SFDAT_LEN];
94962306a36Sopenharmony_ci	int err;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time);
95262306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl);
95362306a36Sopenharmony_ci	if (err)
95462306a36Sopenharmony_ci		return err;
95562306a36Sopenharmony_ci	mlxsw_sp->bridge->ageing_time = ageing_time;
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
96062306a36Sopenharmony_ci					    unsigned long ageing_clock_t)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
96362306a36Sopenharmony_ci	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
96462306a36Sopenharmony_ci	u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
96762306a36Sopenharmony_ci	    ageing_time > MLXSW_SP_MAX_AGEING_TIME)
96862306a36Sopenharmony_ci		return -ERANGE;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
97462306a36Sopenharmony_ci					  struct net_device *orig_dev,
97562306a36Sopenharmony_ci					  bool vlan_enabled)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
97862306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
98162306a36Sopenharmony_ci	if (WARN_ON(!bridge_device))
98262306a36Sopenharmony_ci		return -EINVAL;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (bridge_device->vlan_enabled == vlan_enabled)
98562306a36Sopenharmony_ci		return 0;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	netdev_err(bridge_device->dev, "VLAN filtering can't be changed for existing bridge\n");
98862306a36Sopenharmony_ci	return -EINVAL;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_port,
99262306a36Sopenharmony_ci						struct net_device *orig_dev,
99362306a36Sopenharmony_ci						u16 vlan_proto)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
99662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
99962306a36Sopenharmony_ci	if (WARN_ON(!bridge_device))
100062306a36Sopenharmony_ci		return -EINVAL;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	netdev_err(bridge_device->dev, "VLAN protocol can't be changed on existing bridge\n");
100362306a36Sopenharmony_ci	return -EINVAL;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
100762306a36Sopenharmony_ci					  struct net_device *orig_dev,
100862306a36Sopenharmony_ci					  bool is_port_mrouter)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
101162306a36Sopenharmony_ci	int err;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
101462306a36Sopenharmony_ci						orig_dev);
101562306a36Sopenharmony_ci	if (!bridge_port)
101662306a36Sopenharmony_ci		return 0;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port,
101962306a36Sopenharmony_ci					 is_port_mrouter);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (!bridge_port->bridge_device->multicast_enabled)
102262306a36Sopenharmony_ci		goto out;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
102562306a36Sopenharmony_ci						   MLXSW_SP_FLOOD_TYPE_MC,
102662306a36Sopenharmony_ci						   is_port_mrouter);
102762306a36Sopenharmony_ci	if (err)
102862306a36Sopenharmony_ci		return err;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ciout:
103162306a36Sopenharmony_ci	bridge_port->mrouter = is_port_mrouter;
103262306a36Sopenharmony_ci	return 0;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	const struct mlxsw_sp_bridge_device *bridge_device;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
104062306a36Sopenharmony_ci	return bridge_device->multicast_enabled ? bridge_port->mrouter :
104162306a36Sopenharmony_ci					bridge_port->flags & BR_MCAST_FLOOD;
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
104562306a36Sopenharmony_ci					 struct net_device *orig_dev,
104662306a36Sopenharmony_ci					 bool mc_disabled)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	enum mlxsw_sp_flood_type packet_type = MLXSW_SP_FLOOD_TYPE_MC;
104962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
105062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
105162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
105262306a36Sopenharmony_ci	int err;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
105562306a36Sopenharmony_ci	 * operation is executed due to it being deferred.
105662306a36Sopenharmony_ci	 */
105762306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
105862306a36Sopenharmony_ci	if (!bridge_device)
105962306a36Sopenharmony_ci		return 0;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (bridge_device->multicast_enabled == !mc_disabled)
106262306a36Sopenharmony_ci		return 0;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	bridge_device->multicast_enabled = !mc_disabled;
106562306a36Sopenharmony_ci	err = mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp, bridge_device,
106662306a36Sopenharmony_ci						 !mc_disabled);
106762306a36Sopenharmony_ci	if (err)
106862306a36Sopenharmony_ci		goto err_mc_enable_sync;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
107162306a36Sopenharmony_ci		bool member = mlxsw_sp_mc_flood(bridge_port);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		err = mlxsw_sp_bridge_ports_flood_table_set(bridge_port,
107462306a36Sopenharmony_ci							    packet_type,
107562306a36Sopenharmony_ci							    member);
107662306a36Sopenharmony_ci		if (err)
107762306a36Sopenharmony_ci			goto err_flood_table_set;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cierr_flood_table_set:
108362306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(bridge_port,
108462306a36Sopenharmony_ci					     &bridge_device->ports_list, list) {
108562306a36Sopenharmony_ci		bool member = mlxsw_sp_mc_flood(bridge_port);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		mlxsw_sp_bridge_ports_flood_table_set(bridge_port, packet_type,
108862306a36Sopenharmony_ci						      !member);
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp, bridge_device,
109162306a36Sopenharmony_ci					   mc_disabled);
109262306a36Sopenharmony_cierr_mc_enable_sync:
109362306a36Sopenharmony_ci	bridge_device->multicast_enabled = mc_disabled;
109462306a36Sopenharmony_ci	return err;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry_port *
109862306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry,
109962306a36Sopenharmony_ci			       u16 local_port)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	list_for_each_entry(mdb_entry_port, &mdb_entry->ports_list, list) {
110462306a36Sopenharmony_ci		if (mdb_entry_port->local_port == local_port)
110562306a36Sopenharmony_ci			return mdb_entry_port;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	return NULL;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry_port *
111262306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp,
111362306a36Sopenharmony_ci			    struct mlxsw_sp_mdb_entry *mdb_entry,
111462306a36Sopenharmony_ci			    u16 local_port)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
111762306a36Sopenharmony_ci	int err;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port);
112062306a36Sopenharmony_ci	if (mdb_entry_port) {
112162306a36Sopenharmony_ci		if (mdb_entry_port->mrouter &&
112262306a36Sopenharmony_ci		    refcount_read(&mdb_entry_port->refcount) == 1)
112362306a36Sopenharmony_ci			mdb_entry->ports_count++;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		refcount_inc(&mdb_entry_port->refcount);
112662306a36Sopenharmony_ci		return mdb_entry_port;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
113062306a36Sopenharmony_ci					  mdb_entry->key.fid, local_port, true);
113162306a36Sopenharmony_ci	if (err)
113262306a36Sopenharmony_ci		return ERR_PTR(err);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL);
113562306a36Sopenharmony_ci	if (!mdb_entry_port) {
113662306a36Sopenharmony_ci		err = -ENOMEM;
113762306a36Sopenharmony_ci		goto err_mdb_entry_port_alloc;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	mdb_entry_port->local_port = local_port;
114162306a36Sopenharmony_ci	refcount_set(&mdb_entry_port->refcount, 1);
114262306a36Sopenharmony_ci	list_add(&mdb_entry_port->list, &mdb_entry->ports_list);
114362306a36Sopenharmony_ci	mdb_entry->ports_count++;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	return mdb_entry_port;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cierr_mdb_entry_port_alloc:
114862306a36Sopenharmony_ci	mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
114962306a36Sopenharmony_ci				    mdb_entry->key.fid, local_port, false);
115062306a36Sopenharmony_ci	return ERR_PTR(err);
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic void
115462306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_put(struct mlxsw_sp *mlxsw_sp,
115562306a36Sopenharmony_ci			    struct mlxsw_sp_mdb_entry *mdb_entry,
115662306a36Sopenharmony_ci			    u16 local_port, bool force)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port);
116162306a36Sopenharmony_ci	if (!mdb_entry_port)
116262306a36Sopenharmony_ci		return;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (!force && !refcount_dec_and_test(&mdb_entry_port->refcount)) {
116562306a36Sopenharmony_ci		if (mdb_entry_port->mrouter &&
116662306a36Sopenharmony_ci		    refcount_read(&mdb_entry_port->refcount) == 1)
116762306a36Sopenharmony_ci			mdb_entry->ports_count--;
116862306a36Sopenharmony_ci		return;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	mdb_entry->ports_count--;
117262306a36Sopenharmony_ci	list_del(&mdb_entry_port->list);
117362306a36Sopenharmony_ci	kfree(mdb_entry_port);
117462306a36Sopenharmony_ci	mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
117562306a36Sopenharmony_ci				    mdb_entry->key.fid, local_port, false);
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic __always_unused struct mlxsw_sp_mdb_entry_port *
117962306a36Sopenharmony_cimlxsw_sp_mdb_entry_mrouter_port_get(struct mlxsw_sp *mlxsw_sp,
118062306a36Sopenharmony_ci				    struct mlxsw_sp_mdb_entry *mdb_entry,
118162306a36Sopenharmony_ci				    u16 local_port)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
118462306a36Sopenharmony_ci	int err;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port);
118762306a36Sopenharmony_ci	if (mdb_entry_port) {
118862306a36Sopenharmony_ci		if (!mdb_entry_port->mrouter)
118962306a36Sopenharmony_ci			refcount_inc(&mdb_entry_port->refcount);
119062306a36Sopenharmony_ci		return mdb_entry_port;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
119462306a36Sopenharmony_ci					  mdb_entry->key.fid, local_port, true);
119562306a36Sopenharmony_ci	if (err)
119662306a36Sopenharmony_ci		return ERR_PTR(err);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL);
119962306a36Sopenharmony_ci	if (!mdb_entry_port) {
120062306a36Sopenharmony_ci		err = -ENOMEM;
120162306a36Sopenharmony_ci		goto err_mdb_entry_port_alloc;
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	mdb_entry_port->local_port = local_port;
120562306a36Sopenharmony_ci	refcount_set(&mdb_entry_port->refcount, 1);
120662306a36Sopenharmony_ci	mdb_entry_port->mrouter = true;
120762306a36Sopenharmony_ci	list_add(&mdb_entry_port->list, &mdb_entry->ports_list);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	return mdb_entry_port;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_cierr_mdb_entry_port_alloc:
121262306a36Sopenharmony_ci	mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
121362306a36Sopenharmony_ci				    mdb_entry->key.fid, local_port, false);
121462306a36Sopenharmony_ci	return ERR_PTR(err);
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic __always_unused void
121862306a36Sopenharmony_cimlxsw_sp_mdb_entry_mrouter_port_put(struct mlxsw_sp *mlxsw_sp,
121962306a36Sopenharmony_ci				    struct mlxsw_sp_mdb_entry *mdb_entry,
122062306a36Sopenharmony_ci				    u16 local_port)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port);
122562306a36Sopenharmony_ci	if (!mdb_entry_port)
122662306a36Sopenharmony_ci		return;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (!mdb_entry_port->mrouter)
122962306a36Sopenharmony_ci		return;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	mdb_entry_port->mrouter = false;
123262306a36Sopenharmony_ci	if (!refcount_dec_and_test(&mdb_entry_port->refcount))
123362306a36Sopenharmony_ci		return;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	list_del(&mdb_entry_port->list);
123662306a36Sopenharmony_ci	kfree(mdb_entry_port);
123762306a36Sopenharmony_ci	mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid,
123862306a36Sopenharmony_ci				    mdb_entry->key.fid, local_port, false);
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic void
124262306a36Sopenharmony_cimlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp,
124362306a36Sopenharmony_ci				   struct mlxsw_sp_bridge_device *bridge_device,
124462306a36Sopenharmony_ci				   bool add)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_router_port(mlxsw_sp);
124762306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) {
125062306a36Sopenharmony_ci		if (add)
125162306a36Sopenharmony_ci			mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry,
125262306a36Sopenharmony_ci							    local_port);
125362306a36Sopenharmony_ci		else
125462306a36Sopenharmony_ci			mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry,
125562306a36Sopenharmony_ci							    local_port);
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic int
126062306a36Sopenharmony_cimlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
126162306a36Sopenharmony_ci				  struct net_device *orig_dev,
126262306a36Sopenharmony_ci				  bool is_mrouter)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
126562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/* It's possible we failed to enslave the port, yet this
126862306a36Sopenharmony_ci	 * operation is executed due to it being deferred.
126962306a36Sopenharmony_ci	 */
127062306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
127162306a36Sopenharmony_ci	if (!bridge_device)
127262306a36Sopenharmony_ci		return 0;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (bridge_device->mrouter != is_mrouter)
127562306a36Sopenharmony_ci		mlxsw_sp_bridge_mrouter_update_mdb(mlxsw_sp, bridge_device,
127662306a36Sopenharmony_ci						   is_mrouter);
127762306a36Sopenharmony_ci	bridge_device->mrouter = is_mrouter;
127862306a36Sopenharmony_ci	return 0;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx,
128262306a36Sopenharmony_ci				  const struct switchdev_attr *attr,
128362306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
128662306a36Sopenharmony_ci	int err;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	switch (attr->id) {
128962306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
129062306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port,
129162306a36Sopenharmony_ci						       attr->orig_dev,
129262306a36Sopenharmony_ci						       attr->u.stp_state);
129362306a36Sopenharmony_ci		break;
129462306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
129562306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port,
129662306a36Sopenharmony_ci							  attr->orig_dev,
129762306a36Sopenharmony_ci							  attr->u.brport_flags,
129862306a36Sopenharmony_ci							  extack);
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
130162306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port,
130262306a36Sopenharmony_ci						      attr->orig_dev,
130362306a36Sopenharmony_ci						      attr->u.brport_flags);
130462306a36Sopenharmony_ci		break;
130562306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
130662306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port,
130762306a36Sopenharmony_ci						       attr->u.ageing_time);
130862306a36Sopenharmony_ci		break;
130962306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
131062306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port,
131162306a36Sopenharmony_ci						     attr->orig_dev,
131262306a36Sopenharmony_ci						     attr->u.vlan_filtering);
131362306a36Sopenharmony_ci		break;
131462306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL:
131562306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port,
131662306a36Sopenharmony_ci							   attr->orig_dev,
131762306a36Sopenharmony_ci							   attr->u.vlan_protocol);
131862306a36Sopenharmony_ci		break;
131962306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_MROUTER:
132062306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port,
132162306a36Sopenharmony_ci						     attr->orig_dev,
132262306a36Sopenharmony_ci						     attr->u.mrouter);
132362306a36Sopenharmony_ci		break;
132462306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
132562306a36Sopenharmony_ci		err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port,
132662306a36Sopenharmony_ci						    attr->orig_dev,
132762306a36Sopenharmony_ci						    attr->u.mc_disabled);
132862306a36Sopenharmony_ci		break;
132962306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
133062306a36Sopenharmony_ci		err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port,
133162306a36Sopenharmony_ci							attr->orig_dev,
133262306a36Sopenharmony_ci							attr->u.mrouter);
133362306a36Sopenharmony_ci		break;
133462306a36Sopenharmony_ci	default:
133562306a36Sopenharmony_ci		err = -EOPNOTSUPP;
133662306a36Sopenharmony_ci		break;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	return err;
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic int
134562306a36Sopenharmony_cimlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
134662306a36Sopenharmony_ci			    struct mlxsw_sp_bridge_port *bridge_port,
134762306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
135062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
135162306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
135262306a36Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
135362306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
135462306a36Sopenharmony_ci	int err;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
135762306a36Sopenharmony_ci	fid = bridge_device->ops->fid_get(bridge_device, vid, extack);
135862306a36Sopenharmony_ci	if (IS_ERR(fid))
135962306a36Sopenharmony_ci		return PTR_ERR(fid);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port,
136262306a36Sopenharmony_ci				     bridge_port->flags & BR_FLOOD);
136362306a36Sopenharmony_ci	if (err)
136462306a36Sopenharmony_ci		goto err_fid_uc_flood_set;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port,
136762306a36Sopenharmony_ci				     mlxsw_sp_mc_flood(bridge_port));
136862306a36Sopenharmony_ci	if (err)
136962306a36Sopenharmony_ci		goto err_fid_mc_flood_set;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port,
137262306a36Sopenharmony_ci				     true);
137362306a36Sopenharmony_ci	if (err)
137462306a36Sopenharmony_ci		goto err_fid_bc_flood_set;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
137762306a36Sopenharmony_ci	if (err)
137862306a36Sopenharmony_ci		goto err_fid_port_vid_map;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	mlxsw_sp_port_vlan->fid = fid;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return 0;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cierr_fid_port_vid_map:
138562306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false);
138662306a36Sopenharmony_cierr_fid_bc_flood_set:
138762306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false);
138862306a36Sopenharmony_cierr_fid_mc_flood_set:
138962306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false);
139062306a36Sopenharmony_cierr_fid_uc_flood_set:
139162306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
139262306a36Sopenharmony_ci	return err;
139362306a36Sopenharmony_ci}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cistatic void
139662306a36Sopenharmony_cimlxsw_sp_port_vlan_fid_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
139962306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
140062306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
140162306a36Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	mlxsw_sp_port_vlan->fid = NULL;
140462306a36Sopenharmony_ci	mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
140562306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false);
140662306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false);
140762306a36Sopenharmony_ci	mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false);
140862306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_cistatic u16
141262306a36Sopenharmony_cimlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port,
141362306a36Sopenharmony_ci			     u16 vid, bool is_pvid)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	if (is_pvid)
141662306a36Sopenharmony_ci		return vid;
141762306a36Sopenharmony_ci	else if (mlxsw_sp_port->pvid == vid)
141862306a36Sopenharmony_ci		return 0;	/* Dis-allow untagged packets */
141962306a36Sopenharmony_ci	else
142062306a36Sopenharmony_ci		return mlxsw_sp_port->pvid;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic int
142462306a36Sopenharmony_cimlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
142562306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
142662306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
142962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
143062306a36Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
143162306a36Sopenharmony_ci	int err;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/* No need to continue if only VLAN flags were changed */
143462306a36Sopenharmony_ci	if (mlxsw_sp_port_vlan->bridge_port)
143562306a36Sopenharmony_ci		return 0;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
143862306a36Sopenharmony_ci					  extack);
143962306a36Sopenharmony_ci	if (err)
144062306a36Sopenharmony_ci		return err;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid,
144362306a36Sopenharmony_ci					     bridge_port->flags & BR_LEARNING);
144462306a36Sopenharmony_ci	if (err)
144562306a36Sopenharmony_ci		goto err_port_vid_learning_set;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
144862306a36Sopenharmony_ci					bridge_port->stp_state);
144962306a36Sopenharmony_ci	if (err)
145062306a36Sopenharmony_ci		goto err_port_vid_stp_set;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_get(bridge_port, vid);
145362306a36Sopenharmony_ci	if (!bridge_vlan) {
145462306a36Sopenharmony_ci		err = -ENOMEM;
145562306a36Sopenharmony_ci		goto err_bridge_vlan_get;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	list_add(&mlxsw_sp_port_vlan->bridge_vlan_node,
145962306a36Sopenharmony_ci		 &bridge_vlan->port_vlan_list);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge,
146262306a36Sopenharmony_ci				 bridge_port->dev, extack);
146362306a36Sopenharmony_ci	mlxsw_sp_port_vlan->bridge_port = bridge_port;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	return 0;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cierr_bridge_vlan_get:
146862306a36Sopenharmony_ci	mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED);
146962306a36Sopenharmony_cierr_port_vid_stp_set:
147062306a36Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
147162306a36Sopenharmony_cierr_port_vid_learning_set:
147262306a36Sopenharmony_ci	mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan);
147362306a36Sopenharmony_ci	return err;
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_civoid
147762306a36Sopenharmony_cimlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
148062306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
148162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_vlan *bridge_vlan;
148262306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
148362306a36Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
148462306a36Sopenharmony_ci	bool last_port;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021Q &&
148762306a36Sopenharmony_ci		    mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021D))
148862306a36Sopenharmony_ci		return;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
149162306a36Sopenharmony_ci	bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
149262306a36Sopenharmony_ci	last_port = list_is_singular(&bridge_vlan->port_vlan_list);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	list_del(&mlxsw_sp_port_vlan->bridge_vlan_node);
149562306a36Sopenharmony_ci	mlxsw_sp_bridge_vlan_put(bridge_vlan);
149662306a36Sopenharmony_ci	mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED);
149762306a36Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
149862306a36Sopenharmony_ci	if (last_port)
149962306a36Sopenharmony_ci		mlxsw_sp_bridge_port_fdb_flush(mlxsw_sp_port->mlxsw_sp,
150062306a36Sopenharmony_ci					       bridge_port,
150162306a36Sopenharmony_ci					       mlxsw_sp_fid_index(fid));
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port,
150462306a36Sopenharmony_ci				       mlxsw_sp_fid_index(fid));
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp_port->mlxsw_sp->bridge, bridge_port);
150962306a36Sopenharmony_ci	mlxsw_sp_port_vlan->bridge_port = NULL;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_cistatic int
151362306a36Sopenharmony_cimlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
151462306a36Sopenharmony_ci			      struct mlxsw_sp_bridge_port *bridge_port,
151562306a36Sopenharmony_ci			      u16 vid, bool is_untagged, bool is_pvid,
151662306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
151962306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
152062306a36Sopenharmony_ci	u16 old_pvid = mlxsw_sp_port->pvid;
152162306a36Sopenharmony_ci	u16 proto;
152262306a36Sopenharmony_ci	int err;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* The only valid scenario in which a port-vlan already exists, is if
152562306a36Sopenharmony_ci	 * the VLAN flags were changed and the port-vlan is associated with the
152662306a36Sopenharmony_ci	 * correct bridge port
152762306a36Sopenharmony_ci	 */
152862306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
152962306a36Sopenharmony_ci	if (mlxsw_sp_port_vlan &&
153062306a36Sopenharmony_ci	    mlxsw_sp_port_vlan->bridge_port != bridge_port)
153162306a36Sopenharmony_ci		return -EEXIST;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
153462306a36Sopenharmony_ci		mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
153562306a36Sopenharmony_ci							       vid);
153662306a36Sopenharmony_ci		if (IS_ERR(mlxsw_sp_port_vlan))
153762306a36Sopenharmony_ci			return PTR_ERR(mlxsw_sp_port_vlan);
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
154162306a36Sopenharmony_ci				     is_untagged);
154262306a36Sopenharmony_ci	if (err)
154362306a36Sopenharmony_ci		goto err_port_vlan_set;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	br_vlan_get_proto(bridge_port->bridge_device->dev, &proto);
154662306a36Sopenharmony_ci	err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid, proto);
154762306a36Sopenharmony_ci	if (err)
154862306a36Sopenharmony_ci		goto err_port_pvid_set;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
155162306a36Sopenharmony_ci					     extack);
155262306a36Sopenharmony_ci	if (err)
155362306a36Sopenharmony_ci		goto err_port_vlan_bridge_join;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	return 0;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_cierr_port_vlan_bridge_join:
155862306a36Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid, proto);
155962306a36Sopenharmony_cierr_port_pvid_set:
156062306a36Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
156162306a36Sopenharmony_cierr_port_vlan_set:
156262306a36Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
156362306a36Sopenharmony_ci	return err;
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_cistatic int
156762306a36Sopenharmony_cimlxsw_sp_br_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
156862306a36Sopenharmony_ci			    struct net_device *br_dev,
156962306a36Sopenharmony_ci			    const struct switchdev_obj_port_vlan *vlan,
157062306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	return mlxsw_sp_router_bridge_vlan_add(mlxsw_sp, br_dev, vlan->vid,
157562306a36Sopenharmony_ci					       flag_pvid, extack);
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
157962306a36Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan,
158062306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
158162306a36Sopenharmony_ci{
158262306a36Sopenharmony_ci	bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
158362306a36Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
158462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
158562306a36Sopenharmony_ci	struct net_device *orig_dev = vlan->obj.orig_dev;
158662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (netif_is_bridge_master(orig_dev)) {
158962306a36Sopenharmony_ci		int err = 0;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		if (br_vlan_enabled(orig_dev))
159262306a36Sopenharmony_ci			err = mlxsw_sp_br_rif_pvid_change(mlxsw_sp, orig_dev,
159362306a36Sopenharmony_ci							  vlan, extack);
159462306a36Sopenharmony_ci		if (!err)
159562306a36Sopenharmony_ci			err = -EOPNOTSUPP;
159662306a36Sopenharmony_ci		return err;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
160062306a36Sopenharmony_ci	if (WARN_ON(!bridge_port))
160162306a36Sopenharmony_ci		return -EINVAL;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	if (!bridge_port->bridge_device->vlan_enabled)
160462306a36Sopenharmony_ci		return 0;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	return mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
160762306a36Sopenharmony_ci					     vlan->vid, flag_untagged,
160862306a36Sopenharmony_ci					     flag_pvid, extack);
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	return lagged ? MLXSW_REG_SFDF_FLUSH_PER_LAG_AND_FID :
161462306a36Sopenharmony_ci			MLXSW_REG_SFDF_FLUSH_PER_PORT_AND_FID;
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic int
161862306a36Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp,
161962306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
162062306a36Sopenharmony_ci			       u16 fid_index)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	bool lagged = bridge_port->lagged;
162362306a36Sopenharmony_ci	char sfdf_pl[MLXSW_REG_SFDF_LEN];
162462306a36Sopenharmony_ci	u16 system_port;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	system_port = lagged ? bridge_port->lag_id : bridge_port->system_port;
162762306a36Sopenharmony_ci	mlxsw_reg_sfdf_pack(sfdf_pl, mlxsw_sp_fdb_flush_type(lagged));
162862306a36Sopenharmony_ci	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
162962306a36Sopenharmony_ci	mlxsw_reg_sfdf_port_fid_system_port_set(sfdf_pl, system_port);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_cistatic enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
163762306a36Sopenharmony_ci			 MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG;
163862306a36Sopenharmony_ci}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_cistatic enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
164362306a36Sopenharmony_ci			MLXSW_REG_SFD_OP_WRITE_REMOVE;
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_cistatic int
164762306a36Sopenharmony_cimlxsw_sp_port_fdb_tun_uc_op4(struct mlxsw_sp *mlxsw_sp, bool dynamic,
164862306a36Sopenharmony_ci			     const char *mac, u16 fid, __be32 addr, bool adding)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	char *sfd_pl;
165162306a36Sopenharmony_ci	u8 num_rec;
165262306a36Sopenharmony_ci	u32 uip;
165362306a36Sopenharmony_ci	int err;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
165662306a36Sopenharmony_ci	if (!sfd_pl)
165762306a36Sopenharmony_ci		return -ENOMEM;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	uip = be32_to_cpu(addr);
166062306a36Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
166162306a36Sopenharmony_ci	mlxsw_reg_sfd_uc_tunnel_pack4(sfd_pl, 0,
166262306a36Sopenharmony_ci				      mlxsw_sp_sfd_rec_policy(dynamic), mac,
166362306a36Sopenharmony_ci				      fid, MLXSW_REG_SFD_REC_ACTION_NOP, uip);
166462306a36Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
166562306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
166662306a36Sopenharmony_ci	if (err)
166762306a36Sopenharmony_ci		goto out;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
167062306a36Sopenharmony_ci		err = -EBUSY;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ciout:
167362306a36Sopenharmony_ci	kfree(sfd_pl);
167462306a36Sopenharmony_ci	return err;
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(struct mlxsw_sp *mlxsw_sp,
167862306a36Sopenharmony_ci						  const char *mac, u16 fid,
167962306a36Sopenharmony_ci						  u32 kvdl_index, bool adding)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	char *sfd_pl;
168262306a36Sopenharmony_ci	u8 num_rec;
168362306a36Sopenharmony_ci	int err;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
168662306a36Sopenharmony_ci	if (!sfd_pl)
168762306a36Sopenharmony_ci		return -ENOMEM;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
169062306a36Sopenharmony_ci	mlxsw_reg_sfd_uc_tunnel_pack6(sfd_pl, 0, mac, fid,
169162306a36Sopenharmony_ci				      MLXSW_REG_SFD_REC_ACTION_NOP, kvdl_index);
169262306a36Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
169362306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
169462306a36Sopenharmony_ci	if (err)
169562306a36Sopenharmony_ci		goto out;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
169862306a36Sopenharmony_ci		err = -EBUSY;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ciout:
170162306a36Sopenharmony_ci	kfree(sfd_pl);
170262306a36Sopenharmony_ci	return err;
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tun_uc_op6_add(struct mlxsw_sp *mlxsw_sp,
170662306a36Sopenharmony_ci					    const char *mac, u16 fid,
170762306a36Sopenharmony_ci					    const struct in6_addr *addr)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	u32 kvdl_index;
171062306a36Sopenharmony_ci	int err;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	err = mlxsw_sp_nve_ipv6_addr_kvdl_set(mlxsw_sp, addr, &kvdl_index);
171362306a36Sopenharmony_ci	if (err)
171462306a36Sopenharmony_ci		return err;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	err = mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid,
171762306a36Sopenharmony_ci						     kvdl_index, true);
171862306a36Sopenharmony_ci	if (err)
171962306a36Sopenharmony_ci		goto err_sfd_write;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	err = mlxsw_sp_nve_ipv6_addr_map_replace(mlxsw_sp, mac, fid, addr);
172262306a36Sopenharmony_ci	if (err)
172362306a36Sopenharmony_ci		/* Replace can fail only for creating new mapping, so removing
172462306a36Sopenharmony_ci		 * the FDB entry in the error path is OK.
172562306a36Sopenharmony_ci		 */
172662306a36Sopenharmony_ci		goto err_addr_replace;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return 0;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cierr_addr_replace:
173162306a36Sopenharmony_ci	mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, kvdl_index,
173262306a36Sopenharmony_ci					       false);
173362306a36Sopenharmony_cierr_sfd_write:
173462306a36Sopenharmony_ci	mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr);
173562306a36Sopenharmony_ci	return err;
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic void mlxsw_sp_port_fdb_tun_uc_op6_del(struct mlxsw_sp *mlxsw_sp,
173962306a36Sopenharmony_ci					     const char *mac, u16 fid,
174062306a36Sopenharmony_ci					     const struct in6_addr *addr)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	mlxsw_sp_nve_ipv6_addr_map_del(mlxsw_sp, mac, fid);
174362306a36Sopenharmony_ci	mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, 0, false);
174462306a36Sopenharmony_ci	mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr);
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic int
174862306a36Sopenharmony_cimlxsw_sp_port_fdb_tun_uc_op6(struct mlxsw_sp *mlxsw_sp, const char *mac,
174962306a36Sopenharmony_ci			     u16 fid, const struct in6_addr *addr, bool adding)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	if (adding)
175262306a36Sopenharmony_ci		return mlxsw_sp_port_fdb_tun_uc_op6_add(mlxsw_sp, mac, fid,
175362306a36Sopenharmony_ci							addr);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	mlxsw_sp_port_fdb_tun_uc_op6_del(mlxsw_sp, mac, fid, addr);
175662306a36Sopenharmony_ci	return 0;
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp,
176062306a36Sopenharmony_ci					  const char *mac, u16 fid,
176162306a36Sopenharmony_ci					  enum mlxsw_sp_l3proto proto,
176262306a36Sopenharmony_ci					  const union mlxsw_sp_l3addr *addr,
176362306a36Sopenharmony_ci					  bool adding, bool dynamic)
176462306a36Sopenharmony_ci{
176562306a36Sopenharmony_ci	switch (proto) {
176662306a36Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
176762306a36Sopenharmony_ci		return mlxsw_sp_port_fdb_tun_uc_op4(mlxsw_sp, dynamic, mac, fid,
176862306a36Sopenharmony_ci						    addr->addr4, adding);
176962306a36Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
177062306a36Sopenharmony_ci		return mlxsw_sp_port_fdb_tun_uc_op6(mlxsw_sp, mac, fid,
177162306a36Sopenharmony_ci						    &addr->addr6, adding);
177262306a36Sopenharmony_ci	default:
177362306a36Sopenharmony_ci		WARN_ON(1);
177462306a36Sopenharmony_ci		return -EOPNOTSUPP;
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_cistatic int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u16 local_port,
177962306a36Sopenharmony_ci				     const char *mac, u16 fid, u16 vid,
178062306a36Sopenharmony_ci				     bool adding,
178162306a36Sopenharmony_ci				     enum mlxsw_reg_sfd_rec_action action,
178262306a36Sopenharmony_ci				     enum mlxsw_reg_sfd_rec_policy policy)
178362306a36Sopenharmony_ci{
178462306a36Sopenharmony_ci	char *sfd_pl;
178562306a36Sopenharmony_ci	u8 num_rec;
178662306a36Sopenharmony_ci	int err;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
178962306a36Sopenharmony_ci	if (!sfd_pl)
179062306a36Sopenharmony_ci		return -ENOMEM;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
179362306a36Sopenharmony_ci	mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, mac, fid, vid, action,
179462306a36Sopenharmony_ci			      local_port);
179562306a36Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
179662306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
179762306a36Sopenharmony_ci	if (err)
179862306a36Sopenharmony_ci		goto out;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
180162306a36Sopenharmony_ci		err = -EBUSY;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ciout:
180462306a36Sopenharmony_ci	kfree(sfd_pl);
180562306a36Sopenharmony_ci	return err;
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u16 local_port,
180962306a36Sopenharmony_ci				   const char *mac, u16 fid, u16 vid,
181062306a36Sopenharmony_ci				   bool adding, bool dynamic)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, vid,
181362306a36Sopenharmony_ci					 adding, MLXSW_REG_SFD_REC_ACTION_NOP,
181462306a36Sopenharmony_ci					 mlxsw_sp_sfd_rec_policy(dynamic));
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ciint mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
181862306a36Sopenharmony_ci			bool adding)
181962306a36Sopenharmony_ci{
182062306a36Sopenharmony_ci	return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 0, mac, fid, 0, adding,
182162306a36Sopenharmony_ci					 MLXSW_REG_SFD_REC_ACTION_FORWARD_IP_ROUTER,
182262306a36Sopenharmony_ci					 MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY);
182362306a36Sopenharmony_ci}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
182662306a36Sopenharmony_ci				       const char *mac, u16 fid, u16 lag_vid,
182762306a36Sopenharmony_ci				       bool adding, bool dynamic)
182862306a36Sopenharmony_ci{
182962306a36Sopenharmony_ci	char *sfd_pl;
183062306a36Sopenharmony_ci	u8 num_rec;
183162306a36Sopenharmony_ci	int err;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
183462306a36Sopenharmony_ci	if (!sfd_pl)
183562306a36Sopenharmony_ci		return -ENOMEM;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
183862306a36Sopenharmony_ci	mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
183962306a36Sopenharmony_ci				  mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
184062306a36Sopenharmony_ci				  lag_vid, lag_id);
184162306a36Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
184262306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
184362306a36Sopenharmony_ci	if (err)
184462306a36Sopenharmony_ci		goto out;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
184762306a36Sopenharmony_ci		err = -EBUSY;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ciout:
185062306a36Sopenharmony_ci	kfree(sfd_pl);
185162306a36Sopenharmony_ci	return err;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic int
185562306a36Sopenharmony_cimlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port,
185662306a36Sopenharmony_ci		      struct switchdev_notifier_fdb_info *fdb_info, bool adding)
185762306a36Sopenharmony_ci{
185862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
185962306a36Sopenharmony_ci	struct net_device *orig_dev = fdb_info->info.dev;
186062306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
186162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
186262306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
186362306a36Sopenharmony_ci	u16 fid_index, vid;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
186662306a36Sopenharmony_ci	if (!bridge_port)
186762306a36Sopenharmony_ci		return -EINVAL;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
187062306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
187162306a36Sopenharmony_ci							       bridge_device,
187262306a36Sopenharmony_ci							       fdb_info->vid);
187362306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
187462306a36Sopenharmony_ci		return 0;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
187762306a36Sopenharmony_ci	vid = mlxsw_sp_port_vlan->vid;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	if (!bridge_port->lagged)
188062306a36Sopenharmony_ci		return mlxsw_sp_port_fdb_uc_op(mlxsw_sp,
188162306a36Sopenharmony_ci					       bridge_port->system_port,
188262306a36Sopenharmony_ci					       fdb_info->addr, fid_index, vid,
188362306a36Sopenharmony_ci					       adding, false);
188462306a36Sopenharmony_ci	else
188562306a36Sopenharmony_ci		return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp,
188662306a36Sopenharmony_ci						   bridge_port->lag_id,
188762306a36Sopenharmony_ci						   fdb_info->addr, fid_index,
188862306a36Sopenharmony_ci						   vid, adding, false);
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_cistatic int mlxsw_sp_mdb_entry_write(struct mlxsw_sp *mlxsw_sp,
189262306a36Sopenharmony_ci				    const struct mlxsw_sp_mdb_entry *mdb_entry,
189362306a36Sopenharmony_ci				    bool adding)
189462306a36Sopenharmony_ci{
189562306a36Sopenharmony_ci	char *sfd_pl;
189662306a36Sopenharmony_ci	u8 num_rec;
189762306a36Sopenharmony_ci	int err;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
190062306a36Sopenharmony_ci	if (!sfd_pl)
190162306a36Sopenharmony_ci		return -ENOMEM;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
190462306a36Sopenharmony_ci	mlxsw_reg_sfd_mc_pack(sfd_pl, 0, mdb_entry->key.addr,
190562306a36Sopenharmony_ci			      mdb_entry->key.fid, MLXSW_REG_SFD_REC_ACTION_NOP,
190662306a36Sopenharmony_ci			      mdb_entry->mid);
190762306a36Sopenharmony_ci	num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
190862306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
190962306a36Sopenharmony_ci	if (err)
191062306a36Sopenharmony_ci		goto out;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
191362306a36Sopenharmony_ci		err = -EBUSY;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ciout:
191662306a36Sopenharmony_ci	kfree(sfd_pl);
191762306a36Sopenharmony_ci	return err;
191862306a36Sopenharmony_ci}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cistatic void
192162306a36Sopenharmony_cimlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp,
192262306a36Sopenharmony_ci				      struct mlxsw_sp_bridge_port *bridge_port,
192362306a36Sopenharmony_ci				      struct mlxsw_sp_ports_bitmap *ports_bm)
192462306a36Sopenharmony_ci{
192562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
192662306a36Sopenharmony_ci	u64 max_lag_members, i;
192762306a36Sopenharmony_ci	int lag_id;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	if (!bridge_port->lagged) {
193062306a36Sopenharmony_ci		set_bit(bridge_port->system_port, ports_bm->bitmap);
193162306a36Sopenharmony_ci	} else {
193262306a36Sopenharmony_ci		max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
193362306a36Sopenharmony_ci						     MAX_LAG_MEMBERS);
193462306a36Sopenharmony_ci		lag_id = bridge_port->lag_id;
193562306a36Sopenharmony_ci		for (i = 0; i < max_lag_members; i++) {
193662306a36Sopenharmony_ci			mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp,
193762306a36Sopenharmony_ci								 lag_id, i);
193862306a36Sopenharmony_ci			if (mlxsw_sp_port)
193962306a36Sopenharmony_ci				set_bit(mlxsw_sp_port->local_port,
194062306a36Sopenharmony_ci					ports_bm->bitmap);
194162306a36Sopenharmony_ci		}
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_cistatic void
194662306a36Sopenharmony_cimlxsw_sp_mc_get_mrouters_bitmap(struct mlxsw_sp_ports_bitmap *flood_bm,
194762306a36Sopenharmony_ci				struct mlxsw_sp_bridge_device *bridge_device,
194862306a36Sopenharmony_ci				struct mlxsw_sp *mlxsw_sp)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	list_for_each_entry(bridge_port, &bridge_device->ports_list, list) {
195362306a36Sopenharmony_ci		if (bridge_port->mrouter) {
195462306a36Sopenharmony_ci			mlxsw_sp_bridge_port_get_ports_bitmap(mlxsw_sp,
195562306a36Sopenharmony_ci							      bridge_port,
195662306a36Sopenharmony_ci							      flood_bm);
195762306a36Sopenharmony_ci		}
195862306a36Sopenharmony_ci	}
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic int mlxsw_sp_mc_mdb_mrouters_add(struct mlxsw_sp *mlxsw_sp,
196262306a36Sopenharmony_ci					struct mlxsw_sp_ports_bitmap *ports_bm,
196362306a36Sopenharmony_ci					struct mlxsw_sp_mdb_entry *mdb_entry)
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
196662306a36Sopenharmony_ci	unsigned int nbits = ports_bm->nbits;
196762306a36Sopenharmony_ci	int i;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	for_each_set_bit(i, ports_bm->bitmap, nbits) {
197062306a36Sopenharmony_ci		mdb_entry_port = mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp,
197162306a36Sopenharmony_ci								     mdb_entry,
197262306a36Sopenharmony_ci								     i);
197362306a36Sopenharmony_ci		if (IS_ERR(mdb_entry_port)) {
197462306a36Sopenharmony_ci			nbits = i;
197562306a36Sopenharmony_ci			goto err_mrouter_port_get;
197662306a36Sopenharmony_ci		}
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	return 0;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_cierr_mrouter_port_get:
198262306a36Sopenharmony_ci	for_each_set_bit(i, ports_bm->bitmap, nbits)
198362306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i);
198462306a36Sopenharmony_ci	return PTR_ERR(mdb_entry_port);
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic void mlxsw_sp_mc_mdb_mrouters_del(struct mlxsw_sp *mlxsw_sp,
198862306a36Sopenharmony_ci					 struct mlxsw_sp_ports_bitmap *ports_bm,
198962306a36Sopenharmony_ci					 struct mlxsw_sp_mdb_entry *mdb_entry)
199062306a36Sopenharmony_ci{
199162306a36Sopenharmony_ci	int i;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	for_each_set_bit(i, ports_bm->bitmap, ports_bm->nbits)
199462306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i);
199562306a36Sopenharmony_ci}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_cistatic int
199862306a36Sopenharmony_cimlxsw_sp_mc_mdb_mrouters_set(struct mlxsw_sp *mlxsw_sp,
199962306a36Sopenharmony_ci			     struct mlxsw_sp_bridge_device *bridge_device,
200062306a36Sopenharmony_ci			     struct mlxsw_sp_mdb_entry *mdb_entry, bool add)
200162306a36Sopenharmony_ci{
200262306a36Sopenharmony_ci	struct mlxsw_sp_ports_bitmap ports_bm;
200362306a36Sopenharmony_ci	int err;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	err = mlxsw_sp_port_bitmap_init(mlxsw_sp, &ports_bm);
200662306a36Sopenharmony_ci	if (err)
200762306a36Sopenharmony_ci		return err;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	mlxsw_sp_mc_get_mrouters_bitmap(&ports_bm, bridge_device, mlxsw_sp);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if (add)
201262306a36Sopenharmony_ci		err = mlxsw_sp_mc_mdb_mrouters_add(mlxsw_sp, &ports_bm,
201362306a36Sopenharmony_ci						   mdb_entry);
201462306a36Sopenharmony_ci	else
201562306a36Sopenharmony_ci		mlxsw_sp_mc_mdb_mrouters_del(mlxsw_sp, &ports_bm, mdb_entry);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	mlxsw_sp_port_bitmap_fini(&ports_bm);
201862306a36Sopenharmony_ci	return err;
201962306a36Sopenharmony_ci}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry *
202262306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp,
202362306a36Sopenharmony_ci			   struct mlxsw_sp_bridge_device *bridge_device,
202462306a36Sopenharmony_ci			   const unsigned char *addr, u16 fid, u16 local_port)
202562306a36Sopenharmony_ci{
202662306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
202762306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
202862306a36Sopenharmony_ci	int err;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
203162306a36Sopenharmony_ci	if (!mdb_entry)
203262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	ether_addr_copy(mdb_entry->key.addr, addr);
203562306a36Sopenharmony_ci	mdb_entry->key.fid = fid;
203662306a36Sopenharmony_ci	err = mlxsw_sp_pgt_mid_alloc(mlxsw_sp, &mdb_entry->mid);
203762306a36Sopenharmony_ci	if (err)
203862306a36Sopenharmony_ci		goto err_pgt_mid_alloc;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	INIT_LIST_HEAD(&mdb_entry->ports_list);
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	err = mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry,
204362306a36Sopenharmony_ci					   true);
204462306a36Sopenharmony_ci	if (err)
204562306a36Sopenharmony_ci		goto err_mdb_mrouters_set;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, mdb_entry,
204862306a36Sopenharmony_ci						     local_port);
204962306a36Sopenharmony_ci	if (IS_ERR(mdb_entry_port)) {
205062306a36Sopenharmony_ci		err = PTR_ERR(mdb_entry_port);
205162306a36Sopenharmony_ci		goto err_mdb_entry_port_get;
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	if (bridge_device->multicast_enabled) {
205562306a36Sopenharmony_ci		err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, true);
205662306a36Sopenharmony_ci		if (err)
205762306a36Sopenharmony_ci			goto err_mdb_entry_write;
205862306a36Sopenharmony_ci	}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	err = rhashtable_insert_fast(&bridge_device->mdb_ht,
206162306a36Sopenharmony_ci				     &mdb_entry->ht_node,
206262306a36Sopenharmony_ci				     mlxsw_sp_mdb_ht_params);
206362306a36Sopenharmony_ci	if (err)
206462306a36Sopenharmony_ci		goto err_rhashtable_insert;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	list_add_tail(&mdb_entry->list, &bridge_device->mdb_list);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	return mdb_entry;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_cierr_rhashtable_insert:
207162306a36Sopenharmony_ci	if (bridge_device->multicast_enabled)
207262306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false);
207362306a36Sopenharmony_cierr_mdb_entry_write:
207462306a36Sopenharmony_ci	mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, false);
207562306a36Sopenharmony_cierr_mdb_entry_port_get:
207662306a36Sopenharmony_ci	mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false);
207762306a36Sopenharmony_cierr_mdb_mrouters_set:
207862306a36Sopenharmony_ci	mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid);
207962306a36Sopenharmony_cierr_pgt_mid_alloc:
208062306a36Sopenharmony_ci	kfree(mdb_entry);
208162306a36Sopenharmony_ci	return ERR_PTR(err);
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_cistatic void
208562306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp,
208662306a36Sopenharmony_ci			   struct mlxsw_sp_mdb_entry *mdb_entry,
208762306a36Sopenharmony_ci			   struct mlxsw_sp_bridge_device *bridge_device,
208862306a36Sopenharmony_ci			   u16 local_port, bool force)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	list_del(&mdb_entry->list);
209162306a36Sopenharmony_ci	rhashtable_remove_fast(&bridge_device->mdb_ht, &mdb_entry->ht_node,
209262306a36Sopenharmony_ci			       mlxsw_sp_mdb_ht_params);
209362306a36Sopenharmony_ci	if (bridge_device->multicast_enabled)
209462306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false);
209562306a36Sopenharmony_ci	mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, force);
209662306a36Sopenharmony_ci	mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false);
209762306a36Sopenharmony_ci	WARN_ON(!list_empty(&mdb_entry->ports_list));
209862306a36Sopenharmony_ci	mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid);
209962306a36Sopenharmony_ci	kfree(mdb_entry);
210062306a36Sopenharmony_ci}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry *
210362306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_get(struct mlxsw_sp *mlxsw_sp,
210462306a36Sopenharmony_ci			  struct mlxsw_sp_bridge_device *bridge_device,
210562306a36Sopenharmony_ci			  const unsigned char *addr, u16 fid, u16 local_port)
210662306a36Sopenharmony_ci{
210762306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_key key = {};
210862306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	ether_addr_copy(key.addr, addr);
211162306a36Sopenharmony_ci	key.fid = fid;
211262306a36Sopenharmony_ci	mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key,
211362306a36Sopenharmony_ci					   mlxsw_sp_mdb_ht_params);
211462306a36Sopenharmony_ci	if (mdb_entry) {
211562306a36Sopenharmony_ci		struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci		mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp,
211862306a36Sopenharmony_ci							     mdb_entry,
211962306a36Sopenharmony_ci							     local_port);
212062306a36Sopenharmony_ci		if (IS_ERR(mdb_entry_port))
212162306a36Sopenharmony_ci			return ERR_CAST(mdb_entry_port);
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci		return mdb_entry;
212462306a36Sopenharmony_ci	}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	return mlxsw_sp_mc_mdb_entry_init(mlxsw_sp, bridge_device, addr, fid,
212762306a36Sopenharmony_ci					  local_port);
212862306a36Sopenharmony_ci}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_cistatic bool
213162306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_remove(struct mlxsw_sp_mdb_entry *mdb_entry,
213262306a36Sopenharmony_ci			     struct mlxsw_sp_mdb_entry_port *removed_entry_port,
213362306a36Sopenharmony_ci			     bool force)
213462306a36Sopenharmony_ci{
213562306a36Sopenharmony_ci	if (mdb_entry->ports_count > 1)
213662306a36Sopenharmony_ci		return false;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	if (force)
213962306a36Sopenharmony_ci		return true;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	if (!removed_entry_port->mrouter &&
214262306a36Sopenharmony_ci	    refcount_read(&removed_entry_port->refcount) > 1)
214362306a36Sopenharmony_ci		return false;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	if (removed_entry_port->mrouter &&
214662306a36Sopenharmony_ci	    refcount_read(&removed_entry_port->refcount) > 2)
214762306a36Sopenharmony_ci		return false;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	return true;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_cistatic void
215362306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_put(struct mlxsw_sp *mlxsw_sp,
215462306a36Sopenharmony_ci			  struct mlxsw_sp_bridge_device *bridge_device,
215562306a36Sopenharmony_ci			  struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port,
215662306a36Sopenharmony_ci			  bool force)
215762306a36Sopenharmony_ci{
215862306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port);
216162306a36Sopenharmony_ci	if (!mdb_entry_port)
216262306a36Sopenharmony_ci		return;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/* Avoid a temporary situation in which the MDB entry points to an empty
216562306a36Sopenharmony_ci	 * PGT entry, as otherwise packets will be temporarily dropped instead
216662306a36Sopenharmony_ci	 * of being flooded. Instead, in this situation, call
216762306a36Sopenharmony_ci	 * mlxsw_sp_mc_mdb_entry_fini(), which first deletes the MDB entry and
216862306a36Sopenharmony_ci	 * then releases the PGT entry.
216962306a36Sopenharmony_ci	 */
217062306a36Sopenharmony_ci	if (mlxsw_sp_mc_mdb_entry_remove(mdb_entry, mdb_entry_port, force))
217162306a36Sopenharmony_ci		mlxsw_sp_mc_mdb_entry_fini(mlxsw_sp, mdb_entry, bridge_device,
217262306a36Sopenharmony_ci					   local_port, force);
217362306a36Sopenharmony_ci	else
217462306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port,
217562306a36Sopenharmony_ci					    force);
217662306a36Sopenharmony_ci}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_cistatic int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
217962306a36Sopenharmony_ci				 const struct switchdev_obj_port_mdb *mdb)
218062306a36Sopenharmony_ci{
218162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
218262306a36Sopenharmony_ci	struct net_device *orig_dev = mdb->obj.orig_dev;
218362306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
218462306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
218562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
218662306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
218762306a36Sopenharmony_ci	u16 fid_index;
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
219062306a36Sopenharmony_ci	if (!bridge_port)
219162306a36Sopenharmony_ci		return 0;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
219462306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
219562306a36Sopenharmony_ci							       bridge_device,
219662306a36Sopenharmony_ci							       mdb->vid);
219762306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
219862306a36Sopenharmony_ci		return 0;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	mdb_entry = mlxsw_sp_mc_mdb_entry_get(mlxsw_sp, bridge_device,
220362306a36Sopenharmony_ci					      mdb->addr, fid_index,
220462306a36Sopenharmony_ci					      mlxsw_sp_port->local_port);
220562306a36Sopenharmony_ci	if (IS_ERR(mdb_entry))
220662306a36Sopenharmony_ci		return PTR_ERR(mdb_entry);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	return 0;
220962306a36Sopenharmony_ci}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_cistatic int
221262306a36Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp,
221362306a36Sopenharmony_ci				   struct mlxsw_sp_bridge_device *bridge_device,
221462306a36Sopenharmony_ci				   bool mc_enabled)
221562306a36Sopenharmony_ci{
221662306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
221762306a36Sopenharmony_ci	int err;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) {
222062306a36Sopenharmony_ci		err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, mc_enabled);
222162306a36Sopenharmony_ci		if (err)
222262306a36Sopenharmony_ci			goto err_mdb_entry_write;
222362306a36Sopenharmony_ci	}
222462306a36Sopenharmony_ci	return 0;
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_cierr_mdb_entry_write:
222762306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(mdb_entry,
222862306a36Sopenharmony_ci					     &bridge_device->mdb_list, list)
222962306a36Sopenharmony_ci		mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, !mc_enabled);
223062306a36Sopenharmony_ci	return err;
223162306a36Sopenharmony_ci}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_cistatic void
223462306a36Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
223562306a36Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
223662306a36Sopenharmony_ci				 bool add)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
223962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
224062306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
224162306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) {
224662306a36Sopenharmony_ci		if (add)
224762306a36Sopenharmony_ci			mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry,
224862306a36Sopenharmony_ci							    local_port);
224962306a36Sopenharmony_ci		else
225062306a36Sopenharmony_ci			mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry,
225162306a36Sopenharmony_ci							    local_port);
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci}
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx,
225662306a36Sopenharmony_ci				 const struct switchdev_obj *obj,
225762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
225862306a36Sopenharmony_ci{
225962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
226062306a36Sopenharmony_ci	const struct switchdev_obj_port_vlan *vlan;
226162306a36Sopenharmony_ci	int err = 0;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	switch (obj->id) {
226462306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
226562306a36Sopenharmony_ci		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci		err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, extack);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci		/* The event is emitted before the changes are actually
227062306a36Sopenharmony_ci		 * applied to the bridge. Therefore schedule the respin
227162306a36Sopenharmony_ci		 * call for later, so that the respin logic sees the
227262306a36Sopenharmony_ci		 * updated bridge state.
227362306a36Sopenharmony_ci		 */
227462306a36Sopenharmony_ci		mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
227562306a36Sopenharmony_ci		break;
227662306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
227762306a36Sopenharmony_ci		err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
227862306a36Sopenharmony_ci					    SWITCHDEV_OBJ_PORT_MDB(obj));
227962306a36Sopenharmony_ci		break;
228062306a36Sopenharmony_ci	default:
228162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
228262306a36Sopenharmony_ci		break;
228362306a36Sopenharmony_ci	}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	return err;
228662306a36Sopenharmony_ci}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_cistatic void
228962306a36Sopenharmony_cimlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port,
229062306a36Sopenharmony_ci			      struct mlxsw_sp_bridge_port *bridge_port, u16 vid)
229162306a36Sopenharmony_ci{
229262306a36Sopenharmony_ci	u16 pvid = mlxsw_sp_port->pvid == vid ? 0 : mlxsw_sp_port->pvid;
229362306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
229462306a36Sopenharmony_ci	u16 proto;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
229762306a36Sopenharmony_ci	if (WARN_ON(!mlxsw_sp_port_vlan))
229862306a36Sopenharmony_ci		return;
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
230162306a36Sopenharmony_ci	br_vlan_get_proto(bridge_port->bridge_device->dev, &proto);
230262306a36Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid, proto);
230362306a36Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
230462306a36Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_cistatic int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
230862306a36Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan)
230962306a36Sopenharmony_ci{
231062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
231162306a36Sopenharmony_ci	struct net_device *orig_dev = vlan->obj.orig_dev;
231262306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	if (netif_is_bridge_master(orig_dev))
231562306a36Sopenharmony_ci		return -EOPNOTSUPP;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
231862306a36Sopenharmony_ci	if (WARN_ON(!bridge_port))
231962306a36Sopenharmony_ci		return -EINVAL;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	if (!bridge_port->bridge_device->vlan_enabled)
232262306a36Sopenharmony_ci		return 0;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vlan->vid);
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	return 0;
232762306a36Sopenharmony_ci}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_cistatic int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
233062306a36Sopenharmony_ci				 const struct switchdev_obj_port_mdb *mdb)
233162306a36Sopenharmony_ci{
233262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
233362306a36Sopenharmony_ci	struct net_device *orig_dev = mdb->obj.orig_dev;
233462306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
233562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
233662306a36Sopenharmony_ci	struct net_device *dev = mlxsw_sp_port->dev;
233762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
233862306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry_key key = {};
233962306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry;
234062306a36Sopenharmony_ci	u16 fid_index;
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
234362306a36Sopenharmony_ci	if (!bridge_port)
234462306a36Sopenharmony_ci		return 0;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
234762306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
234862306a36Sopenharmony_ci							       bridge_device,
234962306a36Sopenharmony_ci							       mdb->vid);
235062306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
235162306a36Sopenharmony_ci		return 0;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	ether_addr_copy(key.addr, mdb->addr);
235662306a36Sopenharmony_ci	key.fid = fid_index;
235762306a36Sopenharmony_ci	mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key,
235862306a36Sopenharmony_ci					   mlxsw_sp_mdb_ht_params);
235962306a36Sopenharmony_ci	if (!mdb_entry) {
236062306a36Sopenharmony_ci		netdev_err(dev, "Unable to remove port from MC DB\n");
236162306a36Sopenharmony_ci		return -EINVAL;
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry,
236562306a36Sopenharmony_ci				  mlxsw_sp_port->local_port, false);
236662306a36Sopenharmony_ci	return 0;
236762306a36Sopenharmony_ci}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_cistatic void
237062306a36Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port,
237162306a36Sopenharmony_ci			       struct mlxsw_sp_bridge_port *bridge_port,
237262306a36Sopenharmony_ci			       u16 fid_index)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
237562306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
237662306a36Sopenharmony_ci	struct mlxsw_sp_mdb_entry *mdb_entry, *tmp;
237762306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mdb_list,
238262306a36Sopenharmony_ci				 list) {
238362306a36Sopenharmony_ci		if (mdb_entry->key.fid != fid_index)
238462306a36Sopenharmony_ci			continue;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci		if (bridge_port->mrouter)
238762306a36Sopenharmony_ci			mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp,
238862306a36Sopenharmony_ci							    mdb_entry,
238962306a36Sopenharmony_ci							    local_port);
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci		mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry,
239262306a36Sopenharmony_ci					  local_port, true);
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx,
239762306a36Sopenharmony_ci				 const struct switchdev_obj *obj)
239862306a36Sopenharmony_ci{
239962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
240062306a36Sopenharmony_ci	int err = 0;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	switch (obj->id) {
240362306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
240462306a36Sopenharmony_ci		err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
240562306a36Sopenharmony_ci					      SWITCHDEV_OBJ_PORT_VLAN(obj));
240662306a36Sopenharmony_ci		break;
240762306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
240862306a36Sopenharmony_ci		err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
240962306a36Sopenharmony_ci					    SWITCHDEV_OBJ_PORT_MDB(obj));
241062306a36Sopenharmony_ci		break;
241162306a36Sopenharmony_ci	default:
241262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
241362306a36Sopenharmony_ci		break;
241462306a36Sopenharmony_ci	}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	return err;
241962306a36Sopenharmony_ci}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_cistatic struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
242262306a36Sopenharmony_ci						   u16 lag_id)
242362306a36Sopenharmony_ci{
242462306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
242562306a36Sopenharmony_ci	u64 max_lag_members;
242662306a36Sopenharmony_ci	int i;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
242962306a36Sopenharmony_ci					     MAX_LAG_MEMBERS);
243062306a36Sopenharmony_ci	for (i = 0; i < max_lag_members; i++) {
243162306a36Sopenharmony_ci		mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
243262306a36Sopenharmony_ci		if (mlxsw_sp_port)
243362306a36Sopenharmony_ci			return mlxsw_sp_port;
243462306a36Sopenharmony_ci	}
243562306a36Sopenharmony_ci	return NULL;
243662306a36Sopenharmony_ci}
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_cistatic int
243962306a36Sopenharmony_cimlxsw_sp_bridge_port_replay(struct mlxsw_sp_bridge_port *bridge_port,
244062306a36Sopenharmony_ci			    struct mlxsw_sp_port *mlxsw_sp_port,
244162306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port_replay_switchdev_objs rso = {
244462306a36Sopenharmony_ci		.brport_dev = bridge_port->dev,
244562306a36Sopenharmony_ci		.mlxsw_sp_port = mlxsw_sp_port,
244662306a36Sopenharmony_ci	};
244762306a36Sopenharmony_ci	struct notifier_block *nb;
244862306a36Sopenharmony_ci	int err;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	nb = &mlxsw_sp_bridge_port_replay_switchdev_objs_nb;
245162306a36Sopenharmony_ci	err = switchdev_bridge_port_replay(bridge_port->dev, mlxsw_sp_port->dev,
245262306a36Sopenharmony_ci					   &rso, NULL, nb, extack);
245362306a36Sopenharmony_ci	if (err)
245462306a36Sopenharmony_ci		goto err_replay;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	return 0;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_cierr_replay:
245962306a36Sopenharmony_ci	nb = &mlxsw_sp_bridge_port_unreplay_switchdev_objs_nb;
246062306a36Sopenharmony_ci	switchdev_bridge_port_replay(bridge_port->dev, mlxsw_sp_port->dev,
246162306a36Sopenharmony_ci				     &rso, NULL, nb, extack);
246262306a36Sopenharmony_ci	return err;
246362306a36Sopenharmony_ci}
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_cistatic int
246662306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_port_join(struct mlxsw_sp_bridge_port *bridge_port,
246762306a36Sopenharmony_ci				     struct mlxsw_sp_port *mlxsw_sp_port,
246862306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
246962306a36Sopenharmony_ci{
247062306a36Sopenharmony_ci	if (is_vlan_dev(bridge_port->dev)) {
247162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge");
247262306a36Sopenharmony_ci		return -EINVAL;
247362306a36Sopenharmony_ci	}
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	/* Port is no longer usable as a router interface */
247662306a36Sopenharmony_ci	if (mlxsw_sp_port->default_vlan->fid)
247762306a36Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	return mlxsw_sp_bridge_port_replay(bridge_port, mlxsw_sp_port, extack);
248062306a36Sopenharmony_ci}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_cistatic int
248362306a36Sopenharmony_cimlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
248462306a36Sopenharmony_ci				struct mlxsw_sp_bridge_port *bridge_port,
248562306a36Sopenharmony_ci				struct mlxsw_sp_port *mlxsw_sp_port,
248662306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
248762306a36Sopenharmony_ci{
248862306a36Sopenharmony_ci	return mlxsw_sp_bridge_vlan_aware_port_join(bridge_port, mlxsw_sp_port,
248962306a36Sopenharmony_ci						    extack);
249062306a36Sopenharmony_ci}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_cistatic void
249362306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_port_leave(struct mlxsw_sp_port *mlxsw_sp_port)
249462306a36Sopenharmony_ci{
249562306a36Sopenharmony_ci	/* Make sure untagged frames are allowed to ingress */
249662306a36Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID,
249762306a36Sopenharmony_ci			       ETH_P_8021Q);
249862306a36Sopenharmony_ci}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_cistatic void
250162306a36Sopenharmony_cimlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
250262306a36Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
250362306a36Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port)
250462306a36Sopenharmony_ci{
250562306a36Sopenharmony_ci	mlxsw_sp_bridge_vlan_aware_port_leave(mlxsw_sp_port);
250662306a36Sopenharmony_ci}
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_cistatic int
250962306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
251062306a36Sopenharmony_ci				      const struct net_device *vxlan_dev,
251162306a36Sopenharmony_ci				      u16 vid, u16 ethertype,
251262306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
251562306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
251662306a36Sopenharmony_ci	struct mlxsw_sp_nve_params params = {
251762306a36Sopenharmony_ci		.type = MLXSW_SP_NVE_TYPE_VXLAN,
251862306a36Sopenharmony_ci		.vni = vxlan->cfg.vni,
251962306a36Sopenharmony_ci		.dev = vxlan_dev,
252062306a36Sopenharmony_ci		.ethertype = ethertype,
252162306a36Sopenharmony_ci	};
252262306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
252362306a36Sopenharmony_ci	int err;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	/* If the VLAN is 0, we need to find the VLAN that is configured as
252662306a36Sopenharmony_ci	 * PVID and egress untagged on the bridge port of the VxLAN device.
252762306a36Sopenharmony_ci	 * It is possible no such VLAN exists
252862306a36Sopenharmony_ci	 */
252962306a36Sopenharmony_ci	if (!vid) {
253062306a36Sopenharmony_ci		err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid);
253162306a36Sopenharmony_ci		if (err || !vid)
253262306a36Sopenharmony_ci			return err;
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
253662306a36Sopenharmony_ci	if (IS_ERR(fid)) {
253762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID");
253862306a36Sopenharmony_ci		return PTR_ERR(fid);
253962306a36Sopenharmony_ci	}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	if (mlxsw_sp_fid_vni_is_set(fid)) {
254262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID");
254362306a36Sopenharmony_ci		err = -EINVAL;
254462306a36Sopenharmony_ci		goto err_vni_exists;
254562306a36Sopenharmony_ci	}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
254862306a36Sopenharmony_ci	if (err)
254962306a36Sopenharmony_ci		goto err_nve_fid_enable;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	return 0;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_cierr_nve_fid_enable:
255462306a36Sopenharmony_cierr_vni_exists:
255562306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
255662306a36Sopenharmony_ci	return err;
255762306a36Sopenharmony_ci}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_cistatic int
256062306a36Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
256162306a36Sopenharmony_ci				 const struct net_device *vxlan_dev, u16 vid,
256262306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
256362306a36Sopenharmony_ci{
256462306a36Sopenharmony_ci	return mlxsw_sp_bridge_vlan_aware_vxlan_join(bridge_device, vxlan_dev,
256562306a36Sopenharmony_ci						     vid, ETH_P_8021Q, extack);
256662306a36Sopenharmony_ci}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_cistatic struct net_device *
256962306a36Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid)
257062306a36Sopenharmony_ci{
257162306a36Sopenharmony_ci	struct net_device *dev;
257262306a36Sopenharmony_ci	struct list_head *iter;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
257562306a36Sopenharmony_ci		u16 pvid;
257662306a36Sopenharmony_ci		int err;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci		if (!netif_is_vxlan(dev))
257962306a36Sopenharmony_ci			continue;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci		err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
258262306a36Sopenharmony_ci		if (err || pvid != vid)
258362306a36Sopenharmony_ci			continue;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci		return dev;
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	return NULL;
258962306a36Sopenharmony_ci}
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_cistatic struct mlxsw_sp_fid *
259262306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
259362306a36Sopenharmony_ci			      u16 vid, struct netlink_ext_ack *extack)
259462306a36Sopenharmony_ci{
259562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
259862306a36Sopenharmony_ci}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_cistatic struct mlxsw_sp_fid *
260162306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
260262306a36Sopenharmony_ci				 u16 vid)
260362306a36Sopenharmony_ci{
260462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
260762306a36Sopenharmony_ci}
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_cistatic u16
261062306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_vid(struct mlxsw_sp_bridge_device *bridge_device,
261162306a36Sopenharmony_ci			      const struct mlxsw_sp_fid *fid)
261262306a36Sopenharmony_ci{
261362306a36Sopenharmony_ci	return mlxsw_sp_fid_8021q_vid(fid);
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = {
261762306a36Sopenharmony_ci	.port_join	= mlxsw_sp_bridge_8021q_port_join,
261862306a36Sopenharmony_ci	.port_leave	= mlxsw_sp_bridge_8021q_port_leave,
261962306a36Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021q_vxlan_join,
262062306a36Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021q_fid_get,
262162306a36Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021q_fid_lookup,
262262306a36Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021q_fid_vid,
262362306a36Sopenharmony_ci};
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_cistatic bool
262662306a36Sopenharmony_cimlxsw_sp_port_is_br_member(const struct mlxsw_sp_port *mlxsw_sp_port,
262762306a36Sopenharmony_ci			   const struct net_device *br_dev)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
263262306a36Sopenharmony_ci			    list) {
263362306a36Sopenharmony_ci		if (mlxsw_sp_port_vlan->bridge_port &&
263462306a36Sopenharmony_ci		    mlxsw_sp_port_vlan->bridge_port->bridge_device->dev ==
263562306a36Sopenharmony_ci		    br_dev)
263662306a36Sopenharmony_ci			return true;
263762306a36Sopenharmony_ci	}
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	return false;
264062306a36Sopenharmony_ci}
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_cistatic int
264362306a36Sopenharmony_cimlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
264462306a36Sopenharmony_ci				struct mlxsw_sp_bridge_port *bridge_port,
264562306a36Sopenharmony_ci				struct mlxsw_sp_port *mlxsw_sp_port,
264662306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
264762306a36Sopenharmony_ci{
264862306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
264962306a36Sopenharmony_ci	struct net_device *dev = bridge_port->dev;
265062306a36Sopenharmony_ci	u16 vid;
265162306a36Sopenharmony_ci	int err;
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
265462306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
265562306a36Sopenharmony_ci	if (WARN_ON(!mlxsw_sp_port_vlan))
265662306a36Sopenharmony_ci		return -EINVAL;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	if (mlxsw_sp_port_is_br_member(mlxsw_sp_port, bridge_device->dev)) {
265962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not bridge VLAN uppers of the same port");
266062306a36Sopenharmony_ci		return -EINVAL;
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	/* Port is no longer usable as a router interface */
266462306a36Sopenharmony_ci	if (mlxsw_sp_port_vlan->fid)
266562306a36Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
266862306a36Sopenharmony_ci					     extack);
266962306a36Sopenharmony_ci	if (err)
267062306a36Sopenharmony_ci		return err;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	err = mlxsw_sp_bridge_port_replay(bridge_port, mlxsw_sp_port, extack);
267362306a36Sopenharmony_ci	if (err)
267462306a36Sopenharmony_ci		goto err_replay;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	return 0;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_cierr_replay:
267962306a36Sopenharmony_ci	mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
268062306a36Sopenharmony_ci	return err;
268162306a36Sopenharmony_ci}
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_cistatic void
268462306a36Sopenharmony_cimlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
268562306a36Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
268662306a36Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port)
268762306a36Sopenharmony_ci{
268862306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
268962306a36Sopenharmony_ci	struct net_device *dev = bridge_port->dev;
269062306a36Sopenharmony_ci	u16 vid;
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID;
269362306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
269462306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port)
269562306a36Sopenharmony_ci		return;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
269862306a36Sopenharmony_ci}
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_cistatic int
270162306a36Sopenharmony_cimlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
270262306a36Sopenharmony_ci				 const struct net_device *vxlan_dev, u16 vid,
270362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
270462306a36Sopenharmony_ci{
270562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
270662306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
270762306a36Sopenharmony_ci	struct mlxsw_sp_nve_params params = {
270862306a36Sopenharmony_ci		.type = MLXSW_SP_NVE_TYPE_VXLAN,
270962306a36Sopenharmony_ci		.vni = vxlan->cfg.vni,
271062306a36Sopenharmony_ci		.dev = vxlan_dev,
271162306a36Sopenharmony_ci		.ethertype = ETH_P_8021Q,
271262306a36Sopenharmony_ci	};
271362306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
271462306a36Sopenharmony_ci	int err;
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
271762306a36Sopenharmony_ci	if (IS_ERR(fid)) {
271862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID");
271962306a36Sopenharmony_ci		return -EINVAL;
272062306a36Sopenharmony_ci	}
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	if (mlxsw_sp_fid_vni_is_set(fid)) {
272362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID");
272462306a36Sopenharmony_ci		err = -EINVAL;
272562306a36Sopenharmony_ci		goto err_vni_exists;
272662306a36Sopenharmony_ci	}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
272962306a36Sopenharmony_ci	if (err)
273062306a36Sopenharmony_ci		goto err_nve_fid_enable;
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	return 0;
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_cierr_nve_fid_enable:
273562306a36Sopenharmony_cierr_vni_exists:
273662306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
273762306a36Sopenharmony_ci	return err;
273862306a36Sopenharmony_ci}
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_cistatic struct mlxsw_sp_fid *
274162306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
274262306a36Sopenharmony_ci			      u16 vid, struct netlink_ext_ack *extack)
274362306a36Sopenharmony_ci{
274462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
274762306a36Sopenharmony_ci}
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_cistatic struct mlxsw_sp_fid *
275062306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
275162306a36Sopenharmony_ci				 u16 vid)
275262306a36Sopenharmony_ci{
275362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	/* The only valid VLAN for a VLAN-unaware bridge is 0 */
275662306a36Sopenharmony_ci	if (vid)
275762306a36Sopenharmony_ci		return NULL;
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	return mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
276062306a36Sopenharmony_ci}
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_cistatic u16
276362306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_vid(struct mlxsw_sp_bridge_device *bridge_device,
276462306a36Sopenharmony_ci			      const struct mlxsw_sp_fid *fid)
276562306a36Sopenharmony_ci{
276662306a36Sopenharmony_ci	return 0;
276762306a36Sopenharmony_ci}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = {
277062306a36Sopenharmony_ci	.port_join	= mlxsw_sp_bridge_8021d_port_join,
277162306a36Sopenharmony_ci	.port_leave	= mlxsw_sp_bridge_8021d_port_leave,
277262306a36Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021d_vxlan_join,
277362306a36Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021d_fid_get,
277462306a36Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021d_fid_lookup,
277562306a36Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021d_fid_vid,
277662306a36Sopenharmony_ci};
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_cistatic int
277962306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_port_join(struct mlxsw_sp_bridge_device *bridge_device,
278062306a36Sopenharmony_ci				 struct mlxsw_sp_bridge_port *bridge_port,
278162306a36Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port,
278262306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
278362306a36Sopenharmony_ci{
278462306a36Sopenharmony_ci	int err;
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, false);
278762306a36Sopenharmony_ci	if (err)
278862306a36Sopenharmony_ci		return err;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	err = mlxsw_sp_bridge_vlan_aware_port_join(bridge_port, mlxsw_sp_port,
279162306a36Sopenharmony_ci						   extack);
279262306a36Sopenharmony_ci	if (err)
279362306a36Sopenharmony_ci		goto err_bridge_vlan_aware_port_join;
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci	return 0;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_cierr_bridge_vlan_aware_port_join:
279862306a36Sopenharmony_ci	mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, false, true);
279962306a36Sopenharmony_ci	return err;
280062306a36Sopenharmony_ci}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_cistatic void
280362306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
280462306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_port *bridge_port,
280562306a36Sopenharmony_ci				  struct mlxsw_sp_port *mlxsw_sp_port)
280662306a36Sopenharmony_ci{
280762306a36Sopenharmony_ci	mlxsw_sp_bridge_vlan_aware_port_leave(mlxsw_sp_port);
280862306a36Sopenharmony_ci	mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, false, true);
280962306a36Sopenharmony_ci}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_cistatic int
281262306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
281362306a36Sopenharmony_ci				  const struct net_device *vxlan_dev, u16 vid,
281462306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
281562306a36Sopenharmony_ci{
281662306a36Sopenharmony_ci	return mlxsw_sp_bridge_vlan_aware_vxlan_join(bridge_device, vxlan_dev,
281762306a36Sopenharmony_ci						     vid, ETH_P_8021AD, extack);
281862306a36Sopenharmony_ci}
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp1_bridge_8021ad_ops = {
282162306a36Sopenharmony_ci	.port_join	= mlxsw_sp_bridge_8021ad_port_join,
282262306a36Sopenharmony_ci	.port_leave	= mlxsw_sp_bridge_8021ad_port_leave,
282362306a36Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021ad_vxlan_join,
282462306a36Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021q_fid_get,
282562306a36Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021q_fid_lookup,
282662306a36Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021q_fid_vid,
282762306a36Sopenharmony_ci};
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_cistatic int
283062306a36Sopenharmony_cimlxsw_sp2_bridge_8021ad_port_join(struct mlxsw_sp_bridge_device *bridge_device,
283162306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_port *bridge_port,
283262306a36Sopenharmony_ci				  struct mlxsw_sp_port *mlxsw_sp_port,
283362306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
283462306a36Sopenharmony_ci{
283562306a36Sopenharmony_ci	int err;
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci	/* The EtherType of decapsulated packets is determined at the egress
283862306a36Sopenharmony_ci	 * port to allow 802.1d and 802.1ad bridges with VXLAN devices to
283962306a36Sopenharmony_ci	 * co-exist.
284062306a36Sopenharmony_ci	 */
284162306a36Sopenharmony_ci	err = mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021AD);
284262306a36Sopenharmony_ci	if (err)
284362306a36Sopenharmony_ci		return err;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	err = mlxsw_sp_bridge_8021ad_port_join(bridge_device, bridge_port,
284662306a36Sopenharmony_ci					       mlxsw_sp_port, extack);
284762306a36Sopenharmony_ci	if (err)
284862306a36Sopenharmony_ci		goto err_bridge_8021ad_port_join;
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	return 0;
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_cierr_bridge_8021ad_port_join:
285362306a36Sopenharmony_ci	mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021Q);
285462306a36Sopenharmony_ci	return err;
285562306a36Sopenharmony_ci}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_cistatic void
285862306a36Sopenharmony_cimlxsw_sp2_bridge_8021ad_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
285962306a36Sopenharmony_ci				   struct mlxsw_sp_bridge_port *bridge_port,
286062306a36Sopenharmony_ci				   struct mlxsw_sp_port *mlxsw_sp_port)
286162306a36Sopenharmony_ci{
286262306a36Sopenharmony_ci	mlxsw_sp_bridge_8021ad_port_leave(bridge_device, bridge_port,
286362306a36Sopenharmony_ci					  mlxsw_sp_port);
286462306a36Sopenharmony_ci	mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021Q);
286562306a36Sopenharmony_ci}
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp2_bridge_8021ad_ops = {
286862306a36Sopenharmony_ci	.port_join	= mlxsw_sp2_bridge_8021ad_port_join,
286962306a36Sopenharmony_ci	.port_leave	= mlxsw_sp2_bridge_8021ad_port_leave,
287062306a36Sopenharmony_ci	.vxlan_join	= mlxsw_sp_bridge_8021ad_vxlan_join,
287162306a36Sopenharmony_ci	.fid_get	= mlxsw_sp_bridge_8021q_fid_get,
287262306a36Sopenharmony_ci	.fid_lookup	= mlxsw_sp_bridge_8021q_fid_lookup,
287362306a36Sopenharmony_ci	.fid_vid	= mlxsw_sp_bridge_8021q_fid_vid,
287462306a36Sopenharmony_ci};
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ciint mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
287762306a36Sopenharmony_ci			      struct net_device *brport_dev,
287862306a36Sopenharmony_ci			      struct net_device *br_dev,
287962306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
288062306a36Sopenharmony_ci{
288162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
288262306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
288362306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
288462306a36Sopenharmony_ci	int err;
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev,
288762306a36Sopenharmony_ci					       extack);
288862306a36Sopenharmony_ci	if (IS_ERR(bridge_port))
288962306a36Sopenharmony_ci		return PTR_ERR(bridge_port);
289062306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	err = bridge_device->ops->port_join(bridge_device, bridge_port,
289362306a36Sopenharmony_ci					    mlxsw_sp_port, extack);
289462306a36Sopenharmony_ci	if (err)
289562306a36Sopenharmony_ci		goto err_port_join;
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	err = mlxsw_sp_netdevice_enslavement_replay(mlxsw_sp, br_dev, extack);
289862306a36Sopenharmony_ci	if (err)
289962306a36Sopenharmony_ci		goto err_replay;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	return 0;
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_cierr_replay:
290462306a36Sopenharmony_ci	bridge_device->ops->port_leave(bridge_device, bridge_port,
290562306a36Sopenharmony_ci				       mlxsw_sp_port);
290662306a36Sopenharmony_cierr_port_join:
290762306a36Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
290862306a36Sopenharmony_ci	return err;
290962306a36Sopenharmony_ci}
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_civoid mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
291262306a36Sopenharmony_ci				struct net_device *brport_dev,
291362306a36Sopenharmony_ci				struct net_device *br_dev)
291462306a36Sopenharmony_ci{
291562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
291662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
291762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
292062306a36Sopenharmony_ci	if (!bridge_device)
292162306a36Sopenharmony_ci		return;
292262306a36Sopenharmony_ci	bridge_port = __mlxsw_sp_bridge_port_find(bridge_device, brport_dev);
292362306a36Sopenharmony_ci	if (!bridge_port)
292462306a36Sopenharmony_ci		return;
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	bridge_device->ops->port_leave(bridge_device, bridge_port,
292762306a36Sopenharmony_ci				       mlxsw_sp_port);
292862306a36Sopenharmony_ci	mlxsw_sp_port_security_set(mlxsw_sp_port, false);
292962306a36Sopenharmony_ci	mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
293062306a36Sopenharmony_ci}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ciint mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
293362306a36Sopenharmony_ci			       const struct net_device *br_dev,
293462306a36Sopenharmony_ci			       const struct net_device *vxlan_dev, u16 vid,
293562306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
293662306a36Sopenharmony_ci{
293762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
294062306a36Sopenharmony_ci	if (WARN_ON(!bridge_device))
294162306a36Sopenharmony_ci		return -EINVAL;
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid,
294462306a36Sopenharmony_ci					      extack);
294562306a36Sopenharmony_ci}
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_civoid mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
294862306a36Sopenharmony_ci				 const struct net_device *vxlan_dev)
294962306a36Sopenharmony_ci{
295062306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
295162306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* If the VxLAN device is down, then the FID does not have a VNI */
295462306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
295562306a36Sopenharmony_ci	if (!fid)
295662306a36Sopenharmony_ci		return;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
295962306a36Sopenharmony_ci	/* Drop both the reference we just took during lookup and the reference
296062306a36Sopenharmony_ci	 * the VXLAN device took.
296162306a36Sopenharmony_ci	 */
296262306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
296362306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
296462306a36Sopenharmony_ci}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_cistatic void
296762306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
296862306a36Sopenharmony_ci				      enum mlxsw_sp_l3proto *proto,
296962306a36Sopenharmony_ci				      union mlxsw_sp_l3addr *addr)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci	if (vxlan_addr->sa.sa_family == AF_INET) {
297262306a36Sopenharmony_ci		addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
297362306a36Sopenharmony_ci		*proto = MLXSW_SP_L3_PROTO_IPV4;
297462306a36Sopenharmony_ci	} else {
297562306a36Sopenharmony_ci		addr->addr6 = vxlan_addr->sin6.sin6_addr;
297662306a36Sopenharmony_ci		*proto = MLXSW_SP_L3_PROTO_IPV6;
297762306a36Sopenharmony_ci	}
297862306a36Sopenharmony_ci}
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_cistatic void
298162306a36Sopenharmony_cimlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto,
298262306a36Sopenharmony_ci				      const union mlxsw_sp_l3addr *addr,
298362306a36Sopenharmony_ci				      union vxlan_addr *vxlan_addr)
298462306a36Sopenharmony_ci{
298562306a36Sopenharmony_ci	switch (proto) {
298662306a36Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
298762306a36Sopenharmony_ci		vxlan_addr->sa.sa_family = AF_INET;
298862306a36Sopenharmony_ci		vxlan_addr->sin.sin_addr.s_addr = addr->addr4;
298962306a36Sopenharmony_ci		break;
299062306a36Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
299162306a36Sopenharmony_ci		vxlan_addr->sa.sa_family = AF_INET6;
299262306a36Sopenharmony_ci		vxlan_addr->sin6.sin6_addr = addr->addr6;
299362306a36Sopenharmony_ci		break;
299462306a36Sopenharmony_ci	}
299562306a36Sopenharmony_ci}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_cistatic void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
299862306a36Sopenharmony_ci					      const char *mac,
299962306a36Sopenharmony_ci					      enum mlxsw_sp_l3proto proto,
300062306a36Sopenharmony_ci					      union mlxsw_sp_l3addr *addr,
300162306a36Sopenharmony_ci					      __be32 vni, bool adding)
300262306a36Sopenharmony_ci{
300362306a36Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info info;
300462306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(dev);
300562306a36Sopenharmony_ci	enum switchdev_notifier_type type;
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
300862306a36Sopenharmony_ci			SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
300962306a36Sopenharmony_ci	mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
301062306a36Sopenharmony_ci	info.remote_port = vxlan->cfg.dst_port;
301162306a36Sopenharmony_ci	info.remote_vni = vni;
301262306a36Sopenharmony_ci	info.remote_ifindex = 0;
301362306a36Sopenharmony_ci	ether_addr_copy(info.eth_addr, mac);
301462306a36Sopenharmony_ci	info.vni = vni;
301562306a36Sopenharmony_ci	info.offloaded = adding;
301662306a36Sopenharmony_ci	call_switchdev_notifiers(type, dev, &info.info, NULL);
301762306a36Sopenharmony_ci}
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev,
302062306a36Sopenharmony_ci					    const char *mac,
302162306a36Sopenharmony_ci					    enum mlxsw_sp_l3proto proto,
302262306a36Sopenharmony_ci					    union mlxsw_sp_l3addr *addr,
302362306a36Sopenharmony_ci					    __be32 vni,
302462306a36Sopenharmony_ci					    bool adding)
302562306a36Sopenharmony_ci{
302662306a36Sopenharmony_ci	if (netif_is_vxlan(dev))
302762306a36Sopenharmony_ci		mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni,
302862306a36Sopenharmony_ci						  adding);
302962306a36Sopenharmony_ci}
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_cistatic void
303262306a36Sopenharmony_cimlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type,
303362306a36Sopenharmony_ci			    const char *mac, u16 vid,
303462306a36Sopenharmony_ci			    struct net_device *dev, bool offloaded, bool locked)
303562306a36Sopenharmony_ci{
303662306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info info = {};
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	info.addr = mac;
303962306a36Sopenharmony_ci	info.vid = vid;
304062306a36Sopenharmony_ci	info.offloaded = offloaded;
304162306a36Sopenharmony_ci	info.locked = locked;
304262306a36Sopenharmony_ci	call_switchdev_notifiers(type, dev, &info.info, NULL);
304362306a36Sopenharmony_ci}
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
304662306a36Sopenharmony_ci					    char *sfn_pl, int rec_index,
304762306a36Sopenharmony_ci					    bool adding)
304862306a36Sopenharmony_ci{
304962306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
305062306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
305162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
305262306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
305362306a36Sopenharmony_ci	u16 local_port, vid, fid, evid = 0;
305462306a36Sopenharmony_ci	enum switchdev_notifier_type type;
305562306a36Sopenharmony_ci	char mac[ETH_ALEN];
305662306a36Sopenharmony_ci	bool do_notification = true;
305762306a36Sopenharmony_ci	int err;
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci	mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port)))
306262306a36Sopenharmony_ci		return;
306362306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp->ports[local_port];
306462306a36Sopenharmony_ci	if (!mlxsw_sp_port) {
306562306a36Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
306662306a36Sopenharmony_ci		goto just_remove;
306762306a36Sopenharmony_ci	}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid);
307062306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
307162306a36Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n");
307262306a36Sopenharmony_ci		goto just_remove;
307362306a36Sopenharmony_ci	}
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
307662306a36Sopenharmony_ci	if (!bridge_port) {
307762306a36Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n");
307862306a36Sopenharmony_ci		goto just_remove;
307962306a36Sopenharmony_ci	}
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
308262306a36Sopenharmony_ci	vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
308362306a36Sopenharmony_ci	evid = mlxsw_sp_port_vlan->vid;
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	if (adding && mlxsw_sp_port->security) {
308662306a36Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac,
308762306a36Sopenharmony_ci					    vid, bridge_port->dev, false, true);
308862306a36Sopenharmony_ci		return;
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_cido_fdb_op:
309262306a36Sopenharmony_ci	err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, evid,
309362306a36Sopenharmony_ci				      adding, true);
309462306a36Sopenharmony_ci	if (err) {
309562306a36Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
309662306a36Sopenharmony_ci		return;
309762306a36Sopenharmony_ci	}
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	if (!do_notification)
310062306a36Sopenharmony_ci		return;
310162306a36Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
310262306a36Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding,
310362306a36Sopenharmony_ci				    false);
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	return;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_cijust_remove:
310862306a36Sopenharmony_ci	adding = false;
310962306a36Sopenharmony_ci	do_notification = false;
311062306a36Sopenharmony_ci	goto do_fdb_op;
311162306a36Sopenharmony_ci}
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
311462306a36Sopenharmony_ci						char *sfn_pl, int rec_index,
311562306a36Sopenharmony_ci						bool adding)
311662306a36Sopenharmony_ci{
311762306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
311862306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
311962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
312062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
312162306a36Sopenharmony_ci	enum switchdev_notifier_type type;
312262306a36Sopenharmony_ci	char mac[ETH_ALEN];
312362306a36Sopenharmony_ci	u16 lag_vid = 0;
312462306a36Sopenharmony_ci	u16 lag_id;
312562306a36Sopenharmony_ci	u16 vid, fid;
312662306a36Sopenharmony_ci	bool do_notification = true;
312762306a36Sopenharmony_ci	int err;
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci	mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id);
313062306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
313162306a36Sopenharmony_ci	if (!mlxsw_sp_port) {
313262306a36Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
313362306a36Sopenharmony_ci		goto just_remove;
313462306a36Sopenharmony_ci	}
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid);
313762306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
313862306a36Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n");
313962306a36Sopenharmony_ci		goto just_remove;
314062306a36Sopenharmony_ci	}
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	bridge_port = mlxsw_sp_port_vlan->bridge_port;
314362306a36Sopenharmony_ci	if (!bridge_port) {
314462306a36Sopenharmony_ci		netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n");
314562306a36Sopenharmony_ci		goto just_remove;
314662306a36Sopenharmony_ci	}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	bridge_device = bridge_port->bridge_device;
314962306a36Sopenharmony_ci	vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
315062306a36Sopenharmony_ci	lag_vid = mlxsw_sp_port_vlan->vid;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	if (adding && mlxsw_sp_port->security) {
315362306a36Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac,
315462306a36Sopenharmony_ci					    vid, bridge_port->dev, false, true);
315562306a36Sopenharmony_ci		return;
315662306a36Sopenharmony_ci	}
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_cido_fdb_op:
315962306a36Sopenharmony_ci	err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
316062306a36Sopenharmony_ci					  adding, true);
316162306a36Sopenharmony_ci	if (err) {
316262306a36Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
316362306a36Sopenharmony_ci		return;
316462306a36Sopenharmony_ci	}
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci	if (!do_notification)
316762306a36Sopenharmony_ci		return;
316862306a36Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
316962306a36Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding,
317062306a36Sopenharmony_ci				    false);
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	return;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_cijust_remove:
317562306a36Sopenharmony_ci	adding = false;
317662306a36Sopenharmony_ci	do_notification = false;
317762306a36Sopenharmony_ci	goto do_fdb_op;
317862306a36Sopenharmony_ci}
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_cistatic int
318162306a36Sopenharmony_ci__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
318262306a36Sopenharmony_ci					    const struct mlxsw_sp_fid *fid,
318362306a36Sopenharmony_ci					    bool adding,
318462306a36Sopenharmony_ci					    struct net_device **nve_dev,
318562306a36Sopenharmony_ci					    u16 *p_vid, __be32 *p_vni)
318662306a36Sopenharmony_ci{
318762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
318862306a36Sopenharmony_ci	struct net_device *br_dev, *dev;
318962306a36Sopenharmony_ci	int nve_ifindex;
319062306a36Sopenharmony_ci	int err;
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci	err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex);
319362306a36Sopenharmony_ci	if (err)
319462306a36Sopenharmony_ci		return err;
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	err = mlxsw_sp_fid_vni(fid, p_vni);
319762306a36Sopenharmony_ci	if (err)
319862306a36Sopenharmony_ci		return err;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
320162306a36Sopenharmony_ci	if (!dev)
320262306a36Sopenharmony_ci		return -EINVAL;
320362306a36Sopenharmony_ci	*nve_dev = dev;
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	if (!netif_running(dev))
320662306a36Sopenharmony_ci		return -EINVAL;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	if (adding && !br_port_flag_is_set(dev, BR_LEARNING))
320962306a36Sopenharmony_ci		return -EINVAL;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	if (adding && netif_is_vxlan(dev)) {
321262306a36Sopenharmony_ci		struct vxlan_dev *vxlan = netdev_priv(dev);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci		if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
321562306a36Sopenharmony_ci			return -EINVAL;
321662306a36Sopenharmony_ci	}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
321962306a36Sopenharmony_ci	if (!br_dev)
322062306a36Sopenharmony_ci		return -EINVAL;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
322362306a36Sopenharmony_ci	if (!bridge_device)
322462306a36Sopenharmony_ci		return -EINVAL;
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	*p_vid = bridge_device->ops->fid_vid(bridge_device, fid);
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	return 0;
322962306a36Sopenharmony_ci}
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
323262306a36Sopenharmony_ci						      char *sfn_pl,
323362306a36Sopenharmony_ci						      int rec_index,
323462306a36Sopenharmony_ci						      bool adding)
323562306a36Sopenharmony_ci{
323662306a36Sopenharmony_ci	enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto;
323762306a36Sopenharmony_ci	enum switchdev_notifier_type type;
323862306a36Sopenharmony_ci	struct net_device *nve_dev;
323962306a36Sopenharmony_ci	union mlxsw_sp_l3addr addr;
324062306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
324162306a36Sopenharmony_ci	char mac[ETH_ALEN];
324262306a36Sopenharmony_ci	u16 fid_index, vid;
324362306a36Sopenharmony_ci	__be32 vni;
324462306a36Sopenharmony_ci	u32 uip;
324562306a36Sopenharmony_ci	int err;
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index,
324862306a36Sopenharmony_ci				       &uip, &sfn_proto);
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index);
325162306a36Sopenharmony_ci	if (!fid)
325262306a36Sopenharmony_ci		goto err_fid_lookup;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip,
325562306a36Sopenharmony_ci					      (enum mlxsw_sp_l3proto) sfn_proto,
325662306a36Sopenharmony_ci					      &addr);
325762306a36Sopenharmony_ci	if (err)
325862306a36Sopenharmony_ci		goto err_ip_resolve;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding,
326162306a36Sopenharmony_ci							  &nve_dev, &vid, &vni);
326262306a36Sopenharmony_ci	if (err)
326362306a36Sopenharmony_ci		goto err_fdb_process;
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci	err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
326662306a36Sopenharmony_ci					     (enum mlxsw_sp_l3proto) sfn_proto,
326762306a36Sopenharmony_ci					     &addr, adding, true);
326862306a36Sopenharmony_ci	if (err)
326962306a36Sopenharmony_ci		goto err_fdb_op;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac,
327262306a36Sopenharmony_ci					(enum mlxsw_sp_l3proto) sfn_proto,
327362306a36Sopenharmony_ci					&addr, vni, adding);
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE :
327662306a36Sopenharmony_ci			SWITCHDEV_FDB_DEL_TO_BRIDGE;
327762306a36Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding, false);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	return;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_cierr_fdb_op:
328462306a36Sopenharmony_cierr_fdb_process:
328562306a36Sopenharmony_cierr_ip_resolve:
328662306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
328762306a36Sopenharmony_cierr_fid_lookup:
328862306a36Sopenharmony_ci	/* Remove an FDB entry in case we cannot process it. Otherwise the
328962306a36Sopenharmony_ci	 * device will keep sending the same notification over and over again.
329062306a36Sopenharmony_ci	 */
329162306a36Sopenharmony_ci	mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
329262306a36Sopenharmony_ci				       (enum mlxsw_sp_l3proto) sfn_proto, &addr,
329362306a36Sopenharmony_ci				       false, true);
329462306a36Sopenharmony_ci}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
329762306a36Sopenharmony_ci					    char *sfn_pl, int rec_index)
329862306a36Sopenharmony_ci{
329962306a36Sopenharmony_ci	switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) {
330062306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC:
330162306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
330262306a36Sopenharmony_ci						rec_index, true);
330362306a36Sopenharmony_ci		break;
330462306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC:
330562306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
330662306a36Sopenharmony_ci						rec_index, false);
330762306a36Sopenharmony_ci		break;
330862306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
330962306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
331062306a36Sopenharmony_ci						    rec_index, true);
331162306a36Sopenharmony_ci		break;
331262306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
331362306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
331462306a36Sopenharmony_ci						    rec_index, false);
331562306a36Sopenharmony_ci		break;
331662306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL:
331762306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
331862306a36Sopenharmony_ci							  rec_index, true);
331962306a36Sopenharmony_ci		break;
332062306a36Sopenharmony_ci	case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL:
332162306a36Sopenharmony_ci		mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
332262306a36Sopenharmony_ci							  rec_index, false);
332362306a36Sopenharmony_ci		break;
332462306a36Sopenharmony_ci	}
332562306a36Sopenharmony_ci}
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci#define MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION 10
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work(struct work_struct *work)
333062306a36Sopenharmony_ci{
333162306a36Sopenharmony_ci	struct mlxsw_sp_bridge *bridge;
333262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
333362306a36Sopenharmony_ci	bool reschedule = false;
333462306a36Sopenharmony_ci	char *sfn_pl;
333562306a36Sopenharmony_ci	int queries;
333662306a36Sopenharmony_ci	u8 num_rec;
333762306a36Sopenharmony_ci	int i;
333862306a36Sopenharmony_ci	int err;
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL);
334162306a36Sopenharmony_ci	if (!sfn_pl)
334262306a36Sopenharmony_ci		return;
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci	bridge = container_of(work, struct mlxsw_sp_bridge, fdb_notify.dw.work);
334562306a36Sopenharmony_ci	mlxsw_sp = bridge->mlxsw_sp;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	rtnl_lock();
334862306a36Sopenharmony_ci	if (list_empty(&bridge->bridges_list))
334962306a36Sopenharmony_ci		goto out;
335062306a36Sopenharmony_ci	reschedule = true;
335162306a36Sopenharmony_ci	queries = MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION;
335262306a36Sopenharmony_ci	while (queries > 0) {
335362306a36Sopenharmony_ci		mlxsw_reg_sfn_pack(sfn_pl);
335462306a36Sopenharmony_ci		err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
335562306a36Sopenharmony_ci		if (err) {
335662306a36Sopenharmony_ci			dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
335762306a36Sopenharmony_ci			goto out;
335862306a36Sopenharmony_ci		}
335962306a36Sopenharmony_ci		num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
336062306a36Sopenharmony_ci		for (i = 0; i < num_rec; i++)
336162306a36Sopenharmony_ci			mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
336262306a36Sopenharmony_ci		if (num_rec != MLXSW_REG_SFN_REC_MAX_COUNT)
336362306a36Sopenharmony_ci			goto out;
336462306a36Sopenharmony_ci		queries--;
336562306a36Sopenharmony_ci	}
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ciout:
336862306a36Sopenharmony_ci	rtnl_unlock();
336962306a36Sopenharmony_ci	kfree(sfn_pl);
337062306a36Sopenharmony_ci	if (!reschedule)
337162306a36Sopenharmony_ci		return;
337262306a36Sopenharmony_ci	mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, !queries);
337362306a36Sopenharmony_ci}
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_cistruct mlxsw_sp_switchdev_event_work {
337662306a36Sopenharmony_ci	struct work_struct work;
337762306a36Sopenharmony_ci	netdevice_tracker dev_tracker;
337862306a36Sopenharmony_ci	union {
337962306a36Sopenharmony_ci		struct switchdev_notifier_fdb_info fdb_info;
338062306a36Sopenharmony_ci		struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info;
338162306a36Sopenharmony_ci	};
338262306a36Sopenharmony_ci	struct net_device *dev;
338362306a36Sopenharmony_ci	unsigned long event;
338462306a36Sopenharmony_ci};
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_cistatic void
338762306a36Sopenharmony_cimlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
338862306a36Sopenharmony_ci					  struct mlxsw_sp_switchdev_event_work *
338962306a36Sopenharmony_ci					  switchdev_work,
339062306a36Sopenharmony_ci					  struct mlxsw_sp_fid *fid, __be32 vni)
339162306a36Sopenharmony_ci{
339262306a36Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info;
339362306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
339462306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
339562306a36Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
339662306a36Sopenharmony_ci	union mlxsw_sp_l3addr addr;
339762306a36Sopenharmony_ci	int err;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci	fdb_info = &switchdev_work->fdb_info;
340062306a36Sopenharmony_ci	err = vxlan_fdb_find_uc(dev, fdb_info->addr, vni, &vxlan_fdb_info);
340162306a36Sopenharmony_ci	if (err)
340262306a36Sopenharmony_ci		return;
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info.remote_ip,
340562306a36Sopenharmony_ci					      &proto, &addr);
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci	switch (switchdev_work->event) {
340862306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
340962306a36Sopenharmony_ci		err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp,
341062306a36Sopenharmony_ci						     vxlan_fdb_info.eth_addr,
341162306a36Sopenharmony_ci						     mlxsw_sp_fid_index(fid),
341262306a36Sopenharmony_ci						     proto, &addr, true, false);
341362306a36Sopenharmony_ci		if (err)
341462306a36Sopenharmony_ci			return;
341562306a36Sopenharmony_ci		vxlan_fdb_info.offloaded = true;
341662306a36Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
341762306a36Sopenharmony_ci					 &vxlan_fdb_info.info, NULL);
341862306a36Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
341962306a36Sopenharmony_ci					    vxlan_fdb_info.eth_addr,
342062306a36Sopenharmony_ci					    fdb_info->vid, dev, true, false);
342162306a36Sopenharmony_ci		break;
342262306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
342362306a36Sopenharmony_ci		err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp,
342462306a36Sopenharmony_ci						     vxlan_fdb_info.eth_addr,
342562306a36Sopenharmony_ci						     mlxsw_sp_fid_index(fid),
342662306a36Sopenharmony_ci						     proto, &addr, false,
342762306a36Sopenharmony_ci						     false);
342862306a36Sopenharmony_ci		vxlan_fdb_info.offloaded = false;
342962306a36Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
343062306a36Sopenharmony_ci					 &vxlan_fdb_info.info, NULL);
343162306a36Sopenharmony_ci		break;
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci}
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_cistatic void
343662306a36Sopenharmony_cimlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work *
343762306a36Sopenharmony_ci					switchdev_work)
343862306a36Sopenharmony_ci{
343962306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
344062306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
344162306a36Sopenharmony_ci	struct net_device *br_dev;
344262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
344362306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
344462306a36Sopenharmony_ci	__be32 vni;
344562306a36Sopenharmony_ci	int err;
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	if (switchdev_work->event != SWITCHDEV_FDB_ADD_TO_DEVICE &&
344862306a36Sopenharmony_ci	    switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE)
344962306a36Sopenharmony_ci		return;
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci	if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE &&
345262306a36Sopenharmony_ci	    (!switchdev_work->fdb_info.added_by_user ||
345362306a36Sopenharmony_ci	     switchdev_work->fdb_info.is_local))
345462306a36Sopenharmony_ci		return;
345562306a36Sopenharmony_ci
345662306a36Sopenharmony_ci	if (!netif_running(dev))
345762306a36Sopenharmony_ci		return;
345862306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
345962306a36Sopenharmony_ci	if (!br_dev)
346062306a36Sopenharmony_ci		return;
346162306a36Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
346262306a36Sopenharmony_ci		return;
346362306a36Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
346462306a36Sopenharmony_ci	if (!mlxsw_sp)
346562306a36Sopenharmony_ci		return;
346662306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
346762306a36Sopenharmony_ci	if (!bridge_device)
346862306a36Sopenharmony_ci		return;
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	fid = bridge_device->ops->fid_lookup(bridge_device,
347162306a36Sopenharmony_ci					     switchdev_work->fdb_info.vid);
347262306a36Sopenharmony_ci	if (!fid)
347362306a36Sopenharmony_ci		return;
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	err = mlxsw_sp_fid_vni(fid, &vni);
347662306a36Sopenharmony_ci	if (err)
347762306a36Sopenharmony_ci		goto out;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	mlxsw_sp_switchdev_bridge_vxlan_fdb_event(mlxsw_sp, switchdev_work, fid,
348062306a36Sopenharmony_ci						  vni);
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ciout:
348362306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
348462306a36Sopenharmony_ci}
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_cistatic void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work)
348762306a36Sopenharmony_ci{
348862306a36Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work =
348962306a36Sopenharmony_ci		container_of(work, struct mlxsw_sp_switchdev_event_work, work);
349062306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
349162306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
349262306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
349362306a36Sopenharmony_ci	int err;
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	rtnl_lock();
349662306a36Sopenharmony_ci	if (netif_is_vxlan(dev)) {
349762306a36Sopenharmony_ci		mlxsw_sp_switchdev_bridge_nve_fdb_event(switchdev_work);
349862306a36Sopenharmony_ci		goto out;
349962306a36Sopenharmony_ci	}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev);
350262306a36Sopenharmony_ci	if (!mlxsw_sp_port)
350362306a36Sopenharmony_ci		goto out;
350462306a36Sopenharmony_ci
350562306a36Sopenharmony_ci	switch (switchdev_work->event) {
350662306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
350762306a36Sopenharmony_ci		fdb_info = &switchdev_work->fdb_info;
350862306a36Sopenharmony_ci		if (!fdb_info->added_by_user || fdb_info->is_local)
350962306a36Sopenharmony_ci			break;
351062306a36Sopenharmony_ci		err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true);
351162306a36Sopenharmony_ci		if (err)
351262306a36Sopenharmony_ci			break;
351362306a36Sopenharmony_ci		mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
351462306a36Sopenharmony_ci					    fdb_info->addr,
351562306a36Sopenharmony_ci					    fdb_info->vid, dev, true, false);
351662306a36Sopenharmony_ci		break;
351762306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
351862306a36Sopenharmony_ci		fdb_info = &switchdev_work->fdb_info;
351962306a36Sopenharmony_ci		mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
352062306a36Sopenharmony_ci		break;
352162306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
352262306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
352362306a36Sopenharmony_ci		/* These events are only used to potentially update an existing
352462306a36Sopenharmony_ci		 * SPAN mirror.
352562306a36Sopenharmony_ci		 */
352662306a36Sopenharmony_ci		break;
352762306a36Sopenharmony_ci	}
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ciout:
353262306a36Sopenharmony_ci	rtnl_unlock();
353362306a36Sopenharmony_ci	kfree(switchdev_work->fdb_info.addr);
353462306a36Sopenharmony_ci	netdev_put(dev, &switchdev_work->dev_tracker);
353562306a36Sopenharmony_ci	kfree(switchdev_work);
353662306a36Sopenharmony_ci}
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_cistatic void
353962306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp,
354062306a36Sopenharmony_ci				 struct mlxsw_sp_switchdev_event_work *
354162306a36Sopenharmony_ci				 switchdev_work)
354262306a36Sopenharmony_ci{
354362306a36Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
354462306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
354562306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
354662306a36Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
354762306a36Sopenharmony_ci	union mlxsw_sp_l3addr addr;
354862306a36Sopenharmony_ci	struct net_device *br_dev;
354962306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
355062306a36Sopenharmony_ci	u16 vid;
355162306a36Sopenharmony_ci	int err;
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	vxlan_fdb_info = &switchdev_work->vxlan_fdb_info;
355462306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
355762306a36Sopenharmony_ci	if (!bridge_device)
355862306a36Sopenharmony_ci		return;
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni);
356162306a36Sopenharmony_ci	if (!fid)
356262306a36Sopenharmony_ci		return;
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip,
356562306a36Sopenharmony_ci					      &proto, &addr);
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	if (is_zero_ether_addr(vxlan_fdb_info->eth_addr)) {
356862306a36Sopenharmony_ci		err = mlxsw_sp_nve_flood_ip_add(mlxsw_sp, fid, proto, &addr);
356962306a36Sopenharmony_ci		if (err) {
357062306a36Sopenharmony_ci			mlxsw_sp_fid_put(fid);
357162306a36Sopenharmony_ci			return;
357262306a36Sopenharmony_ci		}
357362306a36Sopenharmony_ci		vxlan_fdb_info->offloaded = true;
357462306a36Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
357562306a36Sopenharmony_ci					 &vxlan_fdb_info->info, NULL);
357662306a36Sopenharmony_ci		mlxsw_sp_fid_put(fid);
357762306a36Sopenharmony_ci		return;
357862306a36Sopenharmony_ci	}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	/* The device has a single FDB table, whereas Linux has two - one
358162306a36Sopenharmony_ci	 * in the bridge driver and another in the VxLAN driver. We only
358262306a36Sopenharmony_ci	 * program an entry to the device if the MAC points to the VxLAN
358362306a36Sopenharmony_ci	 * device in the bridge's FDB table
358462306a36Sopenharmony_ci	 */
358562306a36Sopenharmony_ci	vid = bridge_device->ops->fid_vid(bridge_device, fid);
358662306a36Sopenharmony_ci	if (br_fdb_find_port(br_dev, vxlan_fdb_info->eth_addr, vid) != dev)
358762306a36Sopenharmony_ci		goto err_br_fdb_find;
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr,
359062306a36Sopenharmony_ci					     mlxsw_sp_fid_index(fid), proto,
359162306a36Sopenharmony_ci					     &addr, true, false);
359262306a36Sopenharmony_ci	if (err)
359362306a36Sopenharmony_ci		goto err_fdb_tunnel_uc_op;
359462306a36Sopenharmony_ci	vxlan_fdb_info->offloaded = true;
359562306a36Sopenharmony_ci	call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
359662306a36Sopenharmony_ci				 &vxlan_fdb_info->info, NULL);
359762306a36Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
359862306a36Sopenharmony_ci				    vxlan_fdb_info->eth_addr, vid, dev, true,
359962306a36Sopenharmony_ci				    false);
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_ci	return;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_cierr_fdb_tunnel_uc_op:
360662306a36Sopenharmony_cierr_br_fdb_find:
360762306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
360862306a36Sopenharmony_ci}
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_cistatic void
361162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp,
361262306a36Sopenharmony_ci				 struct mlxsw_sp_switchdev_event_work *
361362306a36Sopenharmony_ci				 switchdev_work)
361462306a36Sopenharmony_ci{
361562306a36Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
361662306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
361762306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
361862306a36Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(dev);
361962306a36Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
362062306a36Sopenharmony_ci	union mlxsw_sp_l3addr addr;
362162306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
362262306a36Sopenharmony_ci	u16 vid;
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	vxlan_fdb_info = &switchdev_work->vxlan_fdb_info;
362562306a36Sopenharmony_ci	if (!vxlan_fdb_info->offloaded)
362662306a36Sopenharmony_ci		return;
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
362962306a36Sopenharmony_ci	if (!bridge_device)
363062306a36Sopenharmony_ci		return;
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni);
363362306a36Sopenharmony_ci	if (!fid)
363462306a36Sopenharmony_ci		return;
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip,
363762306a36Sopenharmony_ci					      &proto, &addr);
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci	if (is_zero_ether_addr(vxlan_fdb_info->eth_addr)) {
364062306a36Sopenharmony_ci		mlxsw_sp_nve_flood_ip_del(mlxsw_sp, fid, proto, &addr);
364162306a36Sopenharmony_ci		mlxsw_sp_fid_put(fid);
364262306a36Sopenharmony_ci		return;
364362306a36Sopenharmony_ci	}
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr,
364662306a36Sopenharmony_ci				       mlxsw_sp_fid_index(fid), proto, &addr,
364762306a36Sopenharmony_ci				       false, false);
364862306a36Sopenharmony_ci	vid = bridge_device->ops->fid_vid(bridge_device, fid);
364962306a36Sopenharmony_ci	mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
365062306a36Sopenharmony_ci				    vxlan_fdb_info->eth_addr, vid, dev, false,
365162306a36Sopenharmony_ci				    false);
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
365462306a36Sopenharmony_ci}
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_cistatic void mlxsw_sp_switchdev_vxlan_fdb_event_work(struct work_struct *work)
365762306a36Sopenharmony_ci{
365862306a36Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work =
365962306a36Sopenharmony_ci		container_of(work, struct mlxsw_sp_switchdev_event_work, work);
366062306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
366162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
366262306a36Sopenharmony_ci	struct net_device *br_dev;
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	rtnl_lock();
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	if (!netif_running(dev))
366762306a36Sopenharmony_ci		goto out;
366862306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(dev);
366962306a36Sopenharmony_ci	if (!br_dev)
367062306a36Sopenharmony_ci		goto out;
367162306a36Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
367262306a36Sopenharmony_ci		goto out;
367362306a36Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
367462306a36Sopenharmony_ci	if (!mlxsw_sp)
367562306a36Sopenharmony_ci		goto out;
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci	switch (switchdev_work->event) {
367862306a36Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE:
367962306a36Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_fdb_add(mlxsw_sp, switchdev_work);
368062306a36Sopenharmony_ci		break;
368162306a36Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE:
368262306a36Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_fdb_del(mlxsw_sp, switchdev_work);
368362306a36Sopenharmony_ci		break;
368462306a36Sopenharmony_ci	}
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ciout:
368762306a36Sopenharmony_ci	rtnl_unlock();
368862306a36Sopenharmony_ci	netdev_put(dev, &switchdev_work->dev_tracker);
368962306a36Sopenharmony_ci	kfree(switchdev_work);
369062306a36Sopenharmony_ci}
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_cistatic int
369362306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
369462306a36Sopenharmony_ci				      switchdev_work,
369562306a36Sopenharmony_ci				      struct switchdev_notifier_info *info)
369662306a36Sopenharmony_ci{
369762306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
369862306a36Sopenharmony_ci	struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
369962306a36Sopenharmony_ci	struct vxlan_config *cfg = &vxlan->cfg;
370062306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	extack = switchdev_notifier_info_to_extack(info);
370362306a36Sopenharmony_ci	vxlan_fdb_info = container_of(info,
370462306a36Sopenharmony_ci				      struct switchdev_notifier_vxlan_fdb_info,
370562306a36Sopenharmony_ci				      info);
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	if (vxlan_fdb_info->remote_port != cfg->dst_port) {
370862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported");
370962306a36Sopenharmony_ci		return -EOPNOTSUPP;
371062306a36Sopenharmony_ci	}
371162306a36Sopenharmony_ci	if (vxlan_fdb_info->remote_vni != cfg->vni ||
371262306a36Sopenharmony_ci	    vxlan_fdb_info->vni != cfg->vni) {
371362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported");
371462306a36Sopenharmony_ci		return -EOPNOTSUPP;
371562306a36Sopenharmony_ci	}
371662306a36Sopenharmony_ci	if (vxlan_fdb_info->remote_ifindex) {
371762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported");
371862306a36Sopenharmony_ci		return -EOPNOTSUPP;
371962306a36Sopenharmony_ci	}
372062306a36Sopenharmony_ci	if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) {
372162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported");
372262306a36Sopenharmony_ci		return -EOPNOTSUPP;
372362306a36Sopenharmony_ci	}
372462306a36Sopenharmony_ci	if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) {
372562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported");
372662306a36Sopenharmony_ci		return -EOPNOTSUPP;
372762306a36Sopenharmony_ci	}
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	switchdev_work->vxlan_fdb_info = *vxlan_fdb_info;
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_ci	return 0;
373262306a36Sopenharmony_ci}
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci/* Called under rcu_read_lock() */
373562306a36Sopenharmony_cistatic int mlxsw_sp_switchdev_event(struct notifier_block *unused,
373662306a36Sopenharmony_ci				    unsigned long event, void *ptr)
373762306a36Sopenharmony_ci{
373862306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
373962306a36Sopenharmony_ci	struct mlxsw_sp_switchdev_event_work *switchdev_work;
374062306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
374162306a36Sopenharmony_ci	struct switchdev_notifier_info *info = ptr;
374262306a36Sopenharmony_ci	struct net_device *br_dev;
374362306a36Sopenharmony_ci	int err;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	if (event == SWITCHDEV_PORT_ATTR_SET) {
374662306a36Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
374762306a36Sopenharmony_ci						     mlxsw_sp_port_dev_check,
374862306a36Sopenharmony_ci						     mlxsw_sp_port_attr_set);
374962306a36Sopenharmony_ci		return notifier_from_errno(err);
375062306a36Sopenharmony_ci	}
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	/* Tunnel devices are not our uppers, so check their master instead */
375362306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get_rcu(dev);
375462306a36Sopenharmony_ci	if (!br_dev)
375562306a36Sopenharmony_ci		return NOTIFY_DONE;
375662306a36Sopenharmony_ci	if (!netif_is_bridge_master(br_dev))
375762306a36Sopenharmony_ci		return NOTIFY_DONE;
375862306a36Sopenharmony_ci	if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev))
375962306a36Sopenharmony_ci		return NOTIFY_DONE;
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
376262306a36Sopenharmony_ci	if (!switchdev_work)
376362306a36Sopenharmony_ci		return NOTIFY_BAD;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci	switchdev_work->dev = dev;
376662306a36Sopenharmony_ci	switchdev_work->event = event;
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci	switch (event) {
376962306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
377062306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
377162306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
377262306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
377362306a36Sopenharmony_ci		fdb_info = container_of(info,
377462306a36Sopenharmony_ci					struct switchdev_notifier_fdb_info,
377562306a36Sopenharmony_ci					info);
377662306a36Sopenharmony_ci		INIT_WORK(&switchdev_work->work,
377762306a36Sopenharmony_ci			  mlxsw_sp_switchdev_bridge_fdb_event_work);
377862306a36Sopenharmony_ci		memcpy(&switchdev_work->fdb_info, ptr,
377962306a36Sopenharmony_ci		       sizeof(switchdev_work->fdb_info));
378062306a36Sopenharmony_ci		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
378162306a36Sopenharmony_ci		if (!switchdev_work->fdb_info.addr)
378262306a36Sopenharmony_ci			goto err_addr_alloc;
378362306a36Sopenharmony_ci		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
378462306a36Sopenharmony_ci				fdb_info->addr);
378562306a36Sopenharmony_ci		/* Take a reference on the device. This can be either
378662306a36Sopenharmony_ci		 * upper device containig mlxsw_sp_port or just a
378762306a36Sopenharmony_ci		 * mlxsw_sp_port
378862306a36Sopenharmony_ci		 */
378962306a36Sopenharmony_ci		netdev_hold(dev, &switchdev_work->dev_tracker, GFP_ATOMIC);
379062306a36Sopenharmony_ci		break;
379162306a36Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE:
379262306a36Sopenharmony_ci	case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE:
379362306a36Sopenharmony_ci		INIT_WORK(&switchdev_work->work,
379462306a36Sopenharmony_ci			  mlxsw_sp_switchdev_vxlan_fdb_event_work);
379562306a36Sopenharmony_ci		err = mlxsw_sp_switchdev_vxlan_work_prepare(switchdev_work,
379662306a36Sopenharmony_ci							    info);
379762306a36Sopenharmony_ci		if (err)
379862306a36Sopenharmony_ci			goto err_vxlan_work_prepare;
379962306a36Sopenharmony_ci		netdev_hold(dev, &switchdev_work->dev_tracker, GFP_ATOMIC);
380062306a36Sopenharmony_ci		break;
380162306a36Sopenharmony_ci	default:
380262306a36Sopenharmony_ci		kfree(switchdev_work);
380362306a36Sopenharmony_ci		return NOTIFY_DONE;
380462306a36Sopenharmony_ci	}
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	mlxsw_core_schedule_work(&switchdev_work->work);
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	return NOTIFY_DONE;
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_cierr_vxlan_work_prepare:
381162306a36Sopenharmony_cierr_addr_alloc:
381262306a36Sopenharmony_ci	kfree(switchdev_work);
381362306a36Sopenharmony_ci	return NOTIFY_BAD;
381462306a36Sopenharmony_ci}
381562306a36Sopenharmony_ci
381662306a36Sopenharmony_cistruct notifier_block mlxsw_sp_switchdev_notifier = {
381762306a36Sopenharmony_ci	.notifier_call = mlxsw_sp_switchdev_event,
381862306a36Sopenharmony_ci};
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_cistatic int
382162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
382262306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_device *bridge_device,
382362306a36Sopenharmony_ci				  const struct net_device *vxlan_dev, u16 vid,
382462306a36Sopenharmony_ci				  bool flag_untagged, bool flag_pvid,
382562306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
382662306a36Sopenharmony_ci{
382762306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
382862306a36Sopenharmony_ci	__be32 vni = vxlan->cfg.vni;
382962306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
383062306a36Sopenharmony_ci	u16 old_vid;
383162306a36Sopenharmony_ci	int err;
383262306a36Sopenharmony_ci
383362306a36Sopenharmony_ci	/* We cannot have the same VLAN as PVID and egress untagged on multiple
383462306a36Sopenharmony_ci	 * VxLAN devices. Note that we get this notification before the VLAN is
383562306a36Sopenharmony_ci	 * actually added to the bridge's database, so it is not possible for
383662306a36Sopenharmony_ci	 * the lookup function to return 'vxlan_dev'
383762306a36Sopenharmony_ci	 */
383862306a36Sopenharmony_ci	if (flag_untagged && flag_pvid &&
383962306a36Sopenharmony_ci	    mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid)) {
384062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "VLAN already mapped to a different VNI");
384162306a36Sopenharmony_ci		return -EINVAL;
384262306a36Sopenharmony_ci	}
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	if (!netif_running(vxlan_dev))
384562306a36Sopenharmony_ci		return 0;
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_ci	/* First case: FID is not associated with this VNI, but the new VLAN
384862306a36Sopenharmony_ci	 * is both PVID and egress untagged. Need to enable NVE on the FID, if
384962306a36Sopenharmony_ci	 * it exists
385062306a36Sopenharmony_ci	 */
385162306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
385262306a36Sopenharmony_ci	if (!fid) {
385362306a36Sopenharmony_ci		if (!flag_untagged || !flag_pvid)
385462306a36Sopenharmony_ci			return 0;
385562306a36Sopenharmony_ci		return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev,
385662306a36Sopenharmony_ci						      vid, extack);
385762306a36Sopenharmony_ci	}
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci	/* Second case: FID is associated with the VNI and the VLAN associated
386062306a36Sopenharmony_ci	 * with the FID is the same as the notified VLAN. This means the flags
386162306a36Sopenharmony_ci	 * (PVID / egress untagged) were toggled and that NVE should be
386262306a36Sopenharmony_ci	 * disabled on the FID
386362306a36Sopenharmony_ci	 */
386462306a36Sopenharmony_ci	old_vid = mlxsw_sp_fid_8021q_vid(fid);
386562306a36Sopenharmony_ci	if (vid == old_vid) {
386662306a36Sopenharmony_ci		if (WARN_ON(flag_untagged && flag_pvid)) {
386762306a36Sopenharmony_ci			mlxsw_sp_fid_put(fid);
386862306a36Sopenharmony_ci			return -EINVAL;
386962306a36Sopenharmony_ci		}
387062306a36Sopenharmony_ci		mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
387162306a36Sopenharmony_ci		mlxsw_sp_fid_put(fid);
387262306a36Sopenharmony_ci		return 0;
387362306a36Sopenharmony_ci	}
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* Third case: A new VLAN was configured on the VxLAN device, but this
387662306a36Sopenharmony_ci	 * VLAN is not PVID, so there is nothing to do.
387762306a36Sopenharmony_ci	 */
387862306a36Sopenharmony_ci	if (!flag_pvid) {
387962306a36Sopenharmony_ci		mlxsw_sp_fid_put(fid);
388062306a36Sopenharmony_ci		return 0;
388162306a36Sopenharmony_ci	}
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	/* Fourth case: Thew new VLAN is PVID, which means the VLAN currently
388462306a36Sopenharmony_ci	 * mapped to the VNI should be unmapped
388562306a36Sopenharmony_ci	 */
388662306a36Sopenharmony_ci	mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
388762306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	/* Fifth case: The new VLAN is also egress untagged, which means the
389062306a36Sopenharmony_ci	 * VLAN needs to be mapped to the VNI
389162306a36Sopenharmony_ci	 */
389262306a36Sopenharmony_ci	if (!flag_untagged)
389362306a36Sopenharmony_ci		return 0;
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci	err = bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid, extack);
389662306a36Sopenharmony_ci	if (err)
389762306a36Sopenharmony_ci		goto err_vxlan_join;
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	return 0;
390062306a36Sopenharmony_ci
390162306a36Sopenharmony_cierr_vxlan_join:
390262306a36Sopenharmony_ci	bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, old_vid, NULL);
390362306a36Sopenharmony_ci	return err;
390462306a36Sopenharmony_ci}
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_cistatic void
390762306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
390862306a36Sopenharmony_ci				  struct mlxsw_sp_bridge_device *bridge_device,
390962306a36Sopenharmony_ci				  const struct net_device *vxlan_dev, u16 vid)
391062306a36Sopenharmony_ci{
391162306a36Sopenharmony_ci	struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
391262306a36Sopenharmony_ci	__be32 vni = vxlan->cfg.vni;
391362306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	if (!netif_running(vxlan_dev))
391662306a36Sopenharmony_ci		return;
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci	fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
391962306a36Sopenharmony_ci	if (!fid)
392062306a36Sopenharmony_ci		return;
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	/* A different VLAN than the one mapped to the VNI is deleted */
392362306a36Sopenharmony_ci	if (mlxsw_sp_fid_8021q_vid(fid) != vid)
392462306a36Sopenharmony_ci		goto out;
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ciout:
392962306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
393062306a36Sopenharmony_ci}
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_cistatic int
393362306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
393462306a36Sopenharmony_ci				   struct switchdev_notifier_port_obj_info *
393562306a36Sopenharmony_ci				   port_obj_info)
393662306a36Sopenharmony_ci{
393762306a36Sopenharmony_ci	struct switchdev_obj_port_vlan *vlan =
393862306a36Sopenharmony_ci		SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
393962306a36Sopenharmony_ci	bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
394062306a36Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
394162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
394262306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
394362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
394462306a36Sopenharmony_ci	struct net_device *br_dev;
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
394762306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(vxlan_dev);
394862306a36Sopenharmony_ci	if (!br_dev)
394962306a36Sopenharmony_ci		return 0;
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
395262306a36Sopenharmony_ci	if (!mlxsw_sp)
395362306a36Sopenharmony_ci		return 0;
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	port_obj_info->handled = true;
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
395862306a36Sopenharmony_ci	if (!bridge_device)
395962306a36Sopenharmony_ci		return -EINVAL;
396062306a36Sopenharmony_ci
396162306a36Sopenharmony_ci	if (!bridge_device->vlan_enabled)
396262306a36Sopenharmony_ci		return 0;
396362306a36Sopenharmony_ci
396462306a36Sopenharmony_ci	return mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
396562306a36Sopenharmony_ci						 vxlan_dev, vlan->vid,
396662306a36Sopenharmony_ci						 flag_untagged,
396762306a36Sopenharmony_ci						 flag_pvid, extack);
396862306a36Sopenharmony_ci}
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_cistatic void
397162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
397262306a36Sopenharmony_ci				   struct switchdev_notifier_port_obj_info *
397362306a36Sopenharmony_ci				   port_obj_info)
397462306a36Sopenharmony_ci{
397562306a36Sopenharmony_ci	struct switchdev_obj_port_vlan *vlan =
397662306a36Sopenharmony_ci		SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
397762306a36Sopenharmony_ci	struct mlxsw_sp_bridge_device *bridge_device;
397862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
397962306a36Sopenharmony_ci	struct net_device *br_dev;
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	br_dev = netdev_master_upper_dev_get(vxlan_dev);
398262306a36Sopenharmony_ci	if (!br_dev)
398362306a36Sopenharmony_ci		return;
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(br_dev);
398662306a36Sopenharmony_ci	if (!mlxsw_sp)
398762306a36Sopenharmony_ci		return;
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_ci	port_obj_info->handled = true;
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci	bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
399262306a36Sopenharmony_ci	if (!bridge_device)
399362306a36Sopenharmony_ci		return;
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_ci	if (!bridge_device->vlan_enabled)
399662306a36Sopenharmony_ci		return;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, vxlan_dev,
399962306a36Sopenharmony_ci					  vlan->vid);
400062306a36Sopenharmony_ci}
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_cistatic int
400362306a36Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev,
400462306a36Sopenharmony_ci					struct switchdev_notifier_port_obj_info *
400562306a36Sopenharmony_ci					port_obj_info)
400662306a36Sopenharmony_ci{
400762306a36Sopenharmony_ci	int err = 0;
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_ci	switch (port_obj_info->obj->id) {
401062306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
401162306a36Sopenharmony_ci		err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev,
401262306a36Sopenharmony_ci							 port_obj_info);
401362306a36Sopenharmony_ci		break;
401462306a36Sopenharmony_ci	default:
401562306a36Sopenharmony_ci		break;
401662306a36Sopenharmony_ci	}
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	return err;
401962306a36Sopenharmony_ci}
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_cistatic void
402262306a36Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev,
402362306a36Sopenharmony_ci					struct switchdev_notifier_port_obj_info *
402462306a36Sopenharmony_ci					port_obj_info)
402562306a36Sopenharmony_ci{
402662306a36Sopenharmony_ci	switch (port_obj_info->obj->id) {
402762306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
402862306a36Sopenharmony_ci		mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info);
402962306a36Sopenharmony_ci		break;
403062306a36Sopenharmony_ci	default:
403162306a36Sopenharmony_ci		break;
403262306a36Sopenharmony_ci	}
403362306a36Sopenharmony_ci}
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_cistatic int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
403662306a36Sopenharmony_ci					     unsigned long event, void *ptr)
403762306a36Sopenharmony_ci{
403862306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
403962306a36Sopenharmony_ci	int err = 0;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	switch (event) {
404262306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_ADD:
404362306a36Sopenharmony_ci		if (netif_is_vxlan(dev))
404462306a36Sopenharmony_ci			err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr);
404562306a36Sopenharmony_ci		else
404662306a36Sopenharmony_ci			err = switchdev_handle_port_obj_add(dev, ptr,
404762306a36Sopenharmony_ci							mlxsw_sp_port_dev_check,
404862306a36Sopenharmony_ci							mlxsw_sp_port_obj_add);
404962306a36Sopenharmony_ci		return notifier_from_errno(err);
405062306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_DEL:
405162306a36Sopenharmony_ci		if (netif_is_vxlan(dev))
405262306a36Sopenharmony_ci			mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr);
405362306a36Sopenharmony_ci		else
405462306a36Sopenharmony_ci			err = switchdev_handle_port_obj_del(dev, ptr,
405562306a36Sopenharmony_ci							mlxsw_sp_port_dev_check,
405662306a36Sopenharmony_ci							mlxsw_sp_port_obj_del);
405762306a36Sopenharmony_ci		return notifier_from_errno(err);
405862306a36Sopenharmony_ci	case SWITCHDEV_PORT_ATTR_SET:
405962306a36Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
406062306a36Sopenharmony_ci						     mlxsw_sp_port_dev_check,
406162306a36Sopenharmony_ci						     mlxsw_sp_port_attr_set);
406262306a36Sopenharmony_ci		return notifier_from_errno(err);
406362306a36Sopenharmony_ci	}
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci	return NOTIFY_DONE;
406662306a36Sopenharmony_ci}
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_switchdev_blocking_notifier = {
406962306a36Sopenharmony_ci	.notifier_call = mlxsw_sp_switchdev_blocking_event,
407062306a36Sopenharmony_ci};
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ciu8
407362306a36Sopenharmony_cimlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
407462306a36Sopenharmony_ci{
407562306a36Sopenharmony_ci	return bridge_port->stp_state;
407662306a36Sopenharmony_ci}
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_cistatic int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
407962306a36Sopenharmony_ci{
408062306a36Sopenharmony_ci	struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
408162306a36Sopenharmony_ci	struct notifier_block *nb;
408262306a36Sopenharmony_ci	int err;
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
408562306a36Sopenharmony_ci	if (err) {
408662306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
408762306a36Sopenharmony_ci		return err;
408862306a36Sopenharmony_ci	}
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
409162306a36Sopenharmony_ci	if (err) {
409262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n");
409362306a36Sopenharmony_ci		return err;
409462306a36Sopenharmony_ci	}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci	nb = &mlxsw_sp_switchdev_blocking_notifier;
409762306a36Sopenharmony_ci	err = register_switchdev_blocking_notifier(nb);
409862306a36Sopenharmony_ci	if (err) {
409962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n");
410062306a36Sopenharmony_ci		goto err_register_switchdev_blocking_notifier;
410162306a36Sopenharmony_ci	}
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
410462306a36Sopenharmony_ci	bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
410562306a36Sopenharmony_ci	return 0;
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_cierr_register_switchdev_blocking_notifier:
410862306a36Sopenharmony_ci	unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
410962306a36Sopenharmony_ci	return err;
411062306a36Sopenharmony_ci}
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_cistatic void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
411362306a36Sopenharmony_ci{
411462306a36Sopenharmony_ci	struct notifier_block *nb;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
411762306a36Sopenharmony_ci
411862306a36Sopenharmony_ci	nb = &mlxsw_sp_switchdev_blocking_notifier;
411962306a36Sopenharmony_ci	unregister_switchdev_blocking_notifier(nb);
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
412262306a36Sopenharmony_ci}
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_cistatic void mlxsw_sp1_switchdev_init(struct mlxsw_sp *mlxsw_sp)
412562306a36Sopenharmony_ci{
412662306a36Sopenharmony_ci	mlxsw_sp->bridge->bridge_8021ad_ops = &mlxsw_sp1_bridge_8021ad_ops;
412762306a36Sopenharmony_ci}
412862306a36Sopenharmony_ci
412962306a36Sopenharmony_ciconst struct mlxsw_sp_switchdev_ops mlxsw_sp1_switchdev_ops = {
413062306a36Sopenharmony_ci	.init	= mlxsw_sp1_switchdev_init,
413162306a36Sopenharmony_ci};
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_cistatic void mlxsw_sp2_switchdev_init(struct mlxsw_sp *mlxsw_sp)
413462306a36Sopenharmony_ci{
413562306a36Sopenharmony_ci	mlxsw_sp->bridge->bridge_8021ad_ops = &mlxsw_sp2_bridge_8021ad_ops;
413662306a36Sopenharmony_ci}
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ciconst struct mlxsw_sp_switchdev_ops mlxsw_sp2_switchdev_ops = {
413962306a36Sopenharmony_ci	.init	= mlxsw_sp2_switchdev_init,
414062306a36Sopenharmony_ci};
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ciint mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
414362306a36Sopenharmony_ci{
414462306a36Sopenharmony_ci	struct mlxsw_sp_bridge *bridge;
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci	bridge = kzalloc(sizeof(*mlxsw_sp->bridge), GFP_KERNEL);
414762306a36Sopenharmony_ci	if (!bridge)
414862306a36Sopenharmony_ci		return -ENOMEM;
414962306a36Sopenharmony_ci	mlxsw_sp->bridge = bridge;
415062306a36Sopenharmony_ci	bridge->mlxsw_sp = mlxsw_sp;
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp->bridge->bridges_list);
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci	bridge->bridge_8021q_ops = &mlxsw_sp_bridge_8021q_ops;
415562306a36Sopenharmony_ci	bridge->bridge_8021d_ops = &mlxsw_sp_bridge_8021d_ops;
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_ci	mlxsw_sp->switchdev_ops->init(mlxsw_sp);
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci	return mlxsw_sp_fdb_init(mlxsw_sp);
416062306a36Sopenharmony_ci}
416162306a36Sopenharmony_ci
416262306a36Sopenharmony_civoid mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp)
416362306a36Sopenharmony_ci{
416462306a36Sopenharmony_ci	mlxsw_sp_fdb_fini(mlxsw_sp);
416562306a36Sopenharmony_ci	WARN_ON(!list_empty(&mlxsw_sp->bridge->bridges_list));
416662306a36Sopenharmony_ci	kfree(mlxsw_sp->bridge);
416762306a36Sopenharmony_ci}
416862306a36Sopenharmony_ci
4169