0001
0002
0003 #include <net/switchdev.h>
0004
0005 #include "lan966x_main.h"
0006
0007 struct lan966x_pgid_entry {
0008 struct list_head list;
0009 int index;
0010 refcount_t refcount;
0011 u16 ports;
0012 };
0013
0014 struct lan966x_mdb_entry {
0015 struct list_head list;
0016 unsigned char mac[ETH_ALEN];
0017 u16 vid;
0018 u16 ports;
0019 struct lan966x_pgid_entry *pgid;
0020 u8 cpu_copy;
0021 };
0022
0023 void lan966x_mdb_init(struct lan966x *lan966x)
0024 {
0025 INIT_LIST_HEAD(&lan966x->mdb_entries);
0026 INIT_LIST_HEAD(&lan966x->pgid_entries);
0027 }
0028
0029 static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
0030 {
0031 struct lan966x_mdb_entry *mdb_entry, *tmp;
0032
0033 list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
0034 list_del(&mdb_entry->list);
0035 kfree(mdb_entry);
0036 }
0037 }
0038
0039 static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
0040 {
0041 struct lan966x_pgid_entry *pgid_entry, *tmp;
0042
0043 list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
0044 list_del(&pgid_entry->list);
0045 kfree(pgid_entry);
0046 }
0047 }
0048
0049 void lan966x_mdb_deinit(struct lan966x *lan966x)
0050 {
0051 lan966x_mdb_purge_mdb_entries(lan966x);
0052 lan966x_mdb_purge_pgid_entries(lan966x);
0053 }
0054
0055 static struct lan966x_mdb_entry *
0056 lan966x_mdb_entry_get(struct lan966x *lan966x,
0057 const unsigned char *mac,
0058 u16 vid)
0059 {
0060 struct lan966x_mdb_entry *mdb_entry;
0061
0062 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0063 if (ether_addr_equal(mdb_entry->mac, mac) &&
0064 mdb_entry->vid == vid)
0065 return mdb_entry;
0066 }
0067
0068 return NULL;
0069 }
0070
0071 static struct lan966x_mdb_entry *
0072 lan966x_mdb_entry_add(struct lan966x *lan966x,
0073 const struct switchdev_obj_port_mdb *mdb)
0074 {
0075 struct lan966x_mdb_entry *mdb_entry;
0076
0077 mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
0078 if (!mdb_entry)
0079 return ERR_PTR(-ENOMEM);
0080
0081 ether_addr_copy(mdb_entry->mac, mdb->addr);
0082 mdb_entry->vid = mdb->vid;
0083
0084 list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
0085
0086 return mdb_entry;
0087 }
0088
0089 static void lan966x_mdb_encode_mac(unsigned char *mac,
0090 struct lan966x_mdb_entry *mdb_entry,
0091 enum macaccess_entry_type type)
0092 {
0093 ether_addr_copy(mac, mdb_entry->mac);
0094
0095 if (type == ENTRYTYPE_MACV4) {
0096 mac[0] = 0;
0097 mac[1] = mdb_entry->ports >> 8;
0098 mac[2] = mdb_entry->ports & 0xff;
0099 } else if (type == ENTRYTYPE_MACV6) {
0100 mac[0] = mdb_entry->ports >> 8;
0101 mac[1] = mdb_entry->ports & 0xff;
0102 }
0103 }
0104
0105 static int lan966x_mdb_ip_add(struct lan966x_port *port,
0106 const struct switchdev_obj_port_mdb *mdb,
0107 enum macaccess_entry_type type)
0108 {
0109 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0110 struct lan966x *lan966x = port->lan966x;
0111 struct lan966x_mdb_entry *mdb_entry;
0112 unsigned char mac[ETH_ALEN];
0113 bool cpu_copy = false;
0114
0115 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0116 if (!mdb_entry) {
0117 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
0118 if (IS_ERR(mdb_entry))
0119 return PTR_ERR(mdb_entry);
0120 } else {
0121 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0122 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0123 }
0124
0125 if (cpu_port)
0126 mdb_entry->cpu_copy++;
0127 else
0128 mdb_entry->ports |= BIT(port->chip_port);
0129
0130
0131 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
0132 mdb_entry->cpu_copy)
0133 cpu_copy = true;
0134
0135 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0136 return lan966x_mac_ip_learn(lan966x, cpu_copy,
0137 mac, mdb_entry->vid, type);
0138 }
0139
0140 static int lan966x_mdb_ip_del(struct lan966x_port *port,
0141 const struct switchdev_obj_port_mdb *mdb,
0142 enum macaccess_entry_type type)
0143 {
0144 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0145 struct lan966x *lan966x = port->lan966x;
0146 struct lan966x_mdb_entry *mdb_entry;
0147 unsigned char mac[ETH_ALEN];
0148 u16 ports;
0149
0150 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0151 if (!mdb_entry)
0152 return -ENOENT;
0153
0154 ports = mdb_entry->ports;
0155 if (cpu_port) {
0156
0157
0158
0159 mdb_entry->cpu_copy--;
0160 if (mdb_entry->cpu_copy)
0161 return 0;
0162 } else {
0163 ports &= ~BIT(port->chip_port);
0164 }
0165
0166 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0167 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0168
0169 mdb_entry->ports = ports;
0170
0171 if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
0172 list_del(&mdb_entry->list);
0173 kfree(mdb_entry);
0174 return 0;
0175 }
0176
0177 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0178 return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
0179 mac, mdb_entry->vid, type);
0180 }
0181
0182 static struct lan966x_pgid_entry *
0183 lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
0184 {
0185 struct lan966x_pgid_entry *pgid_entry;
0186
0187 pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
0188 if (!pgid_entry)
0189 return ERR_PTR(-ENOMEM);
0190
0191 pgid_entry->ports = ports;
0192 pgid_entry->index = index;
0193 refcount_set(&pgid_entry->refcount, 1);
0194
0195 list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
0196
0197 return pgid_entry;
0198 }
0199
0200 static struct lan966x_pgid_entry *
0201 lan966x_pgid_entry_get(struct lan966x *lan966x,
0202 struct lan966x_mdb_entry *mdb_entry)
0203 {
0204 struct lan966x_pgid_entry *pgid_entry;
0205 int index;
0206
0207
0208
0209
0210 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
0211 if (pgid_entry->ports == mdb_entry->ports) {
0212 refcount_inc(&pgid_entry->refcount);
0213 return pgid_entry;
0214 }
0215 }
0216
0217
0218
0219
0220 for (index = PGID_GP_START; index < PGID_GP_END; index++) {
0221 bool used = false;
0222
0223 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
0224 if (pgid_entry->index == index) {
0225 used = true;
0226 break;
0227 }
0228 }
0229
0230 if (!used)
0231 return lan966x_pgid_entry_add(lan966x, index,
0232 mdb_entry->ports);
0233 }
0234
0235 return ERR_PTR(-ENOSPC);
0236 }
0237
0238 static void lan966x_pgid_entry_del(struct lan966x *lan966x,
0239 struct lan966x_pgid_entry *pgid_entry)
0240 {
0241 if (!refcount_dec_and_test(&pgid_entry->refcount))
0242 return;
0243
0244 list_del(&pgid_entry->list);
0245 kfree(pgid_entry);
0246 }
0247
0248 static int lan966x_mdb_l2_add(struct lan966x_port *port,
0249 const struct switchdev_obj_port_mdb *mdb,
0250 enum macaccess_entry_type type)
0251 {
0252 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0253 struct lan966x *lan966x = port->lan966x;
0254 struct lan966x_pgid_entry *pgid_entry;
0255 struct lan966x_mdb_entry *mdb_entry;
0256 unsigned char mac[ETH_ALEN];
0257
0258 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0259 if (!mdb_entry) {
0260 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
0261 if (IS_ERR(mdb_entry))
0262 return PTR_ERR(mdb_entry);
0263 } else {
0264 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0265 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0266 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0267 }
0268
0269 if (cpu_port) {
0270 mdb_entry->ports |= BIT(CPU_PORT);
0271 mdb_entry->cpu_copy++;
0272 } else {
0273 mdb_entry->ports |= BIT(port->chip_port);
0274 }
0275
0276 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0277 if (IS_ERR(pgid_entry)) {
0278 list_del(&mdb_entry->list);
0279 kfree(mdb_entry);
0280 return PTR_ERR(pgid_entry);
0281 }
0282 mdb_entry->pgid = pgid_entry;
0283
0284
0285 if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
0286 mdb_entry->cpu_copy)
0287 mdb_entry->ports &= BIT(CPU_PORT);
0288
0289 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0290 ANA_PGID_PGID,
0291 lan966x, ANA_PGID(pgid_entry->index));
0292
0293 return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0294 mdb_entry->vid, type);
0295 }
0296
0297 static int lan966x_mdb_l2_del(struct lan966x_port *port,
0298 const struct switchdev_obj_port_mdb *mdb,
0299 enum macaccess_entry_type type)
0300 {
0301 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0302 struct lan966x *lan966x = port->lan966x;
0303 struct lan966x_pgid_entry *pgid_entry;
0304 struct lan966x_mdb_entry *mdb_entry;
0305 unsigned char mac[ETH_ALEN];
0306 u16 ports;
0307
0308 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0309 if (!mdb_entry)
0310 return -ENOENT;
0311
0312 ports = mdb_entry->ports;
0313 if (cpu_port) {
0314
0315
0316
0317 mdb_entry->cpu_copy--;
0318 if (mdb_entry->cpu_copy)
0319 return 0;
0320
0321 ports &= ~BIT(CPU_PORT);
0322 } else {
0323 ports &= ~BIT(port->chip_port);
0324 }
0325
0326 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0327 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0328 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0329
0330 mdb_entry->ports = ports;
0331
0332 if (!mdb_entry->ports) {
0333 list_del(&mdb_entry->list);
0334 kfree(mdb_entry);
0335 return 0;
0336 }
0337
0338 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0339 if (IS_ERR(pgid_entry)) {
0340 list_del(&mdb_entry->list);
0341 kfree(mdb_entry);
0342 return PTR_ERR(pgid_entry);
0343 }
0344 mdb_entry->pgid = pgid_entry;
0345
0346 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0347 ANA_PGID_PGID,
0348 lan966x, ANA_PGID(pgid_entry->index));
0349
0350 return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0351 mdb_entry->vid, type);
0352 }
0353
0354 static enum macaccess_entry_type
0355 lan966x_mdb_classify(const unsigned char *mac)
0356 {
0357 if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
0358 return ENTRYTYPE_MACV4;
0359 if (mac[0] == 0x33 && mac[1] == 0x33)
0360 return ENTRYTYPE_MACV6;
0361 return ENTRYTYPE_LOCKED;
0362 }
0363
0364 int lan966x_handle_port_mdb_add(struct lan966x_port *port,
0365 const struct switchdev_obj *obj)
0366 {
0367 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
0368 enum macaccess_entry_type type;
0369
0370
0371
0372
0373
0374 type = lan966x_mdb_classify(mdb->addr);
0375 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0376 return lan966x_mdb_ip_add(port, mdb, type);
0377
0378 return lan966x_mdb_l2_add(port, mdb, type);
0379 }
0380
0381 int lan966x_handle_port_mdb_del(struct lan966x_port *port,
0382 const struct switchdev_obj *obj)
0383 {
0384 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
0385 enum macaccess_entry_type type;
0386
0387
0388
0389
0390
0391 type = lan966x_mdb_classify(mdb->addr);
0392 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0393 return lan966x_mdb_ip_del(port, mdb, type);
0394
0395 return lan966x_mdb_l2_del(port, mdb, type);
0396 }
0397
0398 static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
0399 struct lan966x_mdb_entry *mdb_entry,
0400 enum macaccess_entry_type type)
0401 {
0402 unsigned char mac[ETH_ALEN];
0403
0404 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0405 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0406 lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
0407 }
0408
0409 static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
0410 struct lan966x_mdb_entry *mdb_entry,
0411 enum macaccess_entry_type type)
0412 {
0413 struct lan966x_pgid_entry *pgid_entry;
0414 unsigned char mac[ETH_ALEN];
0415
0416 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0417 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0418 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0419
0420 mdb_entry->ports |= BIT(CPU_PORT);
0421
0422 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0423 if (IS_ERR(pgid_entry))
0424 return;
0425
0426 mdb_entry->pgid = pgid_entry;
0427
0428 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0429 ANA_PGID_PGID,
0430 lan966x, ANA_PGID(pgid_entry->index));
0431
0432 lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0433 mdb_entry->vid, type);
0434 }
0435
0436 void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
0437 {
0438 struct lan966x_mdb_entry *mdb_entry;
0439 enum macaccess_entry_type type;
0440
0441 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0442 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
0443 continue;
0444
0445 type = lan966x_mdb_classify(mdb_entry->mac);
0446 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0447 lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
0448 else
0449 lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
0450 }
0451 }
0452
0453 static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
0454 struct lan966x_mdb_entry *mdb_entry,
0455 enum macaccess_entry_type type)
0456 {
0457 unsigned char mac[ETH_ALEN];
0458
0459 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0460 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0461 lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
0462 }
0463
0464 static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
0465 struct lan966x_mdb_entry *mdb_entry,
0466 enum macaccess_entry_type type)
0467 {
0468 struct lan966x_pgid_entry *pgid_entry;
0469 unsigned char mac[ETH_ALEN];
0470
0471 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0472 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0473 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0474
0475 mdb_entry->ports &= ~BIT(CPU_PORT);
0476
0477 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0478 if (IS_ERR(pgid_entry))
0479 return;
0480
0481 mdb_entry->pgid = pgid_entry;
0482
0483 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0484 ANA_PGID_PGID,
0485 lan966x, ANA_PGID(pgid_entry->index));
0486
0487 lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0488 mdb_entry->vid, type);
0489 }
0490
0491 void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
0492 {
0493 struct lan966x_mdb_entry *mdb_entry;
0494 enum macaccess_entry_type type;
0495
0496 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0497 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
0498 continue;
0499
0500 type = lan966x_mdb_classify(mdb_entry->mac);
0501 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0502 lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
0503 else
0504 lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
0505 }
0506 }
0507
0508 void lan966x_mdb_clear_entries(struct lan966x *lan966x)
0509 {
0510 struct lan966x_mdb_entry *mdb_entry;
0511 enum macaccess_entry_type type;
0512 unsigned char mac[ETH_ALEN];
0513
0514 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0515 type = lan966x_mdb_classify(mdb_entry->mac);
0516
0517 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0518
0519
0520
0521 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0522 }
0523 }
0524
0525 void lan966x_mdb_restore_entries(struct lan966x *lan966x)
0526 {
0527 struct lan966x_mdb_entry *mdb_entry;
0528 enum macaccess_entry_type type;
0529 unsigned char mac[ETH_ALEN];
0530 bool cpu_copy = false;
0531
0532 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0533 type = lan966x_mdb_classify(mdb_entry->mac);
0534
0535 lan966x_mdb_encode_mac(mac, mdb_entry, type);
0536 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
0537
0538 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
0539 mdb_entry->vid) &&
0540 mdb_entry->cpu_copy)
0541 cpu_copy = true;
0542
0543 lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
0544 mdb_entry->vid, type);
0545 } else {
0546 lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
0547 mdb_entry->mac,
0548 mdb_entry->vid, type);
0549 }
0550 }
0551 }