552 lines · c
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 else128 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 then157 * there is no point to delete and add again the same entry158 */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 the208 * mdb_entry209 */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 resources219 */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 then315 * there is no point to delete and add again the same entry316 */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_type355lan966x_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. The371 * reason is that for ipv4/ipv6 it doesn't require to use any pgid372 * entry, while for l2 is required to use pgid entries373 */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. The388 * reason is that for ipv4/ipv6 it doesn't require to use any pgid389 * entry, while for l2 is required to use pgid entries390 */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 else449 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 else504 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 L2519 * entries because this can be restored at later point520 */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