xref: /openbmc/linux/net/bridge/br_switchdev.c (revision 3e19ae7c)
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,
63078bbb85SVladimir Oltean 			       unsigned long mask,
64078bbb85SVladimir Oltean 			       struct netlink_ext_ack *extack)
653922285dSArkadi Sharshevsky {
663922285dSArkadi Sharshevsky 	struct switchdev_attr attr = {
673922285dSArkadi Sharshevsky 		.orig_dev = p->dev,
683922285dSArkadi Sharshevsky 	};
69d45224d6SFlorian Fainelli 	struct switchdev_notifier_port_attr_info info = {
70d45224d6SFlorian Fainelli 		.attr = &attr,
71d45224d6SFlorian Fainelli 	};
723922285dSArkadi Sharshevsky 	int err;
733922285dSArkadi Sharshevsky 
74304ae3bfSVladimir Oltean 	mask &= BR_PORT_FLAGS_HW_OFFLOAD;
75304ae3bfSVladimir Oltean 	if (!mask)
763922285dSArkadi Sharshevsky 		return 0;
773922285dSArkadi Sharshevsky 
78e18f4c18SVladimir Oltean 	attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS;
79e18f4c18SVladimir Oltean 	attr.u.brport_flags.val = flags;
80e18f4c18SVladimir Oltean 	attr.u.brport_flags.mask = mask;
81304ae3bfSVladimir Oltean 
82d45224d6SFlorian Fainelli 	/* We run from atomic context here */
83d45224d6SFlorian Fainelli 	err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
84078bbb85SVladimir Oltean 				       &info.info, extack);
85d45224d6SFlorian Fainelli 	err = notifier_to_errno(err);
863922285dSArkadi Sharshevsky 	if (err == -EOPNOTSUPP)
873922285dSArkadi Sharshevsky 		return 0;
883922285dSArkadi Sharshevsky 
891ef07644SFlorian Fainelli 	if (err) {
90078bbb85SVladimir Oltean 		if (extack && !extack->_msg)
91078bbb85SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
92078bbb85SVladimir Oltean 					   "bridge flag offload is not supported");
933922285dSArkadi Sharshevsky 		return -EOPNOTSUPP;
943922285dSArkadi Sharshevsky 	}
953922285dSArkadi Sharshevsky 
963922285dSArkadi Sharshevsky 	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
973922285dSArkadi Sharshevsky 	attr.flags = SWITCHDEV_F_DEFER;
981ef07644SFlorian Fainelli 
99dcbdf135SVladimir Oltean 	err = switchdev_port_attr_set(p->dev, &attr, extack);
1003922285dSArkadi Sharshevsky 	if (err) {
101dcbdf135SVladimir Oltean 		if (extack && !extack->_msg)
102dcbdf135SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
103dcbdf135SVladimir Oltean 					   "error setting offload flag on port");
1043922285dSArkadi Sharshevsky 		return err;
1053922285dSArkadi Sharshevsky 	}
1063922285dSArkadi Sharshevsky 
1073922285dSArkadi Sharshevsky 	return 0;
1083922285dSArkadi Sharshevsky }
1096b26b51bSArkadi Sharshevsky 
1106b26b51bSArkadi Sharshevsky void
1116b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
1126b26b51bSArkadi Sharshevsky {
113*3e19ae7cSVladimir Oltean 	const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
114e5b4b898STobias Waldekranz 	struct switchdev_notifier_fdb_info info = {
115e5b4b898STobias Waldekranz 		.addr = fdb->key.addr.addr,
116e5b4b898STobias Waldekranz 		.vid = fdb->key.vlan_id,
117e5b4b898STobias Waldekranz 		.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
1182c4eca3eSVladimir Oltean 		.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags),
119e5b4b898STobias Waldekranz 		.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
120e5b4b898STobias Waldekranz 	};
121e5b4b898STobias Waldekranz 
122*3e19ae7cSVladimir Oltean 	if (!dst)
1236b26b51bSArkadi Sharshevsky 		return;
1246b26b51bSArkadi Sharshevsky 
1256b26b51bSArkadi Sharshevsky 	switch (type) {
1266b26b51bSArkadi Sharshevsky 	case RTM_DELNEIGH:
127e5b4b898STobias Waldekranz 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
128*3e19ae7cSVladimir Oltean 					 dst->dev, &info.info, NULL);
1296b26b51bSArkadi Sharshevsky 		break;
1306b26b51bSArkadi Sharshevsky 	case RTM_NEWNEIGH:
131e5b4b898STobias Waldekranz 		call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
132*3e19ae7cSVladimir Oltean 					 dst->dev, &info.info, NULL);
1336b26b51bSArkadi Sharshevsky 		break;
1346b26b51bSArkadi Sharshevsky 	}
1356b26b51bSArkadi Sharshevsky }
136d66e4348SPetr Machata 
137169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
138169327d5SPetr Machata 			       struct netlink_ext_ack *extack)
139d66e4348SPetr Machata {
140d66e4348SPetr Machata 	struct switchdev_obj_port_vlan v = {
141d66e4348SPetr Machata 		.obj.orig_dev = dev,
142d66e4348SPetr Machata 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
143d66e4348SPetr Machata 		.flags = flags,
144b7a9e0daSVladimir Oltean 		.vid = vid,
145d66e4348SPetr Machata 	};
146d66e4348SPetr Machata 
14769b7320eSPetr Machata 	return switchdev_port_obj_add(dev, &v.obj, extack);
148d66e4348SPetr Machata }
149d66e4348SPetr Machata 
150d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
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,
155b7a9e0daSVladimir Oltean 		.vid = vid,
156d66e4348SPetr Machata 	};
157d66e4348SPetr Machata 
158d66e4348SPetr Machata 	return switchdev_port_obj_del(dev, &v.obj);
159d66e4348SPetr Machata }
160