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