linux/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3#include <net/switchdev.h>
   4
   5#include "lan966x_main.h"
   6
   7struct lan966x_pgid_entry {
   8        struct list_head list;
   9        int index;
  10        refcount_t refcount;
  11        u16 ports;
  12};
  13
  14struct lan966x_mdb_entry {
  15        struct list_head list;
  16        unsigned char mac[ETH_ALEN];
  17        u16 vid;
  18        u16 ports;
  19        struct lan966x_pgid_entry *pgid;
  20        u8 cpu_copy;
  21};
  22
  23void lan966x_mdb_init(struct lan966x *lan966x)
  24{
  25        INIT_LIST_HEAD(&lan966x->mdb_entries);
  26        INIT_LIST_HEAD(&lan966x->pgid_entries);
  27}
  28
  29static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
  30{
  31        struct lan966x_mdb_entry *mdb_entry, *tmp;
  32
  33        list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
  34                list_del(&mdb_entry->list);
  35                kfree(mdb_entry);
  36        }
  37}
  38
  39static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
  40{
  41        struct lan966x_pgid_entry *pgid_entry, *tmp;
  42
  43        list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
  44                list_del(&pgid_entry->list);
  45                kfree(pgid_entry);
  46        }
  47}
  48
  49void lan966x_mdb_deinit(struct lan966x *lan966x)
  50{
  51        lan966x_mdb_purge_mdb_entries(lan966x);
  52        lan966x_mdb_purge_pgid_entries(lan966x);
  53}
  54
  55static struct lan966x_mdb_entry *
  56lan966x_mdb_entry_get(struct lan966x *lan966x,
  57                      const unsigned char *mac,
  58                      u16 vid)
  59{
  60        struct lan966x_mdb_entry *mdb_entry;
  61
  62        list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
  63                if (ether_addr_equal(mdb_entry->mac, mac) &&
  64                    mdb_entry->vid == vid)
  65                        return mdb_entry;
  66        }
  67
  68        return NULL;
  69}
  70
  71static struct lan966x_mdb_entry *
  72lan966x_mdb_entry_add(struct lan966x *lan966x,
  73                      const struct switchdev_obj_port_mdb *mdb)
  74{
  75        struct lan966x_mdb_entry *mdb_entry;
  76
  77        mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
  78        if (!mdb_entry)
  79                return ERR_PTR(-ENOMEM);
  80
  81        ether_addr_copy(mdb_entry->mac, mdb->addr);
  82        mdb_entry->vid = mdb->vid;
  83
  84        list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
  85
  86        return mdb_entry;
  87}
  88
  89static void lan966x_mdb_encode_mac(unsigned char *mac,
  90                                   struct lan966x_mdb_entry *mdb_entry,
  91                                   enum macaccess_entry_type type)
  92{
  93        ether_addr_copy(mac, mdb_entry->mac);
  94
  95        if (type == ENTRYTYPE_MACV4) {
  96                mac[0] = 0;
  97                mac[1] = mdb_entry->ports >> 8;
  98                mac[2] = mdb_entry->ports & 0xff;
  99        } else if (type == ENTRYTYPE_MACV6) {
 100                mac[0] = mdb_entry->ports >> 8;
 101                mac[1] = mdb_entry->ports & 0xff;
 102        }
 103}
 104
 105static int lan966x_mdb_ip_add(struct lan966x_port *port,
 106                              const struct switchdev_obj_port_mdb *mdb,
 107                              enum macaccess_entry_type type)
 108{
 109        bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
 110        struct lan966x *lan966x = port->lan966x;
 111        struct lan966x_mdb_entry *mdb_entry;
 112        unsigned char mac[ETH_ALEN];
 113        bool cpu_copy = false;
 114
 115        mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
 116        if (!mdb_entry) {
 117                mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
 118                if (IS_ERR(mdb_entry))
 119                        return PTR_ERR(mdb_entry);
 120        } else {
 121                lan966x_mdb_encode_mac(mac, mdb_entry, type);
 122                lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 123        }
 124
 125        if (cpu_port)
 126                mdb_entry->cpu_copy++;
 127        else
 128                mdb_entry->ports |= BIT(port->chip_port);
 129
 130        /* Copy the frame to CPU only if the CPU is in the VLAN */
 131        if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
 132            mdb_entry->cpu_copy)
 133                cpu_copy = true;
 134
 135        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 136        return lan966x_mac_ip_learn(lan966x, cpu_copy,
 137                                    mac, mdb_entry->vid, type);
 138}
 139
 140static int lan966x_mdb_ip_del(struct lan966x_port *port,
 141                              const struct switchdev_obj_port_mdb *mdb,
 142                              enum macaccess_entry_type type)
 143{
 144        bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
 145        struct lan966x *lan966x = port->lan966x;
 146        struct lan966x_mdb_entry *mdb_entry;
 147        unsigned char mac[ETH_ALEN];
 148        u16 ports;
 149
 150        mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
 151        if (!mdb_entry)
 152                return -ENOENT;
 153
 154        ports = mdb_entry->ports;
 155        if (cpu_port) {
 156                /* If there are still other references to the CPU port then
 157                 * there is no point to delete and add again the same entry
 158                 */
 159                mdb_entry->cpu_copy--;
 160                if (mdb_entry->cpu_copy)
 161                        return 0;
 162        } else {
 163                ports &= ~BIT(port->chip_port);
 164        }
 165
 166        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 167        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 168
 169        mdb_entry->ports = ports;
 170
 171        if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
 172                list_del(&mdb_entry->list);
 173                kfree(mdb_entry);
 174                return 0;
 175        }
 176
 177        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 178        return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
 179                                    mac, mdb_entry->vid, type);
 180}
 181
 182static struct lan966x_pgid_entry *
 183lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
 184{
 185        struct lan966x_pgid_entry *pgid_entry;
 186
 187        pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
 188        if (!pgid_entry)
 189                return ERR_PTR(-ENOMEM);
 190
 191        pgid_entry->ports = ports;
 192        pgid_entry->index = index;
 193        refcount_set(&pgid_entry->refcount, 1);
 194
 195        list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
 196
 197        return pgid_entry;
 198}
 199
 200static struct lan966x_pgid_entry *
 201lan966x_pgid_entry_get(struct lan966x *lan966x,
 202                       struct lan966x_mdb_entry *mdb_entry)
 203{
 204        struct lan966x_pgid_entry *pgid_entry;
 205        int index;
 206
 207        /* Try to find an existing pgid that uses the same ports as the
 208         * mdb_entry
 209         */
 210        list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
 211                if (pgid_entry->ports == mdb_entry->ports) {
 212                        refcount_inc(&pgid_entry->refcount);
 213                        return pgid_entry;
 214                }
 215        }
 216
 217        /* Try to find an empty pgid entry and allocate one in case it finds it,
 218         * otherwise it means that there are no more resources
 219         */
 220        for (index = PGID_GP_START; index < PGID_GP_END; index++) {
 221                bool used = false;
 222
 223                list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
 224                        if (pgid_entry->index == index) {
 225                                used = true;
 226                                break;
 227                        }
 228                }
 229
 230                if (!used)
 231                        return lan966x_pgid_entry_add(lan966x, index,
 232                                                      mdb_entry->ports);
 233        }
 234
 235        return ERR_PTR(-ENOSPC);
 236}
 237
 238static void lan966x_pgid_entry_del(struct lan966x *lan966x,
 239                                   struct lan966x_pgid_entry *pgid_entry)
 240{
 241        if (!refcount_dec_and_test(&pgid_entry->refcount))
 242                return;
 243
 244        list_del(&pgid_entry->list);
 245        kfree(pgid_entry);
 246}
 247
 248static int lan966x_mdb_l2_add(struct lan966x_port *port,
 249                              const struct switchdev_obj_port_mdb *mdb,
 250                              enum macaccess_entry_type type)
 251{
 252        bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
 253        struct lan966x *lan966x = port->lan966x;
 254        struct lan966x_pgid_entry *pgid_entry;
 255        struct lan966x_mdb_entry *mdb_entry;
 256        unsigned char mac[ETH_ALEN];
 257
 258        mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
 259        if (!mdb_entry) {
 260                mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
 261                if (IS_ERR(mdb_entry))
 262                        return PTR_ERR(mdb_entry);
 263        } else {
 264                lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
 265                lan966x_mdb_encode_mac(mac, mdb_entry, type);
 266                lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 267        }
 268
 269        if (cpu_port) {
 270                mdb_entry->ports |= BIT(CPU_PORT);
 271                mdb_entry->cpu_copy++;
 272        } else {
 273                mdb_entry->ports |= BIT(port->chip_port);
 274        }
 275
 276        pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
 277        if (IS_ERR(pgid_entry)) {
 278                list_del(&mdb_entry->list);
 279                kfree(mdb_entry);
 280                return PTR_ERR(pgid_entry);
 281        }
 282        mdb_entry->pgid = pgid_entry;
 283
 284        /* Copy the frame to CPU only if the CPU is in the VLAN */
 285        if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
 286            mdb_entry->cpu_copy)
 287                mdb_entry->ports &= BIT(CPU_PORT);
 288
 289        lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
 290                ANA_PGID_PGID,
 291                lan966x, ANA_PGID(pgid_entry->index));
 292
 293        return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
 294                                 mdb_entry->vid, type);
 295}
 296
 297static int lan966x_mdb_l2_del(struct lan966x_port *port,
 298                              const struct switchdev_obj_port_mdb *mdb,
 299                              enum macaccess_entry_type type)
 300{
 301        bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
 302        struct lan966x *lan966x = port->lan966x;
 303        struct lan966x_pgid_entry *pgid_entry;
 304        struct lan966x_mdb_entry *mdb_entry;
 305        unsigned char mac[ETH_ALEN];
 306        u16 ports;
 307
 308        mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
 309        if (!mdb_entry)
 310                return -ENOENT;
 311
 312        ports = mdb_entry->ports;
 313        if (cpu_port) {
 314                /* If there are still other references to the CPU port then
 315                 * there is no point to delete and add again the same entry
 316                 */
 317                mdb_entry->cpu_copy--;
 318                if (mdb_entry->cpu_copy)
 319                        return 0;
 320
 321                ports &= ~BIT(CPU_PORT);
 322        } else {
 323                ports &= ~BIT(port->chip_port);
 324        }
 325
 326        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 327        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 328        lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
 329
 330        mdb_entry->ports = ports;
 331
 332        if (!mdb_entry->ports) {
 333                list_del(&mdb_entry->list);
 334                kfree(mdb_entry);
 335                return 0;
 336        }
 337
 338        pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
 339        if (IS_ERR(pgid_entry)) {
 340                list_del(&mdb_entry->list);
 341                kfree(mdb_entry);
 342                return PTR_ERR(pgid_entry);
 343        }
 344        mdb_entry->pgid = pgid_entry;
 345
 346        lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
 347                ANA_PGID_PGID,
 348                lan966x, ANA_PGID(pgid_entry->index));
 349
 350        return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
 351                                 mdb_entry->vid, type);
 352}
 353
 354static enum macaccess_entry_type
 355lan966x_mdb_classify(const unsigned char *mac)
 356{
 357        if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
 358                return ENTRYTYPE_MACV4;
 359        if (mac[0] == 0x33 && mac[1] == 0x33)
 360                return ENTRYTYPE_MACV6;
 361        return ENTRYTYPE_LOCKED;
 362}
 363
 364int lan966x_handle_port_mdb_add(struct lan966x_port *port,
 365                                const struct switchdev_obj *obj)
 366{
 367        const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
 368        enum macaccess_entry_type type;
 369
 370        /* Split the way the entries are added for ipv4/ipv6 and for l2. The
 371         * reason is that for ipv4/ipv6 it doesn't require to use any pgid
 372         * entry, while for l2 is required to use pgid entries
 373         */
 374        type = lan966x_mdb_classify(mdb->addr);
 375        if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
 376                return lan966x_mdb_ip_add(port, mdb, type);
 377
 378        return lan966x_mdb_l2_add(port, mdb, type);
 379}
 380
 381int lan966x_handle_port_mdb_del(struct lan966x_port *port,
 382                                const struct switchdev_obj *obj)
 383{
 384        const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
 385        enum macaccess_entry_type type;
 386
 387        /* Split the way the entries are removed for ipv4/ipv6 and for l2. The
 388         * reason is that for ipv4/ipv6 it doesn't require to use any pgid
 389         * entry, while for l2 is required to use pgid entries
 390         */
 391        type = lan966x_mdb_classify(mdb->addr);
 392        if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
 393                return lan966x_mdb_ip_del(port, mdb, type);
 394
 395        return lan966x_mdb_l2_del(port, mdb, type);
 396}
 397
 398static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
 399                                    struct lan966x_mdb_entry *mdb_entry,
 400                                    enum macaccess_entry_type type)
 401{
 402        unsigned char mac[ETH_ALEN];
 403
 404        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 405        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 406        lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
 407}
 408
 409static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
 410                                    struct lan966x_mdb_entry *mdb_entry,
 411                                    enum macaccess_entry_type type)
 412{
 413        struct lan966x_pgid_entry *pgid_entry;
 414        unsigned char mac[ETH_ALEN];
 415
 416        lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
 417        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 418        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 419
 420        mdb_entry->ports |= BIT(CPU_PORT);
 421
 422        pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
 423        if (IS_ERR(pgid_entry))
 424                return;
 425
 426        mdb_entry->pgid = pgid_entry;
 427
 428        lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
 429                ANA_PGID_PGID,
 430                lan966x, ANA_PGID(pgid_entry->index));
 431
 432        lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
 433                          mdb_entry->vid, type);
 434}
 435
 436void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
 437{
 438        struct lan966x_mdb_entry *mdb_entry;
 439        enum macaccess_entry_type type;
 440
 441        list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
 442                if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
 443                        continue;
 444
 445                type = lan966x_mdb_classify(mdb_entry->mac);
 446                if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
 447                        lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
 448                else
 449                        lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
 450        }
 451}
 452
 453static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
 454                                      struct lan966x_mdb_entry *mdb_entry,
 455                                      enum macaccess_entry_type type)
 456{
 457        unsigned char mac[ETH_ALEN];
 458
 459        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 460        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 461        lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
 462}
 463
 464static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
 465                                      struct lan966x_mdb_entry *mdb_entry,
 466                                      enum macaccess_entry_type type)
 467{
 468        struct lan966x_pgid_entry *pgid_entry;
 469        unsigned char mac[ETH_ALEN];
 470
 471        lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
 472        lan966x_mdb_encode_mac(mac, mdb_entry, type);
 473        lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 474
 475        mdb_entry->ports &= ~BIT(CPU_PORT);
 476
 477        pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
 478        if (IS_ERR(pgid_entry))
 479                return;
 480
 481        mdb_entry->pgid = pgid_entry;
 482
 483        lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
 484                ANA_PGID_PGID,
 485                lan966x, ANA_PGID(pgid_entry->index));
 486
 487        lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
 488                          mdb_entry->vid, type);
 489}
 490
 491void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
 492{
 493        struct lan966x_mdb_entry *mdb_entry;
 494        enum macaccess_entry_type type;
 495
 496        list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
 497                if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
 498                        continue;
 499
 500                type = lan966x_mdb_classify(mdb_entry->mac);
 501                if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
 502                        lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
 503                else
 504                        lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
 505        }
 506}
 507
 508void lan966x_mdb_clear_entries(struct lan966x *lan966x)
 509{
 510        struct lan966x_mdb_entry *mdb_entry;
 511        enum macaccess_entry_type type;
 512        unsigned char mac[ETH_ALEN];
 513
 514        list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
 515                type = lan966x_mdb_classify(mdb_entry->mac);
 516
 517                lan966x_mdb_encode_mac(mac, mdb_entry, type);
 518                /* Remove just the MAC entry, still keep the PGID in case of L2
 519                 * entries because this can be restored at later point
 520                 */
 521                lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
 522        }
 523}
 524
 525void lan966x_mdb_restore_entries(struct lan966x *lan966x)
 526{
 527        struct lan966x_mdb_entry *mdb_entry;
 528        enum macaccess_entry_type type;
 529        unsigned char mac[ETH_ALEN];
 530        bool cpu_copy = false;
 531
 532        list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
 533                type = lan966x_mdb_classify(mdb_entry->mac);
 534
 535                lan966x_mdb_encode_mac(mac, mdb_entry, type);
 536                if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
 537                        /* Copy the frame to CPU only if the CPU is in the VLAN */
 538                        if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
 539                                                                  mdb_entry->vid) &&
 540                            mdb_entry->cpu_copy)
 541                                cpu_copy = true;
 542
 543                        lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
 544                                             mdb_entry->vid, type);
 545                } else {
 546                        lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
 547                                          mdb_entry->mac,
 548                                          mdb_entry->vid, type);
 549                }
 550        }
 551}
 552