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 
mlx5_esw_bridge_dev_same_esw(struct net_device * dev,struct mlx5_eswitch * esw)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 
mlx5_esw_bridge_dev_same_hw(struct net_device * dev,struct mlx5_eswitch * esw)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 *
mlx5_esw_bridge_lag_rep_get(struct net_device * dev,struct mlx5_eswitch * esw)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 *
mlx5_esw_bridge_rep_vport_num_vhca_id_get(struct net_device * dev,struct mlx5_eswitch * esw,u16 * vport_num,u16 * esw_owner_vhca_id)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);
80b56fb19cSRoi Dayan 
81b56fb19cSRoi Dayan 	if (!priv->mdev->priv.eswitch->br_offloads)
82b56fb19cSRoi Dayan 		return NULL;
83b56fb19cSRoi Dayan 
843ee6233eSVlad Buslov 	rpriv = priv->ppriv;
853ee6233eSVlad Buslov 	*vport_num = rpriv->rep->vport;
863ee6233eSVlad Buslov 	*esw_owner_vhca_id = MLX5_CAP_GEN(priv->mdev, vhca_id);
87ff9b7521SVlad Buslov 	return dev;
883ee6233eSVlad Buslov }
893ee6233eSVlad Buslov 
90ff9b7521SVlad Buslov static struct net_device *
mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device * dev,struct mlx5_eswitch * esw,u16 * vport_num,u16 * esw_owner_vhca_id)913ee6233eSVlad Buslov mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct mlx5_eswitch *esw,
923ee6233eSVlad Buslov 						u16 *vport_num, u16 *esw_owner_vhca_id)
933ee6233eSVlad Buslov {
943ee6233eSVlad Buslov 	struct net_device *lower_dev;
953ee6233eSVlad Buslov 	struct list_head *iter;
963ee6233eSVlad Buslov 
97ff9b7521SVlad Buslov 	if (netif_is_lag_master(dev) || mlx5e_eswitch_rep(dev))
98ff9b7521SVlad Buslov 		return mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, vport_num,
993ee6233eSVlad Buslov 								 esw_owner_vhca_id);
1003ee6233eSVlad Buslov 
1013ee6233eSVlad Buslov 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
102ff9b7521SVlad Buslov 		struct net_device *rep;
1033ee6233eSVlad Buslov 
1043ee6233eSVlad Buslov 		if (netif_is_bridge_master(lower_dev))
1053ee6233eSVlad Buslov 			continue;
1063ee6233eSVlad Buslov 
107ff9b7521SVlad Buslov 		rep = mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(lower_dev, esw, vport_num,
1083ee6233eSVlad Buslov 								      esw_owner_vhca_id);
109ff9b7521SVlad Buslov 		if (rep)
110ff9b7521SVlad Buslov 			return rep;
1113ee6233eSVlad Buslov 	}
1123ee6233eSVlad Buslov 
113ff9b7521SVlad Buslov 	return NULL;
114ff9b7521SVlad Buslov }
115ff9b7521SVlad Buslov 
mlx5_esw_bridge_is_local(struct net_device * dev,struct net_device * rep,struct mlx5_eswitch * esw)116ff9b7521SVlad Buslov static bool mlx5_esw_bridge_is_local(struct net_device *dev, struct net_device *rep,
117ff9b7521SVlad Buslov 				     struct mlx5_eswitch *esw)
118ff9b7521SVlad Buslov {
119ff9b7521SVlad Buslov 	struct mlx5_core_dev *mdev;
120ff9b7521SVlad Buslov 	struct mlx5e_priv *priv;
121ff9b7521SVlad Buslov 
122ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_dev_same_esw(rep, esw))
123ff9b7521SVlad Buslov 		return false;
124ff9b7521SVlad Buslov 
125ff9b7521SVlad Buslov 	priv = netdev_priv(rep);
126ff9b7521SVlad Buslov 	mdev = priv->mdev;
127ff9b7521SVlad Buslov 	if (netif_is_lag_master(dev))
128ff9b7521SVlad Buslov 		return mlx5_lag_is_shared_fdb(mdev) && mlx5_lag_is_master(mdev);
129ff9b7521SVlad Buslov 	return true;
1303ee6233eSVlad Buslov }
1313ee6233eSVlad Buslov 
mlx5_esw_bridge_port_changeupper(struct notifier_block * nb,void * ptr)13219e9bfa0SVlad Buslov static int mlx5_esw_bridge_port_changeupper(struct notifier_block *nb, void *ptr)
13319e9bfa0SVlad Buslov {
13419e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
13519e9bfa0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
13619e9bfa0SVlad Buslov 								    netdev_nb);
13719e9bfa0SVlad Buslov 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
13819e9bfa0SVlad Buslov 	struct netdev_notifier_changeupper_info *info = ptr;
139ff9b7521SVlad Buslov 	struct net_device *upper = info->upper_dev, *rep;
140c358ea17SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
1413ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
14219e9bfa0SVlad Buslov 	struct netlink_ext_ack *extack;
1438343268eSVlad Buslov 	int err = 0;
14419e9bfa0SVlad Buslov 
14519e9bfa0SVlad Buslov 	if (!netif_is_bridge_master(upper))
14619e9bfa0SVlad Buslov 		return 0;
14719e9bfa0SVlad Buslov 
148ff9b7521SVlad Buslov 	rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, &vport_num, &esw_owner_vhca_id);
149ff9b7521SVlad Buslov 	if (!rep)
15019e9bfa0SVlad Buslov 		return 0;
15119e9bfa0SVlad Buslov 
15219e9bfa0SVlad Buslov 	extack = netdev_notifier_info_to_extack(&info->info);
15319e9bfa0SVlad Buslov 
154ff9b7521SVlad Buslov 	if (mlx5_esw_bridge_is_local(dev, rep, esw))
155c358ea17SVlad Buslov 		err = info->linking ?
156ade19f0dSVlad Buslov 			mlx5_esw_bridge_vport_link(upper, vport_num, esw_owner_vhca_id,
157c358ea17SVlad Buslov 						   br_offloads, extack) :
158ade19f0dSVlad Buslov 			mlx5_esw_bridge_vport_unlink(upper, vport_num, esw_owner_vhca_id,
159c358ea17SVlad Buslov 						     br_offloads, extack);
160ff9b7521SVlad Buslov 	else if (mlx5_esw_bridge_dev_same_hw(rep, esw))
161c358ea17SVlad Buslov 		err = info->linking ?
162ade19f0dSVlad Buslov 			mlx5_esw_bridge_vport_peer_link(upper, vport_num, esw_owner_vhca_id,
163c358ea17SVlad Buslov 							br_offloads, extack) :
164ade19f0dSVlad Buslov 			mlx5_esw_bridge_vport_peer_unlink(upper, vport_num, esw_owner_vhca_id,
165c358ea17SVlad Buslov 							  br_offloads, extack);
166c358ea17SVlad Buslov 
167c358ea17SVlad Buslov 	return err;
16819e9bfa0SVlad Buslov }
16919e9bfa0SVlad Buslov 
17015f8f168SVlad Buslov static int
mlx5_esw_bridge_changeupper_validate_netdev(void * ptr)17115f8f168SVlad Buslov mlx5_esw_bridge_changeupper_validate_netdev(void *ptr)
17215f8f168SVlad Buslov {
17315f8f168SVlad Buslov 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
17415f8f168SVlad Buslov 	struct netdev_notifier_changeupper_info *info = ptr;
17515f8f168SVlad Buslov 	struct net_device *upper = info->upper_dev;
17615f8f168SVlad Buslov 	struct net_device *lower;
17715f8f168SVlad Buslov 	struct list_head *iter;
17815f8f168SVlad Buslov 
17915f8f168SVlad Buslov 	if (!netif_is_bridge_master(upper) || !netif_is_lag_master(dev))
18015f8f168SVlad Buslov 		return 0;
18115f8f168SVlad Buslov 
18215f8f168SVlad Buslov 	netdev_for_each_lower_dev(dev, lower, iter) {
18315f8f168SVlad Buslov 		struct mlx5_core_dev *mdev;
18415f8f168SVlad Buslov 		struct mlx5e_priv *priv;
18515f8f168SVlad Buslov 
18615f8f168SVlad Buslov 		if (!mlx5e_eswitch_rep(lower))
18715f8f168SVlad Buslov 			continue;
18815f8f168SVlad Buslov 
18915f8f168SVlad Buslov 		priv = netdev_priv(lower);
19015f8f168SVlad Buslov 		mdev = priv->mdev;
19115f8f168SVlad Buslov 		if (!mlx5_lag_is_active(mdev))
19215f8f168SVlad Buslov 			return -EAGAIN;
19315f8f168SVlad Buslov 		if (!mlx5_lag_is_shared_fdb(mdev))
19415f8f168SVlad Buslov 			return -EOPNOTSUPP;
19515f8f168SVlad Buslov 	}
19615f8f168SVlad Buslov 
19715f8f168SVlad Buslov 	return 0;
19815f8f168SVlad Buslov }
19915f8f168SVlad Buslov 
mlx5_esw_bridge_switchdev_port_event(struct notifier_block * nb,unsigned long event,void * ptr)20019e9bfa0SVlad Buslov static int mlx5_esw_bridge_switchdev_port_event(struct notifier_block *nb,
20119e9bfa0SVlad Buslov 						unsigned long event, void *ptr)
20219e9bfa0SVlad Buslov {
20319e9bfa0SVlad Buslov 	int err = 0;
20419e9bfa0SVlad Buslov 
20519e9bfa0SVlad Buslov 	switch (event) {
20619e9bfa0SVlad Buslov 	case NETDEV_PRECHANGEUPPER:
20715f8f168SVlad Buslov 		err = mlx5_esw_bridge_changeupper_validate_netdev(ptr);
20819e9bfa0SVlad Buslov 		break;
20919e9bfa0SVlad Buslov 
21019e9bfa0SVlad Buslov 	case NETDEV_CHANGEUPPER:
21119e9bfa0SVlad Buslov 		err = mlx5_esw_bridge_port_changeupper(nb, ptr);
21219e9bfa0SVlad Buslov 		break;
21319e9bfa0SVlad Buslov 	}
21419e9bfa0SVlad Buslov 
21519e9bfa0SVlad Buslov 	return notifier_from_errno(err);
21619e9bfa0SVlad Buslov }
21719e9bfa0SVlad Buslov 
2183ee6233eSVlad Buslov static int
mlx5_esw_bridge_port_obj_add(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,struct mlx5_esw_bridge_offloads * br_offloads)2193ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_add(struct net_device *dev,
2203ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
2213ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
222d75b9e80SVlad Buslov {
2233ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
2243ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
225d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
22670f0302bSVlad Buslov 	const struct switchdev_obj_port_mdb *mdb;
2273ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2283ee6233eSVlad Buslov 	int err;
229d75b9e80SVlad Buslov 
230ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
231ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2323ee6233eSVlad Buslov 		return 0;
2333ee6233eSVlad Buslov 
2343ee6233eSVlad Buslov 	port_obj_info->handled = true;
235d75b9e80SVlad Buslov 
236d75b9e80SVlad Buslov 	switch (obj->id) {
237d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
238d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2393ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_vlan_add(vport_num, esw_owner_vhca_id, vlan->vid,
2403ee6233eSVlad Buslov 						    vlan->flags, br_offloads, extack);
241d75b9e80SVlad Buslov 		break;
24270f0302bSVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_MDB:
24370f0302bSVlad Buslov 		mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
24455f3e740SVlad Buslov 		err = mlx5_esw_bridge_port_mdb_add(dev, vport_num, esw_owner_vhca_id, mdb->addr,
24570f0302bSVlad Buslov 						   mdb->vid, br_offloads, extack);
24670f0302bSVlad Buslov 		break;
247d75b9e80SVlad Buslov 	default:
248d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
249d75b9e80SVlad Buslov 	}
250d75b9e80SVlad Buslov 	return err;
251d75b9e80SVlad Buslov }
252d75b9e80SVlad Buslov 
2533ee6233eSVlad Buslov static int
mlx5_esw_bridge_port_obj_del(struct net_device * dev,struct switchdev_notifier_port_obj_info * port_obj_info,struct mlx5_esw_bridge_offloads * br_offloads)2543ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_del(struct net_device *dev,
2553ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
2563ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
257d75b9e80SVlad Buslov {
2583ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
259d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
26070f0302bSVlad Buslov 	const struct switchdev_obj_port_mdb *mdb;
2613ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
262d75b9e80SVlad Buslov 
263ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
264ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
2653ee6233eSVlad Buslov 		return 0;
2663ee6233eSVlad Buslov 
2673ee6233eSVlad Buslov 	port_obj_info->handled = true;
268d75b9e80SVlad Buslov 
269d75b9e80SVlad Buslov 	switch (obj->id) {
270d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
271d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
2723ee6233eSVlad Buslov 		mlx5_esw_bridge_port_vlan_del(vport_num, esw_owner_vhca_id, vlan->vid, br_offloads);
273d75b9e80SVlad Buslov 		break;
27470f0302bSVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_MDB:
27570f0302bSVlad Buslov 		mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
27655f3e740SVlad Buslov 		mlx5_esw_bridge_port_mdb_del(dev, vport_num, esw_owner_vhca_id, mdb->addr, mdb->vid,
27770f0302bSVlad Buslov 					     br_offloads);
27870f0302bSVlad Buslov 		break;
279d75b9e80SVlad Buslov 	default:
280d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
281d75b9e80SVlad Buslov 	}
282d75b9e80SVlad Buslov 	return 0;
283d75b9e80SVlad Buslov }
284d75b9e80SVlad Buslov 
2853ee6233eSVlad Buslov static int
mlx5_esw_bridge_port_obj_attr_set(struct net_device * dev,struct switchdev_notifier_port_attr_info * port_attr_info,struct mlx5_esw_bridge_offloads * br_offloads)2863ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
2873ee6233eSVlad Buslov 				  struct switchdev_notifier_port_attr_info *port_attr_info,
2883ee6233eSVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads)
289c636a0f0SVlad Buslov {
2903ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_attr_info->info);
2913ee6233eSVlad Buslov 	const struct switchdev_attr *attr = port_attr_info->attr;
2923ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2938343268eSVlad Buslov 	int err = 0;
294c636a0f0SVlad Buslov 
295ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
296ff9b7521SVlad Buslov 							     &esw_owner_vhca_id))
2973ee6233eSVlad Buslov 		return 0;
2983ee6233eSVlad Buslov 
2993ee6233eSVlad Buslov 	port_attr_info->handled = true;
300c636a0f0SVlad Buslov 
301c636a0f0SVlad Buslov 	switch (attr->id) {
302c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
303c636a0f0SVlad Buslov 		if (attr->u.brport_flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) {
304c636a0f0SVlad Buslov 			NL_SET_ERR_MSG_MOD(extack, "Flag is not supported");
305c636a0f0SVlad Buslov 			err = -EINVAL;
306c636a0f0SVlad Buslov 		}
307c636a0f0SVlad Buslov 		break;
308c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
309c636a0f0SVlad Buslov 		break;
310c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
3113ee6233eSVlad Buslov 		err = mlx5_esw_bridge_ageing_time_set(vport_num, esw_owner_vhca_id,
3123ee6233eSVlad Buslov 						      attr->u.ageing_time, br_offloads);
313c636a0f0SVlad Buslov 		break;
314d75b9e80SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
3153ee6233eSVlad Buslov 		err = mlx5_esw_bridge_vlan_filtering_set(vport_num, esw_owner_vhca_id,
3163ee6233eSVlad Buslov 							 attr->u.vlan_filtering, br_offloads);
317d75b9e80SVlad Buslov 		break;
318c5fcac93SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL:
319c5fcac93SVlad Buslov 		err = mlx5_esw_bridge_vlan_proto_set(vport_num,
320c5fcac93SVlad Buslov 						     esw_owner_vhca_id,
321c5fcac93SVlad Buslov 						     attr->u.vlan_protocol,
322c5fcac93SVlad Buslov 						     br_offloads);
323c5fcac93SVlad Buslov 		break;
32418c2916cSVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
32518c2916cSVlad Buslov 		err = mlx5_esw_bridge_mcast_set(vport_num, esw_owner_vhca_id,
32618c2916cSVlad Buslov 						!attr->u.mc_disabled, br_offloads);
32718c2916cSVlad Buslov 		break;
328c636a0f0SVlad Buslov 	default:
329c636a0f0SVlad Buslov 		err = -EOPNOTSUPP;
330c636a0f0SVlad Buslov 	}
331c636a0f0SVlad Buslov 
332c636a0f0SVlad Buslov 	return err;
333c636a0f0SVlad Buslov }
334c636a0f0SVlad Buslov 
mlx5_esw_bridge_event_blocking(struct notifier_block * nb,unsigned long event,void * ptr)3353ee6233eSVlad Buslov static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
336c636a0f0SVlad Buslov 					  unsigned long event, void *ptr)
337c636a0f0SVlad Buslov {
3383ee6233eSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
3393ee6233eSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
3403ee6233eSVlad Buslov 								    nb_blk);
341c636a0f0SVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
342c636a0f0SVlad Buslov 	int err;
343c636a0f0SVlad Buslov 
344c636a0f0SVlad Buslov 	switch (event) {
345d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_ADD:
3463ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_add(dev, ptr, br_offloads);
347d75b9e80SVlad Buslov 		break;
348d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_DEL:
3493ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_del(dev, ptr, br_offloads);
350d75b9e80SVlad Buslov 		break;
351c636a0f0SVlad Buslov 	case SWITCHDEV_PORT_ATTR_SET:
3523ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
353c636a0f0SVlad Buslov 		break;
354c636a0f0SVlad Buslov 	default:
355c636a0f0SVlad Buslov 		err = 0;
356c636a0f0SVlad Buslov 	}
357c636a0f0SVlad Buslov 
358c636a0f0SVlad Buslov 	return notifier_from_errno(err);
359c636a0f0SVlad Buslov }
360c636a0f0SVlad Buslov 
3617cd6a54aSVlad Buslov static void
mlx5_esw_bridge_cleanup_switchdev_fdb_work(struct mlx5_bridge_switchdev_fdb_work * fdb_work)3627cd6a54aSVlad Buslov mlx5_esw_bridge_cleanup_switchdev_fdb_work(struct mlx5_bridge_switchdev_fdb_work *fdb_work)
3637cd6a54aSVlad Buslov {
3647cd6a54aSVlad Buslov 	dev_put(fdb_work->dev);
3657cd6a54aSVlad Buslov 	kfree(fdb_work->fdb_info.addr);
3667cd6a54aSVlad Buslov 	kfree(fdb_work);
3677cd6a54aSVlad Buslov }
3687cd6a54aSVlad Buslov 
mlx5_esw_bridge_switchdev_fdb_event_work(struct work_struct * work)3697cd6a54aSVlad Buslov static void mlx5_esw_bridge_switchdev_fdb_event_work(struct work_struct *work)
3707cd6a54aSVlad Buslov {
3717cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *fdb_work =
3727cd6a54aSVlad Buslov 		container_of(work, struct mlx5_bridge_switchdev_fdb_work, work);
3737cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info =
3747cd6a54aSVlad Buslov 		&fdb_work->fdb_info;
375c358ea17SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads =
376c358ea17SVlad Buslov 		fdb_work->br_offloads;
3777cd6a54aSVlad Buslov 	struct net_device *dev = fdb_work->dev;
3783ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
3797cd6a54aSVlad Buslov 
3807cd6a54aSVlad Buslov 	rtnl_lock();
3817cd6a54aSVlad Buslov 
382ff9b7521SVlad Buslov 	if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
383ff9b7521SVlad Buslov 						       &esw_owner_vhca_id))
3847cd6a54aSVlad Buslov 		goto out;
3857cd6a54aSVlad Buslov 
3867cd6a54aSVlad Buslov 	if (fdb_work->add)
3873ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_create(dev, vport_num, esw_owner_vhca_id, br_offloads,
3883ee6233eSVlad Buslov 					   fdb_info);
3897cd6a54aSVlad Buslov 	else
3903ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_remove(dev, vport_num, esw_owner_vhca_id, br_offloads,
3913ee6233eSVlad Buslov 					   fdb_info);
3927cd6a54aSVlad Buslov 
3937cd6a54aSVlad Buslov out:
3947cd6a54aSVlad Buslov 	rtnl_unlock();
3957cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup_switchdev_fdb_work(fdb_work);
3967cd6a54aSVlad Buslov }
3977cd6a54aSVlad Buslov 
3987cd6a54aSVlad Buslov static struct mlx5_bridge_switchdev_fdb_work *
mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device * dev,bool add,struct switchdev_notifier_fdb_info * fdb_info,struct mlx5_esw_bridge_offloads * br_offloads)3997cd6a54aSVlad Buslov mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device *dev, bool add,
400c358ea17SVlad Buslov 					struct switchdev_notifier_fdb_info *fdb_info,
401c358ea17SVlad Buslov 					struct mlx5_esw_bridge_offloads *br_offloads)
4027cd6a54aSVlad Buslov {
4037cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
4047cd6a54aSVlad Buslov 	u8 *addr;
4057cd6a54aSVlad Buslov 
4067cd6a54aSVlad Buslov 	work = kzalloc(sizeof(*work), GFP_ATOMIC);
4077cd6a54aSVlad Buslov 	if (!work)
4087cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
4097cd6a54aSVlad Buslov 
4107cd6a54aSVlad Buslov 	INIT_WORK(&work->work, mlx5_esw_bridge_switchdev_fdb_event_work);
4117cd6a54aSVlad Buslov 	memcpy(&work->fdb_info, fdb_info, sizeof(work->fdb_info));
4127cd6a54aSVlad Buslov 
4137cd6a54aSVlad Buslov 	addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
4147cd6a54aSVlad Buslov 	if (!addr) {
4157cd6a54aSVlad Buslov 		kfree(work);
4167cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
4177cd6a54aSVlad Buslov 	}
4187cd6a54aSVlad Buslov 	ether_addr_copy(addr, fdb_info->addr);
4197cd6a54aSVlad Buslov 	work->fdb_info.addr = addr;
4207cd6a54aSVlad Buslov 
4217cd6a54aSVlad Buslov 	dev_hold(dev);
4227cd6a54aSVlad Buslov 	work->dev = dev;
423c358ea17SVlad Buslov 	work->br_offloads = br_offloads;
4247cd6a54aSVlad Buslov 	work->add = add;
4257cd6a54aSVlad Buslov 	return work;
4267cd6a54aSVlad Buslov }
4277cd6a54aSVlad Buslov 
mlx5_esw_bridge_switchdev_event(struct notifier_block * nb,unsigned long event,void * ptr)4287cd6a54aSVlad Buslov static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
4297cd6a54aSVlad Buslov 					   unsigned long event, void *ptr)
4307cd6a54aSVlad Buslov {
4317cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
4327cd6a54aSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
4337cd6a54aSVlad Buslov 								    nb);
4347cd6a54aSVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
4357cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info;
4367cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
437ff9b7521SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
4387cd6a54aSVlad Buslov 	struct switchdev_notifier_info *info = ptr;
439ff9b7521SVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
440ff9b7521SVlad Buslov 	struct net_device *upper, *rep;
4417cd6a54aSVlad Buslov 
442c636a0f0SVlad Buslov 	if (event == SWITCHDEV_PORT_ATTR_SET) {
4433ee6233eSVlad Buslov 		int err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
4443ee6233eSVlad Buslov 
445c636a0f0SVlad Buslov 		return notifier_from_errno(err);
446c636a0f0SVlad Buslov 	}
447c636a0f0SVlad Buslov 
4487cd6a54aSVlad Buslov 	upper = netdev_master_upper_dev_get_rcu(dev);
4497cd6a54aSVlad Buslov 	if (!upper)
4507cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4517cd6a54aSVlad Buslov 	if (!netif_is_bridge_master(upper))
4527cd6a54aSVlad Buslov 		return NOTIFY_DONE;
4537cd6a54aSVlad Buslov 
454ff9b7521SVlad Buslov 	rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw, &vport_num, &esw_owner_vhca_id);
455ff9b7521SVlad Buslov 	if (!rep)
4563ee6233eSVlad Buslov 		return NOTIFY_DONE;
4573ee6233eSVlad Buslov 
4587cd6a54aSVlad Buslov 	switch (event) {
459ff9b7521SVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
460ff9b7521SVlad Buslov 		fdb_info = container_of(info,
461ff9b7521SVlad Buslov 					struct switchdev_notifier_fdb_info,
462ff9b7521SVlad Buslov 					info);
463ff9b7521SVlad Buslov 		mlx5_esw_bridge_fdb_update_used(dev, vport_num, esw_owner_vhca_id, br_offloads,
464ff9b7521SVlad Buslov 						fdb_info);
465ff9b7521SVlad Buslov 		break;
466c358ea17SVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
467ff9b7521SVlad Buslov 		/* only handle the event on peers */
468ff9b7521SVlad Buslov 		if (mlx5_esw_bridge_is_local(dev, rep, esw))
469c358ea17SVlad Buslov 			break;
470*7a3ce807SVlad Buslov 
471*7a3ce807SVlad Buslov 		fdb_info = container_of(info,
472*7a3ce807SVlad Buslov 					struct switchdev_notifier_fdb_info,
473*7a3ce807SVlad Buslov 					info);
474*7a3ce807SVlad Buslov 		/* Mark for deletion to prevent the update wq task from
475*7a3ce807SVlad Buslov 		 * spuriously refreshing the entry which would mark it again as
476*7a3ce807SVlad Buslov 		 * offloaded in SW bridge. After this fallthrough to regular
477*7a3ce807SVlad Buslov 		 * async delete code.
478*7a3ce807SVlad Buslov 		 */
479*7a3ce807SVlad Buslov 		mlx5_esw_bridge_fdb_mark_deleted(dev, vport_num, esw_owner_vhca_id, br_offloads,
480*7a3ce807SVlad Buslov 						 fdb_info);
481c358ea17SVlad Buslov 		fallthrough;
4827cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
4837cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
4847cd6a54aSVlad Buslov 		fdb_info = container_of(info,
4857cd6a54aSVlad Buslov 					struct switchdev_notifier_fdb_info,
4867cd6a54aSVlad Buslov 					info);
4877cd6a54aSVlad Buslov 
4887cd6a54aSVlad Buslov 		work = mlx5_esw_bridge_init_switchdev_fdb_work(dev,
4897cd6a54aSVlad Buslov 							       event == SWITCHDEV_FDB_ADD_TO_DEVICE,
490c358ea17SVlad Buslov 							       fdb_info,
491c358ea17SVlad Buslov 							       br_offloads);
4927cd6a54aSVlad Buslov 		if (IS_ERR(work)) {
4937cd6a54aSVlad Buslov 			WARN_ONCE(1, "Failed to init switchdev work, err=%ld",
4947cd6a54aSVlad Buslov 				  PTR_ERR(work));
4957cd6a54aSVlad Buslov 			return notifier_from_errno(PTR_ERR(work));
4967cd6a54aSVlad Buslov 		}
4977cd6a54aSVlad Buslov 
4987cd6a54aSVlad Buslov 		queue_work(br_offloads->wq, &work->work);
4997cd6a54aSVlad Buslov 		break;
5007cd6a54aSVlad Buslov 	default:
5017cd6a54aSVlad Buslov 		break;
5027cd6a54aSVlad Buslov 	}
5037cd6a54aSVlad Buslov 	return NOTIFY_DONE;
5047cd6a54aSVlad Buslov }
5057cd6a54aSVlad Buslov 
mlx5_esw_bridge_update_work(struct work_struct * work)506c636a0f0SVlad Buslov static void mlx5_esw_bridge_update_work(struct work_struct *work)
507c636a0f0SVlad Buslov {
508c636a0f0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(work,
509c636a0f0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
510c636a0f0SVlad Buslov 								    update_work.work);
511c636a0f0SVlad Buslov 
512c636a0f0SVlad Buslov 	rtnl_lock();
513c636a0f0SVlad Buslov 	mlx5_esw_bridge_update(br_offloads);
514c636a0f0SVlad Buslov 	rtnl_unlock();
515c636a0f0SVlad Buslov 
516c636a0f0SVlad Buslov 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
517c636a0f0SVlad Buslov 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
518c636a0f0SVlad Buslov }
519c636a0f0SVlad Buslov 
mlx5e_rep_bridge_init(struct mlx5e_priv * priv)52019e9bfa0SVlad Buslov void mlx5e_rep_bridge_init(struct mlx5e_priv *priv)
52119e9bfa0SVlad Buslov {
52219e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
52319e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
52419e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
52519e9bfa0SVlad Buslov 		mdev->priv.eswitch;
52619e9bfa0SVlad Buslov 	int err;
52719e9bfa0SVlad Buslov 
52819e9bfa0SVlad Buslov 	rtnl_lock();
52919e9bfa0SVlad Buslov 	br_offloads = mlx5_esw_bridge_init(esw);
53019e9bfa0SVlad Buslov 	rtnl_unlock();
53119e9bfa0SVlad Buslov 	if (IS_ERR(br_offloads)) {
53219e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to init esw bridge (err=%ld)\n", PTR_ERR(br_offloads));
53319e9bfa0SVlad Buslov 		return;
53419e9bfa0SVlad Buslov 	}
53519e9bfa0SVlad Buslov 
5367cd6a54aSVlad Buslov 	br_offloads->wq = alloc_ordered_workqueue("mlx5_bridge_wq", 0);
5377cd6a54aSVlad Buslov 	if (!br_offloads->wq) {
5387cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to allocate bridge offloads workqueue\n");
5397cd6a54aSVlad Buslov 		goto err_alloc_wq;
5407cd6a54aSVlad Buslov 	}
5417cd6a54aSVlad Buslov 
5427cd6a54aSVlad Buslov 	br_offloads->nb.notifier_call = mlx5_esw_bridge_switchdev_event;
5437cd6a54aSVlad Buslov 	err = register_switchdev_notifier(&br_offloads->nb);
5447cd6a54aSVlad Buslov 	if (err) {
5457cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to register switchdev notifier (err=%d)\n", err);
5467cd6a54aSVlad Buslov 		goto err_register_swdev;
5477cd6a54aSVlad Buslov 	}
5487cd6a54aSVlad Buslov 
549c636a0f0SVlad Buslov 	br_offloads->nb_blk.notifier_call = mlx5_esw_bridge_event_blocking;
550c636a0f0SVlad Buslov 	err = register_switchdev_blocking_notifier(&br_offloads->nb_blk);
551c636a0f0SVlad Buslov 	if (err) {
552c636a0f0SVlad Buslov 		esw_warn(mdev, "Failed to register blocking switchdev notifier (err=%d)\n", err);
553c636a0f0SVlad Buslov 		goto err_register_swdev_blk;
554c636a0f0SVlad Buslov 	}
555c636a0f0SVlad Buslov 
55619e9bfa0SVlad Buslov 	br_offloads->netdev_nb.notifier_call = mlx5_esw_bridge_switchdev_port_event;
557880b5176SRoi Dayan 	err = register_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
55819e9bfa0SVlad Buslov 	if (err) {
55919e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to register bridge offloads netdevice notifier (err=%d)\n",
56019e9bfa0SVlad Buslov 			 err);
5617cd6a54aSVlad Buslov 		goto err_register_netdev;
56219e9bfa0SVlad Buslov 	}
5632266bb1eSShay Drory 	INIT_DELAYED_WORK(&br_offloads->update_work, mlx5_esw_bridge_update_work);
5642266bb1eSShay Drory 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
5652266bb1eSShay Drory 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
5667cd6a54aSVlad Buslov 	return;
5677cd6a54aSVlad Buslov 
5687cd6a54aSVlad Buslov err_register_netdev:
569c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
570c636a0f0SVlad Buslov err_register_swdev_blk:
5717cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5727cd6a54aSVlad Buslov err_register_swdev:
5737cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
5747cd6a54aSVlad Buslov err_alloc_wq:
57504f8c12fSVlad Buslov 	rtnl_lock();
5767cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
57704f8c12fSVlad Buslov 	rtnl_unlock();
57819e9bfa0SVlad Buslov }
57919e9bfa0SVlad Buslov 
mlx5e_rep_bridge_cleanup(struct mlx5e_priv * priv)58019e9bfa0SVlad Buslov void mlx5e_rep_bridge_cleanup(struct mlx5e_priv *priv)
58119e9bfa0SVlad Buslov {
58219e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
58319e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
58419e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
58519e9bfa0SVlad Buslov 		mdev->priv.eswitch;
58619e9bfa0SVlad Buslov 
58719e9bfa0SVlad Buslov 	br_offloads = esw->br_offloads;
58819e9bfa0SVlad Buslov 	if (!br_offloads)
58919e9bfa0SVlad Buslov 		return;
59019e9bfa0SVlad Buslov 
5912266bb1eSShay Drory 	cancel_delayed_work_sync(&br_offloads->update_work);
592880b5176SRoi Dayan 	unregister_netdevice_notifier_net(&init_net, &br_offloads->netdev_nb);
593c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
5947cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
5957cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
59619e9bfa0SVlad Buslov 	rtnl_lock();
59719e9bfa0SVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
59819e9bfa0SVlad Buslov 	rtnl_unlock();
59919e9bfa0SVlad Buslov }
600