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;
187cd6a54aSVlad Buslov 	bool add;
197cd6a54aSVlad Buslov };
207cd6a54aSVlad Buslov 
21*3ee6233eSVlad Buslov static bool mlx5_esw_bridge_dev_same_esw(struct net_device *dev, struct mlx5_eswitch *esw)
22*3ee6233eSVlad Buslov {
23*3ee6233eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(dev);
24*3ee6233eSVlad Buslov 
25*3ee6233eSVlad Buslov 	return esw == priv->mdev->priv.eswitch;
26*3ee6233eSVlad Buslov }
27*3ee6233eSVlad Buslov 
28*3ee6233eSVlad Buslov static int mlx5_esw_bridge_vport_num_vhca_id_get(struct net_device *dev, struct mlx5_eswitch *esw,
29*3ee6233eSVlad Buslov 						 u16 *vport_num, u16 *esw_owner_vhca_id)
30*3ee6233eSVlad Buslov {
31*3ee6233eSVlad Buslov 	struct mlx5e_rep_priv *rpriv;
32*3ee6233eSVlad Buslov 	struct mlx5e_priv *priv;
33*3ee6233eSVlad Buslov 
34*3ee6233eSVlad Buslov 	if (!mlx5e_eswitch_rep(dev) || !mlx5_esw_bridge_dev_same_esw(dev, esw))
35*3ee6233eSVlad Buslov 		return -ENODEV;
36*3ee6233eSVlad Buslov 
37*3ee6233eSVlad Buslov 	priv = netdev_priv(dev);
38*3ee6233eSVlad Buslov 	rpriv = priv->ppriv;
39*3ee6233eSVlad Buslov 	*vport_num = rpriv->rep->vport;
40*3ee6233eSVlad Buslov 	*esw_owner_vhca_id = MLX5_CAP_GEN(priv->mdev, vhca_id);
41*3ee6233eSVlad Buslov 	return 0;
42*3ee6233eSVlad Buslov }
43*3ee6233eSVlad Buslov 
44*3ee6233eSVlad Buslov static int
45*3ee6233eSVlad Buslov mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct mlx5_eswitch *esw,
46*3ee6233eSVlad Buslov 						u16 *vport_num, u16 *esw_owner_vhca_id)
47*3ee6233eSVlad Buslov {
48*3ee6233eSVlad Buslov 	struct net_device *lower_dev;
49*3ee6233eSVlad Buslov 	struct list_head *iter;
50*3ee6233eSVlad Buslov 
51*3ee6233eSVlad Buslov 	if (mlx5e_eswitch_rep(dev) && mlx5_esw_bridge_dev_same_esw(dev, esw))
52*3ee6233eSVlad Buslov 		return mlx5_esw_bridge_vport_num_vhca_id_get(dev, esw, vport_num,
53*3ee6233eSVlad Buslov 							     esw_owner_vhca_id);
54*3ee6233eSVlad Buslov 
55*3ee6233eSVlad Buslov 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
56*3ee6233eSVlad Buslov 		int err;
57*3ee6233eSVlad Buslov 
58*3ee6233eSVlad Buslov 		if (netif_is_bridge_master(lower_dev))
59*3ee6233eSVlad Buslov 			continue;
60*3ee6233eSVlad Buslov 
61*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(lower_dev, esw, vport_num,
62*3ee6233eSVlad Buslov 								      esw_owner_vhca_id);
63*3ee6233eSVlad Buslov 		if (!err)
64*3ee6233eSVlad Buslov 			return 0;
65*3ee6233eSVlad Buslov 	}
66*3ee6233eSVlad Buslov 
67*3ee6233eSVlad Buslov 	return -ENODEV;
68*3ee6233eSVlad Buslov }
69*3ee6233eSVlad Buslov 
7019e9bfa0SVlad Buslov static int mlx5_esw_bridge_port_changeupper(struct notifier_block *nb, void *ptr)
7119e9bfa0SVlad Buslov {
7219e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
7319e9bfa0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
7419e9bfa0SVlad Buslov 								    netdev_nb);
7519e9bfa0SVlad Buslov 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
7619e9bfa0SVlad Buslov 	struct netdev_notifier_changeupper_info *info = ptr;
77*3ee6233eSVlad Buslov 	struct net_device *upper = info->upper_dev;
78*3ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
7919e9bfa0SVlad Buslov 	struct netlink_ext_ack *extack;
80*3ee6233eSVlad Buslov 	int ifindex = upper->ifindex;
81*3ee6233eSVlad Buslov 	int err;
8219e9bfa0SVlad Buslov 
8319e9bfa0SVlad Buslov 	if (!netif_is_bridge_master(upper))
8419e9bfa0SVlad Buslov 		return 0;
8519e9bfa0SVlad Buslov 
86*3ee6233eSVlad Buslov 	err = mlx5_esw_bridge_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
87*3ee6233eSVlad Buslov 						    &esw_owner_vhca_id);
88*3ee6233eSVlad Buslov 	if (err)
8919e9bfa0SVlad Buslov 		return 0;
9019e9bfa0SVlad Buslov 
9119e9bfa0SVlad Buslov 	extack = netdev_notifier_info_to_extack(&info->info);
9219e9bfa0SVlad Buslov 
9319e9bfa0SVlad Buslov 	return info->linking ?
94*3ee6233eSVlad Buslov 		mlx5_esw_bridge_vport_link(ifindex, vport_num, esw_owner_vhca_id, br_offloads,
95*3ee6233eSVlad Buslov 					   extack) :
96*3ee6233eSVlad Buslov 		mlx5_esw_bridge_vport_unlink(ifindex, vport_num, esw_owner_vhca_id, br_offloads,
97*3ee6233eSVlad Buslov 					     extack);
9819e9bfa0SVlad Buslov }
9919e9bfa0SVlad Buslov 
10019e9bfa0SVlad Buslov static int mlx5_esw_bridge_switchdev_port_event(struct notifier_block *nb,
10119e9bfa0SVlad Buslov 						unsigned long event, void *ptr)
10219e9bfa0SVlad Buslov {
10319e9bfa0SVlad Buslov 	int err = 0;
10419e9bfa0SVlad Buslov 
10519e9bfa0SVlad Buslov 	switch (event) {
10619e9bfa0SVlad Buslov 	case NETDEV_PRECHANGEUPPER:
10719e9bfa0SVlad Buslov 		break;
10819e9bfa0SVlad Buslov 
10919e9bfa0SVlad Buslov 	case NETDEV_CHANGEUPPER:
11019e9bfa0SVlad Buslov 		err = mlx5_esw_bridge_port_changeupper(nb, ptr);
11119e9bfa0SVlad Buslov 		break;
11219e9bfa0SVlad Buslov 	}
11319e9bfa0SVlad Buslov 
11419e9bfa0SVlad Buslov 	return notifier_from_errno(err);
11519e9bfa0SVlad Buslov }
11619e9bfa0SVlad Buslov 
117*3ee6233eSVlad Buslov static int
118*3ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_add(struct net_device *dev,
119*3ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
120*3ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
121d75b9e80SVlad Buslov {
122*3ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
123*3ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
124d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
125*3ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
126*3ee6233eSVlad Buslov 	int err;
127d75b9e80SVlad Buslov 
128*3ee6233eSVlad Buslov 	err = mlx5_esw_bridge_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
129*3ee6233eSVlad Buslov 						    &esw_owner_vhca_id);
130*3ee6233eSVlad Buslov 	if (err)
131*3ee6233eSVlad Buslov 		return 0;
132*3ee6233eSVlad Buslov 
133*3ee6233eSVlad Buslov 	port_obj_info->handled = true;
134d75b9e80SVlad Buslov 
135d75b9e80SVlad Buslov 	switch (obj->id) {
136d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
137d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
138*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_vlan_add(vport_num, esw_owner_vhca_id, vlan->vid,
139*3ee6233eSVlad Buslov 						    vlan->flags, br_offloads, extack);
140d75b9e80SVlad Buslov 		break;
141d75b9e80SVlad Buslov 	default:
142d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
143d75b9e80SVlad Buslov 	}
144d75b9e80SVlad Buslov 	return err;
145d75b9e80SVlad Buslov }
146d75b9e80SVlad Buslov 
147*3ee6233eSVlad Buslov static int
148*3ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_del(struct net_device *dev,
149*3ee6233eSVlad Buslov 			     struct switchdev_notifier_port_obj_info *port_obj_info,
150*3ee6233eSVlad Buslov 			     struct mlx5_esw_bridge_offloads *br_offloads)
151d75b9e80SVlad Buslov {
152*3ee6233eSVlad Buslov 	const struct switchdev_obj *obj = port_obj_info->obj;
153d75b9e80SVlad Buslov 	const struct switchdev_obj_port_vlan *vlan;
154*3ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
155*3ee6233eSVlad Buslov 	int err;
156d75b9e80SVlad Buslov 
157*3ee6233eSVlad Buslov 	err = mlx5_esw_bridge_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
158*3ee6233eSVlad Buslov 						    &esw_owner_vhca_id);
159*3ee6233eSVlad Buslov 	if (err)
160*3ee6233eSVlad Buslov 		return 0;
161*3ee6233eSVlad Buslov 
162*3ee6233eSVlad Buslov 	port_obj_info->handled = true;
163d75b9e80SVlad Buslov 
164d75b9e80SVlad Buslov 	switch (obj->id) {
165d75b9e80SVlad Buslov 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
166d75b9e80SVlad Buslov 		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
167*3ee6233eSVlad Buslov 		mlx5_esw_bridge_port_vlan_del(vport_num, esw_owner_vhca_id, vlan->vid, br_offloads);
168d75b9e80SVlad Buslov 		break;
169d75b9e80SVlad Buslov 	default:
170d75b9e80SVlad Buslov 		return -EOPNOTSUPP;
171d75b9e80SVlad Buslov 	}
172d75b9e80SVlad Buslov 	return 0;
173d75b9e80SVlad Buslov }
174d75b9e80SVlad Buslov 
175*3ee6233eSVlad Buslov static int
176*3ee6233eSVlad Buslov mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
177*3ee6233eSVlad Buslov 				  struct switchdev_notifier_port_attr_info *port_attr_info,
178*3ee6233eSVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads)
179c636a0f0SVlad Buslov {
180*3ee6233eSVlad Buslov 	struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_attr_info->info);
181*3ee6233eSVlad Buslov 	const struct switchdev_attr *attr = port_attr_info->attr;
182*3ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
183*3ee6233eSVlad Buslov 	int err;
184c636a0f0SVlad Buslov 
185*3ee6233eSVlad Buslov 	err = mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
186*3ee6233eSVlad Buslov 							      &esw_owner_vhca_id);
187*3ee6233eSVlad Buslov 	if (err)
188*3ee6233eSVlad Buslov 		return 0;
189*3ee6233eSVlad Buslov 
190*3ee6233eSVlad Buslov 	port_attr_info->handled = true;
191c636a0f0SVlad Buslov 
192c636a0f0SVlad Buslov 	switch (attr->id) {
193c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
194c636a0f0SVlad Buslov 		if (attr->u.brport_flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) {
195c636a0f0SVlad Buslov 			NL_SET_ERR_MSG_MOD(extack, "Flag is not supported");
196c636a0f0SVlad Buslov 			err = -EINVAL;
197c636a0f0SVlad Buslov 		}
198c636a0f0SVlad Buslov 		break;
199c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
200c636a0f0SVlad Buslov 		break;
201c636a0f0SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
202*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_ageing_time_set(vport_num, esw_owner_vhca_id,
203*3ee6233eSVlad Buslov 						      attr->u.ageing_time, br_offloads);
204c636a0f0SVlad Buslov 		break;
205d75b9e80SVlad Buslov 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
206*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_vlan_filtering_set(vport_num, esw_owner_vhca_id,
207*3ee6233eSVlad Buslov 							 attr->u.vlan_filtering, br_offloads);
208d75b9e80SVlad Buslov 		break;
209c636a0f0SVlad Buslov 	default:
210c636a0f0SVlad Buslov 		err = -EOPNOTSUPP;
211c636a0f0SVlad Buslov 	}
212c636a0f0SVlad Buslov 
213c636a0f0SVlad Buslov 	return err;
214c636a0f0SVlad Buslov }
215c636a0f0SVlad Buslov 
216*3ee6233eSVlad Buslov static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
217c636a0f0SVlad Buslov 					  unsigned long event, void *ptr)
218c636a0f0SVlad Buslov {
219*3ee6233eSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
220*3ee6233eSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
221*3ee6233eSVlad Buslov 								    nb_blk);
222c636a0f0SVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
223c636a0f0SVlad Buslov 	int err;
224c636a0f0SVlad Buslov 
225c636a0f0SVlad Buslov 	switch (event) {
226d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_ADD:
227*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_add(dev, ptr, br_offloads);
228d75b9e80SVlad Buslov 		break;
229d75b9e80SVlad Buslov 	case SWITCHDEV_PORT_OBJ_DEL:
230*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_del(dev, ptr, br_offloads);
231d75b9e80SVlad Buslov 		break;
232c636a0f0SVlad Buslov 	case SWITCHDEV_PORT_ATTR_SET:
233*3ee6233eSVlad Buslov 		err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
234c636a0f0SVlad Buslov 		break;
235c636a0f0SVlad Buslov 	default:
236c636a0f0SVlad Buslov 		err = 0;
237c636a0f0SVlad Buslov 	}
238c636a0f0SVlad Buslov 
239c636a0f0SVlad Buslov 	return notifier_from_errno(err);
240c636a0f0SVlad Buslov }
241c636a0f0SVlad Buslov 
2427cd6a54aSVlad Buslov static void
2437cd6a54aSVlad Buslov mlx5_esw_bridge_cleanup_switchdev_fdb_work(struct mlx5_bridge_switchdev_fdb_work *fdb_work)
2447cd6a54aSVlad Buslov {
2457cd6a54aSVlad Buslov 	dev_put(fdb_work->dev);
2467cd6a54aSVlad Buslov 	kfree(fdb_work->fdb_info.addr);
2477cd6a54aSVlad Buslov 	kfree(fdb_work);
2487cd6a54aSVlad Buslov }
2497cd6a54aSVlad Buslov 
2507cd6a54aSVlad Buslov static void mlx5_esw_bridge_switchdev_fdb_event_work(struct work_struct *work)
2517cd6a54aSVlad Buslov {
2527cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *fdb_work =
2537cd6a54aSVlad Buslov 		container_of(work, struct mlx5_bridge_switchdev_fdb_work, work);
2547cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info =
2557cd6a54aSVlad Buslov 		&fdb_work->fdb_info;
256*3ee6233eSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
2577cd6a54aSVlad Buslov 	struct net_device *dev = fdb_work->dev;
258*3ee6233eSVlad Buslov 	u16 vport_num, esw_owner_vhca_id;
2597cd6a54aSVlad Buslov 	struct mlx5e_priv *priv;
260*3ee6233eSVlad Buslov 	int err;
2617cd6a54aSVlad Buslov 
2627cd6a54aSVlad Buslov 	rtnl_lock();
2637cd6a54aSVlad Buslov 
2647cd6a54aSVlad Buslov 	priv = netdev_priv(dev);
265*3ee6233eSVlad Buslov 	br_offloads = priv->mdev->priv.eswitch->br_offloads;
266*3ee6233eSVlad Buslov 	err = mlx5_esw_bridge_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
267*3ee6233eSVlad Buslov 						    &esw_owner_vhca_id);
268*3ee6233eSVlad Buslov 	if (err)
2697cd6a54aSVlad Buslov 		goto out;
2707cd6a54aSVlad Buslov 
2717cd6a54aSVlad Buslov 	if (fdb_work->add)
272*3ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_create(dev, vport_num, esw_owner_vhca_id, br_offloads,
273*3ee6233eSVlad Buslov 					   fdb_info);
2747cd6a54aSVlad Buslov 	else
275*3ee6233eSVlad Buslov 		mlx5_esw_bridge_fdb_remove(dev, vport_num, esw_owner_vhca_id, br_offloads,
276*3ee6233eSVlad Buslov 					   fdb_info);
2777cd6a54aSVlad Buslov 
2787cd6a54aSVlad Buslov out:
2797cd6a54aSVlad Buslov 	rtnl_unlock();
2807cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup_switchdev_fdb_work(fdb_work);
2817cd6a54aSVlad Buslov }
2827cd6a54aSVlad Buslov 
2837cd6a54aSVlad Buslov static struct mlx5_bridge_switchdev_fdb_work *
2847cd6a54aSVlad Buslov mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device *dev, bool add,
2857cd6a54aSVlad Buslov 					struct switchdev_notifier_fdb_info *fdb_info)
2867cd6a54aSVlad Buslov {
2877cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
2887cd6a54aSVlad Buslov 	u8 *addr;
2897cd6a54aSVlad Buslov 
2907cd6a54aSVlad Buslov 	work = kzalloc(sizeof(*work), GFP_ATOMIC);
2917cd6a54aSVlad Buslov 	if (!work)
2927cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
2937cd6a54aSVlad Buslov 
2947cd6a54aSVlad Buslov 	INIT_WORK(&work->work, mlx5_esw_bridge_switchdev_fdb_event_work);
2957cd6a54aSVlad Buslov 	memcpy(&work->fdb_info, fdb_info, sizeof(work->fdb_info));
2967cd6a54aSVlad Buslov 
2977cd6a54aSVlad Buslov 	addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
2987cd6a54aSVlad Buslov 	if (!addr) {
2997cd6a54aSVlad Buslov 		kfree(work);
3007cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
3017cd6a54aSVlad Buslov 	}
3027cd6a54aSVlad Buslov 	ether_addr_copy(addr, fdb_info->addr);
3037cd6a54aSVlad Buslov 	work->fdb_info.addr = addr;
3047cd6a54aSVlad Buslov 
3057cd6a54aSVlad Buslov 	dev_hold(dev);
3067cd6a54aSVlad Buslov 	work->dev = dev;
3077cd6a54aSVlad Buslov 	work->add = add;
3087cd6a54aSVlad Buslov 	return work;
3097cd6a54aSVlad Buslov }
3107cd6a54aSVlad Buslov 
3117cd6a54aSVlad Buslov static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
3127cd6a54aSVlad Buslov 					   unsigned long event, void *ptr)
3137cd6a54aSVlad Buslov {
3147cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(nb,
3157cd6a54aSVlad Buslov 								    struct mlx5_esw_bridge_offloads,
3167cd6a54aSVlad Buslov 								    nb);
3177cd6a54aSVlad Buslov 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
3187cd6a54aSVlad Buslov 	struct switchdev_notifier_fdb_info *fdb_info;
3197cd6a54aSVlad Buslov 	struct mlx5_bridge_switchdev_fdb_work *work;
3207cd6a54aSVlad Buslov 	struct switchdev_notifier_info *info = ptr;
3217cd6a54aSVlad Buslov 	struct net_device *upper;
3227cd6a54aSVlad Buslov 
323c636a0f0SVlad Buslov 	if (event == SWITCHDEV_PORT_ATTR_SET) {
324*3ee6233eSVlad Buslov 		int err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
325*3ee6233eSVlad Buslov 
326c636a0f0SVlad Buslov 		return notifier_from_errno(err);
327c636a0f0SVlad Buslov 	}
328c636a0f0SVlad Buslov 
3297cd6a54aSVlad Buslov 	upper = netdev_master_upper_dev_get_rcu(dev);
3307cd6a54aSVlad Buslov 	if (!upper)
3317cd6a54aSVlad Buslov 		return NOTIFY_DONE;
3327cd6a54aSVlad Buslov 	if (!netif_is_bridge_master(upper))
3337cd6a54aSVlad Buslov 		return NOTIFY_DONE;
3347cd6a54aSVlad Buslov 
335*3ee6233eSVlad Buslov 	if (!mlx5e_eswitch_rep(dev))
336*3ee6233eSVlad Buslov 		return NOTIFY_DONE;
337*3ee6233eSVlad Buslov 	if (!mlx5_esw_bridge_dev_same_esw(dev, br_offloads->esw))
338*3ee6233eSVlad Buslov 		return NOTIFY_DONE;
339*3ee6233eSVlad Buslov 
3407cd6a54aSVlad Buslov 	switch (event) {
3417cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
3427cd6a54aSVlad Buslov 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
3437cd6a54aSVlad Buslov 		fdb_info = container_of(info,
3447cd6a54aSVlad Buslov 					struct switchdev_notifier_fdb_info,
3457cd6a54aSVlad Buslov 					info);
3467cd6a54aSVlad Buslov 
3477cd6a54aSVlad Buslov 		work = mlx5_esw_bridge_init_switchdev_fdb_work(dev,
3487cd6a54aSVlad Buslov 							       event == SWITCHDEV_FDB_ADD_TO_DEVICE,
3497cd6a54aSVlad Buslov 							       fdb_info);
3507cd6a54aSVlad Buslov 		if (IS_ERR(work)) {
3517cd6a54aSVlad Buslov 			WARN_ONCE(1, "Failed to init switchdev work, err=%ld",
3527cd6a54aSVlad Buslov 				  PTR_ERR(work));
3537cd6a54aSVlad Buslov 			return notifier_from_errno(PTR_ERR(work));
3547cd6a54aSVlad Buslov 		}
3557cd6a54aSVlad Buslov 
3567cd6a54aSVlad Buslov 		queue_work(br_offloads->wq, &work->work);
3577cd6a54aSVlad Buslov 		break;
3587cd6a54aSVlad Buslov 	default:
3597cd6a54aSVlad Buslov 		break;
3607cd6a54aSVlad Buslov 	}
3617cd6a54aSVlad Buslov 	return NOTIFY_DONE;
3627cd6a54aSVlad Buslov }
3637cd6a54aSVlad Buslov 
364c636a0f0SVlad Buslov static void mlx5_esw_bridge_update_work(struct work_struct *work)
365c636a0f0SVlad Buslov {
366c636a0f0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = container_of(work,
367c636a0f0SVlad Buslov 								    struct mlx5_esw_bridge_offloads,
368c636a0f0SVlad Buslov 								    update_work.work);
369c636a0f0SVlad Buslov 
370c636a0f0SVlad Buslov 	rtnl_lock();
371c636a0f0SVlad Buslov 	mlx5_esw_bridge_update(br_offloads);
372c636a0f0SVlad Buslov 	rtnl_unlock();
373c636a0f0SVlad Buslov 
374c636a0f0SVlad Buslov 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
375c636a0f0SVlad Buslov 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
376c636a0f0SVlad Buslov }
377c636a0f0SVlad Buslov 
37819e9bfa0SVlad Buslov void mlx5e_rep_bridge_init(struct mlx5e_priv *priv)
37919e9bfa0SVlad Buslov {
38019e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
38119e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
38219e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
38319e9bfa0SVlad Buslov 		mdev->priv.eswitch;
38419e9bfa0SVlad Buslov 	int err;
38519e9bfa0SVlad Buslov 
38619e9bfa0SVlad Buslov 	rtnl_lock();
38719e9bfa0SVlad Buslov 	br_offloads = mlx5_esw_bridge_init(esw);
38819e9bfa0SVlad Buslov 	rtnl_unlock();
38919e9bfa0SVlad Buslov 	if (IS_ERR(br_offloads)) {
39019e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to init esw bridge (err=%ld)\n", PTR_ERR(br_offloads));
39119e9bfa0SVlad Buslov 		return;
39219e9bfa0SVlad Buslov 	}
39319e9bfa0SVlad Buslov 
3947cd6a54aSVlad Buslov 	br_offloads->wq = alloc_ordered_workqueue("mlx5_bridge_wq", 0);
3957cd6a54aSVlad Buslov 	if (!br_offloads->wq) {
3967cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to allocate bridge offloads workqueue\n");
3977cd6a54aSVlad Buslov 		goto err_alloc_wq;
3987cd6a54aSVlad Buslov 	}
399c636a0f0SVlad Buslov 	INIT_DELAYED_WORK(&br_offloads->update_work, mlx5_esw_bridge_update_work);
400c636a0f0SVlad Buslov 	queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
401c636a0f0SVlad Buslov 			   msecs_to_jiffies(MLX5_ESW_BRIDGE_UPDATE_INTERVAL));
4027cd6a54aSVlad Buslov 
4037cd6a54aSVlad Buslov 	br_offloads->nb.notifier_call = mlx5_esw_bridge_switchdev_event;
4047cd6a54aSVlad Buslov 	err = register_switchdev_notifier(&br_offloads->nb);
4057cd6a54aSVlad Buslov 	if (err) {
4067cd6a54aSVlad Buslov 		esw_warn(mdev, "Failed to register switchdev notifier (err=%d)\n", err);
4077cd6a54aSVlad Buslov 		goto err_register_swdev;
4087cd6a54aSVlad Buslov 	}
4097cd6a54aSVlad Buslov 
410c636a0f0SVlad Buslov 	br_offloads->nb_blk.notifier_call = mlx5_esw_bridge_event_blocking;
411c636a0f0SVlad Buslov 	err = register_switchdev_blocking_notifier(&br_offloads->nb_blk);
412c636a0f0SVlad Buslov 	if (err) {
413c636a0f0SVlad Buslov 		esw_warn(mdev, "Failed to register blocking switchdev notifier (err=%d)\n", err);
414c636a0f0SVlad Buslov 		goto err_register_swdev_blk;
415c636a0f0SVlad Buslov 	}
416c636a0f0SVlad Buslov 
41719e9bfa0SVlad Buslov 	br_offloads->netdev_nb.notifier_call = mlx5_esw_bridge_switchdev_port_event;
41819e9bfa0SVlad Buslov 	err = register_netdevice_notifier(&br_offloads->netdev_nb);
41919e9bfa0SVlad Buslov 	if (err) {
42019e9bfa0SVlad Buslov 		esw_warn(mdev, "Failed to register bridge offloads netdevice notifier (err=%d)\n",
42119e9bfa0SVlad Buslov 			 err);
4227cd6a54aSVlad Buslov 		goto err_register_netdev;
42319e9bfa0SVlad Buslov 	}
4247cd6a54aSVlad Buslov 	return;
4257cd6a54aSVlad Buslov 
4267cd6a54aSVlad Buslov err_register_netdev:
427c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
428c636a0f0SVlad Buslov err_register_swdev_blk:
4297cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
4307cd6a54aSVlad Buslov err_register_swdev:
4317cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
4327cd6a54aSVlad Buslov err_alloc_wq:
4337cd6a54aSVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
43419e9bfa0SVlad Buslov }
43519e9bfa0SVlad Buslov 
43619e9bfa0SVlad Buslov void mlx5e_rep_bridge_cleanup(struct mlx5e_priv *priv)
43719e9bfa0SVlad Buslov {
43819e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
43919e9bfa0SVlad Buslov 	struct mlx5_core_dev *mdev = priv->mdev;
44019e9bfa0SVlad Buslov 	struct mlx5_eswitch *esw =
44119e9bfa0SVlad Buslov 		mdev->priv.eswitch;
44219e9bfa0SVlad Buslov 
44319e9bfa0SVlad Buslov 	br_offloads = esw->br_offloads;
44419e9bfa0SVlad Buslov 	if (!br_offloads)
44519e9bfa0SVlad Buslov 		return;
44619e9bfa0SVlad Buslov 
44719e9bfa0SVlad Buslov 	unregister_netdevice_notifier(&br_offloads->netdev_nb);
448c636a0f0SVlad Buslov 	unregister_switchdev_blocking_notifier(&br_offloads->nb_blk);
4497cd6a54aSVlad Buslov 	unregister_switchdev_notifier(&br_offloads->nb);
450c636a0f0SVlad Buslov 	cancel_delayed_work(&br_offloads->update_work);
4517cd6a54aSVlad Buslov 	destroy_workqueue(br_offloads->wq);
45219e9bfa0SVlad Buslov 	rtnl_lock();
45319e9bfa0SVlad Buslov 	mlx5_esw_bridge_cleanup(esw);
45419e9bfa0SVlad Buslov 	rtnl_unlock();
45519e9bfa0SVlad Buslov }
456