xref: /openbmc/linux/net/bridge/br_switchdev.c (revision c5381154)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26bc506b4SIdo Schimmel #include <linux/kernel.h>
36bc506b4SIdo Schimmel #include <linux/list.h>
46bc506b4SIdo Schimmel #include <linux/netdevice.h>
56bc506b4SIdo Schimmel #include <linux/rtnetlink.h>
66bc506b4SIdo Schimmel #include <linux/skbuff.h>
76bc506b4SIdo Schimmel #include <net/switchdev.h>
86bc506b4SIdo Schimmel 
96bc506b4SIdo Schimmel #include "br_private.h"
106bc506b4SIdo Schimmel 
1147211192STobias Waldekranz static struct static_key_false br_switchdev_tx_fwd_offload;
1247211192STobias Waldekranz 
1347211192STobias Waldekranz static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p,
1447211192STobias Waldekranz 					     const struct sk_buff *skb)
1547211192STobias Waldekranz {
1647211192STobias Waldekranz 	if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload))
1747211192STobias Waldekranz 		return false;
1847211192STobias Waldekranz 
1947211192STobias Waldekranz 	return (p->flags & BR_TX_FWD_OFFLOAD) &&
2047211192STobias Waldekranz 	       (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom);
2147211192STobias Waldekranz }
2247211192STobias Waldekranz 
2347211192STobias Waldekranz bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb)
2447211192STobias Waldekranz {
2547211192STobias Waldekranz 	if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload))
2647211192STobias Waldekranz 		return false;
2747211192STobias Waldekranz 
2847211192STobias Waldekranz 	return BR_INPUT_SKB_CB(skb)->tx_fwd_offload;
2947211192STobias Waldekranz }
3047211192STobias Waldekranz 
31*c5381154SVladimir Oltean void br_switchdev_frame_set_offload_fwd_mark(struct sk_buff *skb)
32*c5381154SVladimir Oltean {
33*c5381154SVladimir Oltean 	skb->offload_fwd_mark = br_switchdev_frame_uses_tx_fwd_offload(skb);
34*c5381154SVladimir Oltean }
35*c5381154SVladimir Oltean 
3647211192STobias Waldekranz /* Mark the frame for TX forwarding offload if this egress port supports it */
3747211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p,
3847211192STobias Waldekranz 					     struct sk_buff *skb)
3947211192STobias Waldekranz {
4047211192STobias Waldekranz 	if (nbp_switchdev_can_offload_tx_fwd(p, skb))
4147211192STobias Waldekranz 		BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true;
4247211192STobias Waldekranz }
4347211192STobias Waldekranz 
4447211192STobias Waldekranz /* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms
4547211192STobias Waldekranz  * that the skb has been already forwarded to, to avoid further cloning to
4647211192STobias Waldekranz  * other ports in the same hwdom by making nbp_switchdev_allowed_egress()
4747211192STobias Waldekranz  * return false.
4847211192STobias Waldekranz  */
4947211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p,
5047211192STobias Waldekranz 					      struct sk_buff *skb)
5147211192STobias Waldekranz {
5247211192STobias Waldekranz 	if (nbp_switchdev_can_offload_tx_fwd(p, skb))
5347211192STobias Waldekranz 		set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms);
5447211192STobias Waldekranz }
5547211192STobias Waldekranz 
566bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
576bc506b4SIdo Schimmel 			      struct sk_buff *skb)
586bc506b4SIdo Schimmel {
59f7cf972fSTobias Waldekranz 	if (p->hwdom)
60f7cf972fSTobias Waldekranz 		BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom;
616bc506b4SIdo Schimmel }
626bc506b4SIdo Schimmel 
636bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
646bc506b4SIdo Schimmel 				  const struct sk_buff *skb)
656bc506b4SIdo Schimmel {
6647211192STobias Waldekranz 	struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb);
6747211192STobias Waldekranz 
6847211192STobias Waldekranz 	return !test_bit(p->hwdom, &cb->fwd_hwdoms) &&
6947211192STobias Waldekranz 		(!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom);
706bc506b4SIdo Schimmel }
713922285dSArkadi Sharshevsky 
723922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */
733922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
743922285dSArkadi Sharshevsky 				  BR_MCAST_FLOOD | BR_BCAST_FLOOD)
753922285dSArkadi Sharshevsky 
763922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p,
773922285dSArkadi Sharshevsky 			       unsigned long flags,
78078bbb85SVladimir Oltean 			       unsigned long mask,
79078bbb85SVladimir Oltean 			       struct netlink_ext_ack *extack)
803922285dSArkadi Sharshevsky {
813922285dSArkadi Sharshevsky 	struct switchdev_attr attr = {
823922285dSArkadi Sharshevsky 		.orig_dev = p->dev,
833922285dSArkadi Sharshevsky 	};
84d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info info = {
85d45224d6SFlorian Fainelli 		.attr = &attr,
86d45224d6SFlorian Fainelli 	};
873922285dSArkadi Sharshevsky 	int err;
883922285dSArkadi Sharshevsky 
89304ae3bfSVladimir Oltean 	mask &= BR_PORT_FLAGS_HW_OFFLOAD;
90304ae3bfSVladimir Oltean 	if (!mask)
913922285dSArkadi Sharshevsky 		return 0;
923922285dSArkadi Sharshevsky 
93e18f4c18SVladimir Oltean 	attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS;
94e18f4c18SVladimir Oltean 	attr.u.brport_flags.val = flags;
95e18f4c18SVladimir Oltean 	attr.u.brport_flags.mask = mask;
96304ae3bfSVladimir Oltean 
97d45224d6SFlorian Fainelli 	/* We run from atomic context here */
98d45224d6SFlorian Fainelli 	err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
99078bbb85SVladimir Oltean 				       &info.info, extack);
100d45224d6SFlorian Fainelli 	err = notifier_to_errno(err);
1013922285dSArkadi Sharshevsky 	if (err == -EOPNOTSUPP)
1023922285dSArkadi Sharshevsky 		return 0;
1033922285dSArkadi Sharshevsky 
1041ef07644SFlorian Fainelli 	if (err) {
105078bbb85SVladimir Oltean 		if (extack && !extack->_msg)
106078bbb85SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
107078bbb85SVladimir Oltean 					   "bridge flag offload is not supported");
1083922285dSArkadi Sharshevsky 		return -EOPNOTSUPP;
1093922285dSArkadi Sharshevsky 	}
1103922285dSArkadi Sharshevsky 
1113922285dSArkadi Sharshevsky 	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
1123922285dSArkadi Sharshevsky 	attr.flags = SWITCHDEV_F_DEFER;
1131ef07644SFlorian Fainelli 
114dcbdf135SVladimir Oltean 	err = switchdev_port_attr_set(p->dev, &attr, extack);
1153922285dSArkadi Sharshevsky 	if (err) {
116dcbdf135SVladimir Oltean 		if (extack && !extack->_msg)
117dcbdf135SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
118dcbdf135SVladimir Oltean 					   "error setting offload flag on port");
1193922285dSArkadi Sharshevsky 		return err;
1203922285dSArkadi Sharshevsky 	}
1213922285dSArkadi Sharshevsky 
1223922285dSArkadi Sharshevsky 	return 0;
1233922285dSArkadi Sharshevsky }
1246b26b51bSArkadi Sharshevsky 
1256b26b51bSArkadi Sharshevsky void
1266eb38bf8STobias Waldekranz br_switchdev_fdb_notify(struct net_bridge *br,
1276eb38bf8STobias Waldekranz 			const struct net_bridge_fdb_entry *fdb, int type)
1286b26b51bSArkadi Sharshevsky {
1293e19ae7cSVladimir Oltean 	const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
1306eb38bf8STobias Waldekranz 	struct net_device *dev = dst ? dst->dev : br->dev;
131e5b4b898STobias Waldekranz 	struct switchdev_notifier_fdb_info info = {
132e5b4b898STobias Waldekranz 		.addr = fdb->key.addr.addr,
133e5b4b898STobias Waldekranz 		.vid = fdb->key.vlan_id,
134e5b4b898STobias Waldekranz 		.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
1352c4eca3eSVladimir Oltean 		.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags),
136e5b4b898STobias Waldekranz 		.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
137e5b4b898STobias Waldekranz 	};
138e5b4b898STobias Waldekranz 
1396b26b51bSArkadi Sharshevsky 	switch (type) {
1406b26b51bSArkadi Sharshevsky 	case RTM_DELNEIGH:
141e5b4b898STobias Waldekranz 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
1426eb38bf8STobias Waldekranz 					 dev, &info.info, NULL);
1436b26b51bSArkadi Sharshevsky 		break;
1446b26b51bSArkadi Sharshevsky 	case RTM_NEWNEIGH:
145e5b4b898STobias Waldekranz 		call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
1466eb38bf8STobias Waldekranz 					 dev, &info.info, NULL);
1476b26b51bSArkadi Sharshevsky 		break;
1486b26b51bSArkadi Sharshevsky 	}
1496b26b51bSArkadi Sharshevsky }
150d66e4348SPetr Machata 
151169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
152169327d5SPetr Machata 			       struct netlink_ext_ack *extack)
153d66e4348SPetr Machata {
154d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
155d66e4348SPetr Machata 		.obj.orig_dev = dev,
156d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
157d66e4348SPetr Machata 		.flags = flags,
158b7a9e0daSVladimir Oltean 		.vid = vid,
159d66e4348SPetr Machata 	};
160d66e4348SPetr Machata 
16169b7320eSPetr Machata 	return switchdev_port_obj_add(dev, &v.obj, extack);
162d66e4348SPetr Machata }
163d66e4348SPetr Machata 
164d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
165d66e4348SPetr Machata {
166d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
167d66e4348SPetr Machata 		.obj.orig_dev = dev,
168d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
169b7a9e0daSVladimir Oltean 		.vid = vid,
170d66e4348SPetr Machata 	};
171d66e4348SPetr Machata 
172d66e4348SPetr Machata 	return switchdev_port_obj_del(dev, &v.obj);
173d66e4348SPetr Machata }
17485826610STobias Waldekranz 
17585826610STobias Waldekranz static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining)
17685826610STobias Waldekranz {
17785826610STobias Waldekranz 	struct net_bridge *br = joining->br;
17885826610STobias Waldekranz 	struct net_bridge_port *p;
17985826610STobias Waldekranz 	int hwdom;
18085826610STobias Waldekranz 
18185826610STobias Waldekranz 	/* joining is yet to be added to the port list. */
18285826610STobias Waldekranz 	list_for_each_entry(p, &br->port_list, list) {
1832f5dc00fSVladimir Oltean 		if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) {
18485826610STobias Waldekranz 			joining->hwdom = p->hwdom;
18585826610STobias Waldekranz 			return 0;
18685826610STobias Waldekranz 		}
18785826610STobias Waldekranz 	}
18885826610STobias Waldekranz 
18985826610STobias Waldekranz 	hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1);
19085826610STobias Waldekranz 	if (hwdom >= BR_HWDOM_MAX)
19185826610STobias Waldekranz 		return -EBUSY;
19285826610STobias Waldekranz 
19385826610STobias Waldekranz 	set_bit(hwdom, &br->busy_hwdoms);
19485826610STobias Waldekranz 	joining->hwdom = hwdom;
19585826610STobias Waldekranz 	return 0;
19685826610STobias Waldekranz }
19785826610STobias Waldekranz 
19885826610STobias Waldekranz static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving)
19985826610STobias Waldekranz {
20085826610STobias Waldekranz 	struct net_bridge *br = leaving->br;
20185826610STobias Waldekranz 	struct net_bridge_port *p;
20285826610STobias Waldekranz 
20385826610STobias Waldekranz 	/* leaving is no longer in the port list. */
20485826610STobias Waldekranz 	list_for_each_entry(p, &br->port_list, list) {
20585826610STobias Waldekranz 		if (p->hwdom == leaving->hwdom)
20685826610STobias Waldekranz 			return;
20785826610STobias Waldekranz 	}
20885826610STobias Waldekranz 
20985826610STobias Waldekranz 	clear_bit(leaving->hwdom, &br->busy_hwdoms);
21085826610STobias Waldekranz }
21185826610STobias Waldekranz 
2122f5dc00fSVladimir Oltean static int nbp_switchdev_add(struct net_bridge_port *p,
2132f5dc00fSVladimir Oltean 			     struct netdev_phys_item_id ppid,
21447211192STobias Waldekranz 			     bool tx_fwd_offload,
2152f5dc00fSVladimir Oltean 			     struct netlink_ext_ack *extack)
21685826610STobias Waldekranz {
21747211192STobias Waldekranz 	int err;
21847211192STobias Waldekranz 
2192f5dc00fSVladimir Oltean 	if (p->offload_count) {
2202f5dc00fSVladimir Oltean 		/* Prevent unsupported configurations such as a bridge port
2212f5dc00fSVladimir Oltean 		 * which is a bonding interface, and the member ports are from
2222f5dc00fSVladimir Oltean 		 * different hardware switches.
2232f5dc00fSVladimir Oltean 		 */
2242f5dc00fSVladimir Oltean 		if (!netdev_phys_item_id_same(&p->ppid, &ppid)) {
2252f5dc00fSVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
2262f5dc00fSVladimir Oltean 					   "Same bridge port cannot be offloaded by two physical switches");
2272f5dc00fSVladimir Oltean 			return -EBUSY;
22885826610STobias Waldekranz 		}
22985826610STobias Waldekranz 
2302f5dc00fSVladimir Oltean 		/* Tolerate drivers that call switchdev_bridge_port_offload()
2312f5dc00fSVladimir Oltean 		 * more than once for the same bridge port, such as when the
2322f5dc00fSVladimir Oltean 		 * bridge port is an offloaded bonding/team interface.
2332f5dc00fSVladimir Oltean 		 */
2342f5dc00fSVladimir Oltean 		p->offload_count++;
2352f5dc00fSVladimir Oltean 
2362f5dc00fSVladimir Oltean 		return 0;
2372f5dc00fSVladimir Oltean 	}
2382f5dc00fSVladimir Oltean 
2392f5dc00fSVladimir Oltean 	p->ppid = ppid;
2402f5dc00fSVladimir Oltean 	p->offload_count = 1;
2412f5dc00fSVladimir Oltean 
24247211192STobias Waldekranz 	err = nbp_switchdev_hwdom_set(p);
24347211192STobias Waldekranz 	if (err)
24447211192STobias Waldekranz 		return err;
24547211192STobias Waldekranz 
24647211192STobias Waldekranz 	if (tx_fwd_offload) {
24747211192STobias Waldekranz 		p->flags |= BR_TX_FWD_OFFLOAD;
24847211192STobias Waldekranz 		static_branch_inc(&br_switchdev_tx_fwd_offload);
24947211192STobias Waldekranz 	}
25047211192STobias Waldekranz 
25147211192STobias Waldekranz 	return 0;
25285826610STobias Waldekranz }
25385826610STobias Waldekranz 
2542f5dc00fSVladimir Oltean static void nbp_switchdev_del(struct net_bridge_port *p)
25585826610STobias Waldekranz {
2562f5dc00fSVladimir Oltean 	if (WARN_ON(!p->offload_count))
2572f5dc00fSVladimir Oltean 		return;
2582f5dc00fSVladimir Oltean 
2592f5dc00fSVladimir Oltean 	p->offload_count--;
2602f5dc00fSVladimir Oltean 
2612f5dc00fSVladimir Oltean 	if (p->offload_count)
2622f5dc00fSVladimir Oltean 		return;
26385826610STobias Waldekranz 
26485826610STobias Waldekranz 	if (p->hwdom)
26585826610STobias Waldekranz 		nbp_switchdev_hwdom_put(p);
26647211192STobias Waldekranz 
26747211192STobias Waldekranz 	if (p->flags & BR_TX_FWD_OFFLOAD) {
26847211192STobias Waldekranz 		p->flags &= ~BR_TX_FWD_OFFLOAD;
26947211192STobias Waldekranz 		static_branch_dec(&br_switchdev_tx_fwd_offload);
27047211192STobias Waldekranz 	}
27185826610STobias Waldekranz }
2722f5dc00fSVladimir Oltean 
2734e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx,
2744e51bf44SVladimir Oltean 				   struct notifier_block *atomic_nb,
2754e51bf44SVladimir Oltean 				   struct notifier_block *blocking_nb,
2764e51bf44SVladimir Oltean 				   struct netlink_ext_ack *extack)
2774e51bf44SVladimir Oltean {
2784e51bf44SVladimir Oltean 	struct net_device *br_dev = p->br->dev;
2794e51bf44SVladimir Oltean 	struct net_device *dev = p->dev;
2804e51bf44SVladimir Oltean 	int err;
2814e51bf44SVladimir Oltean 
2824e51bf44SVladimir Oltean 	err = br_vlan_replay(br_dev, dev, ctx, true, blocking_nb, extack);
2834e51bf44SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
2844e51bf44SVladimir Oltean 		return err;
2854e51bf44SVladimir Oltean 
2864e51bf44SVladimir Oltean 	err = br_mdb_replay(br_dev, dev, ctx, true, blocking_nb, extack);
2874e51bf44SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
2884e51bf44SVladimir Oltean 		return err;
2894e51bf44SVladimir Oltean 
2904e51bf44SVladimir Oltean 	/* Forwarding and termination FDB entries on the port */
2914e51bf44SVladimir Oltean 	err = br_fdb_replay(br_dev, dev, ctx, true, atomic_nb);
2924e51bf44SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
2934e51bf44SVladimir Oltean 		return err;
2944e51bf44SVladimir Oltean 
2954e51bf44SVladimir Oltean 	/* Termination FDB entries on the bridge itself */
2964e51bf44SVladimir Oltean 	err = br_fdb_replay(br_dev, br_dev, ctx, true, atomic_nb);
2974e51bf44SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
2984e51bf44SVladimir Oltean 		return err;
2994e51bf44SVladimir Oltean 
3004e51bf44SVladimir Oltean 	return 0;
3014e51bf44SVladimir Oltean }
3024e51bf44SVladimir Oltean 
3034e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p,
3044e51bf44SVladimir Oltean 				      const void *ctx,
3054e51bf44SVladimir Oltean 				      struct notifier_block *atomic_nb,
3064e51bf44SVladimir Oltean 				      struct notifier_block *blocking_nb)
3074e51bf44SVladimir Oltean {
3084e51bf44SVladimir Oltean 	struct net_device *br_dev = p->br->dev;
3094e51bf44SVladimir Oltean 	struct net_device *dev = p->dev;
3104e51bf44SVladimir Oltean 
3114e51bf44SVladimir Oltean 	br_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL);
3124e51bf44SVladimir Oltean 
3134e51bf44SVladimir Oltean 	br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL);
3144e51bf44SVladimir Oltean 
3154e51bf44SVladimir Oltean 	/* Forwarding and termination FDB entries on the port */
3164e51bf44SVladimir Oltean 	br_fdb_replay(br_dev, dev, ctx, false, atomic_nb);
3174e51bf44SVladimir Oltean 
3184e51bf44SVladimir Oltean 	/* Termination FDB entries on the bridge itself */
3194e51bf44SVladimir Oltean 	br_fdb_replay(br_dev, br_dev, ctx, false, atomic_nb);
3204e51bf44SVladimir Oltean }
3214e51bf44SVladimir Oltean 
3222f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a
3232f5dc00fSVladimir Oltean  * switchdev hardware domain to it.
3242f5dc00fSVladimir Oltean  */
3252f5dc00fSVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev,
3264e51bf44SVladimir Oltean 				  struct net_device *dev, const void *ctx,
3274e51bf44SVladimir Oltean 				  struct notifier_block *atomic_nb,
3284e51bf44SVladimir Oltean 				  struct notifier_block *blocking_nb,
32947211192STobias Waldekranz 				  bool tx_fwd_offload,
3302f5dc00fSVladimir Oltean 				  struct netlink_ext_ack *extack)
3312f5dc00fSVladimir Oltean {
3322f5dc00fSVladimir Oltean 	struct netdev_phys_item_id ppid;
3332f5dc00fSVladimir Oltean 	struct net_bridge_port *p;
3342f5dc00fSVladimir Oltean 	int err;
3352f5dc00fSVladimir Oltean 
3362f5dc00fSVladimir Oltean 	ASSERT_RTNL();
3372f5dc00fSVladimir Oltean 
3382f5dc00fSVladimir Oltean 	p = br_port_get_rtnl(brport_dev);
3392f5dc00fSVladimir Oltean 	if (!p)
3402f5dc00fSVladimir Oltean 		return -ENODEV;
3412f5dc00fSVladimir Oltean 
3422f5dc00fSVladimir Oltean 	err = dev_get_port_parent_id(dev, &ppid, false);
3432f5dc00fSVladimir Oltean 	if (err)
3442f5dc00fSVladimir Oltean 		return err;
3452f5dc00fSVladimir Oltean 
34647211192STobias Waldekranz 	err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack);
3474e51bf44SVladimir Oltean 	if (err)
3484e51bf44SVladimir Oltean 		return err;
3494e51bf44SVladimir Oltean 
3504e51bf44SVladimir Oltean 	err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack);
3514e51bf44SVladimir Oltean 	if (err)
3524e51bf44SVladimir Oltean 		goto out_switchdev_del;
3534e51bf44SVladimir Oltean 
3544e51bf44SVladimir Oltean 	return 0;
3554e51bf44SVladimir Oltean 
3564e51bf44SVladimir Oltean out_switchdev_del:
3574e51bf44SVladimir Oltean 	nbp_switchdev_del(p);
3584e51bf44SVladimir Oltean 
3594e51bf44SVladimir Oltean 	return err;
3602f5dc00fSVladimir Oltean }
3612f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload);
3622f5dc00fSVladimir Oltean 
3634e51bf44SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev,
3644e51bf44SVladimir Oltean 				     const void *ctx,
3654e51bf44SVladimir Oltean 				     struct notifier_block *atomic_nb,
3664e51bf44SVladimir Oltean 				     struct notifier_block *blocking_nb)
3672f5dc00fSVladimir Oltean {
3682f5dc00fSVladimir Oltean 	struct net_bridge_port *p;
3692f5dc00fSVladimir Oltean 
3702f5dc00fSVladimir Oltean 	ASSERT_RTNL();
3712f5dc00fSVladimir Oltean 
3722f5dc00fSVladimir Oltean 	p = br_port_get_rtnl(brport_dev);
3732f5dc00fSVladimir Oltean 	if (!p)
3742f5dc00fSVladimir Oltean 		return;
3752f5dc00fSVladimir Oltean 
3764e51bf44SVladimir Oltean 	nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb);
3774e51bf44SVladimir Oltean 
3782f5dc00fSVladimir Oltean 	nbp_switchdev_del(p);
3792f5dc00fSVladimir Oltean }
3802f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload);
381