119e9bfa0SVlad Buslov // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
219e9bfa0SVlad Buslov /* Copyright (c) 2021 Mellanox Technologies. */
319e9bfa0SVlad Buslov 
419e9bfa0SVlad Buslov #include <linux/netdevice.h>
5c636a0f0SVlad Buslov #include <linux/if_bridge.h>
619e9bfa0SVlad Buslov #include <net/netevent.h>
719e9bfa0SVlad Buslov #include <net/switchdev.h>
819e9bfa0SVlad Buslov #include "bridge.h"
919e9bfa0SVlad Buslov #include "esw/bridge.h"
1019e9bfa0SVlad Buslov #include "en_rep.h"
1119e9bfa0SVlad Buslov 
12c636a0f0SVlad Buslov #define MLX5_ESW_BRIDGE_UPDATE_INTERVAL 1000
13c636a0f0SVlad Buslov 
147cd6a54aSVlad Buslov struct mlx5_bridge_switchdev_fdb_work {
157cd6a54aSVlad Buslov 	struct work_struct work;
167cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info fdb_info;
177cd6a54aSVlad Buslov 	struct net_device *dev;
18c358ea17SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
197cd6a54aSVlad Buslov 	bool add;
207cd6a54aSVlad Buslov };
217cd6a54aSVlad Buslov 
223ee6233eSVlad Buslov static bool mlx5_esw_bridge_dev_same_esw(struct net_device *dev, struct mlx5_eswitch *esw)
233ee6233eSVlad Buslov {
243ee6233eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(dev);
253ee6233eSVlad Buslov 
263ee6233eSVlad Buslov 	return esw == priv->mdev->priv.eswitch;
273ee6233eSVlad Buslov }
283ee6233eSVlad Buslov 
29c358ea17SVlad Buslov static bool mlx5_esw_bridge_dev_same_hw(struct net_device *dev, struct mlx5_eswitch *esw)
30c358ea17SVlad Buslov {
31c358ea17SVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(dev);
32c358ea17SVlad Buslov 	struct mlx5_core_dev *mdev, *esw_mdev;
33c358ea17SVlad Buslov 	u64 system_guid, esw_system_guid;
34c358ea17SVlad Buslov 
35c358ea17SVlad Buslov 	mdev = priv->mdev;
36c358ea17SVlad Buslov 	esw_mdev = esw->dev;
37c358ea17SVlad Buslov 
38c358ea17SVlad Buslov 	system_guid = mlx5_query_nic_system_image_guid(mdev);
39c358ea17SVlad Buslov 	esw_system_guid = mlx5_query_nic_system_image_guid(esw_mdev);
40c358ea17SVlad Buslov 
41c358ea17SVlad Buslov 	return system_guid == esw_system_guid;
42c358ea17SVlad Buslov }
43c358ea17SVlad Buslov 
44ff9b7521SVlad Buslov static struct net_device *
45ff9b7521SVlad Buslov mlx5_esw_bridge_lag_rep_get(struct net_device *dev, struct mlx5_eswitch *esw)
46ff9b7521SVlad Buslov {
47ff9b7521SVlad Buslov 	struct net_device *lower;
48ff9b7521SVlad Buslov 	struct list_head *iter;
49ff9b7521SVlad Buslov 
50ff9b7521SVlad Buslov 	netdev_for_each_lower_dev(dev, lower, iter) {
51ff9b7521SVlad Buslov 		struct mlx5_core_dev *mdev;
52ff9b7521SVlad Buslov 		struct mlx5e_priv *priv;
53ff9b7521SVlad Buslov 
54ff9b7521SVlad Buslov 		if (!mlx5e_eswitch_rep(lower))
55ff9b7521SVlad Buslov 			continue;
56ff9b7521SVlad Buslov 
57ff9b7521SVlad Buslov 		priv = netdev_priv(lower);
58ff9b7521SVlad Buslov 		mdev = priv->mdev;
59ff9b7521SVlad Buslov 		if (mlx5_lag_is_shared_fdb(mdev) && mlx5_esw_bridge_dev_same_esw(lower, esw))
60ff9b7521SVlad Buslov 			return lower;
61ff9b7521SVlad Buslov 	}
62ff9b7521SVlad Buslov 
63ff9b7521SVlad Buslov 	return NULL;
64ff9b7521SVlad Buslov }
65ff9b7521SVlad Buslov 
66ff9b7521SVlad Buslov static struct net_device *
67ff9b7521SVlad Buslov mlx5_esw_bridge_rep_vport_num_vhca_id_get(struct net_device *dev, struct mlx5_eswitch *esw,
683ee6233eSVlad Buslov 					  u16 *vport_num, u16 *esw_owner_vhca_id)
693ee6233eSVlad Buslov {
703ee6233eSVlad Buslov 	struct mlx5e_rep_priv *rpriv;
713ee6233eSVlad Buslov 	struct mlx5e_priv *priv;
723ee6233eSVlad Buslov 
73ff9b7521SVlad Buslov 	if (netif_is_lag_master(dev))
74ff9b7521SVlad Buslov 		dev = mlx5_esw_bridge_lag_rep_get(dev, esw);
75ff9b7521SVlad Buslov 
76ff9b7521SVlad Buslov 	if (!dev || !mlx5e_eswitch_rep(dev) || !mlx5_esw_bridge_dev_same_hw(dev, esw))
77ff9b7521SVlad Buslov 		return NULL;
783ee6233eSVlad Buslov 
793ee6233eSVlad Buslov 	priv = netdev_priv(dev);
803ee6233eSVlad Buslov 	rpriv = priv->ppriv;
813ee6233eSVlad Buslov 	*vport_num = rpriv->rep->vport;
823ee6233eSVlad Buslov 	*esw_owner_vhca_id = MLX5_CAP_GEN(priv->mdev, vhca_id);
83ff9b7521SVlad Buslov 	return dev;
843ee6233eSVlad Buslov }
853ee6233eSVlad Buslov 
86ff9b7521SVlad Buslov static struct net_device *
873ee6233eSVlad Buslov mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct mlx5_eswitch *esw,
883ee6233eSVlad Buslov 						u16 *vport_num, u16 *esw_owner_vhca_id)
893ee6233eSVlad Buslov {
903ee6233eSVlad Buslov 	struct net_device *lower_dev;
913ee6233eSVlad Buslov 	struct list_head *iter;
923ee6233eSVlad Buslov 
93ff9b7521SVlad Buslov 	if (netif_is_lag_master(dev) || mlx5e_eswitch_rep(dev))
94ff9b7521SVlad Buslov 		return mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, vport_num,
953ee6233eSVlad Buslov 								 esw_owner_vhca_id);
963ee6233eSVlad Buslov 
973ee6233eSVlad Buslov 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
98ff9b7521SVlad Buslov 		struct net_device *rep;
993ee6233eSVlad Buslov 
1003ee6233eSVlad Buslov 		if (netif_is_bridge_master(lower_dev))
1013ee6233eSVlad Buslov 			continue;
1023ee6233eSVlad Buslov 
103ff9b7521SVlad Buslov 		rep = mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(lower_dev, esw, vport_num,
1043ee6233eSVlad Buslov 								      esw_owner_vhca_id);
105ff9b7521SVlad Buslov 		if (rep)
106ff9b7521SVlad Buslov 			return rep;
1073ee6233eSVlad Buslov 	}
1083ee6233eSVlad Buslov 
109ff9b7521SVlad Buslov 	return NULL;
110ff9b7521SVlad Buslov }
111ff9b7521SVlad Buslov 
112ff9b7521SVlad Buslov static bool mlx5_esw_bridge_is_local(struct net_device *dev, struct net_device *rep,
113ff9b7521SVlad Buslov 				     struct mlx5_eswitch *esw)
114ff9b7521SVlad Buslov {
115ff9b7521SVlad Buslov 	struct mlx5_core_dev *mdev;
116ff9b7521SVlad Buslov 	struct mlx5e_priv *priv;
117ff9b7521SVlad Buslov 
118ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_dev_same_esw(rep, esw))
119ff9b7521SVlad Buslov 		return false;
120ff9b7521SVlad Buslov 
121ff9b7521SVlad Buslov 	priv = netdev_priv(rep);
122ff9b7521SVlad Buslov 	mdev = priv->mdev;
123ff9b7521SVlad Buslov 	if (netif_is_lag_master(dev))
124ff9b7521SVlad Buslov 		return mlx5_lag_is_shared_fdb(mdev) && mlx5_lag_is_master(mdev);
125ff9b7521SVlad Buslov 	return true;
1263ee6233eSVlad Buslov }
1273ee6233eSVlad Buslov 
12819e9bfa0SVlad Buslov static int mlx5_esw_bridge_port_changeupper(struct notifier_block *nb, void *ptr)
12919e9bfa0SVlad Buslov {
13019e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
13119e9bfa0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
13219e9bfa0SVlad Buslov 								    netdev_nb);
13319e9bfa0SVlad Buslov 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
13419e9bfa0SVlad Buslov 	struct netdev_notifier_changeupper_info *info = ptr;
135ff9b7521SVlad Buslov 	struct net_device *upper = info->upper_dev, *rep;
136c358ea17SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
1373ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
13819e9bfa0SVlad Buslov 	struct netlink_ext_ack *extack;
1393ee6233eSVlad Buslov 	int ifindex = upper->ifindex;
1408343268eSVlad Buslov 	int err = 0;
14119e9bfa0SVlad Buslov 
14219e9bfa0SVlad Buslov 	if (!netif_is_bridge_master(upper))
14319e9bfa0SVlad Buslov 		return 0;
14419e9bfa0SVlad Buslov 
145ff9b7521SVlad Buslov 	rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, &vport_num, &esw_owner_vhca_id);
146ff9b7521SVlad Buslov 	if (!rep)
14719e9bfa0SVlad Buslov 		return 0;
14819e9bfa0SVlad Buslov 
14919e9bfa0SVlad Buslov 	extack = netdev_notifier_info_to_extack(&info->info);
15019e9bfa0SVlad Buslov 
151ff9b7521SVlad Buslov 	if (mlx5_esw_bridge_is_local(dev, rep, esw))
152c358ea17SVlad Buslov 		err = info->linking ?
153c358ea17SVlad Buslov 			mlx5_esw_bridge_vport_link(ifindex, vport_num, esw_owner_vhca_id,
154c358ea17SVlad Buslov 						   br_offloads, extack) :
155c358ea17SVlad Buslov 			mlx5_esw_bridge_vport_unlink(ifindex, vport_num, esw_owner_vhca_id,
156c358ea17SVlad Buslov 						     br_offloads, extack);
157ff9b7521SVlad Buslov 	else if (mlx5_esw_bridge_dev_same_hw(rep, esw))
158c358ea17SVlad Buslov 		err = info->linking ?
159c358ea17SVlad Buslov 			mlx5_esw_bridge_vport_peer_link(ifindex, vport_num, esw_owner_vhca_id,
160c358ea17SVlad Buslov 							br_offloads, extack) :
161c358ea17SVlad Buslov 			mlx5_esw_bridge_vport_peer_unlink(ifindex, vport_num, esw_owner_vhca_id,
162c358ea17SVlad Buslov 							  br_offloads, extack);
163c358ea17SVlad Buslov 
164c358ea17SVlad Buslov 	return err;
16519e9bfa0SVlad Buslov }
16619e9bfa0SVlad Buslov 
16715f8f168SVlad Buslov static int
16815f8f168SVlad Buslov mlx5_esw_bridge_changeupper_validate_netdev(void *ptr)
16915f8f168SVlad Buslov {
17015f8f168SVlad Buslov 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
17115f8f168SVlad Buslov 	struct netdev_notifier_changeupper_info *info = ptr;
17215f8f168SVlad Buslov 	struct net_device *upper = info->upper_dev;
17315f8f168SVlad Buslov 	struct net_device *lower;
17415f8f168SVlad Buslov 	struct list_head *iter;
17515f8f168SVlad Buslov 
17615f8f168SVlad Buslov 	if (!netif_is_bridge_master(upper) || !netif_is_lag_master(dev))
17715f8f168SVlad Buslov 		return 0;
17815f8f168SVlad Buslov 
17915f8f168SVlad Buslov 	netdev_for_each_lower_dev(dev, lower, iter) {
18015f8f168SVlad Buslov 		struct mlx5_core_dev *mdev;
18115f8f168SVlad Buslov 		struct mlx5e_priv *priv;
18215f8f168SVlad Buslov 
18315f8f168SVlad Buslov 		if (!mlx5e_eswitch_rep(lower))
18415f8f168SVlad Buslov 			continue;
18515f8f168SVlad Buslov 
18615f8f168SVlad Buslov 		priv = netdev_priv(lower);
18715f8f168SVlad Buslov 		mdev = priv->mdev;
18815f8f168SVlad Buslov 		if (!mlx5_lag_is_active(mdev))
18915f8f168SVlad Buslov 			return -EAGAIN;
19015f8f168SVlad Buslov 		if (!mlx5_lag_is_shared_fdb(mdev))
19115f8f168SVlad Buslov 			return -EOPNOTSUPP;
19215f8f168SVlad Buslov 	}
19315f8f168SVlad Buslov 
19415f8f168SVlad Buslov 	return 0;
19515f8f168SVlad Buslov }
19615f8f168SVlad Buslov 
19719e9bfa0SVlad Buslov static int mlx5_esw_bridge_switchdev_port_event(struct notifier_block *nb,
19819e9bfa0SVlad Buslov 						unsigned long event, void *ptr)
19919e9bfa0SVlad Buslov {
20019e9bfa0SVlad Buslov 	int err = 0;
20119e9bfa0SVlad Buslov 
20219e9bfa0SVlad Buslov 	switch (event) {
20319e9bfa0SVlad Buslov 	case NETDEV_PRECHANGEUPPER:
20415f8f168SVlad Buslov 		err = mlx5_esw_bridge_changeupper_validate_netdev(ptr);
20519e9bfa0SVlad Buslov 		break;
20619e9bfa0SVlad Buslov 
20719e9bfa0SVlad Buslov 	case NETDEV_CHANGEUPPER:
20819e9bfa0SVlad Buslov 		err = mlx5_esw_bridge_port_changeupper(nb, ptr);
20919e9bfa0SVlad Buslov 		break;
21019e9bfa0SVlad Buslov 	}
21119e9bfa0SVlad Buslov 
21219e9bfa0SVlad Buslov 	return notifier_from_errno(err);
21319e9bfa0SVlad Buslov }
21419e9bfa0SVlad Buslov 
2153ee6233eSVlad Buslov static int
2163ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_add(struct net_device *dev,
2173ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
2183ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
219d75b9e80SVlad Buslov {
2203ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
2213ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
222d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
2233ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2243ee6233eSVlad Buslov 	int err;
225d75b9e80SVlad Buslov 
226ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
227ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2283ee6233eSVlad Buslov 		return 0;
2293ee6233eSVlad Buslov 
2303ee6233eSVlad Buslov 	port_obj_info->handled = true;
231d75b9e80SVlad Buslov 
232d75b9e80SVlad Buslov 	switch (obj->id) {
233d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
234d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2353ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_vlan_add(vport_num, esw_owner_vhca_id, vlan->vid,
2363ee6233eSVlad Buslov 						    vlan->flags, br_offloads, extack);
237d75b9e80SVlad Buslov 		break;
238d75b9e80SVlad Buslov 	default:
239d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
240d75b9e80SVlad Buslov 	}
241d75b9e80SVlad Buslov 	return err;
242d75b9e80SVlad Buslov }
243d75b9e80SVlad Buslov 
2443ee6233eSVlad Buslov static int
2453ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_del(struct net_device *dev,
2463ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
2473ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
248d75b9e80SVlad Buslov {
2493ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
250d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
2513ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
252d75b9e80SVlad Buslov 
253ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
254ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2553ee6233eSVlad Buslov 		return 0;
2563ee6233eSVlad Buslov 
2573ee6233eSVlad Buslov 	port_obj_info->handled = true;
258d75b9e80SVlad Buslov 
259d75b9e80SVlad Buslov 	switch (obj->id) {
260d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
261d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2623ee6233eSVlad Buslov 		mlx5_esw_bridge_port_vlan_del(vport_num, esw_owner_vhca_id, vlan->vid, br_offloads);
263d75b9e80SVlad Buslov 		break;
264d75b9e80SVlad Buslov 	default:
265d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
266d75b9e80SVlad Buslov 	}
267d75b9e80SVlad Buslov 	return 0;
268d75b9e80SVlad Buslov }
269d75b9e80SVlad Buslov 
2703ee6233eSVlad Buslov static int
2713ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
2723ee6233eSVlad Buslov 				  struct switchdev_notifier_port_attr_info *port_attr_info,
2733ee6233eSVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads)
274c636a0f0SVlad Buslov {
2753ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_attr_info->info);
2763ee6233eSVlad Buslov 	const struct switchdev_attr *attr = port_attr_info->attr;
2773ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2788343268eSVlad Buslov 	int err = 0;
279c636a0f0SVlad Buslov 
280ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
281ff9b7521SVlad Buslov 							     &esw_owner_vhca_id))
2823ee6233eSVlad Buslov 		return 0;
2833ee6233eSVlad Buslov 
2843ee6233eSVlad Buslov 	port_attr_info->handled = true;
285c636a0f0SVlad Buslov 
286c636a0f0SVlad Buslov 	switch (attr->id) {
287c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
288c636a0f0SVlad Buslov 		if (attr->u.brport_flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) {
289c636a0f0SVlad Buslov 			NL_SET_ERR_MSG_MOD(extack, "Flag is not supported");
290c636a0f0SVlad Buslov 			err = -EINVAL;
291c636a0f0SVlad Buslov 		}
292c636a0f0SVlad Buslov 		break;
293c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
294c636a0f0SVlad Buslov 		break;
295c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
2963ee6233eSVlad Buslov 		err = mlx5_esw_bridge_ageing_time_set(vport_num, esw_owner_vhca_id,
2973ee6233eSVlad Buslov 						      attr->u.ageing_time, br_offloads);
298c636a0f0SVlad Buslov 		break;
299d75b9e80SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
3003ee6233eSVlad Buslov 		err = mlx5_esw_bridge_vlan_filtering_set(vport_num, esw_owner_vhca_id,
3013ee6233eSVlad Buslov 							 attr->u.vlan_filtering, br_offloads);
302d75b9e80SVlad Buslov 		break;
303c5fcac93SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL:
304c5fcac93SVlad Buslov 		err = mlx5_esw_bridge_vlan_proto_set(vport_num,
305c5fcac93SVlad Buslov 						     esw_owner_vhca_id,
306c5fcac93SVlad Buslov 						     attr->u.vlan_protocol,
307c5fcac93SVlad Buslov 						     br_offloads);
308c5fcac93SVlad Buslov 		break;
309*18c2916cSVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
310*18c2916cSVlad Buslov 		err = mlx5_esw_bridge_mcast_set(vport_num, esw_owner_vhca_id,
311*18c2916cSVlad Buslov 						!attr->u.mc_disabled, br_offloads);
312*18c2916cSVlad Buslov 		break;
313c636a0f0SVlad Buslov 	default:
314c636a0f0SVlad Buslov 		err = -EOPNOTSUPP;
315c636a0f0SVlad Buslov 	}
316c636a0f0SVlad Buslov 
317c636a0f0SVlad Buslov 	return err;
318c636a0f0SVlad Buslov }
319c636a0f0SVlad Buslov 
3203ee6233eSVlad Buslov static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
321c636a0f0SVlad Buslov 					  unsigned long event, void *ptr)
322c636a0f0SVlad Buslov {
3233ee6233eSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
3243ee6233eSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
3253ee6233eSVlad Buslov 								    nb_blk);
326c636a0f0SVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
327c636a0f0SVlad Buslov 	int err;
328c636a0f0SVlad Buslov 
329c636a0f0SVlad Buslov 	switch (event) {
330d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_ADD:
3313ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_add(dev, ptr, br_offloads);
332d75b9e80SVlad Buslov 		break;
333d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_DEL:
3343ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_del(dev, ptr, br_offloads);
335d75b9e80SVlad Buslov 		break;
336c636a0f0SVlad Buslov 	case SWITCHDEV_PORT_ATTR_SET:
3373ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
338c636a0f0SVlad Buslov 		break;
339c636a0f0SVlad Buslov 	default:
340c636a0f0SVlad Buslov 		err = 0;
341c636a0f0SVlad Buslov 	}
342c636a0f0SVlad Buslov 
343c636a0f0SVlad Buslov 	return notifier_from_errno(err);
344c636a0f0SVlad Buslov }
345c636a0f0SVlad Buslov 
3467cd6a54aSVlad Buslov static void
3477cd6a54aSVlad Buslov mlx5_esw_bridge_cleanup_switchdev_fdb_work(struct mlx5_bridge_switchdev_fdb_work *fdb_work)
3487cd6a54aSVlad Buslov {
3497cd6a54aSVlad Buslov 	dev_put(fdb_work->dev);
3507cd6a54aSVlad Buslov 	kfree(fdb_work->fdb_info.addr);
3517cd6a54aSVlad Buslov 	kfree(fdb_work);
3527cd6a54aSVlad Buslov }
3537cd6a54aSVlad Buslov 
3547cd6a54aSVlad Buslov static void mlx5_esw_bridge_switchdev_fdb_event_work(struct work_struct *work)
3557cd6a54aSVlad Buslov {
3567cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *fdb_work =
3577cd6a54aSVlad Buslov 		container_of(work, struct mlx5_bridge_switchdev_fdb_work, work);
3587cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info =
3597cd6a54aSVlad Buslov 		&fdb_work->fdb_info;
360c358ea17SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads =
361c358ea17SVlad Buslov 		fdb_work->br_offloads;
3627cd6a54aSVlad Buslov 	struct net_device *dev = fdb_work->dev;
3633ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
3647cd6a54aSVlad Buslov 
3657cd6a54aSVlad Buslov 	rtnl_lock();
3667cd6a54aSVlad Buslov 
367ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
368ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
3697cd6a54aSVlad Buslov 		goto out;
3707cd6a54aSVlad Buslov 
3717cd6a54aSVlad Buslov 	if (fdb_work->add)
3723ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_create(dev, vport_num, esw_owner_vhca_id, br_offloads,
3733ee6233eSVlad Buslov 					   fdb_info);
3747cd6a54aSVlad Buslov 	else
3753ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_remove(dev, vport_num, esw_owner_vhca_id, br_offloads,
3763ee6233eSVlad Buslov 					   fdb_info);
3777cd6a54aSVlad Buslov 
3787cd6a54aSVlad Buslov out:
3797cd6a54aSVlad Buslov 	rtnl_unlock();
3807cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup_switchdev_fdb_work(fdb_work);
3817cd6a54aSVlad Buslov }
3827cd6a54aSVlad Buslov 
3837cd6a54aSVlad Buslov static struct mlx5_bridge_switchdev_fdb_work *
3847cd6a54aSVlad Buslov mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device *dev, bool add,
385c358ea17SVlad Buslov 					struct switchdev_notifier_fdb_info *fdb_info,
386c358ea17SVlad Buslov 					struct mlx5_esw_bridge_offloads *br_offloads)
3877cd6a54aSVlad Buslov {
3887cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
3897cd6a54aSVlad Buslov 	u8 *addr;
3907cd6a54aSVlad Buslov 
3917cd6a54aSVlad Buslov 	work = kzalloc(sizeof(*work), GFP_ATOMIC);
3927cd6a54aSVlad Buslov 	if (!work)
3937cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
3947cd6a54aSVlad Buslov 
3957cd6a54aSVlad Buslov 	INIT_WORK(&work->work, mlx5_esw_bridge_switchdev_fdb_event_work);
3967cd6a54aSVlad Buslov 	memcpy(&work->fdb_info, fdb_info, sizeof(work->fdb_info));
3977cd6a54aSVlad Buslov 
3987cd6a54aSVlad Buslov 	addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
3997cd6a54aSVlad Buslov 	if (!addr) {
4007cd6a54aSVlad Buslov 		kfree(work);
4017cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
4027cd6a54aSVlad Buslov 	}
4037cd6a54aSVlad Buslov 	ether_addr_copy(addr, fdb_info->addr);
4047cd6a54aSVlad Buslov 	work->fdb_info.addr = addr;
4057cd6a54aSVlad Buslov 
4067cd6a54aSVlad Buslov 	dev_hold(dev);
4077cd6a54aSVlad Buslov 	work->dev = dev;
408c358ea17SVlad Buslov 	work->br_offloads = br_offloads;
4097cd6a54aSVlad Buslov 	work->add = add;
4107cd6a54aSVlad Buslov 	return work;
4117cd6a54aSVlad Buslov }
4127cd6a54aSVlad Buslov 
4137cd6a54aSVlad Buslov static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
4147cd6a54aSVlad Buslov 					   unsigned long event, void *ptr)
4157cd6a54aSVlad Buslov {
4167cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
4177cd6a54aSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
4187cd6a54aSVlad Buslov 								    nb);
4197cd6a54aSVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
4207cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info;
4217cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
422ff9b7521SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
4237cd6a54aSVlad Buslov 	struct switchdev_notifier_info *info = ptr;
424ff9b7521SVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
425ff9b7521SVlad Buslov 	struct net_device *upper, *rep;
4267cd6a54aSVlad Buslov 
427c636a0f0SVlad Buslov 	if (event == SWITCHDEV_PORT_ATTR_SET) {
4283ee6233eSVlad Buslov 		int err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
4293ee6233eSVlad Buslov 
430c636a0f0SVlad Buslov 		return notifier_from_errno(err);
431c636a0f0SVlad Buslov 	}
432c636a0f0SVlad Buslov 
4337cd6a54aSVlad Buslov 	upper = netdev_master_upper_dev_get_rcu(dev);
4347cd6a54aSVlad Buslov 	if (!upper)
4357cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4367cd6a54aSVlad Buslov 	if (!netif_is_bridge_master(upper))
4377cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4387cd6a54aSVlad Buslov 
439ff9b7521SVlad Buslov 	rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, &vport_num, &esw_owner_vhca_id);
440ff9b7521SVlad Buslov 	if (!rep)
4413ee6233eSVlad Buslov 		return NOTIFY_DONE;
4423ee6233eSVlad Buslov 
4437cd6a54aSVlad Buslov 	switch (event) {
444ff9b7521SVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
445ff9b7521SVlad Buslov 		fdb_info = container_of(info,
446ff9b7521SVlad Buslov 					struct switchdev_notifier_fdb_info,
447ff9b7521SVlad Buslov 					info);
448ff9b7521SVlad Buslov 		mlx5_esw_bridge_fdb_update_used(dev, vport_num, esw_owner_vhca_id, br_offloads,
449ff9b7521SVlad Buslov 						fdb_info);
450ff9b7521SVlad Buslov 		break;
451c358ea17SVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
452ff9b7521SVlad Buslov 		/* only handle the event on peers */
453ff9b7521SVlad Buslov 		if (mlx5_esw_bridge_is_local(dev, rep, esw))
454c358ea17SVlad Buslov 			break;
455c358ea17SVlad Buslov 		fallthrough;
4567cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
4577cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
4587cd6a54aSVlad Buslov 		fdb_info = container_of(info,
4597cd6a54aSVlad Buslov 					struct switchdev_notifier_fdb_info,
4607cd6a54aSVlad Buslov 					info);
4617cd6a54aSVlad Buslov 
4627cd6a54aSVlad Buslov 		work = mlx5_esw_bridge_init_switchdev_fdb_work(dev,
4637cd6a54aSVlad Buslov 							       event == SWITCHDEV_FDB_ADD_TO_DEVICE,
464c358ea17SVlad Buslov 							       fdb_info,
465c358ea17SVlad Buslov 							       br_offloads);
4667cd6a54aSVlad Buslov 		if (IS_ERR(work)) {
4677cd6a54aSVlad Buslov 			WARN_ONCE(1, "Failed to init switchdev work, err=%ld",
4687cd6a54aSVlad Buslov 				  PTR_ERR(work));
4697cd6a54aSVlad Buslov 			return notifier_from_errno(PTR_ERR(work));
4707cd6a54aSVlad Buslov 		}
4717cd6a54aSVlad Buslov 
4727cd6a54aSVlad Buslov 		queue_work(br_offloads->wq, &work->work);
4737cd6a54aSVlad Buslov 		break;
4747cd6a54aSVlad Buslov 	default:
4757cd6a54aSVlad Buslov 		break;
4767cd6a54aSVlad Buslov 	}
4777cd6a54aSVlad Buslov 	return NOTIFY_DONE;
4787cd6a54aSVlad Buslov }
4797cd6a54aSVlad Buslov 
480c636a0f0SVlad Buslov static void mlx5_esw_bridge_update_work(struct work_struct *work)
481c636a0f0SVlad Buslov {
482c636a0f0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(work,
483c636a0f0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
484c636a0f0SVlad Buslov 								    update_work.work);
485c636a0f0SVlad Buslov 
486c636a0f0SVlad Buslov 	rtnl_lock();
487c636a0f0SVlad Buslov 	mlx5_esw_bridge_update(br_offloads);
488c636a0f0SVlad Buslov 	rtnl_unlock();
489c636a0f0SVlad Buslov 
490c636a0f0SVlad Buslov 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
491c636a0f0SVlad Buslov 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
492c636a0f0SVlad Buslov }
493c636a0f0SVlad Buslov 
49419e9bfa0SVlad Buslov void mlx5e_rep_bridge_init(struct mlx5e_priv *priv)
49519e9bfa0SVlad Buslov {
49619e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
49719e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
49819e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
49919e9bfa0SVlad Buslov 		mdev->priv.eswitch;
50019e9bfa0SVlad Buslov 	int err;
50119e9bfa0SVlad Buslov 
50219e9bfa0SVlad Buslov 	rtnl_lock();
50319e9bfa0SVlad Buslov 	br_offloads = mlx5_esw_bridge_init(esw);
50419e9bfa0SVlad Buslov 	rtnl_unlock();
50519e9bfa0SVlad Buslov 	if (IS_ERR(br_offloads)) {
50619e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to init esw bridge (err=%ld)\n", PTR_ERR(br_offloads));
50719e9bfa0SVlad Buslov 		return;
50819e9bfa0SVlad Buslov 	}
50919e9bfa0SVlad Buslov 
5107cd6a54aSVlad Buslov 	br_offloads->wq = alloc_ordered_workqueue("mlx5_bridge_wq", 0);
5117cd6a54aSVlad Buslov 	if (!br_offloads->wq) {
5127cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to allocate bridge offloads workqueue\n");
5137cd6a54aSVlad Buslov 		goto err_alloc_wq;
5147cd6a54aSVlad Buslov 	}
5157cd6a54aSVlad Buslov 
5167cd6a54aSVlad Buslov 	br_offloads->nb.notifier_call = mlx5_esw_bridge_switchdev_event;
5177cd6a54aSVlad Buslov 	err = register_switchdev_notifier(&br_offloads->nb);
5187cd6a54aSVlad Buslov 	if (err) {
5197cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to register switchdev notifier (err=%d)\n", err);
5207cd6a54aSVlad Buslov 		goto err_register_swdev;
5217cd6a54aSVlad Buslov 	}
5227cd6a54aSVlad Buslov 
523c636a0f0SVlad Buslov 	br_offloads->nb_blk.notifier_call = mlx5_esw_bridge_event_blocking;
524c636a0f0SVlad Buslov 	err = register_switchdev_blocking_notifier(&br_offloads->nb_blk);
525c636a0f0SVlad Buslov 	if (err) {
526c636a0f0SVlad Buslov 		esw_warn(mdev, "Failed to register blocking switchdev notifier (err=%d)\n", err);
527c636a0f0SVlad Buslov 		goto err_register_swdev_blk;
528c636a0f0SVlad Buslov 	}
529c636a0f0SVlad Buslov 
53019e9bfa0SVlad Buslov 	br_offloads->netdev_nb.notifier_call = mlx5_esw_bridge_switchdev_port_event;
531880b5176SRoi Dayan 	err = register_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
53219e9bfa0SVlad Buslov 	if (err) {
53319e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to register bridge offloads netdevice notifier (err=%d)\n",
53419e9bfa0SVlad Buslov 			 err);
5357cd6a54aSVlad Buslov 		goto err_register_netdev;
53619e9bfa0SVlad Buslov 	}
5372266bb1eSShay Drory 	INIT_DELAYED_WORK(&br_offloads->update_work, mlx5_esw_bridge_update_work);
5382266bb1eSShay Drory 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
5392266bb1eSShay Drory 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
5407cd6a54aSVlad Buslov 	return;
5417cd6a54aSVlad Buslov 
5427cd6a54aSVlad Buslov err_register_netdev:
543c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
544c636a0f0SVlad Buslov err_register_swdev_blk:
5457cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5467cd6a54aSVlad Buslov err_register_swdev:
5477cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
5487cd6a54aSVlad Buslov err_alloc_wq:
54904f8c12fSVlad Buslov 	rtnl_lock();
5507cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
55104f8c12fSVlad Buslov 	rtnl_unlock();
55219e9bfa0SVlad Buslov }
55319e9bfa0SVlad Buslov 
55419e9bfa0SVlad Buslov void mlx5e_rep_bridge_cleanup(struct mlx5e_priv *priv)
55519e9bfa0SVlad Buslov {
55619e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
55719e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
55819e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
55919e9bfa0SVlad Buslov 		mdev->priv.eswitch;
56019e9bfa0SVlad Buslov 
56119e9bfa0SVlad Buslov 	br_offloads = esw->br_offloads;
56219e9bfa0SVlad Buslov 	if (!br_offloads)
56319e9bfa0SVlad Buslov 		return;
56419e9bfa0SVlad Buslov 
5652266bb1eSShay Drory 	cancel_delayed_work_sync(&br_offloads->update_work);
566880b5176SRoi Dayan 	unregister_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
567c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
5687cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5697cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
57019e9bfa0SVlad Buslov 	rtnl_lock();
57119e9bfa0SVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
57219e9bfa0SVlad Buslov 	rtnl_unlock();
57319e9bfa0SVlad Buslov }
574