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) { 176bc506b4SIdo Schimmel if (switchdev_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 { 266bc506b4SIdo Schimmel struct switchdev_attr attr = { 276bc506b4SIdo Schimmel .orig_dev = p->dev, 286bc506b4SIdo Schimmel .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, 296bc506b4SIdo Schimmel }; 306bc506b4SIdo Schimmel int err; 316bc506b4SIdo Schimmel 326bc506b4SIdo Schimmel ASSERT_RTNL(); 336bc506b4SIdo Schimmel 346bc506b4SIdo Schimmel err = switchdev_port_attr_get(p->dev, &attr); 356bc506b4SIdo Schimmel if (err) { 366bc506b4SIdo Schimmel if (err == -EOPNOTSUPP) 376bc506b4SIdo Schimmel return 0; 386bc506b4SIdo Schimmel return err; 396bc506b4SIdo Schimmel } 406bc506b4SIdo Schimmel 416bc506b4SIdo Schimmel p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev); 426bc506b4SIdo Schimmel 436bc506b4SIdo Schimmel return 0; 446bc506b4SIdo Schimmel } 456bc506b4SIdo Schimmel 466bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 476bc506b4SIdo Schimmel struct sk_buff *skb) 486bc506b4SIdo Schimmel { 496bc506b4SIdo Schimmel if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark)) 506bc506b4SIdo Schimmel BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark; 516bc506b4SIdo Schimmel } 526bc506b4SIdo Schimmel 536bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 546bc506b4SIdo Schimmel const struct sk_buff *skb) 556bc506b4SIdo Schimmel { 566bc506b4SIdo Schimmel return !skb->offload_fwd_mark || 576bc506b4SIdo Schimmel BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark; 586bc506b4SIdo Schimmel } 593922285dSArkadi Sharshevsky 603922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */ 613922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 623922285dSArkadi Sharshevsky BR_MCAST_FLOOD | BR_BCAST_FLOOD) 633922285dSArkadi Sharshevsky 643922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p, 653922285dSArkadi Sharshevsky unsigned long flags, 663922285dSArkadi Sharshevsky unsigned long mask) 673922285dSArkadi Sharshevsky { 683922285dSArkadi Sharshevsky struct switchdev_attr attr = { 693922285dSArkadi Sharshevsky .orig_dev = p->dev, 703922285dSArkadi Sharshevsky .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT, 713922285dSArkadi Sharshevsky }; 723922285dSArkadi Sharshevsky int err; 733922285dSArkadi Sharshevsky 743922285dSArkadi Sharshevsky if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD) 753922285dSArkadi Sharshevsky return 0; 763922285dSArkadi Sharshevsky 773922285dSArkadi Sharshevsky err = switchdev_port_attr_get(p->dev, &attr); 783922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 793922285dSArkadi Sharshevsky return 0; 803922285dSArkadi Sharshevsky if (err) 813922285dSArkadi Sharshevsky return err; 823922285dSArkadi Sharshevsky 833922285dSArkadi Sharshevsky /* Check if specific bridge flag attribute offload is supported */ 843922285dSArkadi Sharshevsky if (!(attr.u.brport_flags_support & mask)) { 853922285dSArkadi Sharshevsky br_warn(p->br, "bridge flag offload is not supported %u(%s)\n", 863922285dSArkadi Sharshevsky (unsigned int)p->port_no, p->dev->name); 873922285dSArkadi Sharshevsky return -EOPNOTSUPP; 883922285dSArkadi Sharshevsky } 893922285dSArkadi Sharshevsky 903922285dSArkadi Sharshevsky attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 913922285dSArkadi Sharshevsky attr.flags = SWITCHDEV_F_DEFER; 923922285dSArkadi Sharshevsky attr.u.brport_flags = flags; 933922285dSArkadi Sharshevsky err = switchdev_port_attr_set(p->dev, &attr); 943922285dSArkadi Sharshevsky if (err) { 953922285dSArkadi Sharshevsky br_warn(p->br, "error setting offload flag on port %u(%s)\n", 963922285dSArkadi Sharshevsky (unsigned int)p->port_no, p->dev->name); 973922285dSArkadi Sharshevsky return err; 983922285dSArkadi Sharshevsky } 993922285dSArkadi Sharshevsky 1003922285dSArkadi Sharshevsky return 0; 1013922285dSArkadi Sharshevsky } 1026b26b51bSArkadi Sharshevsky 1036b26b51bSArkadi Sharshevsky static void 1046b26b51bSArkadi Sharshevsky br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, 105*816a3bedSPetr Machata u16 vid, struct net_device *dev, 106*816a3bedSPetr Machata bool added_by_user) 1076b26b51bSArkadi Sharshevsky { 1086b26b51bSArkadi Sharshevsky struct switchdev_notifier_fdb_info info; 1096b26b51bSArkadi Sharshevsky unsigned long notifier_type; 1106b26b51bSArkadi Sharshevsky 1116b26b51bSArkadi Sharshevsky info.addr = mac; 1126b26b51bSArkadi Sharshevsky info.vid = vid; 113*816a3bedSPetr Machata info.added_by_user = added_by_user; 1146b26b51bSArkadi Sharshevsky notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; 1156b26b51bSArkadi Sharshevsky call_switchdev_notifiers(notifier_type, dev, &info.info); 1166b26b51bSArkadi Sharshevsky } 1176b26b51bSArkadi Sharshevsky 1186b26b51bSArkadi Sharshevsky void 1196b26b51bSArkadi Sharshevsky br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 1206b26b51bSArkadi Sharshevsky { 121ef9a5a62SRoopa Prabhu if (!fdb->added_by_user || !fdb->dst) 1226b26b51bSArkadi Sharshevsky return; 1236b26b51bSArkadi Sharshevsky 1246b26b51bSArkadi Sharshevsky switch (type) { 1256b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 126eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, 127eb793583SNikolay Aleksandrov fdb->key.vlan_id, 128*816a3bedSPetr Machata fdb->dst->dev, 129*816a3bedSPetr Machata fdb->added_by_user); 1306b26b51bSArkadi Sharshevsky break; 1316b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 132eb793583SNikolay Aleksandrov br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 133eb793583SNikolay Aleksandrov fdb->key.vlan_id, 134*816a3bedSPetr Machata fdb->dst->dev, 135*816a3bedSPetr Machata fdb->added_by_user); 1366b26b51bSArkadi Sharshevsky break; 1376b26b51bSArkadi Sharshevsky } 1386b26b51bSArkadi Sharshevsky } 139