xref: /openbmc/linux/net/bridge/br_switchdev.c (revision 169327d5)
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 
116bc506b4SIdo Schimmel static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
126bc506b4SIdo Schimmel {
136bc506b4SIdo Schimmel 	struct net_bridge_port *p;
146bc506b4SIdo Schimmel 
156bc506b4SIdo Schimmel 	/* dev is yet to be added to the port list. */
166bc506b4SIdo Schimmel 	list_for_each_entry(p, &br->port_list, list) {
176bc506b4SIdo Schimmel 		if (switchdev_port_same_parent_id(dev, p->dev))
186bc506b4SIdo Schimmel 			return p->offload_fwd_mark;
196bc506b4SIdo Schimmel 	}
206bc506b4SIdo Schimmel 
216bc506b4SIdo Schimmel 	return ++br->offload_fwd_mark;
226bc506b4SIdo Schimmel }
236bc506b4SIdo Schimmel 
246bc506b4SIdo Schimmel int nbp_switchdev_mark_set(struct net_bridge_port *p)
256bc506b4SIdo Schimmel {
266bc506b4SIdo Schimmel 	struct switchdev_attr attr = {
276bc506b4SIdo Schimmel 		.orig_dev = p->dev,
286bc506b4SIdo Schimmel 		.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
296bc506b4SIdo Schimmel 	};
306bc506b4SIdo Schimmel 	int err;
316bc506b4SIdo Schimmel 
326bc506b4SIdo Schimmel 	ASSERT_RTNL();
336bc506b4SIdo Schimmel 
346bc506b4SIdo Schimmel 	err = switchdev_port_attr_get(p->dev, &attr);
356bc506b4SIdo Schimmel 	if (err) {
366bc506b4SIdo Schimmel 		if (err == -EOPNOTSUPP)
376bc506b4SIdo Schimmel 			return 0;
386bc506b4SIdo Schimmel 		return err;
396bc506b4SIdo Schimmel 	}
406bc506b4SIdo Schimmel 
416bc506b4SIdo Schimmel 	p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
426bc506b4SIdo Schimmel 
436bc506b4SIdo Schimmel 	return 0;
446bc506b4SIdo Schimmel }
456bc506b4SIdo Schimmel 
466bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
476bc506b4SIdo Schimmel 			      struct sk_buff *skb)
486bc506b4SIdo Schimmel {
496bc506b4SIdo Schimmel 	if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
506bc506b4SIdo Schimmel 		BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
516bc506b4SIdo Schimmel }
526bc506b4SIdo Schimmel 
536bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
546bc506b4SIdo Schimmel 				  const struct sk_buff *skb)
556bc506b4SIdo Schimmel {
566bc506b4SIdo Schimmel 	return !skb->offload_fwd_mark ||
576bc506b4SIdo Schimmel 	       BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
586bc506b4SIdo Schimmel }
593922285dSArkadi Sharshevsky 
603922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */
613922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
623922285dSArkadi Sharshevsky 				  BR_MCAST_FLOOD | BR_BCAST_FLOOD)
633922285dSArkadi Sharshevsky 
643922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p,
653922285dSArkadi Sharshevsky 			       unsigned long flags,
663922285dSArkadi Sharshevsky 			       unsigned long mask)
673922285dSArkadi Sharshevsky {
683922285dSArkadi Sharshevsky 	struct switchdev_attr attr = {
693922285dSArkadi Sharshevsky 		.orig_dev = p->dev,
703922285dSArkadi Sharshevsky 		.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT,
713922285dSArkadi Sharshevsky 	};
723922285dSArkadi Sharshevsky 	int err;
733922285dSArkadi Sharshevsky 
743922285dSArkadi Sharshevsky 	if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
753922285dSArkadi Sharshevsky 		return 0;
763922285dSArkadi Sharshevsky 
773922285dSArkadi Sharshevsky 	err = switchdev_port_attr_get(p->dev, &attr);
783922285dSArkadi Sharshevsky 	if (err == -EOPNOTSUPP)
793922285dSArkadi Sharshevsky 		return 0;
803922285dSArkadi Sharshevsky 	if (err)
813922285dSArkadi Sharshevsky 		return err;
823922285dSArkadi Sharshevsky 
833922285dSArkadi Sharshevsky 	/* Check if specific bridge flag attribute offload is supported */
843922285dSArkadi Sharshevsky 	if (!(attr.u.brport_flags_support & mask)) {
853922285dSArkadi Sharshevsky 		br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
863922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
873922285dSArkadi Sharshevsky 		return -EOPNOTSUPP;
883922285dSArkadi Sharshevsky 	}
893922285dSArkadi Sharshevsky 
903922285dSArkadi Sharshevsky 	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
913922285dSArkadi Sharshevsky 	attr.flags = SWITCHDEV_F_DEFER;
923922285dSArkadi Sharshevsky 	attr.u.brport_flags = flags;
933922285dSArkadi Sharshevsky 	err = switchdev_port_attr_set(p->dev, &attr);
943922285dSArkadi Sharshevsky 	if (err) {
953922285dSArkadi Sharshevsky 		br_warn(p->br, "error setting offload flag on port %u(%s)\n",
963922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
973922285dSArkadi Sharshevsky 		return err;
983922285dSArkadi Sharshevsky 	}
993922285dSArkadi Sharshevsky 
1003922285dSArkadi Sharshevsky 	return 0;
1013922285dSArkadi Sharshevsky }
1026b26b51bSArkadi Sharshevsky 
1036b26b51bSArkadi Sharshevsky static void
1046b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
105816a3bedSPetr Machata 				u16 vid, struct net_device *dev,
106e9ba0fbcSIdo Schimmel 				bool added_by_user, bool offloaded)
1076b26b51bSArkadi Sharshevsky {
1086b26b51bSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info info;
1096b26b51bSArkadi Sharshevsky 	unsigned long notifier_type;
1106b26b51bSArkadi Sharshevsky 
1116b26b51bSArkadi Sharshevsky 	info.addr = mac;
1126b26b51bSArkadi Sharshevsky 	info.vid = vid;
113816a3bedSPetr Machata 	info.added_by_user = added_by_user;
114e9ba0fbcSIdo Schimmel 	info.offloaded = offloaded;
1156b26b51bSArkadi Sharshevsky 	notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
1166b26b51bSArkadi Sharshevsky 	call_switchdev_notifiers(notifier_type, dev, &info.info);
1176b26b51bSArkadi Sharshevsky }
1186b26b51bSArkadi Sharshevsky 
1196b26b51bSArkadi Sharshevsky void
1206b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
1216b26b51bSArkadi Sharshevsky {
122161d82deSPetr Machata 	if (!fdb->dst)
1236b26b51bSArkadi Sharshevsky 		return;
1246b26b51bSArkadi Sharshevsky 
1256b26b51bSArkadi Sharshevsky 	switch (type) {
1266b26b51bSArkadi Sharshevsky 	case RTM_DELNEIGH:
127eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
128eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
129816a3bedSPetr Machata 						fdb->dst->dev,
130e9ba0fbcSIdo Schimmel 						fdb->added_by_user,
131e9ba0fbcSIdo Schimmel 						fdb->offloaded);
1326b26b51bSArkadi Sharshevsky 		break;
1336b26b51bSArkadi Sharshevsky 	case RTM_NEWNEIGH:
134eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
135eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
136816a3bedSPetr Machata 						fdb->dst->dev,
137e9ba0fbcSIdo Schimmel 						fdb->added_by_user,
138e9ba0fbcSIdo Schimmel 						fdb->offloaded);
1396b26b51bSArkadi Sharshevsky 		break;
1406b26b51bSArkadi Sharshevsky 	}
1416b26b51bSArkadi Sharshevsky }
142d66e4348SPetr Machata 
143169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
144169327d5SPetr Machata 			       struct netlink_ext_ack *extack)
145d66e4348SPetr Machata {
146d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
147d66e4348SPetr Machata 		.obj.orig_dev = dev,
148d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
149d66e4348SPetr Machata 		.flags = flags,
150d66e4348SPetr Machata 		.vid_begin = vid,
151d66e4348SPetr Machata 		.vid_end = vid,
152d66e4348SPetr Machata 	};
153d66e4348SPetr Machata 
154d66e4348SPetr Machata 	return switchdev_port_obj_add(dev, &v.obj);
155d66e4348SPetr Machata }
156d66e4348SPetr Machata 
157d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
158d66e4348SPetr Machata {
159d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
160d66e4348SPetr Machata 		.obj.orig_dev = dev,
161d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
162d66e4348SPetr Machata 		.vid_begin = vid,
163d66e4348SPetr Machata 		.vid_end = vid,
164d66e4348SPetr Machata 	};
165d66e4348SPetr Machata 
166d66e4348SPetr Machata 	return switchdev_port_obj_del(dev, &v.obj);
167d66e4348SPetr Machata }
168