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 1147211192STobias Waldekranz static struct static_key_false br_switchdev_tx_fwd_offload; 1247211192STobias Waldekranz 1347211192STobias Waldekranz static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p, 1447211192STobias Waldekranz const struct sk_buff *skb) 1547211192STobias Waldekranz { 1647211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 1747211192STobias Waldekranz return false; 1847211192STobias Waldekranz 1947211192STobias Waldekranz return (p->flags & BR_TX_FWD_OFFLOAD) && 2047211192STobias Waldekranz (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom); 2147211192STobias Waldekranz } 2247211192STobias Waldekranz 2347211192STobias Waldekranz bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb) 2447211192STobias Waldekranz { 2547211192STobias Waldekranz if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 2647211192STobias Waldekranz return false; 2747211192STobias Waldekranz 2847211192STobias Waldekranz return BR_INPUT_SKB_CB(skb)->tx_fwd_offload; 2947211192STobias Waldekranz } 3047211192STobias Waldekranz 31*c5381154SVladimir Oltean void br_switchdev_frame_set_offload_fwd_mark(struct sk_buff *skb) 32*c5381154SVladimir Oltean { 33*c5381154SVladimir Oltean skb->offload_fwd_mark = br_switchdev_frame_uses_tx_fwd_offload(skb); 34*c5381154SVladimir Oltean } 35*c5381154SVladimir Oltean 3647211192STobias Waldekranz /* Mark the frame for TX forwarding offload if this egress port supports it */ 3747211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p, 3847211192STobias Waldekranz struct sk_buff *skb) 3947211192STobias Waldekranz { 4047211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 4147211192STobias Waldekranz BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true; 4247211192STobias Waldekranz } 4347211192STobias Waldekranz 4447211192STobias Waldekranz /* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms 4547211192STobias Waldekranz * that the skb has been already forwarded to, to avoid further cloning to 4647211192STobias Waldekranz * other ports in the same hwdom by making nbp_switchdev_allowed_egress() 4747211192STobias Waldekranz * return false. 4847211192STobias Waldekranz */ 4947211192STobias Waldekranz void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p, 5047211192STobias Waldekranz struct sk_buff *skb) 5147211192STobias Waldekranz { 5247211192STobias Waldekranz if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 5347211192STobias Waldekranz set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms); 5447211192STobias Waldekranz } 5547211192STobias Waldekranz 566bc506b4SIdo Schimmel void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 576bc506b4SIdo Schimmel struct sk_buff *skb) 586bc506b4SIdo Schimmel { 59f7cf972fSTobias Waldekranz if (p->hwdom) 60f7cf972fSTobias Waldekranz BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom; 616bc506b4SIdo Schimmel } 626bc506b4SIdo Schimmel 636bc506b4SIdo Schimmel bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 646bc506b4SIdo Schimmel const struct sk_buff *skb) 656bc506b4SIdo Schimmel { 6647211192STobias Waldekranz struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb); 6747211192STobias Waldekranz 6847211192STobias Waldekranz return !test_bit(p->hwdom, &cb->fwd_hwdoms) && 6947211192STobias Waldekranz (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom); 706bc506b4SIdo Schimmel } 713922285dSArkadi Sharshevsky 723922285dSArkadi Sharshevsky /* Flags that can be offloaded to hardware */ 733922285dSArkadi Sharshevsky #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 743922285dSArkadi Sharshevsky BR_MCAST_FLOOD | BR_BCAST_FLOOD) 753922285dSArkadi Sharshevsky 763922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p, 773922285dSArkadi Sharshevsky unsigned long flags, 78078bbb85SVladimir Oltean unsigned long mask, 79078bbb85SVladimir Oltean struct netlink_ext_ack *extack) 803922285dSArkadi Sharshevsky { 813922285dSArkadi Sharshevsky struct switchdev_attr attr = { 823922285dSArkadi Sharshevsky .orig_dev = p->dev, 833922285dSArkadi Sharshevsky }; 84d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info info = { 85d45224d6SFlorian Fainelli .attr = &attr, 86d45224d6SFlorian Fainelli }; 873922285dSArkadi Sharshevsky int err; 883922285dSArkadi Sharshevsky 89304ae3bfSVladimir Oltean mask &= BR_PORT_FLAGS_HW_OFFLOAD; 90304ae3bfSVladimir Oltean if (!mask) 913922285dSArkadi Sharshevsky return 0; 923922285dSArkadi Sharshevsky 93e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 94e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 95e18f4c18SVladimir Oltean attr.u.brport_flags.mask = mask; 96304ae3bfSVladimir Oltean 97d45224d6SFlorian Fainelli /* We run from atomic context here */ 98d45224d6SFlorian Fainelli err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 99078bbb85SVladimir Oltean &info.info, extack); 100d45224d6SFlorian Fainelli err = notifier_to_errno(err); 1013922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 1023922285dSArkadi Sharshevsky return 0; 1033922285dSArkadi Sharshevsky 1041ef07644SFlorian Fainelli if (err) { 105078bbb85SVladimir Oltean if (extack && !extack->_msg) 106078bbb85SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 107078bbb85SVladimir Oltean "bridge flag offload is not supported"); 1083922285dSArkadi Sharshevsky return -EOPNOTSUPP; 1093922285dSArkadi Sharshevsky } 1103922285dSArkadi Sharshevsky 1113922285dSArkadi Sharshevsky attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 1123922285dSArkadi Sharshevsky attr.flags = SWITCHDEV_F_DEFER; 1131ef07644SFlorian Fainelli 114dcbdf135SVladimir Oltean err = switchdev_port_attr_set(p->dev, &attr, extack); 1153922285dSArkadi Sharshevsky if (err) { 116dcbdf135SVladimir Oltean if (extack && !extack->_msg) 117dcbdf135SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 118dcbdf135SVladimir Oltean "error setting offload flag on port"); 1193922285dSArkadi Sharshevsky return err; 1203922285dSArkadi Sharshevsky } 1213922285dSArkadi Sharshevsky 1223922285dSArkadi Sharshevsky return 0; 1233922285dSArkadi Sharshevsky } 1246b26b51bSArkadi Sharshevsky 1256b26b51bSArkadi Sharshevsky void 1266eb38bf8STobias Waldekranz br_switchdev_fdb_notify(struct net_bridge *br, 1276eb38bf8STobias Waldekranz const struct net_bridge_fdb_entry *fdb, int type) 1286b26b51bSArkadi Sharshevsky { 1293e19ae7cSVladimir Oltean const struct net_bridge_port *dst = READ_ONCE(fdb->dst); 1306eb38bf8STobias Waldekranz struct net_device *dev = dst ? dst->dev : br->dev; 131e5b4b898STobias Waldekranz struct switchdev_notifier_fdb_info info = { 132e5b4b898STobias Waldekranz .addr = fdb->key.addr.addr, 133e5b4b898STobias Waldekranz .vid = fdb->key.vlan_id, 134e5b4b898STobias Waldekranz .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), 1352c4eca3eSVladimir Oltean .is_local = test_bit(BR_FDB_LOCAL, &fdb->flags), 136e5b4b898STobias Waldekranz .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), 137e5b4b898STobias Waldekranz }; 138e5b4b898STobias Waldekranz 1396b26b51bSArkadi Sharshevsky switch (type) { 1406b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 141e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 1426eb38bf8STobias Waldekranz dev, &info.info, NULL); 1436b26b51bSArkadi Sharshevsky break; 1446b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 145e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 1466eb38bf8STobias Waldekranz dev, &info.info, NULL); 1476b26b51bSArkadi Sharshevsky break; 1486b26b51bSArkadi Sharshevsky } 1496b26b51bSArkadi Sharshevsky } 150d66e4348SPetr Machata 151169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 152169327d5SPetr Machata struct netlink_ext_ack *extack) 153d66e4348SPetr Machata { 154d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 155d66e4348SPetr Machata .obj.orig_dev = dev, 156d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 157d66e4348SPetr Machata .flags = flags, 158b7a9e0daSVladimir Oltean .vid = vid, 159d66e4348SPetr Machata }; 160d66e4348SPetr Machata 16169b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 162d66e4348SPetr Machata } 163d66e4348SPetr Machata 164d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 165d66e4348SPetr Machata { 166d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 167d66e4348SPetr Machata .obj.orig_dev = dev, 168d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 169b7a9e0daSVladimir Oltean .vid = vid, 170d66e4348SPetr Machata }; 171d66e4348SPetr Machata 172d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 173d66e4348SPetr Machata } 17485826610STobias Waldekranz 17585826610STobias Waldekranz static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) 17685826610STobias Waldekranz { 17785826610STobias Waldekranz struct net_bridge *br = joining->br; 17885826610STobias Waldekranz struct net_bridge_port *p; 17985826610STobias Waldekranz int hwdom; 18085826610STobias Waldekranz 18185826610STobias Waldekranz /* joining is yet to be added to the port list. */ 18285826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 1832f5dc00fSVladimir Oltean if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { 18485826610STobias Waldekranz joining->hwdom = p->hwdom; 18585826610STobias Waldekranz return 0; 18685826610STobias Waldekranz } 18785826610STobias Waldekranz } 18885826610STobias Waldekranz 18985826610STobias Waldekranz hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1); 19085826610STobias Waldekranz if (hwdom >= BR_HWDOM_MAX) 19185826610STobias Waldekranz return -EBUSY; 19285826610STobias Waldekranz 19385826610STobias Waldekranz set_bit(hwdom, &br->busy_hwdoms); 19485826610STobias Waldekranz joining->hwdom = hwdom; 19585826610STobias Waldekranz return 0; 19685826610STobias Waldekranz } 19785826610STobias Waldekranz 19885826610STobias Waldekranz static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) 19985826610STobias Waldekranz { 20085826610STobias Waldekranz struct net_bridge *br = leaving->br; 20185826610STobias Waldekranz struct net_bridge_port *p; 20285826610STobias Waldekranz 20385826610STobias Waldekranz /* leaving is no longer in the port list. */ 20485826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 20585826610STobias Waldekranz if (p->hwdom == leaving->hwdom) 20685826610STobias Waldekranz return; 20785826610STobias Waldekranz } 20885826610STobias Waldekranz 20985826610STobias Waldekranz clear_bit(leaving->hwdom, &br->busy_hwdoms); 21085826610STobias Waldekranz } 21185826610STobias Waldekranz 2122f5dc00fSVladimir Oltean static int nbp_switchdev_add(struct net_bridge_port *p, 2132f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid, 21447211192STobias Waldekranz bool tx_fwd_offload, 2152f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 21685826610STobias Waldekranz { 21747211192STobias Waldekranz int err; 21847211192STobias Waldekranz 2192f5dc00fSVladimir Oltean if (p->offload_count) { 2202f5dc00fSVladimir Oltean /* Prevent unsupported configurations such as a bridge port 2212f5dc00fSVladimir Oltean * which is a bonding interface, and the member ports are from 2222f5dc00fSVladimir Oltean * different hardware switches. 2232f5dc00fSVladimir Oltean */ 2242f5dc00fSVladimir Oltean if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { 2252f5dc00fSVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 2262f5dc00fSVladimir Oltean "Same bridge port cannot be offloaded by two physical switches"); 2272f5dc00fSVladimir Oltean return -EBUSY; 22885826610STobias Waldekranz } 22985826610STobias Waldekranz 2302f5dc00fSVladimir Oltean /* Tolerate drivers that call switchdev_bridge_port_offload() 2312f5dc00fSVladimir Oltean * more than once for the same bridge port, such as when the 2322f5dc00fSVladimir Oltean * bridge port is an offloaded bonding/team interface. 2332f5dc00fSVladimir Oltean */ 2342f5dc00fSVladimir Oltean p->offload_count++; 2352f5dc00fSVladimir Oltean 2362f5dc00fSVladimir Oltean return 0; 2372f5dc00fSVladimir Oltean } 2382f5dc00fSVladimir Oltean 2392f5dc00fSVladimir Oltean p->ppid = ppid; 2402f5dc00fSVladimir Oltean p->offload_count = 1; 2412f5dc00fSVladimir Oltean 24247211192STobias Waldekranz err = nbp_switchdev_hwdom_set(p); 24347211192STobias Waldekranz if (err) 24447211192STobias Waldekranz return err; 24547211192STobias Waldekranz 24647211192STobias Waldekranz if (tx_fwd_offload) { 24747211192STobias Waldekranz p->flags |= BR_TX_FWD_OFFLOAD; 24847211192STobias Waldekranz static_branch_inc(&br_switchdev_tx_fwd_offload); 24947211192STobias Waldekranz } 25047211192STobias Waldekranz 25147211192STobias Waldekranz return 0; 25285826610STobias Waldekranz } 25385826610STobias Waldekranz 2542f5dc00fSVladimir Oltean static void nbp_switchdev_del(struct net_bridge_port *p) 25585826610STobias Waldekranz { 2562f5dc00fSVladimir Oltean if (WARN_ON(!p->offload_count)) 2572f5dc00fSVladimir Oltean return; 2582f5dc00fSVladimir Oltean 2592f5dc00fSVladimir Oltean p->offload_count--; 2602f5dc00fSVladimir Oltean 2612f5dc00fSVladimir Oltean if (p->offload_count) 2622f5dc00fSVladimir Oltean return; 26385826610STobias Waldekranz 26485826610STobias Waldekranz if (p->hwdom) 26585826610STobias Waldekranz nbp_switchdev_hwdom_put(p); 26647211192STobias Waldekranz 26747211192STobias Waldekranz if (p->flags & BR_TX_FWD_OFFLOAD) { 26847211192STobias Waldekranz p->flags &= ~BR_TX_FWD_OFFLOAD; 26947211192STobias Waldekranz static_branch_dec(&br_switchdev_tx_fwd_offload); 27047211192STobias Waldekranz } 27185826610STobias Waldekranz } 2722f5dc00fSVladimir Oltean 2734e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 2744e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 2754e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 2764e51bf44SVladimir Oltean struct netlink_ext_ack *extack) 2774e51bf44SVladimir Oltean { 2784e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 2794e51bf44SVladimir Oltean struct net_device *dev = p->dev; 2804e51bf44SVladimir Oltean int err; 2814e51bf44SVladimir Oltean 2824e51bf44SVladimir Oltean err = br_vlan_replay(br_dev, dev, ctx, true, blocking_nb, extack); 2834e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2844e51bf44SVladimir Oltean return err; 2854e51bf44SVladimir Oltean 2864e51bf44SVladimir Oltean err = br_mdb_replay(br_dev, dev, ctx, true, blocking_nb, extack); 2874e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2884e51bf44SVladimir Oltean return err; 2894e51bf44SVladimir Oltean 2904e51bf44SVladimir Oltean /* Forwarding and termination FDB entries on the port */ 2914e51bf44SVladimir Oltean err = br_fdb_replay(br_dev, dev, ctx, true, atomic_nb); 2924e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2934e51bf44SVladimir Oltean return err; 2944e51bf44SVladimir Oltean 2954e51bf44SVladimir Oltean /* Termination FDB entries on the bridge itself */ 2964e51bf44SVladimir Oltean err = br_fdb_replay(br_dev, br_dev, ctx, true, atomic_nb); 2974e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 2984e51bf44SVladimir Oltean return err; 2994e51bf44SVladimir Oltean 3004e51bf44SVladimir Oltean return 0; 3014e51bf44SVladimir Oltean } 3024e51bf44SVladimir Oltean 3034e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 3044e51bf44SVladimir Oltean const void *ctx, 3054e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3064e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 3074e51bf44SVladimir Oltean { 3084e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 3094e51bf44SVladimir Oltean struct net_device *dev = p->dev; 3104e51bf44SVladimir Oltean 3114e51bf44SVladimir Oltean br_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 3124e51bf44SVladimir Oltean 3134e51bf44SVladimir Oltean br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 3144e51bf44SVladimir Oltean 3154e51bf44SVladimir Oltean /* Forwarding and termination FDB entries on the port */ 3164e51bf44SVladimir Oltean br_fdb_replay(br_dev, dev, ctx, false, atomic_nb); 3174e51bf44SVladimir Oltean 3184e51bf44SVladimir Oltean /* Termination FDB entries on the bridge itself */ 3194e51bf44SVladimir Oltean br_fdb_replay(br_dev, br_dev, ctx, false, atomic_nb); 3204e51bf44SVladimir Oltean } 3214e51bf44SVladimir Oltean 3222f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a 3232f5dc00fSVladimir Oltean * switchdev hardware domain to it. 3242f5dc00fSVladimir Oltean */ 3252f5dc00fSVladimir Oltean int switchdev_bridge_port_offload(struct net_device *brport_dev, 3264e51bf44SVladimir Oltean struct net_device *dev, const void *ctx, 3274e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3284e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 32947211192STobias Waldekranz bool tx_fwd_offload, 3302f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 3312f5dc00fSVladimir Oltean { 3322f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid; 3332f5dc00fSVladimir Oltean struct net_bridge_port *p; 3342f5dc00fSVladimir Oltean int err; 3352f5dc00fSVladimir Oltean 3362f5dc00fSVladimir Oltean ASSERT_RTNL(); 3372f5dc00fSVladimir Oltean 3382f5dc00fSVladimir Oltean p = br_port_get_rtnl(brport_dev); 3392f5dc00fSVladimir Oltean if (!p) 3402f5dc00fSVladimir Oltean return -ENODEV; 3412f5dc00fSVladimir Oltean 3422f5dc00fSVladimir Oltean err = dev_get_port_parent_id(dev, &ppid, false); 3432f5dc00fSVladimir Oltean if (err) 3442f5dc00fSVladimir Oltean return err; 3452f5dc00fSVladimir Oltean 34647211192STobias Waldekranz err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 3474e51bf44SVladimir Oltean if (err) 3484e51bf44SVladimir Oltean return err; 3494e51bf44SVladimir Oltean 3504e51bf44SVladimir Oltean err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 3514e51bf44SVladimir Oltean if (err) 3524e51bf44SVladimir Oltean goto out_switchdev_del; 3534e51bf44SVladimir Oltean 3544e51bf44SVladimir Oltean return 0; 3554e51bf44SVladimir Oltean 3564e51bf44SVladimir Oltean out_switchdev_del: 3574e51bf44SVladimir Oltean nbp_switchdev_del(p); 3584e51bf44SVladimir Oltean 3594e51bf44SVladimir Oltean return err; 3602f5dc00fSVladimir Oltean } 3612f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload); 3622f5dc00fSVladimir Oltean 3634e51bf44SVladimir Oltean void switchdev_bridge_port_unoffload(struct net_device *brport_dev, 3644e51bf44SVladimir Oltean const void *ctx, 3654e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 3664e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 3672f5dc00fSVladimir Oltean { 3682f5dc00fSVladimir Oltean struct net_bridge_port *p; 3692f5dc00fSVladimir Oltean 3702f5dc00fSVladimir Oltean ASSERT_RTNL(); 3712f5dc00fSVladimir Oltean 3722f5dc00fSVladimir Oltean p = br_port_get_rtnl(brport_dev); 3732f5dc00fSVladimir Oltean if (!p) 3742f5dc00fSVladimir Oltean return; 3752f5dc00fSVladimir Oltean 3764e51bf44SVladimir Oltean nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 3774e51bf44SVladimir Oltean 3782f5dc00fSVladimir Oltean nbp_switchdev_del(p); 3792f5dc00fSVladimir Oltean } 3802f5dc00fSVladimir Oltean EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload); 381