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 11*47211192STobias Waldekranz static struct static_key_false br_switchdev_tx_fwd_offload; 12*47211192STobias Waldekranz 13*47211192STobias Waldekranz static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p, 14*47211192STobias Waldekranz const struct sk_buff *skb) 15*47211192STobias Waldekranz { 16*47211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 17*47211192STobias Waldekranz return false; 18*47211192STobias Waldekranz 19*47211192STobias Waldekranz return (p->flags & BR_TX_FWD_OFFLOAD) && 20*47211192STobias Waldekranz (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom); 21*47211192STobias Waldekranz } 22*47211192STobias Waldekranz 23*47211192STobias Waldekranz bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb) 24*47211192STobias Waldekranz { 25*47211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 26*47211192STobias Waldekranz return false; 27*47211192STobias Waldekranz 28*47211192STobias Waldekranz return BR_INPUT_SKB_CB(skb)->tx_fwd_offload; 29*47211192STobias Waldekranz } 30*47211192STobias Waldekranz 31*47211192STobias Waldekranz /* Mark the frame for TX forwarding offload if this egress port supports it */ 32*47211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p, 33*47211192STobias Waldekranz struct sk_buff *skb) 34*47211192STobias Waldekranz { 35*47211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 36*47211192STobias Waldekranz BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true; 37*47211192STobias Waldekranz } 38*47211192STobias Waldekranz 39*47211192STobias Waldekranz /* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms 40*47211192STobias Waldekranz * that the skb has been already forwarded to, to avoid further cloning to 41*47211192STobias Waldekranz * other ports in the same hwdom by making nbp_switchdev_allowed_egress() 42*47211192STobias Waldekranz * return false. 43*47211192STobias Waldekranz */ 44*47211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p, 45*47211192STobias Waldekranz struct sk_buff *skb) 46*47211192STobias Waldekranz { 47*47211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 48*47211192STobias Waldekranz set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms); 49*47211192STobias Waldekranz } 50*47211192STobias Waldekranz 516bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 526bc506b4SIdo Schimmel struct sk_buff *skb) 536bc506b4SIdo Schimmel { 54f7cf972fSTobias Waldekranz if (p->hwdom) 55f7cf972fSTobias Waldekranz BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom; 566bc506b4SIdo Schimmel } 576bc506b4SIdo Schimmel 586bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 596bc506b4SIdo Schimmel const struct sk_buff *skb) 606bc506b4SIdo Schimmel { 61*47211192STobias Waldekranz struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb); 62*47211192STobias Waldekranz 63*47211192STobias Waldekranz return !test_bit(p->hwdom, &cb->fwd_hwdoms) && 64*47211192STobias Waldekranz (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom); 656bc506b4SIdo Schimmel } 663922285dSArkadi Sharshevsky 673922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */ 683922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 693922285dSArkadi Sharshevsky BR_MCAST_FLOOD | BR_BCAST_FLOOD) 703922285dSArkadi Sharshevsky 713922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p, 723922285dSArkadi Sharshevsky unsigned long flags, 73078bbb85SVladimir Oltean unsigned long mask, 74078bbb85SVladimir Oltean struct netlink_ext_ack *extack) 753922285dSArkadi Sharshevsky { 763922285dSArkadi Sharshevsky struct switchdev_attr attr = { 773922285dSArkadi Sharshevsky .orig_dev = p->dev, 783922285dSArkadi Sharshevsky }; 79d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info info = { 80d45224d6SFlorian Fainelli .attr = &attr, 81d45224d6SFlorian Fainelli }; 823922285dSArkadi Sharshevsky int err; 833922285dSArkadi Sharshevsky 84304ae3bfSVladimir Oltean mask &= BR_PORT_FLAGS_HW_OFFLOAD; 85304ae3bfSVladimir Oltean if (!mask) 863922285dSArkadi Sharshevsky return 0; 873922285dSArkadi Sharshevsky 88e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 89e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 90e18f4c18SVladimir Oltean attr.u.brport_flags.mask = mask; 91304ae3bfSVladimir Oltean 92d45224d6SFlorian Fainelli /* We run from atomic context here */ 93d45224d6SFlorian Fainelli err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 94078bbb85SVladimir Oltean &info.info, extack); 95d45224d6SFlorian Fainelli err = notifier_to_errno(err); 963922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 973922285dSArkadi Sharshevsky return 0; 983922285dSArkadi Sharshevsky 991ef07644SFlorian Fainelli if (err) { 100078bbb85SVladimir Oltean if (extack && !extack->_msg) 101078bbb85SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 102078bbb85SVladimir Oltean "bridge flag offload is not supported"); 1033922285dSArkadi Sharshevsky return -EOPNOTSUPP; 1043922285dSArkadi Sharshevsky } 1053922285dSArkadi Sharshevsky 1063922285dSArkadi Sharshevsky attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 1073922285dSArkadi Sharshevsky attr.flags = SWITCHDEV_F_DEFER; 1081ef07644SFlorian Fainelli 109dcbdf135SVladimir Oltean err = switchdev_port_attr_set(p->dev, &attr, extack); 1103922285dSArkadi Sharshevsky if (err) { 111dcbdf135SVladimir Oltean if (extack && !extack->_msg) 112dcbdf135SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 113dcbdf135SVladimir Oltean "error setting offload flag on port"); 1143922285dSArkadi Sharshevsky return err; 1153922285dSArkadi Sharshevsky } 1163922285dSArkadi Sharshevsky 1173922285dSArkadi Sharshevsky return 0; 1183922285dSArkadi Sharshevsky } 1196b26b51bSArkadi Sharshevsky 1206b26b51bSArkadi Sharshevsky void 1216eb38bf8STobias Waldekranz br_switchdev_fdb_notify(struct net_bridge *br, 1226eb38bf8STobias Waldekranz const struct net_bridge_fdb_entry *fdb, int type) 1236b26b51bSArkadi Sharshevsky { 1243e19ae7cSVladimir Oltean const struct net_bridge_port *dst = READ_ONCE(fdb->dst); 1256eb38bf8STobias Waldekranz struct net_device *dev = dst ? dst->dev : br->dev; 126e5b4b898STobias Waldekranz struct switchdev_notifier_fdb_info info = { 127e5b4b898STobias Waldekranz .addr = fdb->key.addr.addr, 128e5b4b898STobias Waldekranz .vid = fdb->key.vlan_id, 129e5b4b898STobias Waldekranz .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), 1302c4eca3eSVladimir Oltean .is_local = test_bit(BR_FDB_LOCAL, &fdb->flags), 131e5b4b898STobias Waldekranz .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), 132e5b4b898STobias Waldekranz }; 133e5b4b898STobias Waldekranz 1346b26b51bSArkadi Sharshevsky switch (type) { 1356b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 136e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 1376eb38bf8STobias Waldekranz dev, &info.info, NULL); 1386b26b51bSArkadi Sharshevsky break; 1396b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 140e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 1416eb38bf8STobias Waldekranz dev, &info.info, NULL); 1426b26b51bSArkadi Sharshevsky break; 1436b26b51bSArkadi Sharshevsky } 1446b26b51bSArkadi Sharshevsky } 145d66e4348SPetr Machata 146169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 147169327d5SPetr Machata struct netlink_ext_ack *extack) 148d66e4348SPetr Machata { 149d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 150d66e4348SPetr Machata .obj.orig_dev = dev, 151d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 152d66e4348SPetr Machata .flags = flags, 153b7a9e0daSVladimir Oltean .vid = vid, 154d66e4348SPetr Machata }; 155d66e4348SPetr Machata 15669b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 157d66e4348SPetr Machata } 158d66e4348SPetr Machata 159d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 160d66e4348SPetr Machata { 161d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 162d66e4348SPetr Machata .obj.orig_dev = dev, 163d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 164b7a9e0daSVladimir Oltean .vid = vid, 165d66e4348SPetr Machata }; 166d66e4348SPetr Machata 167d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 168d66e4348SPetr Machata } 16985826610STobias Waldekranz 17085826610STobias Waldekranz static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) 17185826610STobias Waldekranz { 17285826610STobias Waldekranz struct net_bridge *br = joining->br; 17385826610STobias Waldekranz struct net_bridge_port *p; 17485826610STobias Waldekranz int hwdom; 17585826610STobias Waldekranz 17685826610STobias Waldekranz /* joining is yet to be added to the port list. */ 17785826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 1782f5dc00fSVladimir Oltean if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { 17985826610STobias Waldekranz joining->hwdom = p->hwdom; 18085826610STobias Waldekranz return 0; 18185826610STobias Waldekranz } 18285826610STobias Waldekranz } 18385826610STobias Waldekranz 18485826610STobias Waldekranz hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1); 18585826610STobias Waldekranz if (hwdom >= BR_HWDOM_MAX) 18685826610STobias Waldekranz return -EBUSY; 18785826610STobias Waldekranz 18885826610STobias Waldekranz set_bit(hwdom, &br->busy_hwdoms); 18985826610STobias Waldekranz joining->hwdom = hwdom; 19085826610STobias Waldekranz return 0; 19185826610STobias Waldekranz } 19285826610STobias Waldekranz 19385826610STobias Waldekranz static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) 19485826610STobias Waldekranz { 19585826610STobias Waldekranz struct net_bridge *br = leaving->br; 19685826610STobias Waldekranz struct net_bridge_port *p; 19785826610STobias Waldekranz 19885826610STobias Waldekranz /* leaving is no longer in the port list. */ 19985826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 20085826610STobias Waldekranz if (p->hwdom == leaving->hwdom) 20185826610STobias Waldekranz return; 20285826610STobias Waldekranz } 20385826610STobias Waldekranz 20485826610STobias Waldekranz clear_bit(leaving->hwdom, &br->busy_hwdoms); 20585826610STobias Waldekranz } 20685826610STobias Waldekranz 2072f5dc00fSVladimir Oltean static int nbp_switchdev_add(struct net_bridge_port *p, 2082f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid, 209*47211192STobias Waldekranz bool tx_fwd_offload, 2102f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 21185826610STobias Waldekranz { 212*47211192STobias Waldekranz int err; 213*47211192STobias Waldekranz 2142f5dc00fSVladimir Oltean if (p->offload_count) { 2152f5dc00fSVladimir Oltean /* Prevent unsupported configurations such as a bridge port 2162f5dc00fSVladimir Oltean * which is a bonding interface, and the member ports are from 2172f5dc00fSVladimir Oltean * different hardware switches. 2182f5dc00fSVladimir Oltean */ 2192f5dc00fSVladimir Oltean if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { 2202f5dc00fSVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 2212f5dc00fSVladimir Oltean "Same bridge port cannot be offloaded by two physical switches"); 2222f5dc00fSVladimir Oltean return -EBUSY; 22385826610STobias Waldekranz } 22485826610STobias Waldekranz 2252f5dc00fSVladimir Oltean /* Tolerate drivers that call switchdev_bridge_port_offload() 2262f5dc00fSVladimir Oltean * more than once for the same bridge port, such as when the 2272f5dc00fSVladimir Oltean * bridge port is an offloaded bonding/team interface. 2282f5dc00fSVladimir Oltean */ 2292f5dc00fSVladimir Oltean p->offload_count++; 2302f5dc00fSVladimir Oltean 2312f5dc00fSVladimir Oltean return 0; 2322f5dc00fSVladimir Oltean } 2332f5dc00fSVladimir Oltean 2342f5dc00fSVladimir Oltean p->ppid = ppid; 2352f5dc00fSVladimir Oltean p->offload_count = 1; 2362f5dc00fSVladimir Oltean 237*47211192STobias Waldekranz err = nbp_switchdev_hwdom_set(p); 238*47211192STobias Waldekranz if (err) 239*47211192STobias Waldekranz return err; 240*47211192STobias Waldekranz 241*47211192STobias Waldekranz if (tx_fwd_offload) { 242*47211192STobias Waldekranz p->flags |= BR_TX_FWD_OFFLOAD; 243*47211192STobias Waldekranz static_branch_inc(&br_switchdev_tx_fwd_offload); 244*47211192STobias Waldekranz } 245*47211192STobias Waldekranz 246*47211192STobias Waldekranz return 0; 24785826610STobias Waldekranz } 24885826610STobias Waldekranz 2492f5dc00fSVladimir Oltean static void nbp_switchdev_del(struct net_bridge_port *p) 25085826610STobias Waldekranz { 2512f5dc00fSVladimir Oltean if (WARN_ON(!p->offload_count)) 2522f5dc00fSVladimir Oltean return; 2532f5dc00fSVladimir Oltean 2542f5dc00fSVladimir Oltean p->offload_count--; 2552f5dc00fSVladimir Oltean 2562f5dc00fSVladimir Oltean if (p->offload_count) 2572f5dc00fSVladimir Oltean return; 25885826610STobias Waldekranz 25985826610STobias Waldekranz if (p->hwdom) 26085826610STobias Waldekranz nbp_switchdev_hwdom_put(p); 261*47211192STobias Waldekranz 262*47211192STobias Waldekranz if (p->flags & BR_TX_FWD_OFFLOAD) { 263*47211192STobias Waldekranz p->flags &= ~BR_TX_FWD_OFFLOAD; 264*47211192STobias Waldekranz static_branch_dec(&br_switchdev_tx_fwd_offload); 265*47211192STobias Waldekranz } 26685826610STobias Waldekranz } 2672f5dc00fSVladimir Oltean 2684e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 2694e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 2704e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 2714e51bf44SVladimir Oltean struct netlink_ext_ack *extack) 2724e51bf44SVladimir Oltean { 2734e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 2744e51bf44SVladimir Oltean struct net_device *dev = p->dev; 2754e51bf44SVladimir Oltean int err; 2764e51bf44SVladimir Oltean 2774e51bf44SVladimir Oltean err = br_vlan_replay(br_dev, dev, ctx, true, blocking_nb, extack); 2784e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2794e51bf44SVladimir Oltean return err; 2804e51bf44SVladimir Oltean 2814e51bf44SVladimir Oltean err = br_mdb_replay(br_dev, dev, ctx, true, blocking_nb, extack); 2824e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2834e51bf44SVladimir Oltean return err; 2844e51bf44SVladimir Oltean 2854e51bf44SVladimir Oltean /* Forwarding and termination FDB entries on the port */ 2864e51bf44SVladimir Oltean err = br_fdb_replay(br_dev, dev, ctx, true, atomic_nb); 2874e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2884e51bf44SVladimir Oltean return err; 2894e51bf44SVladimir Oltean 2904e51bf44SVladimir Oltean /* Termination FDB entries on the bridge itself */ 2914e51bf44SVladimir Oltean err = br_fdb_replay(br_dev, br_dev, ctx, true, atomic_nb); 2924e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2934e51bf44SVladimir Oltean return err; 2944e51bf44SVladimir Oltean 2954e51bf44SVladimir Oltean return 0; 2964e51bf44SVladimir Oltean } 2974e51bf44SVladimir Oltean 2984e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 2994e51bf44SVladimir Oltean const void *ctx, 3004e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3014e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 3024e51bf44SVladimir Oltean { 3034e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 3044e51bf44SVladimir Oltean struct net_device *dev = p->dev; 3054e51bf44SVladimir Oltean 3064e51bf44SVladimir Oltean br_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 3074e51bf44SVladimir Oltean 3084e51bf44SVladimir Oltean br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 3094e51bf44SVladimir Oltean 3104e51bf44SVladimir Oltean /* Forwarding and termination FDB entries on the port */ 3114e51bf44SVladimir Oltean br_fdb_replay(br_dev, dev, ctx, false, atomic_nb); 3124e51bf44SVladimir Oltean 3134e51bf44SVladimir Oltean /* Termination FDB entries on the bridge itself */ 3144e51bf44SVladimir Oltean br_fdb_replay(br_dev, br_dev, ctx, false, atomic_nb); 3154e51bf44SVladimir Oltean } 3164e51bf44SVladimir Oltean 3172f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a 3182f5dc00fSVladimir Oltean * switchdev hardware domain to it. 3192f5dc00fSVladimir Oltean */ 3202f5dc00fSVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev, 3214e51bf44SVladimir Oltean struct net_device *dev, const void *ctx, 3224e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3234e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 324*47211192STobias Waldekranz bool tx_fwd_offload, 3252f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 3262f5dc00fSVladimir Oltean { 3272f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid; 3282f5dc00fSVladimir Oltean struct net_bridge_port *p; 3292f5dc00fSVladimir Oltean int err; 3302f5dc00fSVladimir Oltean 3312f5dc00fSVladimir Oltean ASSERT_RTNL(); 3322f5dc00fSVladimir Oltean 3332f5dc00fSVladimir Oltean p = br_port_get_rtnl(brport_dev); 3342f5dc00fSVladimir Oltean if (!p) 3352f5dc00fSVladimir Oltean return -ENODEV; 3362f5dc00fSVladimir Oltean 3372f5dc00fSVladimir Oltean err = dev_get_port_parent_id(dev, &ppid, false); 3382f5dc00fSVladimir Oltean if (err) 3392f5dc00fSVladimir Oltean return err; 3402f5dc00fSVladimir Oltean 341*47211192STobias Waldekranz err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 3424e51bf44SVladimir Oltean if (err) 3434e51bf44SVladimir Oltean return err; 3444e51bf44SVladimir Oltean 3454e51bf44SVladimir Oltean err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 3464e51bf44SVladimir Oltean if (err) 3474e51bf44SVladimir Oltean goto out_switchdev_del; 3484e51bf44SVladimir Oltean 3494e51bf44SVladimir Oltean return 0; 3504e51bf44SVladimir Oltean 3514e51bf44SVladimir Oltean out_switchdev_del: 3524e51bf44SVladimir Oltean nbp_switchdev_del(p); 3534e51bf44SVladimir Oltean 3544e51bf44SVladimir Oltean return err; 3552f5dc00fSVladimir Oltean } 3562f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload); 3572f5dc00fSVladimir Oltean 3584e51bf44SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev, 3594e51bf44SVladimir Oltean const void *ctx, 3604e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3614e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 3622f5dc00fSVladimir Oltean { 3632f5dc00fSVladimir Oltean struct net_bridge_port *p; 3642f5dc00fSVladimir Oltean 3652f5dc00fSVladimir Oltean ASSERT_RTNL(); 3662f5dc00fSVladimir Oltean 3672f5dc00fSVladimir Oltean p = br_port_get_rtnl(brport_dev); 3682f5dc00fSVladimir Oltean if (!p) 3692f5dc00fSVladimir Oltean return; 3702f5dc00fSVladimir Oltean 3714e51bf44SVladimir Oltean nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 3724e51bf44SVladimir Oltean 3732f5dc00fSVladimir Oltean nbp_switchdev_del(p); 3742f5dc00fSVladimir Oltean } 3752f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload); 376