xref: /openbmc/linux/net/bridge/br_switchdev.c (revision b7a9e0da)
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,
681ef07644SFlorian Fainelli 		.u.brport_flags = mask,
693922285dSArkadi Sharshevsky 	};
70d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info info = {
71d45224d6SFlorian Fainelli 		.attr = &attr,
72d45224d6SFlorian Fainelli 	};
733922285dSArkadi Sharshevsky 	int err;
743922285dSArkadi Sharshevsky 
753922285dSArkadi Sharshevsky 	if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
763922285dSArkadi Sharshevsky 		return 0;
773922285dSArkadi Sharshevsky 
78d45224d6SFlorian Fainelli 	/* We run from atomic context here */
79d45224d6SFlorian Fainelli 	err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
80d45224d6SFlorian Fainelli 				       &info.info, NULL);
81d45224d6SFlorian Fainelli 	err = notifier_to_errno(err);
823922285dSArkadi Sharshevsky 	if (err == -EOPNOTSUPP)
833922285dSArkadi Sharshevsky 		return 0;
843922285dSArkadi Sharshevsky 
851ef07644SFlorian Fainelli 	if (err) {
863922285dSArkadi Sharshevsky 		br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
873922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
883922285dSArkadi Sharshevsky 		return -EOPNOTSUPP;
893922285dSArkadi Sharshevsky 	}
903922285dSArkadi Sharshevsky 
913922285dSArkadi Sharshevsky 	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
923922285dSArkadi Sharshevsky 	attr.flags = SWITCHDEV_F_DEFER;
933922285dSArkadi Sharshevsky 	attr.u.brport_flags = flags;
941ef07644SFlorian Fainelli 
953922285dSArkadi Sharshevsky 	err = switchdev_port_attr_set(p->dev, &attr);
963922285dSArkadi Sharshevsky 	if (err) {
973922285dSArkadi Sharshevsky 		br_warn(p->br, "error setting offload flag on port %u(%s)\n",
983922285dSArkadi Sharshevsky 			(unsigned int)p->port_no, p->dev->name);
993922285dSArkadi Sharshevsky 		return err;
1003922285dSArkadi Sharshevsky 	}
1013922285dSArkadi Sharshevsky 
1023922285dSArkadi Sharshevsky 	return 0;
1033922285dSArkadi Sharshevsky }
1046b26b51bSArkadi Sharshevsky 
1056b26b51bSArkadi Sharshevsky static void
1066b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
107816a3bedSPetr Machata 				u16 vid, struct net_device *dev,
108e9ba0fbcSIdo Schimmel 				bool added_by_user, bool offloaded)
1096b26b51bSArkadi Sharshevsky {
1106b26b51bSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info info;
1116b26b51bSArkadi Sharshevsky 	unsigned long notifier_type;
1126b26b51bSArkadi Sharshevsky 
1136b26b51bSArkadi Sharshevsky 	info.addr = mac;
1146b26b51bSArkadi Sharshevsky 	info.vid = vid;
115816a3bedSPetr Machata 	info.added_by_user = added_by_user;
116e9ba0fbcSIdo Schimmel 	info.offloaded = offloaded;
1176b26b51bSArkadi Sharshevsky 	notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
1186685987cSPetr Machata 	call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
1196b26b51bSArkadi Sharshevsky }
1206b26b51bSArkadi Sharshevsky 
1216b26b51bSArkadi Sharshevsky void
1226b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
1236b26b51bSArkadi Sharshevsky {
124161d82deSPetr Machata 	if (!fdb->dst)
1256b26b51bSArkadi Sharshevsky 		return;
1266b26b51bSArkadi Sharshevsky 
1276b26b51bSArkadi Sharshevsky 	switch (type) {
1286b26b51bSArkadi Sharshevsky 	case RTM_DELNEIGH:
129eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
130eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
131816a3bedSPetr Machata 						fdb->dst->dev,
132ac3ca6afSNikolay Aleksandrov 						test_bit(BR_FDB_ADDED_BY_USER,
133ac3ca6afSNikolay Aleksandrov 							 &fdb->flags),
134d38c6e3dSNikolay Aleksandrov 						test_bit(BR_FDB_OFFLOADED,
135d38c6e3dSNikolay Aleksandrov 							 &fdb->flags));
1366b26b51bSArkadi Sharshevsky 		break;
1376b26b51bSArkadi Sharshevsky 	case RTM_NEWNEIGH:
138eb793583SNikolay Aleksandrov 		br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
139eb793583SNikolay Aleksandrov 						fdb->key.vlan_id,
140816a3bedSPetr Machata 						fdb->dst->dev,
141ac3ca6afSNikolay Aleksandrov 						test_bit(BR_FDB_ADDED_BY_USER,
142ac3ca6afSNikolay Aleksandrov 							 &fdb->flags),
143d38c6e3dSNikolay Aleksandrov 						test_bit(BR_FDB_OFFLOADED,
144d38c6e3dSNikolay Aleksandrov 							 &fdb->flags));
1456b26b51bSArkadi Sharshevsky 		break;
1466b26b51bSArkadi Sharshevsky 	}
1476b26b51bSArkadi Sharshevsky }
148d66e4348SPetr Machata 
149169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
150169327d5SPetr Machata 			       struct netlink_ext_ack *extack)
151d66e4348SPetr Machata {
152d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
153d66e4348SPetr Machata 		.obj.orig_dev = dev,
154d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
155d66e4348SPetr Machata 		.flags = flags,
156*b7a9e0daSVladimir Oltean 		.vid = vid,
157d66e4348SPetr Machata 	};
158d66e4348SPetr Machata 
15969b7320eSPetr Machata 	return switchdev_port_obj_add(dev, &v.obj, extack);
160d66e4348SPetr Machata }
161d66e4348SPetr Machata 
162d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
163d66e4348SPetr Machata {
164d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
165d66e4348SPetr Machata 		.obj.orig_dev = dev,
166d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
167*b7a9e0daSVladimir Oltean 		.vid = vid,
168d66e4348SPetr Machata 	};
169d66e4348SPetr Machata 
170d66e4348SPetr Machata 	return switchdev_port_obj_del(dev, &v.obj);
171d66e4348SPetr Machata }
172