cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lan966x_fdb.c (6007B)


      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 lan966x *lan966x;
     12	unsigned long event;
     13};
     14
     15struct lan966x_fdb_entry {
     16	struct list_head list;
     17	unsigned char mac[ETH_ALEN] __aligned(2);
     18	u16 vid;
     19	u32 references;
     20};
     21
     22static struct lan966x_fdb_entry *
     23lan966x_fdb_find_entry(struct lan966x *lan966x,
     24		       struct switchdev_notifier_fdb_info *fdb_info)
     25{
     26	struct lan966x_fdb_entry *fdb_entry;
     27
     28	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
     29		if (fdb_entry->vid == fdb_info->vid &&
     30		    ether_addr_equal(fdb_entry->mac, fdb_info->addr))
     31			return fdb_entry;
     32	}
     33
     34	return NULL;
     35}
     36
     37static void lan966x_fdb_add_entry(struct lan966x *lan966x,
     38				  struct switchdev_notifier_fdb_info *fdb_info)
     39{
     40	struct lan966x_fdb_entry *fdb_entry;
     41
     42	fdb_entry = lan966x_fdb_find_entry(lan966x, fdb_info);
     43	if (fdb_entry) {
     44		fdb_entry->references++;
     45		return;
     46	}
     47
     48	fdb_entry = kzalloc(sizeof(*fdb_entry), GFP_KERNEL);
     49	if (!fdb_entry)
     50		return;
     51
     52	ether_addr_copy(fdb_entry->mac, fdb_info->addr);
     53	fdb_entry->vid = fdb_info->vid;
     54	fdb_entry->references = 1;
     55	list_add_tail(&fdb_entry->list, &lan966x->fdb_entries);
     56}
     57
     58static bool lan966x_fdb_del_entry(struct lan966x *lan966x,
     59				  struct switchdev_notifier_fdb_info *fdb_info)
     60{
     61	struct lan966x_fdb_entry *fdb_entry, *tmp;
     62
     63	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries,
     64				 list) {
     65		if (fdb_entry->vid == fdb_info->vid &&
     66		    ether_addr_equal(fdb_entry->mac, fdb_info->addr)) {
     67			fdb_entry->references--;
     68			if (!fdb_entry->references) {
     69				list_del(&fdb_entry->list);
     70				kfree(fdb_entry);
     71				return true;
     72			}
     73			break;
     74		}
     75	}
     76
     77	return false;
     78}
     79
     80void lan966x_fdb_write_entries(struct lan966x *lan966x, u16 vid)
     81{
     82	struct lan966x_fdb_entry *fdb_entry;
     83
     84	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
     85		if (fdb_entry->vid != vid)
     86			continue;
     87
     88		lan966x_mac_cpu_learn(lan966x, fdb_entry->mac, fdb_entry->vid);
     89	}
     90}
     91
     92void lan966x_fdb_erase_entries(struct lan966x *lan966x, u16 vid)
     93{
     94	struct lan966x_fdb_entry *fdb_entry;
     95
     96	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
     97		if (fdb_entry->vid != vid)
     98			continue;
     99
    100		lan966x_mac_cpu_forget(lan966x, fdb_entry->mac, fdb_entry->vid);
    101	}
    102}
    103
    104static void lan966x_fdb_purge_entries(struct lan966x *lan966x)
    105{
    106	struct lan966x_fdb_entry *fdb_entry, *tmp;
    107
    108	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries, list) {
    109		list_del(&fdb_entry->list);
    110		kfree(fdb_entry);
    111	}
    112}
    113
    114int lan966x_fdb_init(struct lan966x *lan966x)
    115{
    116	INIT_LIST_HEAD(&lan966x->fdb_entries);
    117	lan966x->fdb_work = alloc_ordered_workqueue("lan966x_order", 0);
    118	if (!lan966x->fdb_work)
    119		return -ENOMEM;
    120
    121	return 0;
    122}
    123
    124void lan966x_fdb_deinit(struct lan966x *lan966x)
    125{
    126	destroy_workqueue(lan966x->fdb_work);
    127	lan966x_fdb_purge_entries(lan966x);
    128}
    129
    130static void lan966x_fdb_event_work(struct work_struct *work)
    131{
    132	struct lan966x_fdb_event_work *fdb_work =
    133		container_of(work, struct lan966x_fdb_event_work, work);
    134	struct switchdev_notifier_fdb_info *fdb_info;
    135	struct net_device *dev = fdb_work->dev;
    136	struct lan966x_port *port;
    137	struct lan966x *lan966x;
    138	int ret;
    139
    140	fdb_info = &fdb_work->fdb_info;
    141	lan966x = fdb_work->lan966x;
    142
    143	if (lan966x_netdevice_check(dev)) {
    144		port = netdev_priv(dev);
    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	} else {
    161		if (!netif_is_bridge_master(dev))
    162			goto out;
    163
    164		/* In case the bridge is called */
    165		switch (fdb_work->event) {
    166		case SWITCHDEV_FDB_ADD_TO_DEVICE:
    167			/* If there is no front port in this vlan, there is no
    168			 * point to copy the frame to CPU because it would be
    169			 * just dropped at later point. So add it only if
    170			 * there is a port but it is required to store the fdb
    171			 * entry for later point when a port actually gets in
    172			 * the vlan.
    173			 */
    174			lan966x_fdb_add_entry(lan966x, fdb_info);
    175			if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
    176								   fdb_info->vid))
    177				break;
    178
    179			lan966x_mac_cpu_learn(lan966x, fdb_info->addr,
    180					      fdb_info->vid);
    181			break;
    182		case SWITCHDEV_FDB_DEL_TO_DEVICE:
    183			ret = lan966x_fdb_del_entry(lan966x, fdb_info);
    184			if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
    185								   fdb_info->vid))
    186				break;
    187
    188			if (ret)
    189				lan966x_mac_cpu_forget(lan966x, fdb_info->addr,
    190						       fdb_info->vid);
    191			break;
    192		}
    193	}
    194
    195out:
    196	kfree(fdb_work->fdb_info.addr);
    197	kfree(fdb_work);
    198	dev_put(dev);
    199}
    200
    201int lan966x_handle_fdb(struct net_device *dev,
    202		       struct net_device *orig_dev,
    203		       unsigned long event, const void *ctx,
    204		       const struct switchdev_notifier_fdb_info *fdb_info)
    205{
    206	struct lan966x_port *port = netdev_priv(dev);
    207	struct lan966x *lan966x = port->lan966x;
    208	struct lan966x_fdb_event_work *fdb_work;
    209
    210	if (ctx && ctx != port)
    211		return 0;
    212
    213	switch (event) {
    214	case SWITCHDEV_FDB_ADD_TO_DEVICE:
    215	case SWITCHDEV_FDB_DEL_TO_DEVICE:
    216		if (lan966x_netdevice_check(orig_dev) &&
    217		    !fdb_info->added_by_user)
    218			break;
    219
    220		fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);
    221		if (!fdb_work)
    222			return -ENOMEM;
    223
    224		fdb_work->dev = orig_dev;
    225		fdb_work->lan966x = lan966x;
    226		fdb_work->event = event;
    227		INIT_WORK(&fdb_work->work, lan966x_fdb_event_work);
    228		memcpy(&fdb_work->fdb_info, fdb_info, sizeof(fdb_work->fdb_info));
    229		fdb_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
    230		if (!fdb_work->fdb_info.addr)
    231			goto err_addr_alloc;
    232
    233		ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr);
    234		dev_hold(orig_dev);
    235
    236		queue_work(lan966x->fdb_work, &fdb_work->work);
    237		break;
    238	}
    239
    240	return 0;
    241err_addr_alloc:
    242	kfree(fdb_work);
    243	return -ENOMEM;
    244}