brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 2ea263e Raw
290 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include <net/switchdev.h>4 5#include "lan966x_main.h"6 7struct lan966x_fdb_event_work {8	struct work_struct work;9	struct switchdev_notifier_fdb_info fdb_info;10	struct net_device *dev;11	struct net_device *orig_dev;12	struct lan966x *lan966x;13	unsigned long event;14};15 16struct lan966x_fdb_entry {17	struct list_head list;18	unsigned char mac[ETH_ALEN] __aligned(2);19	u16 vid;20	u32 references;21};22 23static struct lan966x_fdb_entry *24lan966x_fdb_find_entry(struct lan966x *lan966x,25		       struct switchdev_notifier_fdb_info *fdb_info)26{27	struct lan966x_fdb_entry *fdb_entry;28 29	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {30		if (fdb_entry->vid == fdb_info->vid &&31		    ether_addr_equal(fdb_entry->mac, fdb_info->addr))32			return fdb_entry;33	}34 35	return NULL;36}37 38static void lan966x_fdb_add_entry(struct lan966x *lan966x,39				  struct switchdev_notifier_fdb_info *fdb_info)40{41	struct lan966x_fdb_entry *fdb_entry;42 43	fdb_entry = lan966x_fdb_find_entry(lan966x, fdb_info);44	if (fdb_entry) {45		fdb_entry->references++;46		return;47	}48 49	fdb_entry = kzalloc(sizeof(*fdb_entry), GFP_KERNEL);50	if (!fdb_entry)51		return;52 53	ether_addr_copy(fdb_entry->mac, fdb_info->addr);54	fdb_entry->vid = fdb_info->vid;55	fdb_entry->references = 1;56	list_add_tail(&fdb_entry->list, &lan966x->fdb_entries);57}58 59static bool lan966x_fdb_del_entry(struct lan966x *lan966x,60				  struct switchdev_notifier_fdb_info *fdb_info)61{62	struct lan966x_fdb_entry *fdb_entry, *tmp;63 64	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries,65				 list) {66		if (fdb_entry->vid == fdb_info->vid &&67		    ether_addr_equal(fdb_entry->mac, fdb_info->addr)) {68			fdb_entry->references--;69			if (!fdb_entry->references) {70				list_del(&fdb_entry->list);71				kfree(fdb_entry);72				return true;73			}74			break;75		}76	}77 78	return false;79}80 81void lan966x_fdb_write_entries(struct lan966x *lan966x, u16 vid)82{83	struct lan966x_fdb_entry *fdb_entry;84 85	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {86		if (fdb_entry->vid != vid)87			continue;88 89		lan966x_mac_cpu_learn(lan966x, fdb_entry->mac, fdb_entry->vid);90	}91}92 93void lan966x_fdb_erase_entries(struct lan966x *lan966x, u16 vid)94{95	struct lan966x_fdb_entry *fdb_entry;96 97	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {98		if (fdb_entry->vid != vid)99			continue;100 101		lan966x_mac_cpu_forget(lan966x, fdb_entry->mac, fdb_entry->vid);102	}103}104 105static void lan966x_fdb_purge_entries(struct lan966x *lan966x)106{107	struct lan966x_fdb_entry *fdb_entry, *tmp;108 109	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries, list) {110		list_del(&fdb_entry->list);111		kfree(fdb_entry);112	}113}114 115int lan966x_fdb_init(struct lan966x *lan966x)116{117	INIT_LIST_HEAD(&lan966x->fdb_entries);118	lan966x->fdb_work = alloc_ordered_workqueue("lan966x_order", 0);119	if (!lan966x->fdb_work)120		return -ENOMEM;121 122	return 0;123}124 125void lan966x_fdb_deinit(struct lan966x *lan966x)126{127	destroy_workqueue(lan966x->fdb_work);128	lan966x_fdb_purge_entries(lan966x);129}130 131void lan966x_fdb_flush_workqueue(struct lan966x *lan966x)132{133	flush_workqueue(lan966x->fdb_work);134}135 136static void lan966x_fdb_port_event_work(struct lan966x_fdb_event_work *fdb_work)137{138	struct switchdev_notifier_fdb_info *fdb_info;139	struct lan966x_port *port;140	struct lan966x *lan966x;141 142	lan966x = fdb_work->lan966x;143	port = netdev_priv(fdb_work->orig_dev);144	fdb_info = &fdb_work->fdb_info;145 146	switch (fdb_work->event) {147	case SWITCHDEV_FDB_ADD_TO_DEVICE:148		if (!fdb_info->added_by_user)149			break;150		lan966x_mac_add_entry(lan966x, port, fdb_info->addr,151				      fdb_info->vid);152		break;153	case SWITCHDEV_FDB_DEL_TO_DEVICE:154		if (!fdb_info->added_by_user)155			break;156		lan966x_mac_del_entry(lan966x, fdb_info->addr,157				      fdb_info->vid);158		break;159	}160}161 162static void lan966x_fdb_bridge_event_work(struct lan966x_fdb_event_work *fdb_work)163{164	struct switchdev_notifier_fdb_info *fdb_info;165	struct lan966x *lan966x;166	int ret;167 168	lan966x = fdb_work->lan966x;169	fdb_info = &fdb_work->fdb_info;170 171	/* In case the bridge is called */172	switch (fdb_work->event) {173	case SWITCHDEV_FDB_ADD_TO_DEVICE:174		/* If there is no front port in this vlan, there is no175		 * point to copy the frame to CPU because it would be176		 * just dropped at later point. So add it only if177		 * there is a port but it is required to store the fdb178		 * entry for later point when a port actually gets in179		 * the vlan.180		 */181		lan966x_fdb_add_entry(lan966x, fdb_info);182		if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,183							   fdb_info->vid))184			break;185 186		lan966x_mac_cpu_learn(lan966x, fdb_info->addr,187				      fdb_info->vid);188		break;189	case SWITCHDEV_FDB_DEL_TO_DEVICE:190		ret = lan966x_fdb_del_entry(lan966x, fdb_info);191		if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,192							   fdb_info->vid))193			break;194 195		if (ret)196			lan966x_mac_cpu_forget(lan966x, fdb_info->addr,197					       fdb_info->vid);198		break;199	}200}201 202static void lan966x_fdb_lag_event_work(struct lan966x_fdb_event_work *fdb_work)203{204	struct switchdev_notifier_fdb_info *fdb_info;205	struct lan966x_port *port;206	struct lan966x *lan966x;207 208	if (!lan966x_lag_first_port(fdb_work->orig_dev, fdb_work->dev))209		return;210 211	lan966x = fdb_work->lan966x;212	port = netdev_priv(fdb_work->dev);213	fdb_info = &fdb_work->fdb_info;214 215	switch (fdb_work->event) {216	case SWITCHDEV_FDB_ADD_TO_DEVICE:217		if (!fdb_info->added_by_user)218			break;219		lan966x_mac_add_entry(lan966x, port, fdb_info->addr,220				      fdb_info->vid);221		break;222	case SWITCHDEV_FDB_DEL_TO_DEVICE:223		if (!fdb_info->added_by_user)224			break;225		lan966x_mac_del_entry(lan966x, fdb_info->addr, fdb_info->vid);226		break;227	}228}229 230static void lan966x_fdb_event_work(struct work_struct *work)231{232	struct lan966x_fdb_event_work *fdb_work =233		container_of(work, struct lan966x_fdb_event_work, work);234 235	if (lan966x_netdevice_check(fdb_work->orig_dev))236		lan966x_fdb_port_event_work(fdb_work);237	else if (netif_is_bridge_master(fdb_work->orig_dev))238		lan966x_fdb_bridge_event_work(fdb_work);239	else if (netif_is_lag_master(fdb_work->orig_dev))240		lan966x_fdb_lag_event_work(fdb_work);241 242	kfree(fdb_work->fdb_info.addr);243	kfree(fdb_work);244}245 246int lan966x_handle_fdb(struct net_device *dev,247		       struct net_device *orig_dev,248		       unsigned long event, const void *ctx,249		       const struct switchdev_notifier_fdb_info *fdb_info)250{251	struct lan966x_port *port = netdev_priv(dev);252	struct lan966x *lan966x = port->lan966x;253	struct lan966x_fdb_event_work *fdb_work;254 255	if (ctx && ctx != port)256		return 0;257 258	switch (event) {259	case SWITCHDEV_FDB_ADD_TO_DEVICE:260	case SWITCHDEV_FDB_DEL_TO_DEVICE:261		if (lan966x_netdevice_check(orig_dev) &&262		    !fdb_info->added_by_user)263			break;264 265		fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);266		if (!fdb_work)267			return -ENOMEM;268 269		fdb_work->dev = dev;270		fdb_work->orig_dev = orig_dev;271		fdb_work->lan966x = lan966x;272		fdb_work->event = event;273		INIT_WORK(&fdb_work->work, lan966x_fdb_event_work);274		memcpy(&fdb_work->fdb_info, fdb_info, sizeof(fdb_work->fdb_info));275		fdb_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);276		if (!fdb_work->fdb_info.addr)277			goto err_addr_alloc;278 279		ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr);280 281		queue_work(lan966x->fdb_work, &fdb_work->work);282		break;283	}284 285	return 0;286err_addr_alloc:287	kfree(fdb_work);288	return -ENOMEM;289}290