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;
223*70f0302bSVlad Buslov 	const struct switchdev_obj_port_mdb *mdb;
2243ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2253ee6233eSVlad Buslov 	int err;
226d75b9e80SVlad Buslov 
227ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
228ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2293ee6233eSVlad Buslov 		return 0;
2303ee6233eSVlad Buslov 
2313ee6233eSVlad Buslov 	port_obj_info->handled = true;
232d75b9e80SVlad Buslov 
233d75b9e80SVlad Buslov 	switch (obj->id) {
234d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
235d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2363ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_vlan_add(vport_num, esw_owner_vhca_id, vlan->vid,
2373ee6233eSVlad Buslov 						    vlan->flags, br_offloads, extack);
238d75b9e80SVlad Buslov 		break;
239*70f0302bSVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_MDB:
240*70f0302bSVlad Buslov 		mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
241*70f0302bSVlad Buslov 		err = mlx5_esw_bridge_port_mdb_add(vport_num, esw_owner_vhca_id, mdb->addr,
242*70f0302bSVlad Buslov 						   mdb->vid, br_offloads, extack);
243*70f0302bSVlad Buslov 		break;
244d75b9e80SVlad Buslov 	default:
245d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
246d75b9e80SVlad Buslov 	}
247d75b9e80SVlad Buslov 	return err;
248d75b9e80SVlad Buslov }
249d75b9e80SVlad Buslov 
2503ee6233eSVlad Buslov static int
2513ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_del(struct net_device *dev,
2523ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
2533ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
254d75b9e80SVlad Buslov {
2553ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
256d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
257*70f0302bSVlad Buslov 	const struct switchdev_obj_port_mdb *mdb;
2583ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
259d75b9e80SVlad Buslov 
260ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
261ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2623ee6233eSVlad Buslov 		return 0;
2633ee6233eSVlad Buslov 
2643ee6233eSVlad Buslov 	port_obj_info->handled = true;
265d75b9e80SVlad Buslov 
266d75b9e80SVlad Buslov 	switch (obj->id) {
267d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
268d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2693ee6233eSVlad Buslov 		mlx5_esw_bridge_port_vlan_del(vport_num, esw_owner_vhca_id, vlan->vid, br_offloads);
270d75b9e80SVlad Buslov 		break;
271*70f0302bSVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_MDB:
272*70f0302bSVlad Buslov 		mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
273*70f0302bSVlad Buslov 		mlx5_esw_bridge_port_mdb_del(vport_num, esw_owner_vhca_id, mdb->addr, mdb->vid,
274*70f0302bSVlad Buslov 					     br_offloads);
275*70f0302bSVlad Buslov 		break;
276d75b9e80SVlad Buslov 	default:
277d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
278d75b9e80SVlad Buslov 	}
279d75b9e80SVlad Buslov 	return 0;
280d75b9e80SVlad Buslov }
281d75b9e80SVlad Buslov 
2823ee6233eSVlad Buslov static int
2833ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
2843ee6233eSVlad Buslov 				  struct switchdev_notifier_port_attr_info *port_attr_info,
2853ee6233eSVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads)
286c636a0f0SVlad Buslov {
2873ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_attr_info->info);
2883ee6233eSVlad Buslov 	const struct switchdev_attr *attr = port_attr_info->attr;
2893ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2908343268eSVlad Buslov 	int err = 0;
291c636a0f0SVlad Buslov 
292ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
293ff9b7521SVlad Buslov 							     &esw_owner_vhca_id))
2943ee6233eSVlad Buslov 		return 0;
2953ee6233eSVlad Buslov 
2963ee6233eSVlad Buslov 	port_attr_info->handled = true;
297c636a0f0SVlad Buslov 
298c636a0f0SVlad Buslov 	switch (attr->id) {
299c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
300c636a0f0SVlad Buslov 		if (attr->u.brport_flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) {
301c636a0f0SVlad Buslov 			NL_SET_ERR_MSG_MOD(extack, "Flag is not supported");
302c636a0f0SVlad Buslov 			err = -EINVAL;
303c636a0f0SVlad Buslov 		}
304c636a0f0SVlad Buslov 		break;
305c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
306c636a0f0SVlad Buslov 		break;
307c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
3083ee6233eSVlad Buslov 		err = mlx5_esw_bridge_ageing_time_set(vport_num, esw_owner_vhca_id,
3093ee6233eSVlad Buslov 						      attr->u.ageing_time, br_offloads);
310c636a0f0SVlad Buslov 		break;
311d75b9e80SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
3123ee6233eSVlad Buslov 		err = mlx5_esw_bridge_vlan_filtering_set(vport_num, esw_owner_vhca_id,
3133ee6233eSVlad Buslov 							 attr->u.vlan_filtering, br_offloads);
314d75b9e80SVlad Buslov 		break;
315c5fcac93SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL:
316c5fcac93SVlad Buslov 		err = mlx5_esw_bridge_vlan_proto_set(vport_num,
317c5fcac93SVlad Buslov 						     esw_owner_vhca_id,
318c5fcac93SVlad Buslov 						     attr->u.vlan_protocol,
319c5fcac93SVlad Buslov 						     br_offloads);
320c5fcac93SVlad Buslov 		break;
32118c2916cSVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
32218c2916cSVlad Buslov 		err = mlx5_esw_bridge_mcast_set(vport_num, esw_owner_vhca_id,
32318c2916cSVlad Buslov 						!attr->u.mc_disabled, br_offloads);
32418c2916cSVlad Buslov 		break;
325c636a0f0SVlad Buslov 	default:
326c636a0f0SVlad Buslov 		err = -EOPNOTSUPP;
327c636a0f0SVlad Buslov 	}
328c636a0f0SVlad Buslov 
329c636a0f0SVlad Buslov 	return err;
330c636a0f0SVlad Buslov }
331c636a0f0SVlad Buslov 
3323ee6233eSVlad Buslov static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
333c636a0f0SVlad Buslov 					  unsigned long event, void *ptr)
334c636a0f0SVlad Buslov {
3353ee6233eSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
3363ee6233eSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
3373ee6233eSVlad Buslov 								    nb_blk);
338c636a0f0SVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
339c636a0f0SVlad Buslov 	int err;
340c636a0f0SVlad Buslov 
341c636a0f0SVlad Buslov 	switch (event) {
342d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_ADD:
3433ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_add(dev, ptr, br_offloads);
344d75b9e80SVlad Buslov 		break;
345d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_DEL:
3463ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_del(dev, ptr, br_offloads);
347d75b9e80SVlad Buslov 		break;
348c636a0f0SVlad Buslov 	case SWITCHDEV_PORT_ATTR_SET:
3493ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
350c636a0f0SVlad Buslov 		break;
351c636a0f0SVlad Buslov 	default:
352c636a0f0SVlad Buslov 		err = 0;
353c636a0f0SVlad Buslov 	}
354c636a0f0SVlad Buslov 
355c636a0f0SVlad Buslov 	return notifier_from_errno(err);
356c636a0f0SVlad Buslov }
357c636a0f0SVlad Buslov 
3587cd6a54aSVlad Buslov static void
3597cd6a54aSVlad Buslov mlx5_esw_bridge_cleanup_switchdev_fdb_work(struct mlx5_bridge_switchdev_fdb_work *fdb_work)
3607cd6a54aSVlad Buslov {
3617cd6a54aSVlad Buslov 	dev_put(fdb_work->dev);
3627cd6a54aSVlad Buslov 	kfree(fdb_work->fdb_info.addr);
3637cd6a54aSVlad Buslov 	kfree(fdb_work);
3647cd6a54aSVlad Buslov }
3657cd6a54aSVlad Buslov 
3667cd6a54aSVlad Buslov static void mlx5_esw_bridge_switchdev_fdb_event_work(struct work_struct *work)
3677cd6a54aSVlad Buslov {
3687cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *fdb_work =
3697cd6a54aSVlad Buslov 		container_of(work, struct mlx5_bridge_switchdev_fdb_work, work);
3707cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info =
3717cd6a54aSVlad Buslov 		&fdb_work->fdb_info;
372c358ea17SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads =
373c358ea17SVlad Buslov 		fdb_work->br_offloads;
3747cd6a54aSVlad Buslov 	struct net_device *dev = fdb_work->dev;
3753ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
3767cd6a54aSVlad Buslov 
3777cd6a54aSVlad Buslov 	rtnl_lock();
3787cd6a54aSVlad Buslov 
379ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
380ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
3817cd6a54aSVlad Buslov 		goto out;
3827cd6a54aSVlad Buslov 
3837cd6a54aSVlad Buslov 	if (fdb_work->add)
3843ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_create(dev, vport_num, esw_owner_vhca_id, br_offloads,
3853ee6233eSVlad Buslov 					   fdb_info);
3867cd6a54aSVlad Buslov 	else
3873ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_remove(dev, vport_num, esw_owner_vhca_id, br_offloads,
3883ee6233eSVlad Buslov 					   fdb_info);
3897cd6a54aSVlad Buslov 
3907cd6a54aSVlad Buslov out:
3917cd6a54aSVlad Buslov 	rtnl_unlock();
3927cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup_switchdev_fdb_work(fdb_work);
3937cd6a54aSVlad Buslov }
3947cd6a54aSVlad Buslov 
3957cd6a54aSVlad Buslov static struct mlx5_bridge_switchdev_fdb_work *
3967cd6a54aSVlad Buslov mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device *dev, bool add,
397c358ea17SVlad Buslov 					struct switchdev_notifier_fdb_info *fdb_info,
398c358ea17SVlad Buslov 					struct mlx5_esw_bridge_offloads *br_offloads)
3997cd6a54aSVlad Buslov {
4007cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
4017cd6a54aSVlad Buslov 	u8 *addr;
4027cd6a54aSVlad Buslov 
4037cd6a54aSVlad Buslov 	work = kzalloc(sizeof(*work), GFP_ATOMIC);
4047cd6a54aSVlad Buslov 	if (!work)
4057cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
4067cd6a54aSVlad Buslov 
4077cd6a54aSVlad Buslov 	INIT_WORK(&work->work, mlx5_esw_bridge_switchdev_fdb_event_work);
4087cd6a54aSVlad Buslov 	memcpy(&work->fdb_info, fdb_info, sizeof(work->fdb_info));
4097cd6a54aSVlad Buslov 
4107cd6a54aSVlad Buslov 	addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
4117cd6a54aSVlad Buslov 	if (!addr) {
4127cd6a54aSVlad Buslov 		kfree(work);
4137cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
4147cd6a54aSVlad Buslov 	}
4157cd6a54aSVlad Buslov 	ether_addr_copy(addr, fdb_info->addr);
4167cd6a54aSVlad Buslov 	work->fdb_info.addr = addr;
4177cd6a54aSVlad Buslov 
4187cd6a54aSVlad Buslov 	dev_hold(dev);
4197cd6a54aSVlad Buslov 	work->dev = dev;
420c358ea17SVlad Buslov 	work->br_offloads = br_offloads;
4217cd6a54aSVlad Buslov 	work->add = add;
4227cd6a54aSVlad Buslov 	return work;
4237cd6a54aSVlad Buslov }
4247cd6a54aSVlad Buslov 
4257cd6a54aSVlad Buslov static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
4267cd6a54aSVlad Buslov 					   unsigned long event, void *ptr)
4277cd6a54aSVlad Buslov {
4287cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
4297cd6a54aSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
4307cd6a54aSVlad Buslov 								    nb);
4317cd6a54aSVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
4327cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info;
4337cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
434ff9b7521SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
4357cd6a54aSVlad Buslov 	struct switchdev_notifier_info *info = ptr;
436ff9b7521SVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
437ff9b7521SVlad Buslov 	struct net_device *upper, *rep;
4387cd6a54aSVlad Buslov 
439c636a0f0SVlad Buslov 	if (event == SWITCHDEV_PORT_ATTR_SET) {
4403ee6233eSVlad Buslov 		int err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
4413ee6233eSVlad Buslov 
442c636a0f0SVlad Buslov 		return notifier_from_errno(err);
443c636a0f0SVlad Buslov 	}
444c636a0f0SVlad Buslov 
4457cd6a54aSVlad Buslov 	upper = netdev_master_upper_dev_get_rcu(dev);
4467cd6a54aSVlad Buslov 	if (!upper)
4477cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4487cd6a54aSVlad Buslov 	if (!netif_is_bridge_master(upper))
4497cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4507cd6a54aSVlad Buslov 
451ff9b7521SVlad Buslov 	rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, &vport_num, &esw_owner_vhca_id);
452ff9b7521SVlad Buslov 	if (!rep)
4533ee6233eSVlad Buslov 		return NOTIFY_DONE;
4543ee6233eSVlad Buslov 
4557cd6a54aSVlad Buslov 	switch (event) {
456ff9b7521SVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
457ff9b7521SVlad Buslov 		fdb_info = container_of(info,
458ff9b7521SVlad Buslov 					struct switchdev_notifier_fdb_info,
459ff9b7521SVlad Buslov 					info);
460ff9b7521SVlad Buslov 		mlx5_esw_bridge_fdb_update_used(dev, vport_num, esw_owner_vhca_id, br_offloads,
461ff9b7521SVlad Buslov 						fdb_info);
462ff9b7521SVlad Buslov 		break;
463c358ea17SVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
464ff9b7521SVlad Buslov 		/* only handle the event on peers */
465ff9b7521SVlad Buslov 		if (mlx5_esw_bridge_is_local(dev, rep, esw))
466c358ea17SVlad Buslov 			break;
467c358ea17SVlad Buslov 		fallthrough;
4687cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
4697cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
4707cd6a54aSVlad Buslov 		fdb_info = container_of(info,
4717cd6a54aSVlad Buslov 					struct switchdev_notifier_fdb_info,
4727cd6a54aSVlad Buslov 					info);
4737cd6a54aSVlad Buslov 
4747cd6a54aSVlad Buslov 		work = mlx5_esw_bridge_init_switchdev_fdb_work(dev,
4757cd6a54aSVlad Buslov 							       event == SWITCHDEV_FDB_ADD_TO_DEVICE,
476c358ea17SVlad Buslov 							       fdb_info,
477c358ea17SVlad Buslov 							       br_offloads);
4787cd6a54aSVlad Buslov 		if (IS_ERR(work)) {
4797cd6a54aSVlad Buslov 			WARN_ONCE(1, "Failed to init switchdev work, err=%ld",
4807cd6a54aSVlad Buslov 				  PTR_ERR(work));
4817cd6a54aSVlad Buslov 			return notifier_from_errno(PTR_ERR(work));
4827cd6a54aSVlad Buslov 		}
4837cd6a54aSVlad Buslov 
4847cd6a54aSVlad Buslov 		queue_work(br_offloads->wq, &work->work);
4857cd6a54aSVlad Buslov 		break;
4867cd6a54aSVlad Buslov 	default:
4877cd6a54aSVlad Buslov 		break;
4887cd6a54aSVlad Buslov 	}
4897cd6a54aSVlad Buslov 	return NOTIFY_DONE;
4907cd6a54aSVlad Buslov }
4917cd6a54aSVlad Buslov 
492c636a0f0SVlad Buslov static void mlx5_esw_bridge_update_work(struct work_struct *work)
493c636a0f0SVlad Buslov {
494c636a0f0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(work,
495c636a0f0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
496c636a0f0SVlad Buslov 								    update_work.work);
497c636a0f0SVlad Buslov 
498c636a0f0SVlad Buslov 	rtnl_lock();
499c636a0f0SVlad Buslov 	mlx5_esw_bridge_update(br_offloads);
500c636a0f0SVlad Buslov 	rtnl_unlock();
501c636a0f0SVlad Buslov 
502c636a0f0SVlad Buslov 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
503c636a0f0SVlad Buslov 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
504c636a0f0SVlad Buslov }
505c636a0f0SVlad Buslov 
50619e9bfa0SVlad Buslov void mlx5e_rep_bridge_init(struct mlx5e_priv *priv)
50719e9bfa0SVlad Buslov {
50819e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
50919e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
51019e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
51119e9bfa0SVlad Buslov 		mdev->priv.eswitch;
51219e9bfa0SVlad Buslov 	int err;
51319e9bfa0SVlad Buslov 
51419e9bfa0SVlad Buslov 	rtnl_lock();
51519e9bfa0SVlad Buslov 	br_offloads = mlx5_esw_bridge_init(esw);
51619e9bfa0SVlad Buslov 	rtnl_unlock();
51719e9bfa0SVlad Buslov 	if (IS_ERR(br_offloads)) {
51819e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to init esw bridge (err=%ld)\n", PTR_ERR(br_offloads));
51919e9bfa0SVlad Buslov 		return;
52019e9bfa0SVlad Buslov 	}
52119e9bfa0SVlad Buslov 
5227cd6a54aSVlad Buslov 	br_offloads->wq = alloc_ordered_workqueue("mlx5_bridge_wq", 0);
5237cd6a54aSVlad Buslov 	if (!br_offloads->wq) {
5247cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to allocate bridge offloads workqueue\n");
5257cd6a54aSVlad Buslov 		goto err_alloc_wq;
5267cd6a54aSVlad Buslov 	}
5277cd6a54aSVlad Buslov 
5287cd6a54aSVlad Buslov 	br_offloads->nb.notifier_call = mlx5_esw_bridge_switchdev_event;
5297cd6a54aSVlad Buslov 	err = register_switchdev_notifier(&br_offloads->nb);
5307cd6a54aSVlad Buslov 	if (err) {
5317cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to register switchdev notifier (err=%d)\n", err);
5327cd6a54aSVlad Buslov 		goto err_register_swdev;
5337cd6a54aSVlad Buslov 	}
5347cd6a54aSVlad Buslov 
535c636a0f0SVlad Buslov 	br_offloads->nb_blk.notifier_call = mlx5_esw_bridge_event_blocking;
536c636a0f0SVlad Buslov 	err = register_switchdev_blocking_notifier(&br_offloads->nb_blk);
537c636a0f0SVlad Buslov 	if (err) {
538c636a0f0SVlad Buslov 		esw_warn(mdev, "Failed to register blocking switchdev notifier (err=%d)\n", err);
539c636a0f0SVlad Buslov 		goto err_register_swdev_blk;
540c636a0f0SVlad Buslov 	}
541c636a0f0SVlad Buslov 
54219e9bfa0SVlad Buslov 	br_offloads->netdev_nb.notifier_call = mlx5_esw_bridge_switchdev_port_event;
543880b5176SRoi Dayan 	err = register_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
54419e9bfa0SVlad Buslov 	if (err) {
54519e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to register bridge offloads netdevice notifier (err=%d)\n",
54619e9bfa0SVlad Buslov 			 err);
5477cd6a54aSVlad Buslov 		goto err_register_netdev;
54819e9bfa0SVlad Buslov 	}
5492266bb1eSShay Drory 	INIT_DELAYED_WORK(&br_offloads->update_work, mlx5_esw_bridge_update_work);
5502266bb1eSShay Drory 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
5512266bb1eSShay Drory 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
5527cd6a54aSVlad Buslov 	return;
5537cd6a54aSVlad Buslov 
5547cd6a54aSVlad Buslov err_register_netdev:
555c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
556c636a0f0SVlad Buslov err_register_swdev_blk:
5577cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5587cd6a54aSVlad Buslov err_register_swdev:
5597cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
5607cd6a54aSVlad Buslov err_alloc_wq:
56104f8c12fSVlad Buslov 	rtnl_lock();
5627cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
56304f8c12fSVlad Buslov 	rtnl_unlock();
56419e9bfa0SVlad Buslov }
56519e9bfa0SVlad Buslov 
56619e9bfa0SVlad Buslov void mlx5e_rep_bridge_cleanup(struct mlx5e_priv *priv)
56719e9bfa0SVlad Buslov {
56819e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
56919e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
57019e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
57119e9bfa0SVlad Buslov 		mdev->priv.eswitch;
57219e9bfa0SVlad Buslov 
57319e9bfa0SVlad Buslov 	br_offloads = esw->br_offloads;
57419e9bfa0SVlad Buslov 	if (!br_offloads)
57519e9bfa0SVlad Buslov 		return;
57619e9bfa0SVlad Buslov 
5772266bb1eSShay Drory 	cancel_delayed_work_sync(&br_offloads->update_work);
578880b5176SRoi Dayan 	unregister_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
579c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
5807cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5817cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
58219e9bfa0SVlad Buslov 	rtnl_lock();
58319e9bfa0SVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
58419e9bfa0SVlad Buslov 	rtnl_unlock();
58519e9bfa0SVlad Buslov }
586