linux/net/bridge/br_switchdev.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/kernel.h>
   3#include <linux/list.h>
   4#include <linux/netdevice.h>
   5#include <linux/rtnetlink.h>
   6#include <linux/skbuff.h>
   7#include <net/switchdev.h>
   8
   9#include "br_private.h"
  10
  11static struct static_key_false br_switchdev_tx_fwd_offload;
  12
  13static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p,
  14                                             const struct sk_buff *skb)
  15{
  16        if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload))
  17                return false;
  18
  19        return (p->flags & BR_TX_FWD_OFFLOAD) &&
  20               (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom);
  21}
  22
  23bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb)
  24{
  25        if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload))
  26                return false;
  27
  28        return BR_INPUT_SKB_CB(skb)->tx_fwd_offload;
  29}
  30
  31void br_switchdev_frame_set_offload_fwd_mark(struct sk_buff *skb)
  32{
  33        skb->offload_fwd_mark = br_switchdev_frame_uses_tx_fwd_offload(skb);
  34}
  35
  36/* Mark the frame for TX forwarding offload if this egress port supports it */
  37void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p,
  38                                             struct sk_buff *skb)
  39{
  40        if (nbp_switchdev_can_offload_tx_fwd(p, skb))
  41                BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true;
  42}
  43
  44/* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms
  45 * that the skb has been already forwarded to, to avoid further cloning to
  46 * other ports in the same hwdom by making nbp_switchdev_allowed_egress()
  47 * return false.
  48 */
  49void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p,
  50                                              struct sk_buff *skb)
  51{
  52        if (nbp_switchdev_can_offload_tx_fwd(p, skb))
  53                set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms);
  54}
  55
  56void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
  57                              struct sk_buff *skb)
  58{
  59        if (p->hwdom)
  60                BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom;
  61}
  62
  63bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
  64                                  const struct sk_buff *skb)
  65{
  66        struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb);
  67
  68        return !test_bit(p->hwdom, &cb->fwd_hwdoms) &&
  69                (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom);
  70}
  71
  72/* Flags that can be offloaded to hardware */
  73#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
  74                                  BR_MCAST_FLOOD | BR_BCAST_FLOOD)
  75
  76int br_switchdev_set_port_flag(struct net_bridge_port *p,
  77                               unsigned long flags,
  78                               unsigned long mask,
  79                               struct netlink_ext_ack *extack)
  80{
  81        struct switchdev_attr attr = {
  82                .orig_dev = p->dev,
  83        };
  84        struct switchdev_notifier_port_attr_info info = {
  85                .attr = &attr,
  86        };
  87        int err;
  88
  89        mask &= BR_PORT_FLAGS_HW_OFFLOAD;
  90        if (!mask)
  91                return 0;
  92
  93        attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS;
  94        attr.u.brport_flags.val = flags;
  95        attr.u.brport_flags.mask = mask;
  96
  97        /* We run from atomic context here */
  98        err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
  99                                       &info.info, extack);
 100        err = notifier_to_errno(err);
 101        if (err == -EOPNOTSUPP)
 102                return 0;
 103
 104        if (err) {
 105                if (extack && !extack->_msg)
 106                        NL_SET_ERR_MSG_MOD(extack,
 107                                           "bridge flag offload is not supported");
 108                return -EOPNOTSUPP;
 109        }
 110
 111        attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
 112        attr.flags = SWITCHDEV_F_DEFER;
 113
 114        err = switchdev_port_attr_set(p->dev, &attr, extack);
 115        if (err) {
 116                if (extack && !extack->_msg)
 117                        NL_SET_ERR_MSG_MOD(extack,
 118                                           "error setting offload flag on port");
 119                return err;
 120        }
 121
 122        return 0;
 123}
 124
 125void
 126br_switchdev_fdb_notify(struct net_bridge *br,
 127                        const struct net_bridge_fdb_entry *fdb, int type)
 128{
 129        const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
 130        struct switchdev_notifier_fdb_info info = {
 131                .addr = fdb->key.addr.addr,
 132                .vid = fdb->key.vlan_id,
 133                .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
 134                .is_local = test_bit(BR_FDB_LOCAL, &fdb->flags),
 135                .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
 136        };
 137        struct net_device *dev = (!dst || info.is_local) ? br->dev : dst->dev;
 138
 139        switch (type) {
 140        case RTM_DELNEIGH:
 141                call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
 142                                         dev, &info.info, NULL);
 143                break;
 144        case RTM_NEWNEIGH:
 145                call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
 146                                         dev, &info.info, NULL);
 147                break;
 148        }
 149}
 150
 151int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
 152                               struct netlink_ext_ack *extack)
 153{
 154        struct switchdev_obj_port_vlan v = {
 155                .obj.orig_dev = dev,
 156                .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
 157                .flags = flags,
 158                .vid = vid,
 159        };
 160
 161        return switchdev_port_obj_add(dev, &v.obj, extack);
 162}
 163
 164int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
 165{
 166        struct switchdev_obj_port_vlan v = {
 167                .obj.orig_dev = dev,
 168                .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
 169                .vid = vid,
 170        };
 171
 172        return switchdev_port_obj_del(dev, &v.obj);
 173}
 174
 175static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining)
 176{
 177        struct net_bridge *br = joining->br;
 178        struct net_bridge_port *p;
 179        int hwdom;
 180
 181        /* joining is yet to be added to the port list. */
 182        list_for_each_entry(p, &br->port_list, list) {
 183                if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) {
 184                        joining->hwdom = p->hwdom;
 185                        return 0;
 186                }
 187        }
 188
 189        hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1);
 190        if (hwdom >= BR_HWDOM_MAX)
 191                return -EBUSY;
 192
 193        set_bit(hwdom, &br->busy_hwdoms);
 194        joining->hwdom = hwdom;
 195        return 0;
 196}
 197
 198static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving)
 199{
 200        struct net_bridge *br = leaving->br;
 201        struct net_bridge_port *p;
 202
 203        /* leaving is no longer in the port list. */
 204        list_for_each_entry(p, &br->port_list, list) {
 205                if (p->hwdom == leaving->hwdom)
 206                        return;
 207        }
 208
 209        clear_bit(leaving->hwdom, &br->busy_hwdoms);
 210}
 211
 212static int nbp_switchdev_add(struct net_bridge_port *p,
 213                             struct netdev_phys_item_id ppid,
 214                             bool tx_fwd_offload,
 215                             struct netlink_ext_ack *extack)
 216{
 217        int err;
 218
 219        if (p->offload_count) {
 220                /* Prevent unsupported configurations such as a bridge port
 221                 * which is a bonding interface, and the member ports are from
 222                 * different hardware switches.
 223                 */
 224                if (!netdev_phys_item_id_same(&p->ppid, &ppid)) {
 225                        NL_SET_ERR_MSG_MOD(extack,
 226                                           "Same bridge port cannot be offloaded by two physical switches");
 227                        return -EBUSY;
 228                }
 229
 230                /* Tolerate drivers that call switchdev_bridge_port_offload()
 231                 * more than once for the same bridge port, such as when the
 232                 * bridge port is an offloaded bonding/team interface.
 233                 */
 234                p->offload_count++;
 235
 236                return 0;
 237        }
 238
 239        p->ppid = ppid;
 240        p->offload_count = 1;
 241
 242        err = nbp_switchdev_hwdom_set(p);
 243        if (err)
 244                return err;
 245
 246        if (tx_fwd_offload) {
 247                p->flags |= BR_TX_FWD_OFFLOAD;
 248                static_branch_inc(&br_switchdev_tx_fwd_offload);
 249        }
 250
 251        return 0;
 252}
 253
 254static void nbp_switchdev_del(struct net_bridge_port *p)
 255{
 256        if (WARN_ON(!p->offload_count))
 257                return;
 258
 259        p->offload_count--;
 260
 261        if (p->offload_count)
 262                return;
 263
 264        if (p->hwdom)
 265                nbp_switchdev_hwdom_put(p);
 266
 267        if (p->flags & BR_TX_FWD_OFFLOAD) {
 268                p->flags &= ~BR_TX_FWD_OFFLOAD;
 269                static_branch_dec(&br_switchdev_tx_fwd_offload);
 270        }
 271}
 272
 273static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx,
 274                                   struct notifier_block *atomic_nb,
 275                                   struct notifier_block *blocking_nb,
 276                                   struct netlink_ext_ack *extack)
 277{
 278        struct net_device *br_dev = p->br->dev;
 279        struct net_device *dev = p->dev;
 280        int err;
 281
 282        err = br_vlan_replay(br_dev, dev, ctx, true, blocking_nb, extack);
 283        if (err && err != -EOPNOTSUPP)
 284                return err;
 285
 286        err = br_mdb_replay(br_dev, dev, ctx, true, blocking_nb, extack);
 287        if (err && err != -EOPNOTSUPP)
 288                return err;
 289
 290        err = br_fdb_replay(br_dev, ctx, true, atomic_nb);
 291        if (err && err != -EOPNOTSUPP)
 292                return err;
 293
 294        return 0;
 295}
 296
 297static void nbp_switchdev_unsync_objs(struct net_bridge_port *p,
 298                                      const void *ctx,
 299                                      struct notifier_block *atomic_nb,
 300                                      struct notifier_block *blocking_nb)
 301{
 302        struct net_device *br_dev = p->br->dev;
 303        struct net_device *dev = p->dev;
 304
 305        br_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL);
 306
 307        br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL);
 308
 309        br_fdb_replay(br_dev, ctx, false, atomic_nb);
 310}
 311
 312/* Let the bridge know that this port is offloaded, so that it can assign a
 313 * switchdev hardware domain to it.
 314 */
 315int br_switchdev_port_offload(struct net_bridge_port *p,
 316                              struct net_device *dev, const void *ctx,
 317                              struct notifier_block *atomic_nb,
 318                              struct notifier_block *blocking_nb,
 319                              bool tx_fwd_offload,
 320                              struct netlink_ext_ack *extack)
 321{
 322        struct netdev_phys_item_id ppid;
 323        int err;
 324
 325        err = dev_get_port_parent_id(dev, &ppid, false);
 326        if (err)
 327                return err;
 328
 329        err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack);
 330        if (err)
 331                return err;
 332
 333        err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack);
 334        if (err)
 335                goto out_switchdev_del;
 336
 337        return 0;
 338
 339out_switchdev_del:
 340        nbp_switchdev_del(p);
 341
 342        return err;
 343}
 344
 345void br_switchdev_port_unoffload(struct net_bridge_port *p, const void *ctx,
 346                                 struct notifier_block *atomic_nb,
 347                                 struct notifier_block *blocking_nb)
 348{
 349        nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb);
 350
 351        nbp_switchdev_del(p);
 352}
 353