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 | \ 75fa1c8334SHans 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 334*6284c723STobias Waldekranz static int br_switchdev_vlan_attr_replay(struct net_device *br_dev, 335*6284c723STobias Waldekranz const void *ctx, 336*6284c723STobias Waldekranz struct notifier_block *nb, 337*6284c723STobias Waldekranz struct netlink_ext_ack *extack) 338*6284c723STobias Waldekranz { 339*6284c723STobias Waldekranz struct switchdev_notifier_port_attr_info attr_info = { 340*6284c723STobias Waldekranz .info = { 341*6284c723STobias Waldekranz .dev = br_dev, 342*6284c723STobias Waldekranz .extack = extack, 343*6284c723STobias Waldekranz .ctx = ctx, 344*6284c723STobias Waldekranz }, 345*6284c723STobias Waldekranz }; 346*6284c723STobias Waldekranz struct net_bridge *br = netdev_priv(br_dev); 347*6284c723STobias Waldekranz struct net_bridge_vlan_group *vg; 348*6284c723STobias Waldekranz struct switchdev_attr attr; 349*6284c723STobias Waldekranz struct net_bridge_vlan *v; 350*6284c723STobias Waldekranz int err; 351*6284c723STobias Waldekranz 352*6284c723STobias Waldekranz attr_info.attr = &attr; 353*6284c723STobias Waldekranz attr.orig_dev = br_dev; 354*6284c723STobias Waldekranz 355*6284c723STobias Waldekranz vg = br_vlan_group(br); 356*6284c723STobias Waldekranz 357*6284c723STobias Waldekranz list_for_each_entry(v, &vg->vlan_list, vlist) { 358*6284c723STobias Waldekranz if (v->msti) { 359*6284c723STobias Waldekranz attr.id = SWITCHDEV_ATTR_ID_VLAN_MSTI; 360*6284c723STobias Waldekranz attr.u.vlan_msti.vid = v->vid; 361*6284c723STobias Waldekranz attr.u.vlan_msti.msti = v->msti; 362*6284c723STobias Waldekranz 363*6284c723STobias Waldekranz err = nb->notifier_call(nb, SWITCHDEV_PORT_ATTR_SET, 364*6284c723STobias Waldekranz &attr_info); 365*6284c723STobias Waldekranz err = notifier_to_errno(err); 366*6284c723STobias Waldekranz if (err) 367*6284c723STobias Waldekranz return err; 368*6284c723STobias Waldekranz } 369*6284c723STobias Waldekranz } 370*6284c723STobias Waldekranz 371*6284c723STobias Waldekranz return 0; 372*6284c723STobias Waldekranz } 373*6284c723STobias Waldekranz 374326b212eSVladimir Oltean static int 375326b212eSVladimir Oltean br_switchdev_vlan_replay_one(struct notifier_block *nb, 3764a6849e4SVladimir Oltean struct net_device *dev, 3774a6849e4SVladimir Oltean struct switchdev_obj_port_vlan *vlan, 3784a6849e4SVladimir Oltean const void *ctx, unsigned long action, 3794a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 3804a6849e4SVladimir Oltean { 3814a6849e4SVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 3824a6849e4SVladimir Oltean .info = { 3834a6849e4SVladimir Oltean .dev = dev, 3844a6849e4SVladimir Oltean .extack = extack, 3854a6849e4SVladimir Oltean .ctx = ctx, 3864a6849e4SVladimir Oltean }, 3874a6849e4SVladimir Oltean .obj = &vlan->obj, 3884a6849e4SVladimir Oltean }; 3894a6849e4SVladimir Oltean int err; 3904a6849e4SVladimir Oltean 3914a6849e4SVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 3924a6849e4SVladimir Oltean return notifier_to_errno(err); 3934a6849e4SVladimir Oltean } 3944a6849e4SVladimir Oltean 395b28d580eSVladimir Oltean static int br_switchdev_vlan_replay_group(struct notifier_block *nb, 396326b212eSVladimir Oltean struct net_device *dev, 397b28d580eSVladimir Oltean struct net_bridge_vlan_group *vg, 398b28d580eSVladimir Oltean const void *ctx, unsigned long action, 3994a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 4004a6849e4SVladimir Oltean { 4014a6849e4SVladimir Oltean struct net_bridge_vlan *v; 4024a6849e4SVladimir Oltean int err = 0; 4034a6849e4SVladimir Oltean u16 pvid; 4044a6849e4SVladimir Oltean 4054a6849e4SVladimir Oltean if (!vg) 4064a6849e4SVladimir Oltean return 0; 4074a6849e4SVladimir Oltean 4084a6849e4SVladimir Oltean pvid = br_get_pvid(vg); 4094a6849e4SVladimir Oltean 4104a6849e4SVladimir Oltean list_for_each_entry(v, &vg->vlan_list, vlist) { 4114a6849e4SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 4124a6849e4SVladimir Oltean .obj.orig_dev = dev, 4134a6849e4SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 4144a6849e4SVladimir Oltean .flags = br_vlan_flags(v, pvid), 4154a6849e4SVladimir Oltean .vid = v->vid, 4164a6849e4SVladimir Oltean }; 4174a6849e4SVladimir Oltean 4184a6849e4SVladimir Oltean if (!br_vlan_should_use(v)) 4194a6849e4SVladimir Oltean continue; 4204a6849e4SVladimir Oltean 421326b212eSVladimir Oltean err = br_switchdev_vlan_replay_one(nb, dev, &vlan, ctx, 422326b212eSVladimir Oltean action, extack); 4234a6849e4SVladimir Oltean if (err) 4244a6849e4SVladimir Oltean return err; 4254a6849e4SVladimir Oltean } 4264a6849e4SVladimir Oltean 427b28d580eSVladimir Oltean return 0; 428b28d580eSVladimir Oltean } 429b28d580eSVladimir Oltean 430b28d580eSVladimir Oltean static int br_switchdev_vlan_replay(struct net_device *br_dev, 431b28d580eSVladimir Oltean const void *ctx, bool adding, 432b28d580eSVladimir Oltean struct notifier_block *nb, 433b28d580eSVladimir Oltean struct netlink_ext_ack *extack) 434b28d580eSVladimir Oltean { 435b28d580eSVladimir Oltean struct net_bridge *br = netdev_priv(br_dev); 436b28d580eSVladimir Oltean struct net_bridge_port *p; 437b28d580eSVladimir Oltean unsigned long action; 438b28d580eSVladimir Oltean int err; 439b28d580eSVladimir Oltean 440b28d580eSVladimir Oltean ASSERT_RTNL(); 441b28d580eSVladimir Oltean 442b28d580eSVladimir Oltean if (!nb) 443b28d580eSVladimir Oltean return 0; 444b28d580eSVladimir Oltean 445b28d580eSVladimir Oltean if (!netif_is_bridge_master(br_dev)) 446b28d580eSVladimir Oltean return -EINVAL; 447b28d580eSVladimir Oltean 448b28d580eSVladimir Oltean if (adding) 449b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 450b28d580eSVladimir Oltean else 451b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 452b28d580eSVladimir Oltean 453b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, br_dev, br_vlan_group(br), 454b28d580eSVladimir Oltean ctx, action, extack); 455b28d580eSVladimir Oltean if (err) 4564a6849e4SVladimir Oltean return err; 457b28d580eSVladimir Oltean 458b28d580eSVladimir Oltean list_for_each_entry(p, &br->port_list, list) { 459b28d580eSVladimir Oltean struct net_device *dev = p->dev; 460b28d580eSVladimir Oltean 461b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, dev, 462b28d580eSVladimir Oltean nbp_vlan_group(p), 463b28d580eSVladimir Oltean ctx, action, extack); 464b28d580eSVladimir Oltean if (err) 465b28d580eSVladimir Oltean return err; 466b28d580eSVladimir Oltean } 467b28d580eSVladimir Oltean 468*6284c723STobias Waldekranz if (adding) { 469*6284c723STobias Waldekranz err = br_switchdev_vlan_attr_replay(br_dev, ctx, nb, extack); 470*6284c723STobias Waldekranz if (err) 471*6284c723STobias Waldekranz return err; 472*6284c723STobias Waldekranz } 473*6284c723STobias Waldekranz 474b28d580eSVladimir Oltean return 0; 4754a6849e4SVladimir Oltean } 4764a6849e4SVladimir Oltean 4779776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 478326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info { 4799776457cSVladimir Oltean struct net_bridge_port *port; 4809776457cSVladimir Oltean struct br_ip ip; 4819776457cSVladimir Oltean }; 4829776457cSVladimir Oltean 483326b212eSVladimir Oltean static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *priv) 4849776457cSVladimir Oltean { 485326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *data = priv; 4869776457cSVladimir Oltean struct net_bridge_port_group __rcu **pp; 4879776457cSVladimir Oltean struct net_bridge_port_group *p; 4889776457cSVladimir Oltean struct net_bridge_mdb_entry *mp; 4899776457cSVladimir Oltean struct net_bridge_port *port = data->port; 4909776457cSVladimir Oltean struct net_bridge *br = port->br; 4919776457cSVladimir Oltean 4929776457cSVladimir Oltean if (err) 4939776457cSVladimir Oltean goto err; 4949776457cSVladimir Oltean 4959776457cSVladimir Oltean spin_lock_bh(&br->multicast_lock); 4969776457cSVladimir Oltean mp = br_mdb_ip_get(br, &data->ip); 4979776457cSVladimir Oltean if (!mp) 4989776457cSVladimir Oltean goto out; 4999776457cSVladimir Oltean for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; 5009776457cSVladimir Oltean pp = &p->next) { 5019776457cSVladimir Oltean if (p->key.port != port) 5029776457cSVladimir Oltean continue; 5039776457cSVladimir Oltean p->flags |= MDB_PG_FLAGS_OFFLOAD; 5049776457cSVladimir Oltean } 5059776457cSVladimir Oltean out: 5069776457cSVladimir Oltean spin_unlock_bh(&br->multicast_lock); 5079776457cSVladimir Oltean err: 5089776457cSVladimir Oltean kfree(priv); 5099776457cSVladimir Oltean } 5109776457cSVladimir Oltean 5119776457cSVladimir Oltean static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb, 5129776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp) 5139776457cSVladimir Oltean { 5149776457cSVladimir Oltean if (mp->addr.proto == htons(ETH_P_IP)) 5159776457cSVladimir Oltean ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr); 5169776457cSVladimir Oltean #if IS_ENABLED(CONFIG_IPV6) 5179776457cSVladimir Oltean else if (mp->addr.proto == htons(ETH_P_IPV6)) 5189776457cSVladimir Oltean ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb->addr); 5199776457cSVladimir Oltean #endif 5209776457cSVladimir Oltean else 5219776457cSVladimir Oltean ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr); 5229776457cSVladimir Oltean 5239776457cSVladimir Oltean mdb->vid = mp->addr.vid; 5249776457cSVladimir Oltean } 5259776457cSVladimir Oltean 526326b212eSVladimir Oltean static void br_switchdev_host_mdb_one(struct net_device *dev, 5279776457cSVladimir Oltean struct net_device *lower_dev, 5289776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 5299776457cSVladimir Oltean int type) 5309776457cSVladimir Oltean { 5319776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 5329776457cSVladimir Oltean .obj = { 5339776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_HOST_MDB, 5349776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 5359776457cSVladimir Oltean .orig_dev = dev, 5369776457cSVladimir Oltean }, 5379776457cSVladimir Oltean }; 5389776457cSVladimir Oltean 5399776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 5409776457cSVladimir Oltean 5419776457cSVladimir Oltean switch (type) { 5429776457cSVladimir Oltean case RTM_NEWMDB: 5439776457cSVladimir Oltean switchdev_port_obj_add(lower_dev, &mdb.obj, NULL); 5449776457cSVladimir Oltean break; 5459776457cSVladimir Oltean case RTM_DELMDB: 5469776457cSVladimir Oltean switchdev_port_obj_del(lower_dev, &mdb.obj); 5479776457cSVladimir Oltean break; 5489776457cSVladimir Oltean } 5499776457cSVladimir Oltean } 5509776457cSVladimir Oltean 551326b212eSVladimir Oltean static void br_switchdev_host_mdb(struct net_device *dev, 5529776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, int type) 5539776457cSVladimir Oltean { 5549776457cSVladimir Oltean struct net_device *lower_dev; 5559776457cSVladimir Oltean struct list_head *iter; 5569776457cSVladimir Oltean 5579776457cSVladimir Oltean netdev_for_each_lower_dev(dev, lower_dev, iter) 558326b212eSVladimir Oltean br_switchdev_host_mdb_one(dev, lower_dev, mp, type); 5599776457cSVladimir Oltean } 5609776457cSVladimir Oltean 561326b212eSVladimir Oltean static int 562326b212eSVladimir Oltean br_switchdev_mdb_replay_one(struct notifier_block *nb, struct net_device *dev, 5639776457cSVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 5649776457cSVladimir Oltean unsigned long action, const void *ctx, 5659776457cSVladimir Oltean struct netlink_ext_ack *extack) 5669776457cSVladimir Oltean { 5679776457cSVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 5689776457cSVladimir Oltean .info = { 5699776457cSVladimir Oltean .dev = dev, 5709776457cSVladimir Oltean .extack = extack, 5719776457cSVladimir Oltean .ctx = ctx, 5729776457cSVladimir Oltean }, 5739776457cSVladimir Oltean .obj = &mdb->obj, 5749776457cSVladimir Oltean }; 5759776457cSVladimir Oltean int err; 5769776457cSVladimir Oltean 5779776457cSVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 5789776457cSVladimir Oltean return notifier_to_errno(err); 5799776457cSVladimir Oltean } 5809776457cSVladimir Oltean 581326b212eSVladimir Oltean static int br_switchdev_mdb_queue_one(struct list_head *mdb_list, 5829776457cSVladimir Oltean enum switchdev_obj_id id, 5839776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp, 5849776457cSVladimir Oltean struct net_device *orig_dev) 5859776457cSVladimir Oltean { 5869776457cSVladimir Oltean struct switchdev_obj_port_mdb *mdb; 5879776457cSVladimir Oltean 5889776457cSVladimir Oltean mdb = kzalloc(sizeof(*mdb), GFP_ATOMIC); 5899776457cSVladimir Oltean if (!mdb) 5909776457cSVladimir Oltean return -ENOMEM; 5919776457cSVladimir Oltean 5929776457cSVladimir Oltean mdb->obj.id = id; 5939776457cSVladimir Oltean mdb->obj.orig_dev = orig_dev; 5949776457cSVladimir Oltean br_switchdev_mdb_populate(mdb, mp); 5959776457cSVladimir Oltean list_add_tail(&mdb->obj.list, mdb_list); 5969776457cSVladimir Oltean 5979776457cSVladimir Oltean return 0; 5989776457cSVladimir Oltean } 5999776457cSVladimir Oltean 6009776457cSVladimir Oltean void br_switchdev_mdb_notify(struct net_device *dev, 6019776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 6029776457cSVladimir Oltean struct net_bridge_port_group *pg, 6039776457cSVladimir Oltean int type) 6049776457cSVladimir Oltean { 605326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *complete_info; 6069776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 6079776457cSVladimir Oltean .obj = { 6089776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_PORT_MDB, 6099776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 6109776457cSVladimir Oltean }, 6119776457cSVladimir Oltean }; 6129776457cSVladimir Oltean 6139776457cSVladimir Oltean if (!pg) 614326b212eSVladimir Oltean return br_switchdev_host_mdb(dev, mp, type); 6159776457cSVladimir Oltean 6169776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 6179776457cSVladimir Oltean 6189776457cSVladimir Oltean mdb.obj.orig_dev = pg->key.port->dev; 6199776457cSVladimir Oltean switch (type) { 6209776457cSVladimir Oltean case RTM_NEWMDB: 6219776457cSVladimir Oltean complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC); 6229776457cSVladimir Oltean if (!complete_info) 6239776457cSVladimir Oltean break; 6249776457cSVladimir Oltean complete_info->port = pg->key.port; 6259776457cSVladimir Oltean complete_info->ip = mp->addr; 6269776457cSVladimir Oltean mdb.obj.complete_priv = complete_info; 627326b212eSVladimir Oltean mdb.obj.complete = br_switchdev_mdb_complete; 6289776457cSVladimir Oltean if (switchdev_port_obj_add(pg->key.port->dev, &mdb.obj, NULL)) 6299776457cSVladimir Oltean kfree(complete_info); 6309776457cSVladimir Oltean break; 6319776457cSVladimir Oltean case RTM_DELMDB: 6329776457cSVladimir Oltean switchdev_port_obj_del(pg->key.port->dev, &mdb.obj); 6339776457cSVladimir Oltean break; 6349776457cSVladimir Oltean } 6359776457cSVladimir Oltean } 6369776457cSVladimir Oltean #endif 6379776457cSVladimir Oltean 638326b212eSVladimir Oltean static int 639326b212eSVladimir Oltean br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev, 640326b212eSVladimir Oltean const void *ctx, bool adding, struct notifier_block *nb, 6419776457cSVladimir Oltean struct netlink_ext_ack *extack) 6429776457cSVladimir Oltean { 6439776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 6449776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp; 6459776457cSVladimir Oltean struct switchdev_obj *obj, *tmp; 6469776457cSVladimir Oltean struct net_bridge *br; 6479776457cSVladimir Oltean unsigned long action; 6489776457cSVladimir Oltean LIST_HEAD(mdb_list); 6499776457cSVladimir Oltean int err = 0; 6509776457cSVladimir Oltean 6519776457cSVladimir Oltean ASSERT_RTNL(); 6529776457cSVladimir Oltean 6539776457cSVladimir Oltean if (!nb) 6549776457cSVladimir Oltean return 0; 6559776457cSVladimir Oltean 6569776457cSVladimir Oltean if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev)) 6579776457cSVladimir Oltean return -EINVAL; 6589776457cSVladimir Oltean 6599776457cSVladimir Oltean br = netdev_priv(br_dev); 6609776457cSVladimir Oltean 6619776457cSVladimir Oltean if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 6629776457cSVladimir Oltean return 0; 6639776457cSVladimir Oltean 6649776457cSVladimir Oltean /* We cannot walk over br->mdb_list protected just by the rtnl_mutex, 6659776457cSVladimir Oltean * because the write-side protection is br->multicast_lock. But we 6669776457cSVladimir Oltean * need to emulate the [ blocking ] calling context of a regular 6679776457cSVladimir Oltean * switchdev event, so since both br->multicast_lock and RCU read side 6689776457cSVladimir Oltean * critical sections are atomic, we have no choice but to pick the RCU 6699776457cSVladimir Oltean * read side lock, queue up all our events, leave the critical section 6709776457cSVladimir Oltean * and notify switchdev from blocking context. 6719776457cSVladimir Oltean */ 6729776457cSVladimir Oltean rcu_read_lock(); 6739776457cSVladimir Oltean 6749776457cSVladimir Oltean hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { 6759776457cSVladimir Oltean struct net_bridge_port_group __rcu * const *pp; 6769776457cSVladimir Oltean const struct net_bridge_port_group *p; 6779776457cSVladimir Oltean 6789776457cSVladimir Oltean if (mp->host_joined) { 679326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6809776457cSVladimir Oltean SWITCHDEV_OBJ_ID_HOST_MDB, 6819776457cSVladimir Oltean mp, br_dev); 6829776457cSVladimir Oltean if (err) { 6839776457cSVladimir Oltean rcu_read_unlock(); 6849776457cSVladimir Oltean goto out_free_mdb; 6859776457cSVladimir Oltean } 6869776457cSVladimir Oltean } 6879776457cSVladimir Oltean 6889776457cSVladimir Oltean for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; 6899776457cSVladimir Oltean pp = &p->next) { 6909776457cSVladimir Oltean if (p->key.port->dev != dev) 6919776457cSVladimir Oltean continue; 6929776457cSVladimir Oltean 693326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6949776457cSVladimir Oltean SWITCHDEV_OBJ_ID_PORT_MDB, 6959776457cSVladimir Oltean mp, dev); 6969776457cSVladimir Oltean if (err) { 6979776457cSVladimir Oltean rcu_read_unlock(); 6989776457cSVladimir Oltean goto out_free_mdb; 6999776457cSVladimir Oltean } 7009776457cSVladimir Oltean } 7019776457cSVladimir Oltean } 7029776457cSVladimir Oltean 7039776457cSVladimir Oltean rcu_read_unlock(); 7049776457cSVladimir Oltean 7059776457cSVladimir Oltean if (adding) 7069776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 7079776457cSVladimir Oltean else 7089776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 7099776457cSVladimir Oltean 7109776457cSVladimir Oltean list_for_each_entry(obj, &mdb_list, list) { 711326b212eSVladimir Oltean err = br_switchdev_mdb_replay_one(nb, dev, 712326b212eSVladimir Oltean SWITCHDEV_OBJ_PORT_MDB(obj), 7139776457cSVladimir Oltean action, ctx, extack); 7149776457cSVladimir Oltean if (err) 7159776457cSVladimir Oltean goto out_free_mdb; 7169776457cSVladimir Oltean } 7179776457cSVladimir Oltean 7189776457cSVladimir Oltean out_free_mdb: 7199776457cSVladimir Oltean list_for_each_entry_safe(obj, tmp, &mdb_list, list) { 7209776457cSVladimir Oltean list_del(&obj->list); 7219776457cSVladimir Oltean kfree(SWITCHDEV_OBJ_PORT_MDB(obj)); 7229776457cSVladimir Oltean } 7239776457cSVladimir Oltean 7249776457cSVladimir Oltean if (err) 7259776457cSVladimir Oltean return err; 7269776457cSVladimir Oltean #endif 7279776457cSVladimir Oltean 7289776457cSVladimir Oltean return 0; 7299776457cSVladimir Oltean } 7309776457cSVladimir Oltean 7314e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 7324e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7334e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 7344e51bf44SVladimir Oltean struct netlink_ext_ack *extack) 7354e51bf44SVladimir Oltean { 7364e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 7374e51bf44SVladimir Oltean struct net_device *dev = p->dev; 7384e51bf44SVladimir Oltean int err; 7394e51bf44SVladimir Oltean 740b28d580eSVladimir Oltean err = br_switchdev_vlan_replay(br_dev, ctx, true, blocking_nb, extack); 7414e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7424e51bf44SVladimir Oltean return err; 7434e51bf44SVladimir Oltean 744326b212eSVladimir Oltean err = br_switchdev_mdb_replay(br_dev, dev, ctx, true, blocking_nb, 745326b212eSVladimir Oltean extack); 7464e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7474e51bf44SVladimir Oltean return err; 7484e51bf44SVladimir Oltean 749326b212eSVladimir Oltean err = br_switchdev_fdb_replay(br_dev, ctx, true, atomic_nb); 7504e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7514e51bf44SVladimir Oltean return err; 7524e51bf44SVladimir Oltean 7534e51bf44SVladimir Oltean return 0; 7544e51bf44SVladimir Oltean } 7554e51bf44SVladimir Oltean 7564e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 7574e51bf44SVladimir Oltean const void *ctx, 7584e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7594e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 7604e51bf44SVladimir Oltean { 7614e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 7624e51bf44SVladimir Oltean struct net_device *dev = p->dev; 7634e51bf44SVladimir Oltean 764263029aeSVladimir Oltean br_switchdev_fdb_replay(br_dev, ctx, false, atomic_nb); 7654e51bf44SVladimir Oltean 766326b212eSVladimir Oltean br_switchdev_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 7674e51bf44SVladimir Oltean 768b28d580eSVladimir Oltean br_switchdev_vlan_replay(br_dev, ctx, false, blocking_nb, NULL); 7694e51bf44SVladimir Oltean } 7704e51bf44SVladimir Oltean 7712f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a 7722f5dc00fSVladimir Oltean * switchdev hardware domain to it. 7732f5dc00fSVladimir Oltean */ 774957e2235SVladimir Oltean int br_switchdev_port_offload(struct net_bridge_port *p, 7754e51bf44SVladimir Oltean struct net_device *dev, const void *ctx, 7764e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7774e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 77847211192STobias Waldekranz bool tx_fwd_offload, 7792f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 7802f5dc00fSVladimir Oltean { 7812f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid; 7822f5dc00fSVladimir Oltean int err; 7832f5dc00fSVladimir Oltean 7842f5dc00fSVladimir Oltean err = dev_get_port_parent_id(dev, &ppid, false); 7852f5dc00fSVladimir Oltean if (err) 7862f5dc00fSVladimir Oltean return err; 7872f5dc00fSVladimir Oltean 78847211192STobias Waldekranz err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 7894e51bf44SVladimir Oltean if (err) 7904e51bf44SVladimir Oltean return err; 7914e51bf44SVladimir Oltean 7924e51bf44SVladimir Oltean err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 7934e51bf44SVladimir Oltean if (err) 7944e51bf44SVladimir Oltean goto out_switchdev_del; 7954e51bf44SVladimir Oltean 7964e51bf44SVladimir Oltean return 0; 7974e51bf44SVladimir Oltean 7984e51bf44SVladimir Oltean out_switchdev_del: 7994e51bf44SVladimir Oltean nbp_switchdev_del(p); 8004e51bf44SVladimir Oltean 8014e51bf44SVladimir Oltean return err; 8022f5dc00fSVladimir Oltean } 8032f5dc00fSVladimir Oltean 804957e2235SVladimir Oltean void br_switchdev_port_unoffload(struct net_bridge_port *p, const void *ctx, 8054e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 8064e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 8072f5dc00fSVladimir Oltean { 8084e51bf44SVladimir Oltean nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 8094e51bf44SVladimir Oltean 8102f5dc00fSVladimir Oltean nbp_switchdev_del(p); 8112f5dc00fSVladimir Oltean } 812