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 */ 749c0ca02bSIdo Schimmel #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | BR_PORT_MAB | \ 75c3976a3fSArınç ÜNAL BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_PORT_LOCKED | \ 76c3976a3fSArınç ÜNAL BR_HAIRPIN_MODE | BR_ISOLATED | BR_MULTICAST_TO_UNICAST) 773922285dSArkadi Sharshevsky 783922285dSArkadi Sharshevsky int br_switchdev_set_port_flag(struct net_bridge_port *p, 793922285dSArkadi Sharshevsky unsigned long flags, 80078bbb85SVladimir Oltean unsigned long mask, 81078bbb85SVladimir Oltean struct netlink_ext_ack *extack) 823922285dSArkadi Sharshevsky { 833922285dSArkadi Sharshevsky struct switchdev_attr attr = { 843922285dSArkadi Sharshevsky .orig_dev = p->dev, 853922285dSArkadi Sharshevsky }; 86d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info info = { 87d45224d6SFlorian Fainelli .attr = &attr, 88d45224d6SFlorian Fainelli }; 893922285dSArkadi Sharshevsky int err; 903922285dSArkadi Sharshevsky 91304ae3bfSVladimir Oltean mask &= BR_PORT_FLAGS_HW_OFFLOAD; 92304ae3bfSVladimir Oltean if (!mask) 933922285dSArkadi Sharshevsky return 0; 943922285dSArkadi Sharshevsky 95e18f4c18SVladimir Oltean attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 96e18f4c18SVladimir Oltean attr.u.brport_flags.val = flags; 97e18f4c18SVladimir Oltean attr.u.brport_flags.mask = mask; 98304ae3bfSVladimir Oltean 99d45224d6SFlorian Fainelli /* We run from atomic context here */ 100d45224d6SFlorian Fainelli err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 101078bbb85SVladimir Oltean &info.info, extack); 102d45224d6SFlorian Fainelli err = notifier_to_errno(err); 1033922285dSArkadi Sharshevsky if (err == -EOPNOTSUPP) 1043922285dSArkadi Sharshevsky return 0; 1053922285dSArkadi Sharshevsky 1061ef07644SFlorian Fainelli if (err) { 107*028fb19cSLeon Romanovsky NL_SET_ERR_MSG_WEAK_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) { 117*028fb19cSLeon Romanovsky NL_SET_ERR_MSG_WEAK_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 125fab9eca8SVladimir Oltean static void br_switchdev_fdb_populate(struct net_bridge *br, 126fab9eca8SVladimir Oltean struct switchdev_notifier_fdb_info *item, 127fab9eca8SVladimir Oltean const struct net_bridge_fdb_entry *fdb, 128fab9eca8SVladimir Oltean const void *ctx) 129fab9eca8SVladimir Oltean { 130fab9eca8SVladimir Oltean const struct net_bridge_port *p = READ_ONCE(fdb->dst); 131fab9eca8SVladimir Oltean 132fab9eca8SVladimir Oltean item->addr = fdb->key.addr.addr; 133fab9eca8SVladimir Oltean item->vid = fdb->key.vlan_id; 134fab9eca8SVladimir Oltean item->added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); 135fab9eca8SVladimir Oltean item->offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags); 136fab9eca8SVladimir Oltean item->is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); 13727fabd02SHans J. Schultz item->locked = false; 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 14827fabd02SHans J. Schultz if (test_bit(BR_FDB_LOCKED, &fdb->flags)) 14927fabd02SHans J. Schultz return; 15027fabd02SHans J. Schultz 151fab9eca8SVladimir Oltean br_switchdev_fdb_populate(br, &item, fdb, NULL); 152e5b4b898STobias Waldekranz 1536b26b51bSArkadi Sharshevsky switch (type) { 1546b26b51bSArkadi Sharshevsky case RTM_DELNEIGH: 155e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 156fab9eca8SVladimir Oltean item.info.dev, &item.info, NULL); 1576b26b51bSArkadi Sharshevsky break; 1586b26b51bSArkadi Sharshevsky case RTM_NEWNEIGH: 159e5b4b898STobias Waldekranz call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 160fab9eca8SVladimir Oltean item.info.dev, &item.info, NULL); 1616b26b51bSArkadi Sharshevsky break; 1626b26b51bSArkadi Sharshevsky } 1636b26b51bSArkadi Sharshevsky } 164d66e4348SPetr Machata 165169327d5SPetr Machata int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 1668d23a54fSVladimir Oltean bool changed, struct netlink_ext_ack *extack) 167d66e4348SPetr Machata { 168d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 169d66e4348SPetr Machata .obj.orig_dev = dev, 170d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 171d66e4348SPetr Machata .flags = flags, 172b7a9e0daSVladimir Oltean .vid = vid, 1738d23a54fSVladimir Oltean .changed = changed, 174d66e4348SPetr Machata }; 175d66e4348SPetr Machata 17669b7320eSPetr Machata return switchdev_port_obj_add(dev, &v.obj, extack); 177d66e4348SPetr Machata } 178d66e4348SPetr Machata 179d66e4348SPetr Machata int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 180d66e4348SPetr Machata { 181d66e4348SPetr Machata struct switchdev_obj_port_vlan v = { 182d66e4348SPetr Machata .obj.orig_dev = dev, 183d66e4348SPetr Machata .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 184b7a9e0daSVladimir Oltean .vid = vid, 185d66e4348SPetr Machata }; 186d66e4348SPetr Machata 187d66e4348SPetr Machata return switchdev_port_obj_del(dev, &v.obj); 188d66e4348SPetr Machata } 18985826610STobias Waldekranz 19085826610STobias Waldekranz static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) 19185826610STobias Waldekranz { 19285826610STobias Waldekranz struct net_bridge *br = joining->br; 19385826610STobias Waldekranz struct net_bridge_port *p; 19485826610STobias Waldekranz int hwdom; 19585826610STobias Waldekranz 19685826610STobias Waldekranz /* joining is yet to be added to the port list. */ 19785826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 1982f5dc00fSVladimir Oltean if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { 19985826610STobias Waldekranz joining->hwdom = p->hwdom; 20085826610STobias Waldekranz return 0; 20185826610STobias Waldekranz } 20285826610STobias Waldekranz } 20385826610STobias Waldekranz 20485826610STobias Waldekranz hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1); 20585826610STobias Waldekranz if (hwdom >= BR_HWDOM_MAX) 20685826610STobias Waldekranz return -EBUSY; 20785826610STobias Waldekranz 20885826610STobias Waldekranz set_bit(hwdom, &br->busy_hwdoms); 20985826610STobias Waldekranz joining->hwdom = hwdom; 21085826610STobias Waldekranz return 0; 21185826610STobias Waldekranz } 21285826610STobias Waldekranz 21385826610STobias Waldekranz static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) 21485826610STobias Waldekranz { 21585826610STobias Waldekranz struct net_bridge *br = leaving->br; 21685826610STobias Waldekranz struct net_bridge_port *p; 21785826610STobias Waldekranz 21885826610STobias Waldekranz /* leaving is no longer in the port list. */ 21985826610STobias Waldekranz list_for_each_entry(p, &br->port_list, list) { 22085826610STobias Waldekranz if (p->hwdom == leaving->hwdom) 22185826610STobias Waldekranz return; 22285826610STobias Waldekranz } 22385826610STobias Waldekranz 22485826610STobias Waldekranz clear_bit(leaving->hwdom, &br->busy_hwdoms); 22585826610STobias Waldekranz } 22685826610STobias Waldekranz 2272f5dc00fSVladimir Oltean static int nbp_switchdev_add(struct net_bridge_port *p, 2282f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid, 22947211192STobias Waldekranz bool tx_fwd_offload, 2302f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 23185826610STobias Waldekranz { 23247211192STobias Waldekranz int err; 23347211192STobias Waldekranz 2342f5dc00fSVladimir Oltean if (p->offload_count) { 2352f5dc00fSVladimir Oltean /* Prevent unsupported configurations such as a bridge port 2362f5dc00fSVladimir Oltean * which is a bonding interface, and the member ports are from 2372f5dc00fSVladimir Oltean * different hardware switches. 2382f5dc00fSVladimir Oltean */ 2392f5dc00fSVladimir Oltean if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { 2402f5dc00fSVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 2412f5dc00fSVladimir Oltean "Same bridge port cannot be offloaded by two physical switches"); 2422f5dc00fSVladimir Oltean return -EBUSY; 24385826610STobias Waldekranz } 24485826610STobias Waldekranz 2452f5dc00fSVladimir Oltean /* Tolerate drivers that call switchdev_bridge_port_offload() 2462f5dc00fSVladimir Oltean * more than once for the same bridge port, such as when the 2472f5dc00fSVladimir Oltean * bridge port is an offloaded bonding/team interface. 2482f5dc00fSVladimir Oltean */ 2492f5dc00fSVladimir Oltean p->offload_count++; 2502f5dc00fSVladimir Oltean 2512f5dc00fSVladimir Oltean return 0; 2522f5dc00fSVladimir Oltean } 2532f5dc00fSVladimir Oltean 2542f5dc00fSVladimir Oltean p->ppid = ppid; 2552f5dc00fSVladimir Oltean p->offload_count = 1; 2562f5dc00fSVladimir Oltean 25747211192STobias Waldekranz err = nbp_switchdev_hwdom_set(p); 25847211192STobias Waldekranz if (err) 25947211192STobias Waldekranz return err; 26047211192STobias Waldekranz 26147211192STobias Waldekranz if (tx_fwd_offload) { 26247211192STobias Waldekranz p->flags |= BR_TX_FWD_OFFLOAD; 26347211192STobias Waldekranz static_branch_inc(&br_switchdev_tx_fwd_offload); 26447211192STobias Waldekranz } 26547211192STobias Waldekranz 26647211192STobias Waldekranz return 0; 26785826610STobias Waldekranz } 26885826610STobias Waldekranz 2692f5dc00fSVladimir Oltean static void nbp_switchdev_del(struct net_bridge_port *p) 27085826610STobias Waldekranz { 2712f5dc00fSVladimir Oltean if (WARN_ON(!p->offload_count)) 2722f5dc00fSVladimir Oltean return; 2732f5dc00fSVladimir Oltean 2742f5dc00fSVladimir Oltean p->offload_count--; 2752f5dc00fSVladimir Oltean 2762f5dc00fSVladimir Oltean if (p->offload_count) 2772f5dc00fSVladimir Oltean return; 27885826610STobias Waldekranz 27985826610STobias Waldekranz if (p->hwdom) 28085826610STobias Waldekranz nbp_switchdev_hwdom_put(p); 28147211192STobias Waldekranz 28247211192STobias Waldekranz if (p->flags & BR_TX_FWD_OFFLOAD) { 28347211192STobias Waldekranz p->flags &= ~BR_TX_FWD_OFFLOAD; 28447211192STobias Waldekranz static_branch_dec(&br_switchdev_tx_fwd_offload); 28547211192STobias Waldekranz } 28685826610STobias Waldekranz } 2872f5dc00fSVladimir Oltean 288326b212eSVladimir Oltean static int 289326b212eSVladimir Oltean br_switchdev_fdb_replay_one(struct net_bridge *br, struct notifier_block *nb, 2905cda5272SVladimir Oltean const struct net_bridge_fdb_entry *fdb, 2915cda5272SVladimir Oltean unsigned long action, const void *ctx) 2925cda5272SVladimir Oltean { 2935cda5272SVladimir Oltean struct switchdev_notifier_fdb_info item; 2945cda5272SVladimir Oltean int err; 2955cda5272SVladimir Oltean 296fab9eca8SVladimir Oltean br_switchdev_fdb_populate(br, &item, fdb, ctx); 2975cda5272SVladimir Oltean 2985cda5272SVladimir Oltean err = nb->notifier_call(nb, action, &item); 2995cda5272SVladimir Oltean return notifier_to_errno(err); 3005cda5272SVladimir Oltean } 3015cda5272SVladimir Oltean 302326b212eSVladimir Oltean static int 303326b212eSVladimir Oltean br_switchdev_fdb_replay(const struct net_device *br_dev, const void *ctx, 3045cda5272SVladimir Oltean bool adding, struct notifier_block *nb) 3055cda5272SVladimir Oltean { 3065cda5272SVladimir Oltean struct net_bridge_fdb_entry *fdb; 3075cda5272SVladimir Oltean struct net_bridge *br; 3085cda5272SVladimir Oltean unsigned long action; 3095cda5272SVladimir Oltean int err = 0; 3105cda5272SVladimir Oltean 3115cda5272SVladimir Oltean if (!nb) 3125cda5272SVladimir Oltean return 0; 3135cda5272SVladimir Oltean 3145cda5272SVladimir Oltean if (!netif_is_bridge_master(br_dev)) 3155cda5272SVladimir Oltean return -EINVAL; 3165cda5272SVladimir Oltean 3175cda5272SVladimir Oltean br = netdev_priv(br_dev); 3185cda5272SVladimir Oltean 3195cda5272SVladimir Oltean if (adding) 3205cda5272SVladimir Oltean action = SWITCHDEV_FDB_ADD_TO_DEVICE; 3215cda5272SVladimir Oltean else 3225cda5272SVladimir Oltean action = SWITCHDEV_FDB_DEL_TO_DEVICE; 3235cda5272SVladimir Oltean 3245cda5272SVladimir Oltean rcu_read_lock(); 3255cda5272SVladimir Oltean 3265cda5272SVladimir Oltean hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { 327326b212eSVladimir Oltean err = br_switchdev_fdb_replay_one(br, nb, fdb, action, ctx); 3285cda5272SVladimir Oltean if (err) 3295cda5272SVladimir Oltean break; 3305cda5272SVladimir Oltean } 3315cda5272SVladimir Oltean 3325cda5272SVladimir Oltean rcu_read_unlock(); 3335cda5272SVladimir Oltean 3345cda5272SVladimir Oltean return err; 3355cda5272SVladimir Oltean } 3365cda5272SVladimir Oltean 3376284c723STobias Waldekranz static int br_switchdev_vlan_attr_replay(struct net_device *br_dev, 3386284c723STobias Waldekranz const void *ctx, 3396284c723STobias Waldekranz struct notifier_block *nb, 3406284c723STobias Waldekranz struct netlink_ext_ack *extack) 3416284c723STobias Waldekranz { 3426284c723STobias Waldekranz struct switchdev_notifier_port_attr_info attr_info = { 3436284c723STobias Waldekranz .info = { 3446284c723STobias Waldekranz .dev = br_dev, 3456284c723STobias Waldekranz .extack = extack, 3466284c723STobias Waldekranz .ctx = ctx, 3476284c723STobias Waldekranz }, 3486284c723STobias Waldekranz }; 3496284c723STobias Waldekranz struct net_bridge *br = netdev_priv(br_dev); 3506284c723STobias Waldekranz struct net_bridge_vlan_group *vg; 3516284c723STobias Waldekranz struct switchdev_attr attr; 3526284c723STobias Waldekranz struct net_bridge_vlan *v; 3536284c723STobias Waldekranz int err; 3546284c723STobias Waldekranz 3556284c723STobias Waldekranz attr_info.attr = &attr; 3566284c723STobias Waldekranz attr.orig_dev = br_dev; 3576284c723STobias Waldekranz 3586284c723STobias Waldekranz vg = br_vlan_group(br); 3597f40ea21SClément Léger if (!vg) 3607f40ea21SClément Léger return 0; 3616284c723STobias Waldekranz 3626284c723STobias Waldekranz list_for_each_entry(v, &vg->vlan_list, vlist) { 3636284c723STobias Waldekranz if (v->msti) { 3646284c723STobias Waldekranz attr.id = SWITCHDEV_ATTR_ID_VLAN_MSTI; 3656284c723STobias Waldekranz attr.u.vlan_msti.vid = v->vid; 3666284c723STobias Waldekranz attr.u.vlan_msti.msti = v->msti; 3676284c723STobias Waldekranz 3686284c723STobias Waldekranz err = nb->notifier_call(nb, SWITCHDEV_PORT_ATTR_SET, 3696284c723STobias Waldekranz &attr_info); 3706284c723STobias Waldekranz err = notifier_to_errno(err); 3716284c723STobias Waldekranz if (err) 3726284c723STobias Waldekranz return err; 3736284c723STobias Waldekranz } 3746284c723STobias Waldekranz } 3756284c723STobias Waldekranz 3766284c723STobias Waldekranz return 0; 3776284c723STobias Waldekranz } 3786284c723STobias Waldekranz 379326b212eSVladimir Oltean static int 380326b212eSVladimir Oltean br_switchdev_vlan_replay_one(struct notifier_block *nb, 3814a6849e4SVladimir Oltean struct net_device *dev, 3824a6849e4SVladimir Oltean struct switchdev_obj_port_vlan *vlan, 3834a6849e4SVladimir Oltean const void *ctx, unsigned long action, 3844a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 3854a6849e4SVladimir Oltean { 3864a6849e4SVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 3874a6849e4SVladimir Oltean .info = { 3884a6849e4SVladimir Oltean .dev = dev, 3894a6849e4SVladimir Oltean .extack = extack, 3904a6849e4SVladimir Oltean .ctx = ctx, 3914a6849e4SVladimir Oltean }, 3924a6849e4SVladimir Oltean .obj = &vlan->obj, 3934a6849e4SVladimir Oltean }; 3944a6849e4SVladimir Oltean int err; 3954a6849e4SVladimir Oltean 3964a6849e4SVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 3974a6849e4SVladimir Oltean return notifier_to_errno(err); 3984a6849e4SVladimir Oltean } 3994a6849e4SVladimir Oltean 400b28d580eSVladimir Oltean static int br_switchdev_vlan_replay_group(struct notifier_block *nb, 401326b212eSVladimir Oltean struct net_device *dev, 402b28d580eSVladimir Oltean struct net_bridge_vlan_group *vg, 403b28d580eSVladimir Oltean const void *ctx, unsigned long action, 4044a6849e4SVladimir Oltean struct netlink_ext_ack *extack) 4054a6849e4SVladimir Oltean { 4064a6849e4SVladimir Oltean struct net_bridge_vlan *v; 4074a6849e4SVladimir Oltean int err = 0; 4084a6849e4SVladimir Oltean u16 pvid; 4094a6849e4SVladimir Oltean 4104a6849e4SVladimir Oltean if (!vg) 4114a6849e4SVladimir Oltean return 0; 4124a6849e4SVladimir Oltean 4134a6849e4SVladimir Oltean pvid = br_get_pvid(vg); 4144a6849e4SVladimir Oltean 4154a6849e4SVladimir Oltean list_for_each_entry(v, &vg->vlan_list, vlist) { 4164a6849e4SVladimir Oltean struct switchdev_obj_port_vlan vlan = { 4174a6849e4SVladimir Oltean .obj.orig_dev = dev, 4184a6849e4SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 4194a6849e4SVladimir Oltean .flags = br_vlan_flags(v, pvid), 4204a6849e4SVladimir Oltean .vid = v->vid, 4214a6849e4SVladimir Oltean }; 4224a6849e4SVladimir Oltean 4234a6849e4SVladimir Oltean if (!br_vlan_should_use(v)) 4244a6849e4SVladimir Oltean continue; 4254a6849e4SVladimir Oltean 426326b212eSVladimir Oltean err = br_switchdev_vlan_replay_one(nb, dev, &vlan, ctx, 427326b212eSVladimir Oltean action, extack); 4284a6849e4SVladimir Oltean if (err) 4294a6849e4SVladimir Oltean return err; 4304a6849e4SVladimir Oltean } 4314a6849e4SVladimir Oltean 432b28d580eSVladimir Oltean return 0; 433b28d580eSVladimir Oltean } 434b28d580eSVladimir Oltean 435b28d580eSVladimir Oltean static int br_switchdev_vlan_replay(struct net_device *br_dev, 436b28d580eSVladimir Oltean const void *ctx, bool adding, 437b28d580eSVladimir Oltean struct notifier_block *nb, 438b28d580eSVladimir Oltean struct netlink_ext_ack *extack) 439b28d580eSVladimir Oltean { 440b28d580eSVladimir Oltean struct net_bridge *br = netdev_priv(br_dev); 441b28d580eSVladimir Oltean struct net_bridge_port *p; 442b28d580eSVladimir Oltean unsigned long action; 443b28d580eSVladimir Oltean int err; 444b28d580eSVladimir Oltean 445b28d580eSVladimir Oltean ASSERT_RTNL(); 446b28d580eSVladimir Oltean 447b28d580eSVladimir Oltean if (!nb) 448b28d580eSVladimir Oltean return 0; 449b28d580eSVladimir Oltean 450b28d580eSVladimir Oltean if (!netif_is_bridge_master(br_dev)) 451b28d580eSVladimir Oltean return -EINVAL; 452b28d580eSVladimir Oltean 453b28d580eSVladimir Oltean if (adding) 454b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 455b28d580eSVladimir Oltean else 456b28d580eSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 457b28d580eSVladimir Oltean 458b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, br_dev, br_vlan_group(br), 459b28d580eSVladimir Oltean ctx, action, extack); 460b28d580eSVladimir Oltean if (err) 4614a6849e4SVladimir Oltean return err; 462b28d580eSVladimir Oltean 463b28d580eSVladimir Oltean list_for_each_entry(p, &br->port_list, list) { 464b28d580eSVladimir Oltean struct net_device *dev = p->dev; 465b28d580eSVladimir Oltean 466b28d580eSVladimir Oltean err = br_switchdev_vlan_replay_group(nb, dev, 467b28d580eSVladimir Oltean nbp_vlan_group(p), 468b28d580eSVladimir Oltean ctx, action, extack); 469b28d580eSVladimir Oltean if (err) 470b28d580eSVladimir Oltean return err; 471b28d580eSVladimir Oltean } 472b28d580eSVladimir Oltean 4736284c723STobias Waldekranz if (adding) { 4746284c723STobias Waldekranz err = br_switchdev_vlan_attr_replay(br_dev, ctx, nb, extack); 4756284c723STobias Waldekranz if (err) 4766284c723STobias Waldekranz return err; 4776284c723STobias Waldekranz } 4786284c723STobias Waldekranz 479b28d580eSVladimir Oltean return 0; 4804a6849e4SVladimir Oltean } 4814a6849e4SVladimir Oltean 4829776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 483326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info { 4849776457cSVladimir Oltean struct net_bridge_port *port; 4859776457cSVladimir Oltean struct br_ip ip; 4869776457cSVladimir Oltean }; 4879776457cSVladimir Oltean 488326b212eSVladimir Oltean static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *priv) 4899776457cSVladimir Oltean { 490326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *data = priv; 4919776457cSVladimir Oltean struct net_bridge_port_group __rcu **pp; 4929776457cSVladimir Oltean struct net_bridge_port_group *p; 4939776457cSVladimir Oltean struct net_bridge_mdb_entry *mp; 4949776457cSVladimir Oltean struct net_bridge_port *port = data->port; 4959776457cSVladimir Oltean struct net_bridge *br = port->br; 4969776457cSVladimir Oltean 4979776457cSVladimir Oltean if (err) 4989776457cSVladimir Oltean goto err; 4999776457cSVladimir Oltean 5009776457cSVladimir Oltean spin_lock_bh(&br->multicast_lock); 5019776457cSVladimir Oltean mp = br_mdb_ip_get(br, &data->ip); 5029776457cSVladimir Oltean if (!mp) 5039776457cSVladimir Oltean goto out; 5049776457cSVladimir Oltean for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; 5059776457cSVladimir Oltean pp = &p->next) { 5069776457cSVladimir Oltean if (p->key.port != port) 5079776457cSVladimir Oltean continue; 5089776457cSVladimir Oltean p->flags |= MDB_PG_FLAGS_OFFLOAD; 5099776457cSVladimir Oltean } 5109776457cSVladimir Oltean out: 5119776457cSVladimir Oltean spin_unlock_bh(&br->multicast_lock); 5129776457cSVladimir Oltean err: 5139776457cSVladimir Oltean kfree(priv); 5149776457cSVladimir Oltean } 5159776457cSVladimir Oltean 5169776457cSVladimir Oltean static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb, 5179776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp) 5189776457cSVladimir Oltean { 5199776457cSVladimir Oltean if (mp->addr.proto == htons(ETH_P_IP)) 5209776457cSVladimir Oltean ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr); 5219776457cSVladimir Oltean #if IS_ENABLED(CONFIG_IPV6) 5229776457cSVladimir Oltean else if (mp->addr.proto == htons(ETH_P_IPV6)) 5239776457cSVladimir Oltean ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb->addr); 5249776457cSVladimir Oltean #endif 5259776457cSVladimir Oltean else 5269776457cSVladimir Oltean ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr); 5279776457cSVladimir Oltean 5289776457cSVladimir Oltean mdb->vid = mp->addr.vid; 5299776457cSVladimir Oltean } 5309776457cSVladimir Oltean 531326b212eSVladimir Oltean static void br_switchdev_host_mdb_one(struct net_device *dev, 5329776457cSVladimir Oltean struct net_device *lower_dev, 5339776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 5349776457cSVladimir Oltean int type) 5359776457cSVladimir Oltean { 5369776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 5379776457cSVladimir Oltean .obj = { 5389776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_HOST_MDB, 5399776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 5409776457cSVladimir Oltean .orig_dev = dev, 5419776457cSVladimir Oltean }, 5429776457cSVladimir Oltean }; 5439776457cSVladimir Oltean 5449776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 5459776457cSVladimir Oltean 5469776457cSVladimir Oltean switch (type) { 5479776457cSVladimir Oltean case RTM_NEWMDB: 5489776457cSVladimir Oltean switchdev_port_obj_add(lower_dev, &mdb.obj, NULL); 5499776457cSVladimir Oltean break; 5509776457cSVladimir Oltean case RTM_DELMDB: 5519776457cSVladimir Oltean switchdev_port_obj_del(lower_dev, &mdb.obj); 5529776457cSVladimir Oltean break; 5539776457cSVladimir Oltean } 5549776457cSVladimir Oltean } 5559776457cSVladimir Oltean 556326b212eSVladimir Oltean static void br_switchdev_host_mdb(struct net_device *dev, 5579776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, int type) 5589776457cSVladimir Oltean { 5599776457cSVladimir Oltean struct net_device *lower_dev; 5609776457cSVladimir Oltean struct list_head *iter; 5619776457cSVladimir Oltean 5629776457cSVladimir Oltean netdev_for_each_lower_dev(dev, lower_dev, iter) 563326b212eSVladimir Oltean br_switchdev_host_mdb_one(dev, lower_dev, mp, type); 5649776457cSVladimir Oltean } 5659776457cSVladimir Oltean 566326b212eSVladimir Oltean static int 567326b212eSVladimir Oltean br_switchdev_mdb_replay_one(struct notifier_block *nb, struct net_device *dev, 5689776457cSVladimir Oltean const struct switchdev_obj_port_mdb *mdb, 5699776457cSVladimir Oltean unsigned long action, const void *ctx, 5709776457cSVladimir Oltean struct netlink_ext_ack *extack) 5719776457cSVladimir Oltean { 5729776457cSVladimir Oltean struct switchdev_notifier_port_obj_info obj_info = { 5739776457cSVladimir Oltean .info = { 5749776457cSVladimir Oltean .dev = dev, 5759776457cSVladimir Oltean .extack = extack, 5769776457cSVladimir Oltean .ctx = ctx, 5779776457cSVladimir Oltean }, 5789776457cSVladimir Oltean .obj = &mdb->obj, 5799776457cSVladimir Oltean }; 5809776457cSVladimir Oltean int err; 5819776457cSVladimir Oltean 5829776457cSVladimir Oltean err = nb->notifier_call(nb, action, &obj_info); 5839776457cSVladimir Oltean return notifier_to_errno(err); 5849776457cSVladimir Oltean } 5859776457cSVladimir Oltean 586326b212eSVladimir Oltean static int br_switchdev_mdb_queue_one(struct list_head *mdb_list, 5879776457cSVladimir Oltean enum switchdev_obj_id id, 5889776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp, 5899776457cSVladimir Oltean struct net_device *orig_dev) 5909776457cSVladimir Oltean { 5919776457cSVladimir Oltean struct switchdev_obj_port_mdb *mdb; 5929776457cSVladimir Oltean 5939776457cSVladimir Oltean mdb = kzalloc(sizeof(*mdb), GFP_ATOMIC); 5949776457cSVladimir Oltean if (!mdb) 5959776457cSVladimir Oltean return -ENOMEM; 5969776457cSVladimir Oltean 5979776457cSVladimir Oltean mdb->obj.id = id; 5989776457cSVladimir Oltean mdb->obj.orig_dev = orig_dev; 5999776457cSVladimir Oltean br_switchdev_mdb_populate(mdb, mp); 6009776457cSVladimir Oltean list_add_tail(&mdb->obj.list, mdb_list); 6019776457cSVladimir Oltean 6029776457cSVladimir Oltean return 0; 6039776457cSVladimir Oltean } 6049776457cSVladimir Oltean 6059776457cSVladimir Oltean void br_switchdev_mdb_notify(struct net_device *dev, 6069776457cSVladimir Oltean struct net_bridge_mdb_entry *mp, 6079776457cSVladimir Oltean struct net_bridge_port_group *pg, 6089776457cSVladimir Oltean int type) 6099776457cSVladimir Oltean { 610326b212eSVladimir Oltean struct br_switchdev_mdb_complete_info *complete_info; 6119776457cSVladimir Oltean struct switchdev_obj_port_mdb mdb = { 6129776457cSVladimir Oltean .obj = { 6139776457cSVladimir Oltean .id = SWITCHDEV_OBJ_ID_PORT_MDB, 6149776457cSVladimir Oltean .flags = SWITCHDEV_F_DEFER, 6159776457cSVladimir Oltean }, 6169776457cSVladimir Oltean }; 6179776457cSVladimir Oltean 6189776457cSVladimir Oltean if (!pg) 619326b212eSVladimir Oltean return br_switchdev_host_mdb(dev, mp, type); 6209776457cSVladimir Oltean 6219776457cSVladimir Oltean br_switchdev_mdb_populate(&mdb, mp); 6229776457cSVladimir Oltean 6239776457cSVladimir Oltean mdb.obj.orig_dev = pg->key.port->dev; 6249776457cSVladimir Oltean switch (type) { 6259776457cSVladimir Oltean case RTM_NEWMDB: 6269776457cSVladimir Oltean complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC); 6279776457cSVladimir Oltean if (!complete_info) 6289776457cSVladimir Oltean break; 6299776457cSVladimir Oltean complete_info->port = pg->key.port; 6309776457cSVladimir Oltean complete_info->ip = mp->addr; 6319776457cSVladimir Oltean mdb.obj.complete_priv = complete_info; 632326b212eSVladimir Oltean mdb.obj.complete = br_switchdev_mdb_complete; 6339776457cSVladimir Oltean if (switchdev_port_obj_add(pg->key.port->dev, &mdb.obj, NULL)) 6349776457cSVladimir Oltean kfree(complete_info); 6359776457cSVladimir Oltean break; 6369776457cSVladimir Oltean case RTM_DELMDB: 6379776457cSVladimir Oltean switchdev_port_obj_del(pg->key.port->dev, &mdb.obj); 6389776457cSVladimir Oltean break; 6399776457cSVladimir Oltean } 6409776457cSVladimir Oltean } 6419776457cSVladimir Oltean #endif 6429776457cSVladimir Oltean 643326b212eSVladimir Oltean static int 644326b212eSVladimir Oltean br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev, 645326b212eSVladimir Oltean const void *ctx, bool adding, struct notifier_block *nb, 6469776457cSVladimir Oltean struct netlink_ext_ack *extack) 6479776457cSVladimir Oltean { 6489776457cSVladimir Oltean #ifdef CONFIG_BRIDGE_IGMP_SNOOPING 6499776457cSVladimir Oltean const struct net_bridge_mdb_entry *mp; 6509776457cSVladimir Oltean struct switchdev_obj *obj, *tmp; 6519776457cSVladimir Oltean struct net_bridge *br; 6529776457cSVladimir Oltean unsigned long action; 6539776457cSVladimir Oltean LIST_HEAD(mdb_list); 6549776457cSVladimir Oltean int err = 0; 6559776457cSVladimir Oltean 6569776457cSVladimir Oltean ASSERT_RTNL(); 6579776457cSVladimir Oltean 6589776457cSVladimir Oltean if (!nb) 6599776457cSVladimir Oltean return 0; 6609776457cSVladimir Oltean 6619776457cSVladimir Oltean if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev)) 6629776457cSVladimir Oltean return -EINVAL; 6639776457cSVladimir Oltean 6649776457cSVladimir Oltean br = netdev_priv(br_dev); 6659776457cSVladimir Oltean 6669776457cSVladimir Oltean if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 6679776457cSVladimir Oltean return 0; 6689776457cSVladimir Oltean 6699776457cSVladimir Oltean /* We cannot walk over br->mdb_list protected just by the rtnl_mutex, 6709776457cSVladimir Oltean * because the write-side protection is br->multicast_lock. But we 6719776457cSVladimir Oltean * need to emulate the [ blocking ] calling context of a regular 6729776457cSVladimir Oltean * switchdev event, so since both br->multicast_lock and RCU read side 6739776457cSVladimir Oltean * critical sections are atomic, we have no choice but to pick the RCU 6749776457cSVladimir Oltean * read side lock, queue up all our events, leave the critical section 6759776457cSVladimir Oltean * and notify switchdev from blocking context. 6769776457cSVladimir Oltean */ 6779776457cSVladimir Oltean rcu_read_lock(); 6789776457cSVladimir Oltean 6799776457cSVladimir Oltean hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { 6809776457cSVladimir Oltean struct net_bridge_port_group __rcu * const *pp; 6819776457cSVladimir Oltean const struct net_bridge_port_group *p; 6829776457cSVladimir Oltean 6839776457cSVladimir Oltean if (mp->host_joined) { 684326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6859776457cSVladimir Oltean SWITCHDEV_OBJ_ID_HOST_MDB, 6869776457cSVladimir Oltean mp, br_dev); 6879776457cSVladimir Oltean if (err) { 6889776457cSVladimir Oltean rcu_read_unlock(); 6899776457cSVladimir Oltean goto out_free_mdb; 6909776457cSVladimir Oltean } 6919776457cSVladimir Oltean } 6929776457cSVladimir Oltean 6939776457cSVladimir Oltean for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; 6949776457cSVladimir Oltean pp = &p->next) { 6959776457cSVladimir Oltean if (p->key.port->dev != dev) 6969776457cSVladimir Oltean continue; 6979776457cSVladimir Oltean 698326b212eSVladimir Oltean err = br_switchdev_mdb_queue_one(&mdb_list, 6999776457cSVladimir Oltean SWITCHDEV_OBJ_ID_PORT_MDB, 7009776457cSVladimir Oltean mp, dev); 7019776457cSVladimir Oltean if (err) { 7029776457cSVladimir Oltean rcu_read_unlock(); 7039776457cSVladimir Oltean goto out_free_mdb; 7049776457cSVladimir Oltean } 7059776457cSVladimir Oltean } 7069776457cSVladimir Oltean } 7079776457cSVladimir Oltean 7089776457cSVladimir Oltean rcu_read_unlock(); 7099776457cSVladimir Oltean 7109776457cSVladimir Oltean if (adding) 7119776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_ADD; 7129776457cSVladimir Oltean else 7139776457cSVladimir Oltean action = SWITCHDEV_PORT_OBJ_DEL; 7149776457cSVladimir Oltean 7159776457cSVladimir Oltean list_for_each_entry(obj, &mdb_list, list) { 716326b212eSVladimir Oltean err = br_switchdev_mdb_replay_one(nb, dev, 717326b212eSVladimir Oltean SWITCHDEV_OBJ_PORT_MDB(obj), 7189776457cSVladimir Oltean action, ctx, extack); 7199776457cSVladimir Oltean if (err) 7209776457cSVladimir Oltean goto out_free_mdb; 7219776457cSVladimir Oltean } 7229776457cSVladimir Oltean 7239776457cSVladimir Oltean out_free_mdb: 7249776457cSVladimir Oltean list_for_each_entry_safe(obj, tmp, &mdb_list, list) { 7259776457cSVladimir Oltean list_del(&obj->list); 7269776457cSVladimir Oltean kfree(SWITCHDEV_OBJ_PORT_MDB(obj)); 7279776457cSVladimir Oltean } 7289776457cSVladimir Oltean 7299776457cSVladimir Oltean if (err) 7309776457cSVladimir Oltean return err; 7319776457cSVladimir Oltean #endif 7329776457cSVladimir Oltean 7339776457cSVladimir Oltean return 0; 7349776457cSVladimir Oltean } 7359776457cSVladimir Oltean 7364e51bf44SVladimir Oltean static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 7374e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7384e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 7394e51bf44SVladimir Oltean struct netlink_ext_ack *extack) 7404e51bf44SVladimir Oltean { 7414e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 7424e51bf44SVladimir Oltean struct net_device *dev = p->dev; 7434e51bf44SVladimir Oltean int err; 7444e51bf44SVladimir Oltean 745b28d580eSVladimir Oltean err = br_switchdev_vlan_replay(br_dev, ctx, true, blocking_nb, extack); 7464e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7474e51bf44SVladimir Oltean return err; 7484e51bf44SVladimir Oltean 749326b212eSVladimir Oltean err = br_switchdev_mdb_replay(br_dev, dev, ctx, true, blocking_nb, 750326b212eSVladimir Oltean extack); 7514e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7524e51bf44SVladimir Oltean return err; 7534e51bf44SVladimir Oltean 754326b212eSVladimir Oltean err = br_switchdev_fdb_replay(br_dev, ctx, true, atomic_nb); 7554e51bf44SVladimir Oltean if (err && err != -EOPNOTSUPP) 7564e51bf44SVladimir Oltean return err; 7574e51bf44SVladimir Oltean 7584e51bf44SVladimir Oltean return 0; 7594e51bf44SVladimir Oltean } 7604e51bf44SVladimir Oltean 7614e51bf44SVladimir Oltean static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 7624e51bf44SVladimir Oltean const void *ctx, 7634e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7644e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 7654e51bf44SVladimir Oltean { 7664e51bf44SVladimir Oltean struct net_device *br_dev = p->br->dev; 7674e51bf44SVladimir Oltean struct net_device *dev = p->dev; 7684e51bf44SVladimir Oltean 769263029aeSVladimir Oltean br_switchdev_fdb_replay(br_dev, ctx, false, atomic_nb); 7704e51bf44SVladimir Oltean 771326b212eSVladimir Oltean br_switchdev_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 7724e51bf44SVladimir Oltean 773b28d580eSVladimir Oltean br_switchdev_vlan_replay(br_dev, ctx, false, blocking_nb, NULL); 7744e51bf44SVladimir Oltean } 7754e51bf44SVladimir Oltean 7762f5dc00fSVladimir Oltean /* Let the bridge know that this port is offloaded, so that it can assign a 7772f5dc00fSVladimir Oltean * switchdev hardware domain to it. 7782f5dc00fSVladimir Oltean */ 779957e2235SVladimir Oltean int br_switchdev_port_offload(struct net_bridge_port *p, 7804e51bf44SVladimir Oltean struct net_device *dev, const void *ctx, 7814e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 7824e51bf44SVladimir Oltean struct notifier_block *blocking_nb, 78347211192STobias Waldekranz bool tx_fwd_offload, 7842f5dc00fSVladimir Oltean struct netlink_ext_ack *extack) 7852f5dc00fSVladimir Oltean { 7862f5dc00fSVladimir Oltean struct netdev_phys_item_id ppid; 7872f5dc00fSVladimir Oltean int err; 7882f5dc00fSVladimir Oltean 7892f5dc00fSVladimir Oltean err = dev_get_port_parent_id(dev, &ppid, false); 7902f5dc00fSVladimir Oltean if (err) 7912f5dc00fSVladimir Oltean return err; 7922f5dc00fSVladimir Oltean 79347211192STobias Waldekranz err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 7944e51bf44SVladimir Oltean if (err) 7954e51bf44SVladimir Oltean return err; 7964e51bf44SVladimir Oltean 7974e51bf44SVladimir Oltean err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 7984e51bf44SVladimir Oltean if (err) 7994e51bf44SVladimir Oltean goto out_switchdev_del; 8004e51bf44SVladimir Oltean 8014e51bf44SVladimir Oltean return 0; 8024e51bf44SVladimir Oltean 8034e51bf44SVladimir Oltean out_switchdev_del: 8044e51bf44SVladimir Oltean nbp_switchdev_del(p); 8054e51bf44SVladimir Oltean 8064e51bf44SVladimir Oltean return err; 8072f5dc00fSVladimir Oltean } 8082f5dc00fSVladimir Oltean 809957e2235SVladimir Oltean void br_switchdev_port_unoffload(struct net_bridge_port *p, const void *ctx, 8104e51bf44SVladimir Oltean struct notifier_block *atomic_nb, 8114e51bf44SVladimir Oltean struct notifier_block *blocking_nb) 8122f5dc00fSVladimir Oltean { 8134e51bf44SVladimir Oltean nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 8144e51bf44SVladimir Oltean 8152f5dc00fSVladimir Oltean nbp_switchdev_del(p); 8162f5dc00fSVladimir Oltean } 817