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, 132e9ba0fbcSIdo Schimmel fdb->added_by_user, 133e9ba0fbcSIdo Schimmel fdb->offloaded); 1346b26b51bSArkadi Sharshevsky break; 1356b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 136eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 137eb793583SNikolay Aleksandrov fdb->key.vlan_id, 138816a3bedSPetr Machata fdb->dst->dev, 139e9ba0fbcSIdo Schimmel fdb->added_by_user, 140e9ba0fbcSIdo Schimmel fdb->offloaded); 1416b26b51bSArkadi Sharshevsky break; 1426b26b51bSArkadi Sharshevsky } 1436b26b51bSArkadi Sharshevsky } 144d66e4348SPetr Machata 145169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 146169327d5SPetr Machata struct netlink_ext_ack *extack) 147d66e4348SPetr Machata { 148d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 149d66e4348SPetr Machata .obj.orig_dev = dev, 150d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 151d66e4348SPetr Machata .flags = flags, 152d66e4348SPetr Machata .vid_begin = vid, 153d66e4348SPetr Machata .vid_end = vid, 154d66e4348SPetr Machata }; 155d66e4348SPetr Machata 15669b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 157d66e4348SPetr Machata } 158d66e4348SPetr Machata 159d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 160d66e4348SPetr Machata { 161d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 162d66e4348SPetr Machata .obj.orig_dev = dev, 163d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 164d66e4348SPetr Machata .vid_begin = vid, 165d66e4348SPetr Machata .vid_end = vid, 166d66e4348SPetr Machata }; 167d66e4348SPetr Machata 168d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 169d66e4348SPetr Machata } 170