xref: /openbmc/linux/net/bridge/br_switchdev.c (revision 304ae3bf)
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) {
17bccb3025SFlorian Fainelli 		if (netdev_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 {
26bccb3025SFlorian Fainelli 	struct netdev_phys_item_id ppid = { };
276bc506b4SIdo Schimmel 	int err;
286bc506b4SIdo Schimmel 
296bc506b4SIdo Schimmel 	ASSERT_RTNL();
306bc506b4SIdo Schimmel 
31bccb3025SFlorian Fainelli 	err = dev_get_port_parent_id(p->dev, &ppid, true);
326bc506b4SIdo Schimmel 	if (err) {
336bc506b4SIdo Schimmel 		if (err == -EOPNOTSUPP)
346bc506b4SIdo Schimmel 			return 0;
356bc506b4SIdo Schimmel 		return err;
366bc506b4SIdo Schimmel 	}
376bc506b4SIdo Schimmel 
386bc506b4SIdo Schimmel 	p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
396bc506b4SIdo Schimmel 
406bc506b4SIdo Schimmel 	return 0;
416bc506b4SIdo Schimmel }
426bc506b4SIdo Schimmel 
436bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
446bc506b4SIdo Schimmel 			      struct sk_buff *skb)
456bc506b4SIdo Schimmel {
466bc506b4SIdo Schimmel 	if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
476bc506b4SIdo Schimmel 		BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
486bc506b4SIdo Schimmel }
496bc506b4SIdo Schimmel 
506bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
516bc506b4SIdo Schimmel 				  const struct sk_buff *skb)
526bc506b4SIdo Schimmel {
536bc506b4SIdo Schimmel 	return !skb->offload_fwd_mark ||
546bc506b4SIdo Schimmel 	       BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
556bc506b4SIdo Schimmel }
563922285dSArkadi Sharshevsky 
573922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */
583922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
593922285dSArkadi Sharshevsky 				  BR_MCAST_FLOOD | BR_BCAST_FLOOD)
603922285dSArkadi Sharshevsky 
613922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p,
623922285dSArkadi Sharshevsky 			       unsigned long flags,
633922285dSArkadi Sharshevsky 			       unsigned long mask)
643922285dSArkadi Sharshevsky {
653922285dSArkadi Sharshevsky 	struct switchdev_attr attr = {
663922285dSArkadi Sharshevsky 		.orig_dev = p->dev,
671ef07644SFlorian Fainelli 		.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
683922285dSArkadi Sharshevsky 	};
69d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info info = {
70d45224d6SFlorian Fainelli 		.attr = &attr,
71d45224d6SFlorian Fainelli 	};
723922285dSArkadi Sharshevsky 	int err;
733922285dSArkadi Sharshevsky 
74*304ae3bfSVladimir Oltean 	mask &= BR_PORT_FLAGS_HW_OFFLOAD;
75*304ae3bfSVladimir Oltean 	if (!mask)
763922285dSArkadi Sharshevsky 		return 0;
773922285dSArkadi Sharshevsky 
78*304ae3bfSVladimir Oltean 	attr.u.brport_flags = mask;
79*304ae3bfSVladimir Oltean 
80d45224d6SFlorian Fainelli 	/* We run from atomic context here */
81d45224d6SFlorian Fainelli 	err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
82d45224d6SFlorian Fainelli 				       &info.info, NULL);
83d45224d6SFlorian Fainelli 	err = notifier_to_errno(err);
843922285dSArkadi Sharshevsky 	if (err == -EOPNOTSUPP)
853922285dSArkadi Sharshevsky 		return 0;
863922285dSArkadi Sharshevsky 
871ef07644SFlorian Fainelli 	if (err) {
883922285dSArkadi Sharshevsky 		br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
893922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
903922285dSArkadi Sharshevsky 		return -EOPNOTSUPP;
913922285dSArkadi Sharshevsky 	}
923922285dSArkadi Sharshevsky 
933922285dSArkadi Sharshevsky 	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
943922285dSArkadi Sharshevsky 	attr.flags = SWITCHDEV_F_DEFER;
953922285dSArkadi Sharshevsky 	attr.u.brport_flags = flags;
961ef07644SFlorian Fainelli 
973922285dSArkadi Sharshevsky 	err = switchdev_port_attr_set(p->dev, &attr);
983922285dSArkadi Sharshevsky 	if (err) {
993922285dSArkadi Sharshevsky 		br_warn(p->br, "error setting offload flag on port %u(%s)\n",
1003922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
1013922285dSArkadi Sharshevsky 		return err;
1023922285dSArkadi Sharshevsky 	}
1033922285dSArkadi Sharshevsky 
1043922285dSArkadi Sharshevsky 	return 0;
1053922285dSArkadi Sharshevsky }
1066b26b51bSArkadi Sharshevsky 
1076b26b51bSArkadi Sharshevsky static void
1086b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
109816a3bedSPetr Machata 				u16 vid, struct net_device *dev,
110e9ba0fbcSIdo Schimmel 				bool added_by_user, bool offloaded)
1116b26b51bSArkadi Sharshevsky {
1126b26b51bSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info info;
1136b26b51bSArkadi Sharshevsky 	unsigned long notifier_type;
1146b26b51bSArkadi Sharshevsky 
1156b26b51bSArkadi Sharshevsky 	info.addr = mac;
1166b26b51bSArkadi Sharshevsky 	info.vid = vid;
117816a3bedSPetr Machata 	info.added_by_user = added_by_user;
118e9ba0fbcSIdo Schimmel 	info.offloaded = offloaded;
1196b26b51bSArkadi Sharshevsky 	notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
1206685987cSPetr Machata 	call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
1216b26b51bSArkadi Sharshevsky }
1226b26b51bSArkadi Sharshevsky 
1236b26b51bSArkadi Sharshevsky void
1246b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
1256b26b51bSArkadi Sharshevsky {
126161d82deSPetr Machata 	if (!fdb->dst)
1276b26b51bSArkadi Sharshevsky 		return;
1286b26b51bSArkadi Sharshevsky 
1296b26b51bSArkadi Sharshevsky 	switch (type) {
1306b26b51bSArkadi Sharshevsky 	case RTM_DELNEIGH:
131eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
132eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
133816a3bedSPetr Machata 						fdb->dst->dev,
134ac3ca6afSNikolay Aleksandrov 						test_bit(BR_FDB_ADDED_BY_USER,
135ac3ca6afSNikolay Aleksandrov 							 &fdb->flags),
136d38c6e3dSNikolay Aleksandrov 						test_bit(BR_FDB_OFFLOADED,
137d38c6e3dSNikolay Aleksandrov 							 &fdb->flags));
1386b26b51bSArkadi Sharshevsky 		break;
1396b26b51bSArkadi Sharshevsky 	case RTM_NEWNEIGH:
140eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
141eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
142816a3bedSPetr Machata 						fdb->dst->dev,
143ac3ca6afSNikolay Aleksandrov 						test_bit(BR_FDB_ADDED_BY_USER,
144ac3ca6afSNikolay Aleksandrov 							 &fdb->flags),
145d38c6e3dSNikolay Aleksandrov 						test_bit(BR_FDB_OFFLOADED,
146d38c6e3dSNikolay Aleksandrov 							 &fdb->flags));
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 }
174