18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/list.h>
48c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
58c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
68c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
78c2ecf20Sopenharmony_ci#include <net/switchdev.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "br_private.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	/* dev is yet to be added to the port list. */
168c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
178c2ecf20Sopenharmony_ci		if (netdev_port_same_parent_id(dev, p->dev))
188c2ecf20Sopenharmony_ci			return p->offload_fwd_mark;
198c2ecf20Sopenharmony_ci	}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	return ++br->offload_fwd_mark;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciint nbp_switchdev_mark_set(struct net_bridge_port *p)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct netdev_phys_item_id ppid = { };
278c2ecf20Sopenharmony_ci	int err;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	ASSERT_RTNL();
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	err = dev_get_port_parent_id(p->dev, &ppid, true);
328c2ecf20Sopenharmony_ci	if (err) {
338c2ecf20Sopenharmony_ci		if (err == -EOPNOTSUPP)
348c2ecf20Sopenharmony_ci			return 0;
358c2ecf20Sopenharmony_ci		return err;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid nbp_switchdev_frame_mark(const struct net_bridge_port *p,
448c2ecf20Sopenharmony_ci			      struct sk_buff *skb)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
478c2ecf20Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cibool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
518c2ecf20Sopenharmony_ci				  const struct sk_buff *skb)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	return !skb->offload_fwd_mark ||
548c2ecf20Sopenharmony_ci	       BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Flags that can be offloaded to hardware */
588c2ecf20Sopenharmony_ci#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
598c2ecf20Sopenharmony_ci				  BR_MCAST_FLOOD | BR_BCAST_FLOOD)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint br_switchdev_set_port_flag(struct net_bridge_port *p,
628c2ecf20Sopenharmony_ci			       unsigned long flags,
638c2ecf20Sopenharmony_ci			       unsigned long mask)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
668c2ecf20Sopenharmony_ci		.orig_dev = p->dev,
678c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
688c2ecf20Sopenharmony_ci		.u.brport_flags = mask,
698c2ecf20Sopenharmony_ci	};
708c2ecf20Sopenharmony_ci	struct switchdev_notifier_port_attr_info info = {
718c2ecf20Sopenharmony_ci		.attr = &attr,
728c2ecf20Sopenharmony_ci	};
738c2ecf20Sopenharmony_ci	int err;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
768c2ecf20Sopenharmony_ci		return 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* We run from atomic context here */
798c2ecf20Sopenharmony_ci	err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
808c2ecf20Sopenharmony_ci				       &info.info, NULL);
818c2ecf20Sopenharmony_ci	err = notifier_to_errno(err);
828c2ecf20Sopenharmony_ci	if (err == -EOPNOTSUPP)
838c2ecf20Sopenharmony_ci		return 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (err) {
868c2ecf20Sopenharmony_ci		br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
878c2ecf20Sopenharmony_ci			(unsigned int)p->port_no, p->dev->name);
888c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
928c2ecf20Sopenharmony_ci	attr.flags = SWITCHDEV_F_DEFER;
938c2ecf20Sopenharmony_ci	attr.u.brport_flags = flags;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	err = switchdev_port_attr_set(p->dev, &attr);
968c2ecf20Sopenharmony_ci	if (err) {
978c2ecf20Sopenharmony_ci		br_warn(p->br, "error setting offload flag on port %u(%s)\n",
988c2ecf20Sopenharmony_ci			(unsigned int)p->port_no, p->dev->name);
998c2ecf20Sopenharmony_ci		return err;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void
1068c2ecf20Sopenharmony_cibr_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
1078c2ecf20Sopenharmony_ci				u16 vid, struct net_device *dev,
1088c2ecf20Sopenharmony_ci				bool added_by_user, bool offloaded)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info info;
1118c2ecf20Sopenharmony_ci	unsigned long notifier_type;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	info.addr = mac;
1148c2ecf20Sopenharmony_ci	info.vid = vid;
1158c2ecf20Sopenharmony_ci	info.added_by_user = added_by_user;
1168c2ecf20Sopenharmony_ci	info.offloaded = offloaded;
1178c2ecf20Sopenharmony_ci	notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
1188c2ecf20Sopenharmony_ci	call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid
1228c2ecf20Sopenharmony_cibr_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	if (!fdb->dst)
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci	if (test_bit(BR_FDB_LOCAL, &fdb->flags))
1278c2ecf20Sopenharmony_ci		return;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	switch (type) {
1308c2ecf20Sopenharmony_ci	case RTM_DELNEIGH:
1318c2ecf20Sopenharmony_ci		br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
1328c2ecf20Sopenharmony_ci						fdb->key.vlan_id,
1338c2ecf20Sopenharmony_ci						fdb->dst->dev,
1348c2ecf20Sopenharmony_ci						test_bit(BR_FDB_ADDED_BY_USER,
1358c2ecf20Sopenharmony_ci							 &fdb->flags),
1368c2ecf20Sopenharmony_ci						test_bit(BR_FDB_OFFLOADED,
1378c2ecf20Sopenharmony_ci							 &fdb->flags));
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	case RTM_NEWNEIGH:
1408c2ecf20Sopenharmony_ci		br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
1418c2ecf20Sopenharmony_ci						fdb->key.vlan_id,
1428c2ecf20Sopenharmony_ci						fdb->dst->dev,
1438c2ecf20Sopenharmony_ci						test_bit(BR_FDB_ADDED_BY_USER,
1448c2ecf20Sopenharmony_ci							 &fdb->flags),
1458c2ecf20Sopenharmony_ci						test_bit(BR_FDB_OFFLOADED,
1468c2ecf20Sopenharmony_ci							 &fdb->flags));
1478c2ecf20Sopenharmony_ci		break;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciint br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
1528c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct switchdev_obj_port_vlan v = {
1558c2ecf20Sopenharmony_ci		.obj.orig_dev = dev,
1568c2ecf20Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
1578c2ecf20Sopenharmony_ci		.flags = flags,
1588c2ecf20Sopenharmony_ci		.vid_begin = vid,
1598c2ecf20Sopenharmony_ci		.vid_end = vid,
1608c2ecf20Sopenharmony_ci	};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return switchdev_port_obj_add(dev, &v.obj, extack);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciint br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct switchdev_obj_port_vlan v = {
1688c2ecf20Sopenharmony_ci		.obj.orig_dev = dev,
1698c2ecf20Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
1708c2ecf20Sopenharmony_ci		.vid_begin = vid,
1718c2ecf20Sopenharmony_ci		.vid_end = vid,
1728c2ecf20Sopenharmony_ci	};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return switchdev_port_obj_del(dev, &v.obj);
1758c2ecf20Sopenharmony_ci}
176