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        int port;
 140
 141        /* Build a mask of Multicast group members */
 142        bitmap_zero(ds->bitmap, ds->num_ports);
 143        if (ds->index == info->sw_index)
 144                set_bit(info->port, ds->bitmap);
 145        for (port = 0; port < ds->num_ports; port++)
 146                if (dsa_is_dsa_port(ds, port))
 147                        set_bit(port, ds->bitmap);
 148
 149        if (switchdev_trans_ph_prepare(trans))
 150                return dsa_switch_mdb_prepare_bitmap(ds, mdb, ds->bitmap);
 151
 152        dsa_switch_mdb_add_bitmap(ds, mdb, ds->bitmap);
 153
 154        return 0;
 155}
 156
 157static int dsa_switch_mdb_del(struct dsa_switch *ds,
 158                              struct dsa_notifier_mdb_info *info)
 159{
 160        const struct switchdev_obj_port_mdb *mdb = info->mdb;
 161
 162        if (!ds->ops->port_mdb_del)
 163                return -EOPNOTSUPP;
 164
 165        if (ds->index == info->sw_index)
 166                return ds->ops->port_mdb_del(ds, info->port, mdb);
 167
 168        return 0;
 169}
 170
 171static int
 172dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds,
 173                               const struct switchdev_obj_port_vlan *vlan,
 174                               const unsigned long *bitmap)
 175{
 176        int port, err;
 177
 178        if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
 179                return -EOPNOTSUPP;
 180
 181        for_each_set_bit(port, bitmap, ds->num_ports) {
 182                err = ds->ops->port_vlan_prepare(ds, port, vlan);
 183                if (err)
 184                        return err;
 185        }
 186
 187        return 0;
 188}
 189
 190static void
 191dsa_switch_vlan_add_bitmap(struct dsa_switch *ds,
 192                           const struct switchdev_obj_port_vlan *vlan,
 193                           const unsigned long *bitmap)
 194{
 195        int port;
 196
 197        for_each_set_bit(port, bitmap, ds->num_ports)
 198                ds->ops->port_vlan_add(ds, port, vlan);
 199}
 200
 201static int dsa_switch_vlan_add(struct dsa_switch *ds,
 202                               struct dsa_notifier_vlan_info *info)
 203{
 204        const struct switchdev_obj_port_vlan *vlan = info->vlan;
 205        struct switchdev_trans *trans = info->trans;
 206        int port;
 207
 208        /* Build a mask of VLAN members */
 209        bitmap_zero(ds->bitmap, ds->num_ports);
 210        if (ds->index == info->sw_index)
 211                set_bit(info->port, ds->bitmap);
 212        for (port = 0; port < ds->num_ports; port++)
 213                if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
 214                        set_bit(port, ds->bitmap);
 215
 216        if (switchdev_trans_ph_prepare(trans))
 217                return dsa_switch_vlan_prepare_bitmap(ds, vlan, ds->bitmap);
 218
 219        dsa_switch_vlan_add_bitmap(ds, vlan, ds->bitmap);
 220
 221        return 0;
 222}
 223
 224static int dsa_switch_vlan_del(struct dsa_switch *ds,
 225                               struct dsa_notifier_vlan_info *info)
 226{
 227        const struct switchdev_obj_port_vlan *vlan = info->vlan;
 228
 229        if (!ds->ops->port_vlan_del)
 230                return -EOPNOTSUPP;
 231
 232        if (ds->index == info->sw_index)
 233                return ds->ops->port_vlan_del(ds, info->port, vlan);
 234
 235        return 0;
 236}
 237
 238static int dsa_switch_event(struct notifier_block *nb,
 239                            unsigned long event, void *info)
 240{
 241        struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
 242        int err;
 243
 244        switch (event) {
 245        case DSA_NOTIFIER_AGEING_TIME:
 246                err = dsa_switch_ageing_time(ds, info);
 247                break;
 248        case DSA_NOTIFIER_BRIDGE_JOIN:
 249                err = dsa_switch_bridge_join(ds, info);
 250                break;
 251        case DSA_NOTIFIER_BRIDGE_LEAVE:
 252                err = dsa_switch_bridge_leave(ds, info);
 253                break;
 254        case DSA_NOTIFIER_FDB_ADD:
 255                err = dsa_switch_fdb_add(ds, info);
 256                break;
 257        case DSA_NOTIFIER_FDB_DEL:
 258                err = dsa_switch_fdb_del(ds, info);
 259                break;
 260        case DSA_NOTIFIER_MDB_ADD:
 261                err = dsa_switch_mdb_add(ds, info);
 262                break;
 263        case DSA_NOTIFIER_MDB_DEL:
 264                err = dsa_switch_mdb_del(ds, info);
 265                break;
 266        case DSA_NOTIFIER_VLAN_ADD:
 267                err = dsa_switch_vlan_add(ds, info);
 268                break;
 269        case DSA_NOTIFIER_VLAN_DEL:
 270                err = dsa_switch_vlan_del(ds, info);
 271                break;
 272        default:
 273                err = -EOPNOTSUPP;
 274                break;
 275        }
 276
 277        /* Non-switchdev operations cannot be rolled back. If a DSA driver
 278         * returns an error during the chained call, switch chips may be in an
 279         * inconsistent state.
 280         */
 281        if (err)
 282                dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
 283                        event, err);
 284
 285        return notifier_from_errno(err);
 286}
 287
 288int dsa_switch_register_notifier(struct dsa_switch *ds)
 289{
 290        ds->nb.notifier_call = dsa_switch_event;
 291
 292        return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
 293}
 294
 295void dsa_switch_unregister_notifier(struct dsa_switch *ds)
 296{
 297        int err;
 298
 299        err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
 300        if (err)
 301                dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
 302}
 303