1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <net/switchdev.h> 4 5 #include "lan966x_main.h" 6 7 struct lan966x_pgid_entry { 8 struct list_head list; 9 int index; 10 refcount_t refcount; 11 u16 ports; 12 }; 13 14 struct 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 23 void lan966x_mdb_init(struct lan966x *lan966x) 24 { 25 INIT_LIST_HEAD(&lan966x->mdb_entries); 26 INIT_LIST_HEAD(&lan966x->pgid_entries); 27 } 28 29 static 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 39 static 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 49 void lan966x_mdb_deinit(struct lan966x *lan966x) 50 { 51 lan966x_mdb_purge_mdb_entries(lan966x); 52 lan966x_mdb_purge_pgid_entries(lan966x); 53 } 54 55 static struct lan966x_mdb_entry * 56 lan966x_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 71 static struct lan966x_mdb_entry * 72 lan966x_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 89 static 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 105 static 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 140 static 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 182 static struct lan966x_pgid_entry * 183 lan966x_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 200 static struct lan966x_pgid_entry * 201 lan966x_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 238 static 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 248 static 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 297 static 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 354 static enum macaccess_entry_type 355 lan966x_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 364 int 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 381 int 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 398 static 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 409 static 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 436 void 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 453 static 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 464 static 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 491 void 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