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, 683922285dSArkadi Sharshevsky }; 69d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info info = { 70d45224d6SFlorian Fainelli .attr = &attr, 71d45224d6SFlorian Fainelli }; 723922285dSArkadi Sharshevsky int err; 733922285dSArkadi Sharshevsky 74*304ae3bfSVladimir Oltean mask &= BR_PORT_FLAGS_HW_OFFLOAD; 75*304ae3bfSVladimir Oltean if (!mask) 763922285dSArkadi Sharshevsky return 0; 773922285dSArkadi Sharshevsky 78*304ae3bfSVladimir Oltean attr.u.brport_flags = mask; 79*304ae3bfSVladimir Oltean 80d45224d6SFlorian Fainelli /* We run from atomic context here */ 81d45224d6SFlorian Fainelli err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 82d45224d6SFlorian Fainelli &info.info, NULL); 83d45224d6SFlorian Fainelli err = notifier_to_errno(err); 843922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 853922285dSArkadi Sharshevsky return 0; 863922285dSArkadi Sharshevsky 871ef07644SFlorian Fainelli if (err) { 883922285dSArkadi Sharshevsky br_warn(p->br, "bridge flag offload is not supported %u(%s)\n", 893922285dSArkadi Sharshevsky (unsigned int)p->port_no, p->dev->name); 903922285dSArkadi Sharshevsky return -EOPNOTSUPP; 913922285dSArkadi Sharshevsky } 923922285dSArkadi Sharshevsky 933922285dSArkadi Sharshevsky attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 943922285dSArkadi Sharshevsky attr.flags = SWITCHDEV_F_DEFER; 953922285dSArkadi Sharshevsky attr.u.brport_flags = flags; 961ef07644SFlorian Fainelli 973922285dSArkadi Sharshevsky err = switchdev_port_attr_set(p->dev, &attr); 983922285dSArkadi Sharshevsky if (err) { 993922285dSArkadi Sharshevsky br_warn(p->br, "error setting offload flag on port %u(%s)\n", 1003922285dSArkadi Sharshevsky (unsigned int)p->port_no, p->dev->name); 1013922285dSArkadi Sharshevsky return err; 1023922285dSArkadi Sharshevsky } 1033922285dSArkadi Sharshevsky 1043922285dSArkadi Sharshevsky return 0; 1053922285dSArkadi Sharshevsky } 1066b26b51bSArkadi Sharshevsky 1076b26b51bSArkadi Sharshevsky static void 1086b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, 109816a3bedSPetr Machata u16 vid, struct net_device *dev, 110e9ba0fbcSIdo Schimmel bool added_by_user, bool offloaded) 1116b26b51bSArkadi Sharshevsky { 1126b26b51bSArkadi Sharshevsky struct switchdev_notifier_fdb_info info; 1136b26b51bSArkadi Sharshevsky unsigned long notifier_type; 1146b26b51bSArkadi Sharshevsky 1156b26b51bSArkadi Sharshevsky info.addr = mac; 1166b26b51bSArkadi Sharshevsky info.vid = vid; 117816a3bedSPetr Machata info.added_by_user = added_by_user; 118e9ba0fbcSIdo Schimmel info.offloaded = offloaded; 1196b26b51bSArkadi Sharshevsky notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; 1206685987cSPetr Machata call_switchdev_notifiers(notifier_type, dev, &info.info, NULL); 1216b26b51bSArkadi Sharshevsky } 1226b26b51bSArkadi Sharshevsky 1236b26b51bSArkadi Sharshevsky void 1246b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 1256b26b51bSArkadi Sharshevsky { 126161d82deSPetr Machata if (!fdb->dst) 1276b26b51bSArkadi Sharshevsky return; 1286b26b51bSArkadi Sharshevsky 1296b26b51bSArkadi Sharshevsky switch (type) { 1306b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 131eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, 132eb793583SNikolay Aleksandrov fdb->key.vlan_id, 133816a3bedSPetr Machata fdb->dst->dev, 134ac3ca6afSNikolay Aleksandrov test_bit(BR_FDB_ADDED_BY_USER, 135ac3ca6afSNikolay Aleksandrov &fdb->flags), 136d38c6e3dSNikolay Aleksandrov test_bit(BR_FDB_OFFLOADED, 137d38c6e3dSNikolay Aleksandrov &fdb->flags)); 1386b26b51bSArkadi Sharshevsky break; 1396b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 140eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 141eb793583SNikolay Aleksandrov fdb->key.vlan_id, 142816a3bedSPetr Machata fdb->dst->dev, 143ac3ca6afSNikolay Aleksandrov test_bit(BR_FDB_ADDED_BY_USER, 144ac3ca6afSNikolay Aleksandrov &fdb->flags), 145d38c6e3dSNikolay Aleksandrov test_bit(BR_FDB_OFFLOADED, 146d38c6e3dSNikolay Aleksandrov &fdb->flags)); 1476b26b51bSArkadi Sharshevsky break; 1486b26b51bSArkadi Sharshevsky } 1496b26b51bSArkadi Sharshevsky } 150d66e4348SPetr Machata 151169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 152169327d5SPetr Machata struct netlink_ext_ack *extack) 153d66e4348SPetr Machata { 154d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 155d66e4348SPetr Machata .obj.orig_dev = dev, 156d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 157d66e4348SPetr Machata .flags = flags, 158b7a9e0daSVladimir Oltean .vid = vid, 159d66e4348SPetr Machata }; 160d66e4348SPetr Machata 16169b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 162d66e4348SPetr Machata } 163d66e4348SPetr Machata 164d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 165d66e4348SPetr Machata { 166d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 167d66e4348SPetr Machata .obj.orig_dev = dev, 168d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 169b7a9e0daSVladimir Oltean .vid = vid, 170d66e4348SPetr Machata }; 171d66e4348SPetr Machata 172d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 173d66e4348SPetr Machata } 174