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 78*e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 79*e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 80*e18f4c18SVladimir 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 993922285dSArkadi Sharshevsky err = switchdev_port_attr_set(p->dev, &attr); 1003922285dSArkadi Sharshevsky if (err) { 101078bbb85SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, "error setting offload flag on port"); 1023922285dSArkadi Sharshevsky return err; 1033922285dSArkadi Sharshevsky } 1043922285dSArkadi Sharshevsky 1053922285dSArkadi Sharshevsky return 0; 1063922285dSArkadi Sharshevsky } 1076b26b51bSArkadi Sharshevsky 1086b26b51bSArkadi Sharshevsky static void 1096b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, 110816a3bedSPetr Machata u16 vid, struct net_device *dev, 111e9ba0fbcSIdo Schimmel bool added_by_user, bool offloaded) 1126b26b51bSArkadi Sharshevsky { 1136b26b51bSArkadi Sharshevsky struct switchdev_notifier_fdb_info info; 1146b26b51bSArkadi Sharshevsky unsigned long notifier_type; 1156b26b51bSArkadi Sharshevsky 1166b26b51bSArkadi Sharshevsky info.addr = mac; 1176b26b51bSArkadi Sharshevsky info.vid = vid; 118816a3bedSPetr Machata info.added_by_user = added_by_user; 119e9ba0fbcSIdo Schimmel info.offloaded = offloaded; 1206b26b51bSArkadi Sharshevsky notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; 1216685987cSPetr Machata call_switchdev_notifiers(notifier_type, dev, &info.info, NULL); 1226b26b51bSArkadi Sharshevsky } 1236b26b51bSArkadi Sharshevsky 1246b26b51bSArkadi Sharshevsky void 1256b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 1266b26b51bSArkadi Sharshevsky { 127161d82deSPetr Machata if (!fdb->dst) 1286b26b51bSArkadi Sharshevsky return; 1296b26b51bSArkadi Sharshevsky 1306b26b51bSArkadi Sharshevsky switch (type) { 1316b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 132eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, 133eb793583SNikolay Aleksandrov fdb->key.vlan_id, 134816a3bedSPetr Machata fdb->dst->dev, 135ac3ca6afSNikolay Aleksandrov test_bit(BR_FDB_ADDED_BY_USER, 136ac3ca6afSNikolay Aleksandrov &fdb->flags), 137d38c6e3dSNikolay Aleksandrov test_bit(BR_FDB_OFFLOADED, 138d38c6e3dSNikolay Aleksandrov &fdb->flags)); 1396b26b51bSArkadi Sharshevsky break; 1406b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 141eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 142eb793583SNikolay Aleksandrov fdb->key.vlan_id, 143816a3bedSPetr Machata fdb->dst->dev, 144ac3ca6afSNikolay Aleksandrov test_bit(BR_FDB_ADDED_BY_USER, 145ac3ca6afSNikolay Aleksandrov &fdb->flags), 146d38c6e3dSNikolay Aleksandrov test_bit(BR_FDB_OFFLOADED, 147d38c6e3dSNikolay Aleksandrov &fdb->flags)); 1486b26b51bSArkadi Sharshevsky break; 1496b26b51bSArkadi Sharshevsky } 1506b26b51bSArkadi Sharshevsky } 151d66e4348SPetr Machata 152169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 153169327d5SPetr Machata struct netlink_ext_ack *extack) 154d66e4348SPetr Machata { 155d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 156d66e4348SPetr Machata .obj.orig_dev = dev, 157d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 158d66e4348SPetr Machata .flags = flags, 159b7a9e0daSVladimir Oltean .vid = vid, 160d66e4348SPetr Machata }; 161d66e4348SPetr Machata 16269b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 163d66e4348SPetr Machata } 164d66e4348SPetr Machata 165d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 166d66e4348SPetr Machata { 167d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 168d66e4348SPetr Machata .obj.orig_dev = dev, 169d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 170b7a9e0daSVladimir Oltean .vid = vid, 171d66e4348SPetr Machata }; 172d66e4348SPetr Machata 173d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 174d66e4348SPetr Machata } 175