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> 79776457cSVladimir Oltean #include <net/ip.h> 86bc506b4SIdo Schimmel #include <net/switchdev.h> 96bc506b4SIdo Schimmel 106bc506b4SIdo Schimmel #include "br_private.h" 116bc506b4SIdo Schimmel 1247211192STobias Waldekranz static struct static_key_false br_switchdev_tx_fwd_offload; 1347211192STobias Waldekranz 1447211192STobias Waldekranz static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p, 1547211192STobias Waldekranz const struct sk_buff *skb) 1647211192STobias Waldekranz { 1747211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 1847211192STobias Waldekranz return false; 1947211192STobias Waldekranz 2047211192STobias Waldekranz return (p->flags & BR_TX_FWD_OFFLOAD) && 2147211192STobias Waldekranz (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom); 2247211192STobias Waldekranz } 2347211192STobias Waldekranz 2447211192STobias Waldekranz bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb) 2547211192STobias Waldekranz { 2647211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 2747211192STobias Waldekranz return false; 2847211192STobias Waldekranz 2947211192STobias Waldekranz return BR_INPUT_SKB_CB(skb)->tx_fwd_offload; 3047211192STobias Waldekranz } 3147211192STobias Waldekranz 32c5381154SVladimir Oltean void br_switchdev_frame_set_offload_fwd_mark(struct sk_buff *skb) 33c5381154SVladimir Oltean { 34c5381154SVladimir Oltean skb->offload_fwd_mark = br_switchdev_frame_uses_tx_fwd_offload(skb); 35c5381154SVladimir Oltean } 36c5381154SVladimir Oltean 3747211192STobias Waldekranz /* Mark the frame for TX forwarding offload if this egress port supports it */ 3847211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p, 3947211192STobias Waldekranz struct sk_buff *skb) 4047211192STobias Waldekranz { 4147211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 4247211192STobias Waldekranz BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true; 4347211192STobias Waldekranz } 4447211192STobias Waldekranz 4547211192STobias Waldekranz /* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms 4647211192STobias Waldekranz * that the skb has been already forwarded to, to avoid further cloning to 4747211192STobias Waldekranz * other ports in the same hwdom by making nbp_switchdev_allowed_egress() 4847211192STobias Waldekranz * return false. 4947211192STobias Waldekranz */ 5047211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p, 5147211192STobias Waldekranz struct sk_buff *skb) 5247211192STobias Waldekranz { 5347211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 5447211192STobias Waldekranz set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms); 5547211192STobias Waldekranz } 5647211192STobias Waldekranz 576bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 586bc506b4SIdo Schimmel struct sk_buff *skb) 596bc506b4SIdo Schimmel { 60f7cf972fSTobias Waldekranz if (p->hwdom) 61f7cf972fSTobias Waldekranz BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom; 626bc506b4SIdo Schimmel } 636bc506b4SIdo Schimmel 646bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 656bc506b4SIdo Schimmel const struct sk_buff *skb) 666bc506b4SIdo Schimmel { 6747211192STobias Waldekranz struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb); 6847211192STobias Waldekranz 6947211192STobias Waldekranz return !test_bit(p->hwdom, &cb->fwd_hwdoms) && 7047211192STobias Waldekranz (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom); 716bc506b4SIdo Schimmel } 723922285dSArkadi Sharshevsky 733922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */ 743922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 75*fa1c8334SHans Schultz BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_PORT_LOCKED) 763922285dSArkadi Sharshevsky 773922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p, 783922285dSArkadi Sharshevsky unsigned long flags, 79078bbb85SVladimir Oltean unsigned long mask, 80078bbb85SVladimir Oltean struct netlink_ext_ack *extack) 813922285dSArkadi Sharshevsky { 823922285dSArkadi Sharshevsky struct switchdev_attr attr = { 833922285dSArkadi Sharshevsky .orig_dev = p->dev, 843922285dSArkadi Sharshevsky }; 85d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info info = { 86d45224d6SFlorian Fainelli .attr = &attr, 87d45224d6SFlorian Fainelli }; 883922285dSArkadi Sharshevsky int err; 893922285dSArkadi Sharshevsky 90304ae3bfSVladimir Oltean mask &= BR_PORT_FLAGS_HW_OFFLOAD; 91304ae3bfSVladimir Oltean if (!mask) 923922285dSArkadi Sharshevsky return 0; 933922285dSArkadi Sharshevsky 94e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 95e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 96e18f4c18SVladimir Oltean attr.u.brport_flags.mask = mask; 97304ae3bfSVladimir Oltean 98d45224d6SFlorian Fainelli /* We run from atomic context here */ 99d45224d6SFlorian Fainelli err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 100078bbb85SVladimir Oltean &info.info, extack); 101d45224d6SFlorian Fainelli err = notifier_to_errno(err); 1023922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 1033922285dSArkadi Sharshevsky return 0; 1043922285dSArkadi Sharshevsky 1051ef07644SFlorian Fainelli if (err) { 106078bbb85SVladimir Oltean if (extack && !extack->_msg) 107078bbb85SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 108078bbb85SVladimir Oltean "bridge flag offload is not supported"); 1093922285dSArkadi Sharshevsky return -EOPNOTSUPP; 1103922285dSArkadi Sharshevsky } 1113922285dSArkadi Sharshevsky 1123922285dSArkadi Sharshevsky attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 1133922285dSArkadi Sharshevsky attr.flags = SWITCHDEV_F_DEFER; 1141ef07644SFlorian Fainelli 115dcbdf135SVladimir Oltean err = switchdev_port_attr_set(p->dev, &attr, extack); 1163922285dSArkadi Sharshevsky if (err) { 117dcbdf135SVladimir Oltean if (extack && !extack->_msg) 118dcbdf135SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 119dcbdf135SVladimir Oltean "error setting offload flag on port"); 1203922285dSArkadi Sharshevsky return err; 1213922285dSArkadi Sharshevsky } 1223922285dSArkadi Sharshevsky 1233922285dSArkadi Sharshevsky return 0; 1243922285dSArkadi Sharshevsky } 1256b26b51bSArkadi Sharshevsky 126fab9eca8SVladimir Oltean static void br_switchdev_fdb_populate(struct net_bridge *br, 127fab9eca8SVladimir Oltean struct switchdev_notifier_fdb_info *item, 128fab9eca8SVladimir Oltean const struct net_bridge_fdb_entry *fdb, 129fab9eca8SVladimir Oltean const void *ctx) 130fab9eca8SVladimir Oltean { 131fab9eca8SVladimir Oltean const struct net_bridge_port *p = READ_ONCE(fdb->dst); 132fab9eca8SVladimir Oltean 133fab9eca8SVladimir Oltean item->addr = fdb->key.addr.addr; 134fab9eca8SVladimir Oltean item->vid = fdb->key.vlan_id; 135fab9eca8SVladimir Oltean item->added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); 136fab9eca8SVladimir Oltean item->offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags); 137fab9eca8SVladimir Oltean item->is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); 138fab9eca8SVladimir Oltean item->info.dev = (!p || item->is_local) ? br->dev : p->dev; 139fab9eca8SVladimir Oltean item->info.ctx = ctx; 140fab9eca8SVladimir Oltean } 141fab9eca8SVladimir Oltean 1426b26b51bSArkadi Sharshevsky void 1436eb38bf8STobias Waldekranz br_switchdev_fdb_notify(struct net_bridge *br, 1446eb38bf8STobias Waldekranz const struct net_bridge_fdb_entry *fdb, int type) 1456b26b51bSArkadi Sharshevsky { 146fab9eca8SVladimir Oltean struct switchdev_notifier_fdb_info item; 147fab9eca8SVladimir Oltean 148fab9eca8SVladimir Oltean br_switchdev_fdb_populate(br, &item, fdb, NULL); 149e5b4b898STobias Waldekranz 1506b26b51bSArkadi Sharshevsky switch (type) { 1516b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 152e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 153fab9eca8SVladimir Oltean item.info.dev, &item.info, NULL); 1546b26b51bSArkadi Sharshevsky break; 1556b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 156e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 157fab9eca8SVladimir Oltean item.info.dev, &item.info, NULL); 1586b26b51bSArkadi Sharshevsky break; 1596b26b51bSArkadi Sharshevsky } 1606b26b51bSArkadi Sharshevsky } 161d66e4348SPetr Machata 162169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 1638d23a54fSVladimir Oltean bool changed, struct netlink_ext_ack *extack) 164d66e4348SPetr Machata { 165d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 166d66e4348SPetr Machata .obj.orig_dev = dev, 167d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 168d66e4348SPetr Machata .flags = flags, 169b7a9e0daSVladimir Oltean .vid = vid, 1708d23a54fSVladimir Oltean .changed = changed, 171d66e4348SPetr Machata }; 172d66e4348SPetr Machata 17369b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 174d66e4348SPetr Machata } 175d66e4348SPetr Machata 176d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 177d66e4348SPetr Machata { 178d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 179d66e4348SPetr Machata .obj.orig_dev = dev, 180d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 181b7a9e0daSVladimir Oltean .vid = vid, 182d66e4348SPetr Machata }; 183d66e4348SPetr Machata 184d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 185d66e4348SPetr Machata } 18685826610STobias Waldekranz 18785826610STobias Waldekranz static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) 18885826610STobias Waldekranz { 18985826610STobias Waldekranz struct net_bridge *br = joining->br; 19085826610STobias Waldekranz struct net_bridge_port *p; 19185826610STobias Waldekranz int hwdom; 19285826610STobias Waldekranz 19385826610STobias Waldekranz /* joining is yet to be added to the port list. */ 19485826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 1952f5dc00fSVladimir Oltean if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { 19685826610STobias Waldekranz joining->hwdom = p->hwdom; 19785826610STobias Waldekranz return 0; 19885826610STobias Waldekranz } 19985826610STobias Waldekranz } 20085826610STobias Waldekranz 20185826610STobias Waldekranz hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1); 20285826610STobias Waldekranz if (hwdom >= BR_HWDOM_MAX) 20385826610STobias Waldekranz return -EBUSY; 20485826610STobias Waldekranz 20585826610STobias Waldekranz set_bit(hwdom, &br->busy_hwdoms); 20685826610STobias Waldekranz joining->hwdom = hwdom; 20785826610STobias Waldekranz return 0; 20885826610STobias Waldekranz } 20985826610STobias Waldekranz 21085826610STobias Waldekranz static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) 21185826610STobias Waldekranz { 21285826610STobias Waldekranz struct net_bridge *br = leaving->br; 21385826610STobias Waldekranz struct net_bridge_port *p; 21485826610STobias Waldekranz 21585826610STobias Waldekranz /* leaving is no longer in the port list. */ 21685826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 21785826610STobias Waldekranz if (p->hwdom == leaving->hwdom) 21885826610STobias Waldekranz return; 21985826610STobias Waldekranz } 22085826610STobias Waldekranz 22185826610STobias Waldekranz clear_bit(leaving->hwdom, &br->busy_hwdoms); 22285826610STobias Waldekranz } 22385826610STobias Waldekranz 2242f5dc00fSVladimir Oltean static int nbp_switchdev_add(struct net_bridge_port *p, 2252f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid, 22647211192STobias Waldekranz bool tx_fwd_offload, 2272f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 22885826610STobias Waldekranz { 22947211192STobias Waldekranz int err; 23047211192STobias Waldekranz 2312f5dc00fSVladimir Oltean if (p->offload_count) { 2322f5dc00fSVladimir Oltean /* Prevent unsupported configurations such as a bridge port 2332f5dc00fSVladimir Oltean * which is a bonding interface, and the member ports are from 2342f5dc00fSVladimir Oltean * different hardware switches. 2352f5dc00fSVladimir Oltean */ 2362f5dc00fSVladimir Oltean if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { 2372f5dc00fSVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 2382f5dc00fSVladimir Oltean "Same bridge port cannot be offloaded by two physical switches"); 2392f5dc00fSVladimir Oltean return -EBUSY; 24085826610STobias Waldekranz } 24185826610STobias Waldekranz 2422f5dc00fSVladimir Oltean /* Tolerate drivers that call switchdev_bridge_port_offload() 2432f5dc00fSVladimir Oltean * more than once for the same bridge port, such as when the 2442f5dc00fSVladimir Oltean * bridge port is an offloaded bonding/team interface. 2452f5dc00fSVladimir Oltean */ 2462f5dc00fSVladimir Oltean p->offload_count++; 2472f5dc00fSVladimir Oltean 2482f5dc00fSVladimir Oltean return 0; 2492f5dc00fSVladimir Oltean } 2502f5dc00fSVladimir Oltean 2512f5dc00fSVladimir Oltean p->ppid = ppid; 2522f5dc00fSVladimir Oltean p->offload_count = 1; 2532f5dc00fSVladimir Oltean 25447211192STobias Waldekranz err = nbp_switchdev_hwdom_set(p); 25547211192STobias Waldekranz if (err) 25647211192STobias Waldekranz return err; 25747211192STobias Waldekranz 25847211192STobias Waldekranz if (tx_fwd_offload) { 25947211192STobias Waldekranz p->flags |= BR_TX_FWD_OFFLOAD; 26047211192STobias Waldekranz static_branch_inc(&br_switchdev_tx_fwd_offload); 26147211192STobias Waldekranz } 26247211192STobias Waldekranz 26347211192STobias Waldekranz return 0; 26485826610STobias Waldekranz } 26585826610STobias Waldekranz 2662f5dc00fSVladimir Oltean static void nbp_switchdev_del(struct net_bridge_port *p) 26785826610STobias Waldekranz { 2682f5dc00fSVladimir Oltean if (WARN_ON(!p->offload_count)) 2692f5dc00fSVladimir Oltean return; 2702f5dc00fSVladimir Oltean 2712f5dc00fSVladimir Oltean p->offload_count--; 2722f5dc00fSVladimir Oltean 2732f5dc00fSVladimir Oltean if (p->offload_count) 2742f5dc00fSVladimir Oltean return; 27585826610STobias Waldekranz 27685826610STobias Waldekranz if (p->hwdom) 27785826610STobias Waldekranz nbp_switchdev_hwdom_put(p); 27847211192STobias Waldekranz 27947211192STobias Waldekranz if (p->flags & BR_TX_FWD_OFFLOAD) { 28047211192STobias Waldekranz p->flags &= ~BR_TX_FWD_OFFLOAD; 28147211192STobias Waldekranz static_branch_dec(&br_switchdev_tx_fwd_offload); 28247211192STobias Waldekranz } 28385826610STobias Waldekranz } 2842f5dc00fSVladimir Oltean 285326b212eSVladimir Oltean static int 286326b212eSVladimir Oltean br_switchdev_fdb_replay_one(struct net_bridge *br, struct notifier_block *nb, 2875cda5272SVladimir Oltean const struct net_bridge_fdb_entry *fdb, 2885cda5272SVladimir Oltean unsigned long action, const void *ctx) 2895cda5272SVladimir Oltean { 2905cda5272SVladimir Oltean struct switchdev_notifier_fdb_info item; 2915cda5272SVladimir Oltean int err; 2925cda5272SVladimir Oltean 293fab9eca8SVladimir Oltean br_switchdev_fdb_populate(br, &item, fdb, ctx); 2945cda5272SVladimir Oltean 2955cda5272SVladimir Oltean err = nb->notifier_call(nb, action, &item); 2965cda5272SVladimir Oltean return notifier_to_errno(err); 2975cda5272SVladimir Oltean } 2985cda5272SVladimir Oltean 299326b212eSVladimir Oltean static int 300326b212eSVladimir Oltean br_switchdev_fdb_replay(const struct net_device *br_dev, const void *ctx, 3015cda5272SVladimir Oltean bool adding, struct notifier_block *nb) 3025cda5272SVladimir Oltean { 3035cda5272SVladimir Oltean struct net_bridge_fdb_entry *fdb; 3045cda5272SVladimir Oltean struct net_bridge *br; 3055cda5272SVladimir Oltean unsigned long action; 3065cda5272SVladimir Oltean int err = 0; 3075cda5272SVladimir Oltean 3085cda5272SVladimir Oltean if (!nb) 3095cda5272SVladimir Oltean return 0; 3105cda5272SVladimir Oltean 3115cda5272SVladimir Oltean if (!netif_is_bridge_master(br_dev)) 3125cda5272SVladimir Oltean return -EINVAL; 3135cda5272SVladimir Oltean 3145cda5272SVladimir Oltean br = netdev_priv(br_dev); 3155cda5272SVladimir Oltean 3165cda5272SVladimir Oltean if (adding) 3175cda5272SVladimir Oltean action = SWITCHDEV_FDB_ADD_TO_DEVICE; 3185cda5272SVladimir Oltean else 3195cda5272SVladimir Oltean action = SWITCHDEV_FDB_DEL_TO_DEVICE; 3205cda5272SVladimir Oltean 3215cda5272SVladimir Oltean rcu_read_lock(); 3225cda5272SVladimir Oltean 3235cda5272SVladimir Oltean hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { 324326b212eSVladimir Oltean err = br_switchdev_fdb_replay_one(br, nb, fdb, action, ctx); 3255cda5272SVladimir Oltean if (err) 3265cda5272SVladimir Oltean break; 3275cda5272SVladimir Oltean } 3285cda5272SVladimir Oltean 3295cda5272SVladimir Oltean rcu_read_unlock(); 3305cda5272SVladimir Oltean 3315cda5272SVladimir Oltean return err; 3325cda5272SVladimir Oltean } 3335cda5272SVladimir Oltean 334326b212eSVladimir Oltean static int 335326b212eSVladimir Oltean br_switchdev_vlan_replay_one(struct notifier_block *nb, 3364a6849e4SVladimir Oltean struct net_device *dev, 3374a6849e4SVladimir Oltean struct switchdev_obj_port_vlan *vlan, 3384a6849e4SVladimir Oltean const void *ctx, unsigned long action, 3394a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 3404a6849e4SVladimir Oltean { 3414a6849e4SVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 3424a6849e4SVladimir Oltean .info = { 3434a6849e4SVladimir Oltean .dev = dev, 3444a6849e4SVladimir Oltean .extack = extack, 3454a6849e4SVladimir Oltean .ctx = ctx, 3464a6849e4SVladimir Oltean }, 3474a6849e4SVladimir Oltean .obj = &vlan->obj, 3484a6849e4SVladimir Oltean }; 3494a6849e4SVladimir Oltean int err; 3504a6849e4SVladimir Oltean 3514a6849e4SVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 3524a6849e4SVladimir Oltean return notifier_to_errno(err); 3534a6849e4SVladimir Oltean } 3544a6849e4SVladimir Oltean 355b28d580eSVladimir Oltean static int br_switchdev_vlan_replay_group(struct notifier_block *nb, 356326b212eSVladimir Oltean struct net_device *dev, 357b28d580eSVladimir Oltean struct net_bridge_vlan_group *vg, 358b28d580eSVladimir Oltean const void *ctx, unsigned long action, 3594a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 3604a6849e4SVladimir Oltean { 3614a6849e4SVladimir Oltean struct net_bridge_vlan *v; 3624a6849e4SVladimir Oltean int err = 0; 3634a6849e4SVladimir Oltean u16 pvid; 3644a6849e4SVladimir Oltean 3654a6849e4SVladimir Oltean if (!vg) 3664a6849e4SVladimir Oltean return 0; 3674a6849e4SVladimir Oltean 3684a6849e4SVladimir Oltean pvid = br_get_pvid(vg); 3694a6849e4SVladimir Oltean 3704a6849e4SVladimir Oltean list_for_each_entry(v, &vg->vlan_list, vlist) { 3714a6849e4SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 3724a6849e4SVladimir Oltean .obj.orig_dev = dev, 3734a6849e4SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 3744a6849e4SVladimir Oltean .flags = br_vlan_flags(v, pvid), 3754a6849e4SVladimir Oltean .vid = v->vid, 3764a6849e4SVladimir Oltean }; 3774a6849e4SVladimir Oltean 3784a6849e4SVladimir Oltean if (!br_vlan_should_use(v)) 3794a6849e4SVladimir Oltean continue; 3804a6849e4SVladimir Oltean 381326b212eSVladimir Oltean err = br_switchdev_vlan_replay_one(nb, dev, &vlan, ctx, 382326b212eSVladimir Oltean action, extack); 3834a6849e4SVladimir Oltean if (err) 3844a6849e4SVladimir Oltean return err; 3854a6849e4SVladimir Oltean } 3864a6849e4SVladimir Oltean 387b28d580eSVladimir Oltean return 0; 388b28d580eSVladimir Oltean } 389b28d580eSVladimir Oltean 390b28d580eSVladimir Oltean static int br_switchdev_vlan_replay(struct net_device *br_dev, 391b28d580eSVladimir Oltean const void *ctx, bool adding, 392b28d580eSVladimir Oltean struct notifier_block *nb, 393b28d580eSVladimir Oltean struct netlink_ext_ack *extack) 394b28d580eSVladimir Oltean { 395b28d580eSVladimir Oltean struct net_bridge *br = netdev_priv(br_dev); 396b28d580eSVladimir Oltean struct net_bridge_port *p; 397b28d580eSVladimir Oltean unsigned long action; 398b28d580eSVladimir Oltean int err; 399b28d580eSVladimir Oltean 400b28d580eSVladimir Oltean ASSERT_RTNL(); 401b28d580eSVladimir Oltean 402b28d580eSVladimir Oltean if (!nb) 403b28d580eSVladimir Oltean return 0; 404b28d580eSVladimir Oltean 405b28d580eSVladimir Oltean if (!netif_is_bridge_master(br_dev)) 406b28d580eSVladimir Oltean return -EINVAL; 407b28d580eSVladimir Oltean 408b28d580eSVladimir Oltean if (adding) 409b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 410b28d580eSVladimir Oltean else 411b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 412b28d580eSVladimir Oltean 413b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, br_dev, br_vlan_group(br), 414b28d580eSVladimir Oltean ctx, action, extack); 415b28d580eSVladimir Oltean if (err) 4164a6849e4SVladimir Oltean return err; 417b28d580eSVladimir Oltean 418b28d580eSVladimir Oltean list_for_each_entry(p, &br->port_list, list) { 419b28d580eSVladimir Oltean struct net_device *dev = p->dev; 420b28d580eSVladimir Oltean 421b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, dev, 422b28d580eSVladimir Oltean nbp_vlan_group(p), 423b28d580eSVladimir Oltean ctx, action, extack); 424b28d580eSVladimir Oltean if (err) 425b28d580eSVladimir Oltean return err; 426b28d580eSVladimir Oltean } 427b28d580eSVladimir Oltean 428b28d580eSVladimir Oltean return 0; 4294a6849e4SVladimir Oltean } 4304a6849e4SVladimir Oltean 4319776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 432326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info { 4339776457cSVladimir Oltean struct net_bridge_port *port; 4349776457cSVladimir Oltean struct br_ip ip; 4359776457cSVladimir Oltean }; 4369776457cSVladimir Oltean 437326b212eSVladimir Oltean static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *priv) 4389776457cSVladimir Oltean { 439326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *data = priv; 4409776457cSVladimir Oltean struct net_bridge_port_group __rcu **pp; 4419776457cSVladimir Oltean struct net_bridge_port_group *p; 4429776457cSVladimir Oltean struct net_bridge_mdb_entry *mp; 4439776457cSVladimir Oltean struct net_bridge_port *port = data->port; 4449776457cSVladimir Oltean struct net_bridge *br = port->br; 4459776457cSVladimir Oltean 4469776457cSVladimir Oltean if (err) 4479776457cSVladimir Oltean goto err; 4489776457cSVladimir Oltean 4499776457cSVladimir Oltean spin_lock_bh(&br->multicast_lock); 4509776457cSVladimir Oltean mp = br_mdb_ip_get(br, &data->ip); 4519776457cSVladimir Oltean if (!mp) 4529776457cSVladimir Oltean goto out; 4539776457cSVladimir Oltean for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; 4549776457cSVladimir Oltean pp = &p->next) { 4559776457cSVladimir Oltean if (p->key.port != port) 4569776457cSVladimir Oltean continue; 4579776457cSVladimir Oltean p->flags |= MDB_PG_FLAGS_OFFLOAD; 4589776457cSVladimir Oltean } 4599776457cSVladimir Oltean out: 4609776457cSVladimir Oltean spin_unlock_bh(&br->multicast_lock); 4619776457cSVladimir Oltean err: 4629776457cSVladimir Oltean kfree(priv); 4639776457cSVladimir Oltean } 4649776457cSVladimir Oltean 4659776457cSVladimir Oltean static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb, 4669776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp) 4679776457cSVladimir Oltean { 4689776457cSVladimir Oltean if (mp->addr.proto == htons(ETH_P_IP)) 4699776457cSVladimir Oltean ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr); 4709776457cSVladimir Oltean #if IS_ENABLED(CONFIG_IPV6) 4719776457cSVladimir Oltean else if (mp->addr.proto == htons(ETH_P_IPV6)) 4729776457cSVladimir Oltean ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb->addr); 4739776457cSVladimir Oltean #endif 4749776457cSVladimir Oltean else 4759776457cSVladimir Oltean ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr); 4769776457cSVladimir Oltean 4779776457cSVladimir Oltean mdb->vid = mp->addr.vid; 4789776457cSVladimir Oltean } 4799776457cSVladimir Oltean 480326b212eSVladimir Oltean static void br_switchdev_host_mdb_one(struct net_device *dev, 4819776457cSVladimir Oltean struct net_device *lower_dev, 4829776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 4839776457cSVladimir Oltean int type) 4849776457cSVladimir Oltean { 4859776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 4869776457cSVladimir Oltean .obj = { 4879776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_HOST_MDB, 4889776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 4899776457cSVladimir Oltean .orig_dev = dev, 4909776457cSVladimir Oltean }, 4919776457cSVladimir Oltean }; 4929776457cSVladimir Oltean 4939776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 4949776457cSVladimir Oltean 4959776457cSVladimir Oltean switch (type) { 4969776457cSVladimir Oltean case RTM_NEWMDB: 4979776457cSVladimir Oltean switchdev_port_obj_add(lower_dev, &mdb.obj, NULL); 4989776457cSVladimir Oltean break; 4999776457cSVladimir Oltean case RTM_DELMDB: 5009776457cSVladimir Oltean switchdev_port_obj_del(lower_dev, &mdb.obj); 5019776457cSVladimir Oltean break; 5029776457cSVladimir Oltean } 5039776457cSVladimir Oltean } 5049776457cSVladimir Oltean 505326b212eSVladimir Oltean static void br_switchdev_host_mdb(struct net_device *dev, 5069776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, int type) 5079776457cSVladimir Oltean { 5089776457cSVladimir Oltean struct net_device *lower_dev; 5099776457cSVladimir Oltean struct list_head *iter; 5109776457cSVladimir Oltean 5119776457cSVladimir Oltean netdev_for_each_lower_dev(dev, lower_dev, iter) 512326b212eSVladimir Oltean br_switchdev_host_mdb_one(dev, lower_dev, mp, type); 5139776457cSVladimir Oltean } 5149776457cSVladimir Oltean 515326b212eSVladimir Oltean static int 516326b212eSVladimir Oltean br_switchdev_mdb_replay_one(struct notifier_block *nb, struct net_device *dev, 5179776457cSVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 5189776457cSVladimir Oltean unsigned long action, const void *ctx, 5199776457cSVladimir Oltean struct netlink_ext_ack *extack) 5209776457cSVladimir Oltean { 5219776457cSVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 5229776457cSVladimir Oltean .info = { 5239776457cSVladimir Oltean .dev = dev, 5249776457cSVladimir Oltean .extack = extack, 5259776457cSVladimir Oltean .ctx = ctx, 5269776457cSVladimir Oltean }, 5279776457cSVladimir Oltean .obj = &mdb->obj, 5289776457cSVladimir Oltean }; 5299776457cSVladimir Oltean int err; 5309776457cSVladimir Oltean 5319776457cSVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 5329776457cSVladimir Oltean return notifier_to_errno(err); 5339776457cSVladimir Oltean } 5349776457cSVladimir Oltean 535326b212eSVladimir Oltean static int br_switchdev_mdb_queue_one(struct list_head *mdb_list, 5369776457cSVladimir Oltean enum switchdev_obj_id id, 5379776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp, 5389776457cSVladimir Oltean struct net_device *orig_dev) 5399776457cSVladimir Oltean { 5409776457cSVladimir Oltean struct switchdev_obj_port_mdb *mdb; 5419776457cSVladimir Oltean 5429776457cSVladimir Oltean mdb = kzalloc(sizeof(*mdb), GFP_ATOMIC); 5439776457cSVladimir Oltean if (!mdb) 5449776457cSVladimir Oltean return -ENOMEM; 5459776457cSVladimir Oltean 5469776457cSVladimir Oltean mdb->obj.id = id; 5479776457cSVladimir Oltean mdb->obj.orig_dev = orig_dev; 5489776457cSVladimir Oltean br_switchdev_mdb_populate(mdb, mp); 5499776457cSVladimir Oltean list_add_tail(&mdb->obj.list, mdb_list); 5509776457cSVladimir Oltean 5519776457cSVladimir Oltean return 0; 5529776457cSVladimir Oltean } 5539776457cSVladimir Oltean 5549776457cSVladimir Oltean void br_switchdev_mdb_notify(struct net_device *dev, 5559776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 5569776457cSVladimir Oltean struct net_bridge_port_group *pg, 5579776457cSVladimir Oltean int type) 5589776457cSVladimir Oltean { 559326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *complete_info; 5609776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 5619776457cSVladimir Oltean .obj = { 5629776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_PORT_MDB, 5639776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 5649776457cSVladimir Oltean }, 5659776457cSVladimir Oltean }; 5669776457cSVladimir Oltean 5679776457cSVladimir Oltean if (!pg) 568326b212eSVladimir Oltean return br_switchdev_host_mdb(dev, mp, type); 5699776457cSVladimir Oltean 5709776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 5719776457cSVladimir Oltean 5729776457cSVladimir Oltean mdb.obj.orig_dev = pg->key.port->dev; 5739776457cSVladimir Oltean switch (type) { 5749776457cSVladimir Oltean case RTM_NEWMDB: 5759776457cSVladimir Oltean complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC); 5769776457cSVladimir Oltean if (!complete_info) 5779776457cSVladimir Oltean break; 5789776457cSVladimir Oltean complete_info->port = pg->key.port; 5799776457cSVladimir Oltean complete_info->ip = mp->addr; 5809776457cSVladimir Oltean mdb.obj.complete_priv = complete_info; 581326b212eSVladimir Oltean mdb.obj.complete = br_switchdev_mdb_complete; 5829776457cSVladimir Oltean if (switchdev_port_obj_add(pg->key.port->dev, &mdb.obj, NULL)) 5839776457cSVladimir Oltean kfree(complete_info); 5849776457cSVladimir Oltean break; 5859776457cSVladimir Oltean case RTM_DELMDB: 5869776457cSVladimir Oltean switchdev_port_obj_del(pg->key.port->dev, &mdb.obj); 5879776457cSVladimir Oltean break; 5889776457cSVladimir Oltean } 5899776457cSVladimir Oltean } 5909776457cSVladimir Oltean #endif 5919776457cSVladimir Oltean 592326b212eSVladimir Oltean static int 593326b212eSVladimir Oltean br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev, 594326b212eSVladimir Oltean const void *ctx, bool adding, struct notifier_block *nb, 5959776457cSVladimir Oltean struct netlink_ext_ack *extack) 5969776457cSVladimir Oltean { 5979776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 5989776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp; 5999776457cSVladimir Oltean struct switchdev_obj *obj, *tmp; 6009776457cSVladimir Oltean struct net_bridge *br; 6019776457cSVladimir Oltean unsigned long action; 6029776457cSVladimir Oltean LIST_HEAD(mdb_list); 6039776457cSVladimir Oltean int err = 0; 6049776457cSVladimir Oltean 6059776457cSVladimir Oltean ASSERT_RTNL(); 6069776457cSVladimir Oltean 6079776457cSVladimir Oltean if (!nb) 6089776457cSVladimir Oltean return 0; 6099776457cSVladimir Oltean 6109776457cSVladimir Oltean if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev)) 6119776457cSVladimir Oltean return -EINVAL; 6129776457cSVladimir Oltean 6139776457cSVladimir Oltean br = netdev_priv(br_dev); 6149776457cSVladimir Oltean 6159776457cSVladimir Oltean if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 6169776457cSVladimir Oltean return 0; 6179776457cSVladimir Oltean 6189776457cSVladimir Oltean /* We cannot walk over br->mdb_list protected just by the rtnl_mutex, 6199776457cSVladimir Oltean * because the write-side protection is br->multicast_lock. But we 6209776457cSVladimir Oltean * need to emulate the [ blocking ] calling context of a regular 6219776457cSVladimir Oltean * switchdev event, so since both br->multicast_lock and RCU read side 6229776457cSVladimir Oltean * critical sections are atomic, we have no choice but to pick the RCU 6239776457cSVladimir Oltean * read side lock, queue up all our events, leave the critical section 6249776457cSVladimir Oltean * and notify switchdev from blocking context. 6259776457cSVladimir Oltean */ 6269776457cSVladimir Oltean rcu_read_lock(); 6279776457cSVladimir Oltean 6289776457cSVladimir Oltean hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { 6299776457cSVladimir Oltean struct net_bridge_port_group __rcu * const *pp; 6309776457cSVladimir Oltean const struct net_bridge_port_group *p; 6319776457cSVladimir Oltean 6329776457cSVladimir Oltean if (mp->host_joined) { 633326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6349776457cSVladimir Oltean SWITCHDEV_OBJ_ID_HOST_MDB, 6359776457cSVladimir Oltean mp, br_dev); 6369776457cSVladimir Oltean if (err) { 6379776457cSVladimir Oltean rcu_read_unlock(); 6389776457cSVladimir Oltean goto out_free_mdb; 6399776457cSVladimir Oltean } 6409776457cSVladimir Oltean } 6419776457cSVladimir Oltean 6429776457cSVladimir Oltean for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; 6439776457cSVladimir Oltean pp = &p->next) { 6449776457cSVladimir Oltean if (p->key.port->dev != dev) 6459776457cSVladimir Oltean continue; 6469776457cSVladimir Oltean 647326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6489776457cSVladimir Oltean SWITCHDEV_OBJ_ID_PORT_MDB, 6499776457cSVladimir Oltean mp, dev); 6509776457cSVladimir Oltean if (err) { 6519776457cSVladimir Oltean rcu_read_unlock(); 6529776457cSVladimir Oltean goto out_free_mdb; 6539776457cSVladimir Oltean } 6549776457cSVladimir Oltean } 6559776457cSVladimir Oltean } 6569776457cSVladimir Oltean 6579776457cSVladimir Oltean rcu_read_unlock(); 6589776457cSVladimir Oltean 6599776457cSVladimir Oltean if (adding) 6609776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 6619776457cSVladimir Oltean else 6629776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 6639776457cSVladimir Oltean 6649776457cSVladimir Oltean list_for_each_entry(obj, &mdb_list, list) { 665326b212eSVladimir Oltean err = br_switchdev_mdb_replay_one(nb, dev, 666326b212eSVladimir Oltean SWITCHDEV_OBJ_PORT_MDB(obj), 6679776457cSVladimir Oltean action, ctx, extack); 6689776457cSVladimir Oltean if (err) 6699776457cSVladimir Oltean goto out_free_mdb; 6709776457cSVladimir Oltean } 6719776457cSVladimir Oltean 6729776457cSVladimir Oltean out_free_mdb: 6739776457cSVladimir Oltean list_for_each_entry_safe(obj, tmp, &mdb_list, list) { 6749776457cSVladimir Oltean list_del(&obj->list); 6759776457cSVladimir Oltean kfree(SWITCHDEV_OBJ_PORT_MDB(obj)); 6769776457cSVladimir Oltean } 6779776457cSVladimir Oltean 6789776457cSVladimir Oltean if (err) 6799776457cSVladimir Oltean return err; 6809776457cSVladimir Oltean #endif 6819776457cSVladimir Oltean 6829776457cSVladimir Oltean return 0; 6839776457cSVladimir Oltean } 6849776457cSVladimir Oltean 6854e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 6864e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 6874e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 6884e51bf44SVladimir Oltean struct netlink_ext_ack *extack) 6894e51bf44SVladimir Oltean { 6904e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 6914e51bf44SVladimir Oltean struct net_device *dev = p->dev; 6924e51bf44SVladimir Oltean int err; 6934e51bf44SVladimir Oltean 694b28d580eSVladimir Oltean err = br_switchdev_vlan_replay(br_dev, ctx, true, blocking_nb, extack); 6954e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 6964e51bf44SVladimir Oltean return err; 6974e51bf44SVladimir Oltean 698326b212eSVladimir Oltean err = br_switchdev_mdb_replay(br_dev, dev, ctx, true, blocking_nb, 699326b212eSVladimir Oltean extack); 7004e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7014e51bf44SVladimir Oltean return err; 7024e51bf44SVladimir Oltean 703326b212eSVladimir Oltean err = br_switchdev_fdb_replay(br_dev, ctx, true, atomic_nb); 7044e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7054e51bf44SVladimir Oltean return err; 7064e51bf44SVladimir Oltean 7074e51bf44SVladimir Oltean return 0; 7084e51bf44SVladimir Oltean } 7094e51bf44SVladimir Oltean 7104e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 7114e51bf44SVladimir Oltean const void *ctx, 7124e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7134e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 7144e51bf44SVladimir Oltean { 7154e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 7164e51bf44SVladimir Oltean struct net_device *dev = p->dev; 7174e51bf44SVladimir Oltean 718263029aeSVladimir Oltean br_switchdev_fdb_replay(br_dev, ctx, false, atomic_nb); 7194e51bf44SVladimir Oltean 720326b212eSVladimir Oltean br_switchdev_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 7214e51bf44SVladimir Oltean 722b28d580eSVladimir Oltean br_switchdev_vlan_replay(br_dev, ctx, false, blocking_nb, NULL); 7234e51bf44SVladimir Oltean } 7244e51bf44SVladimir Oltean 7252f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a 7262f5dc00fSVladimir Oltean * switchdev hardware domain to it. 7272f5dc00fSVladimir Oltean */ 728957e2235SVladimir Oltean int br_switchdev_port_offload(struct net_bridge_port *p, 7294e51bf44SVladimir Oltean struct net_device *dev, const void *ctx, 7304e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7314e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 73247211192STobias Waldekranz bool tx_fwd_offload, 7332f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 7342f5dc00fSVladimir Oltean { 7352f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid; 7362f5dc00fSVladimir Oltean int err; 7372f5dc00fSVladimir Oltean 7382f5dc00fSVladimir Oltean err = dev_get_port_parent_id(dev, &ppid, false); 7392f5dc00fSVladimir Oltean if (err) 7402f5dc00fSVladimir Oltean return err; 7412f5dc00fSVladimir Oltean 74247211192STobias Waldekranz err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 7434e51bf44SVladimir Oltean if (err) 7444e51bf44SVladimir Oltean return err; 7454e51bf44SVladimir Oltean 7464e51bf44SVladimir Oltean err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 7474e51bf44SVladimir Oltean if (err) 7484e51bf44SVladimir Oltean goto out_switchdev_del; 7494e51bf44SVladimir Oltean 7504e51bf44SVladimir Oltean return 0; 7514e51bf44SVladimir Oltean 7524e51bf44SVladimir Oltean out_switchdev_del: 7534e51bf44SVladimir Oltean nbp_switchdev_del(p); 7544e51bf44SVladimir Oltean 7554e51bf44SVladimir Oltean return err; 7562f5dc00fSVladimir Oltean } 7572f5dc00fSVladimir Oltean 758957e2235SVladimir Oltean void br_switchdev_port_unoffload(struct net_bridge_port *p, const void *ctx, 7594e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7604e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 7612f5dc00fSVladimir Oltean { 7624e51bf44SVladimir Oltean nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 7634e51bf44SVladimir Oltean 7642f5dc00fSVladimir Oltean nbp_switchdev_del(p); 7652f5dc00fSVladimir Oltean } 766