linux/net/dsa/switch.c
<<
>>
Prefs
   1/*
   2 * Handling of a single switch chip, part of a switch fabric
   3 *
   4 * Copyright (c) 2017 Savoir-faire Linux Inc.
   5 *      Vivien Didelot <vivien.didelot@savoirfairelinux.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 */
  12
  13#include <linux/netdevice.h>
  14#include <linux/notifier.h>
  15#include <net/switchdev.h>
  16
  17#include "dsa_priv.h"
  18
  19static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
  20                                                   unsigned int ageing_time)
  21{
  22        int i;
  23
  24        for (i = 0; i < ds->num_ports; ++i) {
  25                struct dsa_port *dp = &ds->ports[i];
  26
  27                if (dp->ageing_time && dp->ageing_time < ageing_time)
  28                        ageing_time = dp->ageing_time;
  29        }
  30
  31        return ageing_time;
  32}
  33
  34static int dsa_switch_ageing_time(struct dsa_switch *ds,
  35                                  struct dsa_notifier_ageing_time_info *info)
  36{
  37        unsigned int ageing_time = info->ageing_time;
  38        struct switchdev_trans *trans = info->trans;
  39
  40        if (switchdev_trans_ph_prepare(trans)) {
  41                if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
  42                        return -ERANGE;
  43                if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
  44                        return -ERANGE;
  45                return 0;
  46        }
  47
  48        /* Program the fastest ageing time in case of multiple bridges */
  49        ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
  50
  51        if (ds->ops->set_ageing_time)
  52                return ds->ops->set_ageing_time(ds, ageing_time);
  53
  54        return 0;
  55}
  56
  57static int dsa_switch_bridge_join(struct dsa_switch *ds,
  58                                  struct dsa_notifier_bridge_info *info)
  59{
  60        if (ds->index == info->sw_index && ds->ops->port_bridge_join)
  61                return ds->ops->port_bridge_join(ds, info->port, info->br);
  62
  63        if (ds->index != info->sw_index && ds->ops->crosschip_bridge_join)
  64                return ds->ops->crosschip_bridge_join(ds, info->sw_index,
  65                                                      info->port, info->br);
  66
  67        return 0;
  68}
  69
  70static int dsa_switch_bridge_leave(struct dsa_switch *ds,
  71                                   struct dsa_notifier_bridge_info *info)
  72{
  73        if (ds->index == info->sw_index && ds->ops->port_bridge_leave)
  74                ds->ops->port_bridge_leave(ds, info->port, info->br);
  75
  76        if (ds->index != info->sw_index && ds->ops->crosschip_bridge_leave)
  77                ds->ops->crosschip_bridge_leave(ds, info->sw_index, info->port,
  78                                                info->br);
  79
  80        return 0;
  81}
  82
  83static int dsa_switch_fdb_add(struct dsa_switch *ds,
  84                              struct dsa_notifier_fdb_info *info)
  85{
  86        int port = dsa_towards_port(ds, info->sw_index, info->port);
  87
  88        if (!ds->ops->port_fdb_add)
  89                return -EOPNOTSUPP;
  90
  91        return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
  92}
  93
  94static int dsa_switch_fdb_del(struct dsa_switch *ds,
  95                              struct dsa_notifier_fdb_info *info)
  96{
  97        int port = dsa_towards_port(ds, info->sw_index, info->port);
  98
  99        if (!ds->ops->port_fdb_del)
 100                return -EOPNOTSUPP;
 101
 102        return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
 103}
 104
 105static int
 106dsa_switch_mdb_prepare_bitmap(struct dsa_switch *ds,
 107                              const struct switchdev_obj_port_mdb *mdb,
 108                              const unsigned long *bitmap)
 109{
 110        int port, err;
 111
 112        if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
 113                return -EOPNOTSUPP;
 114
 115        for_each_set_bit(port, bitmap, ds->num_ports) {
 116                err = ds->ops->port_mdb_prepare(ds, port, mdb);
 117                if (err)
 118                        return err;
 119        }
 120
 121        return 0;
 122}
 123
 124static void dsa_switch_mdb_add_bitmap(struct dsa_switch *ds,
 125                                      const struct switchdev_obj_port_mdb *mdb,
 126                                      const unsigned long *bitmap)
 127{
 128        int port;
 129
 130        for_each_set_bit(port, bitmap, ds->num_ports)
 131                ds->ops->port_mdb_add(ds, port, mdb);
 132}
 133
 134static int dsa_switch_mdb_add(struct dsa_switch *ds,
 135                              struct dsa_notifier_mdb_info *info)
 136{
 137        const struct switchdev_obj_port_mdb *mdb = info->mdb;
 138        struct switchdev_trans *trans = info->trans;
 139        DECLARE_BITMAP(group, ds->num_ports);
 140        int port;
 141
 142        /* Build a mask of Multicast group members */
 143        bitmap_zero(group, ds->num_ports);
 144        if (ds->index == info->sw_index)
 145                set_bit(info->port, group);
 146        for (port = 0; port < ds->num_ports; port++)
 147                if (dsa_is_dsa_port(ds, port))
 148                        set_bit(port, group);
 149
 150        if (switchdev_trans_ph_prepare(trans))
 151                return dsa_switch_mdb_prepare_bitmap(ds, mdb, group);
 152
 153        dsa_switch_mdb_add_bitmap(ds, mdb, group);
 154
 155        return 0;
 156}
 157
 158static int dsa_switch_mdb_del(struct dsa_switch *ds,
 159                              struct dsa_notifier_mdb_info *info)
 160{
 161        const struct switchdev_obj_port_mdb *mdb = info->mdb;
 162
 163        if (!ds->ops->port_mdb_del)
 164                return -EOPNOTSUPP;
 165
 166        if (ds->index == info->sw_index)
 167                return ds->ops->port_mdb_del(ds, info->port, mdb);
 168
 169        return 0;
 170}
 171
 172static int
 173dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds,
 174                               const struct switchdev_obj_port_vlan *vlan,
 175                               const unsigned long *bitmap)
 176{
 177        int port, err;
 178
 179        if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
 180                return -EOPNOTSUPP;
 181
 182        for_each_set_bit(port, bitmap, ds->num_ports) {
 183                err = ds->ops->port_vlan_prepare(ds, port, vlan);
 184                if (err)
 185                        return err;
 186        }
 187
 188        return 0;
 189}
 190
 191static void
 192dsa_switch_vlan_add_bitmap(struct dsa_switch *ds,
 193                           const struct switchdev_obj_port_vlan *vlan,
 194                           const unsigned long *bitmap)
 195{
 196        int port;
 197
 198        for_each_set_bit(port, bitmap, ds->num_ports)
 199                ds->ops->port_vlan_add(ds, port, vlan);
 200}
 201
 202static int dsa_switch_vlan_add(struct dsa_switch *ds,
 203                               struct dsa_notifier_vlan_info *info)
 204{
 205        const struct switchdev_obj_port_vlan *vlan = info->vlan;
 206        struct switchdev_trans *trans = info->trans;
 207        DECLARE_BITMAP(members, ds->num_ports);
 208        int port;
 209
 210        /* Build a mask of VLAN members */
 211        bitmap_zero(members, ds->num_ports);
 212        if (ds->index == info->sw_index)
 213                set_bit(info->port, members);
 214        for (port = 0; port < ds->num_ports; port++)
 215                if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
 216                        set_bit(port, members);
 217
 218        if (switchdev_trans_ph_prepare(trans))
 219                return dsa_switch_vlan_prepare_bitmap(ds, vlan, members);
 220
 221        dsa_switch_vlan_add_bitmap(ds, vlan, members);
 222
 223        return 0;
 224}
 225
 226static int dsa_switch_vlan_del(struct dsa_switch *ds,
 227                               struct dsa_notifier_vlan_info *info)
 228{
 229        const struct switchdev_obj_port_vlan *vlan = info->vlan;
 230
 231        if (!ds->ops->port_vlan_del)
 232                return -EOPNOTSUPP;
 233
 234        if (ds->index == info->sw_index)
 235                return ds->ops->port_vlan_del(ds, info->port, vlan);
 236
 237        return 0;
 238}
 239
 240static int dsa_switch_event(struct notifier_block *nb,
 241                            unsigned long event, void *info)
 242{
 243        struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
 244        int err;
 245
 246        switch (event) {
 247        case DSA_NOTIFIER_AGEING_TIME:
 248                err = dsa_switch_ageing_time(ds, info);
 249                break;
 250        case DSA_NOTIFIER_BRIDGE_JOIN:
 251                err = dsa_switch_bridge_join(ds, info);
 252                break;
 253        case DSA_NOTIFIER_BRIDGE_LEAVE:
 254                err = dsa_switch_bridge_leave(ds, info);
 255                break;
 256        case DSA_NOTIFIER_FDB_ADD:
 257                err = dsa_switch_fdb_add(ds, info);
 258                break;
 259        case DSA_NOTIFIER_FDB_DEL:
 260                err = dsa_switch_fdb_del(ds, info);
 261                break;
 262        case DSA_NOTIFIER_MDB_ADD:
 263                err = dsa_switch_mdb_add(ds, info);
 264                break;
 265        case DSA_NOTIFIER_MDB_DEL:
 266                err = dsa_switch_mdb_del(ds, info);
 267                break;
 268        case DSA_NOTIFIER_VLAN_ADD:
 269                err = dsa_switch_vlan_add(ds, info);
 270                break;
 271        case DSA_NOTIFIER_VLAN_DEL:
 272                err = dsa_switch_vlan_del(ds, info);
 273                break;
 274        default:
 275                err = -EOPNOTSUPP;
 276                break;
 277        }
 278
 279        /* Non-switchdev operations cannot be rolled back. If a DSA driver
 280         * returns an error during the chained call, switch chips may be in an
 281         * inconsistent state.
 282         */
 283        if (err)
 284                dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
 285                        event, err);
 286
 287        return notifier_from_errno(err);
 288}
 289
 290int dsa_switch_register_notifier(struct dsa_switch *ds)
 291{
 292        ds->nb.notifier_call = dsa_switch_event;
 293
 294        return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
 295}
 296
 297void dsa_switch_unregister_notifier(struct dsa_switch *ds)
 298{
 299        int err;
 300
 301        err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
 302        if (err)
 303                dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
 304}
 305