lan966x_mdb.c (14965B)
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}