brintos

brintos / linux-shallow public Read only

0
0
Text · 14.6 KiB · 2af5526 Raw
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