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 78e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 79e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 80e18f4c18SVladimir 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 99dcbdf135SVladimir Oltean err = switchdev_port_attr_set(p->dev, &attr, extack); 1003922285dSArkadi Sharshevsky if (err) { 101dcbdf135SVladimir Oltean if (extack && !extack->_msg) 102dcbdf135SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 103dcbdf135SVladimir Oltean "error setting offload flag on port"); 1043922285dSArkadi Sharshevsky return err; 1053922285dSArkadi Sharshevsky } 1063922285dSArkadi Sharshevsky 1073922285dSArkadi Sharshevsky return 0; 1083922285dSArkadi Sharshevsky } 1096b26b51bSArkadi Sharshevsky 1106b26b51bSArkadi Sharshevsky void 1116b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 1126b26b51bSArkadi Sharshevsky { 113*3e19ae7cSVladimir Oltean const struct net_bridge_port *dst = READ_ONCE(fdb->dst); 114e5b4b898STobias Waldekranz struct switchdev_notifier_fdb_info info = { 115e5b4b898STobias Waldekranz .addr = fdb->key.addr.addr, 116e5b4b898STobias Waldekranz .vid = fdb->key.vlan_id, 117e5b4b898STobias Waldekranz .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), 1182c4eca3eSVladimir Oltean .is_local = test_bit(BR_FDB_LOCAL, &fdb->flags), 119e5b4b898STobias Waldekranz .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), 120e5b4b898STobias Waldekranz }; 121e5b4b898STobias Waldekranz 122*3e19ae7cSVladimir Oltean if (!dst) 1236b26b51bSArkadi Sharshevsky return; 1246b26b51bSArkadi Sharshevsky 1256b26b51bSArkadi Sharshevsky switch (type) { 1266b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 127e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 128*3e19ae7cSVladimir Oltean dst->dev, &info.info, NULL); 1296b26b51bSArkadi Sharshevsky break; 1306b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 131e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 132*3e19ae7cSVladimir Oltean dst->dev, &info.info, NULL); 1336b26b51bSArkadi Sharshevsky break; 1346b26b51bSArkadi Sharshevsky } 1356b26b51bSArkadi Sharshevsky } 136d66e4348SPetr Machata 137169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 138169327d5SPetr Machata struct netlink_ext_ack *extack) 139d66e4348SPetr Machata { 140d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 141d66e4348SPetr Machata .obj.orig_dev = dev, 142d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 143d66e4348SPetr Machata .flags = flags, 144b7a9e0daSVladimir Oltean .vid = vid, 145d66e4348SPetr Machata }; 146d66e4348SPetr Machata 14769b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 148d66e4348SPetr Machata } 149d66e4348SPetr Machata 150d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 151d66e4348SPetr Machata { 152d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 153d66e4348SPetr Machata .obj.orig_dev = dev, 154d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 155b7a9e0daSVladimir Oltean .vid = vid, 156d66e4348SPetr Machata }; 157d66e4348SPetr Machata 158d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 159d66e4348SPetr Machata } 160