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

hwstats.c (12577B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3#include <linux/debugfs.h>
      4
      5#include "netdevsim.h"
      6
      7#define NSIM_DEV_HWSTATS_TRAFFIC_MS	100
      8
      9static struct list_head *
     10nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
     11			       enum netdev_offload_xstats_type type)
     12{
     13	switch (type) {
     14	case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
     15		return &hwstats->l3_list;
     16	}
     17
     18	WARN_ON_ONCE(1);
     19	return NULL;
     20}
     21
     22static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
     23					  enum netdev_offload_xstats_type type)
     24{
     25	struct nsim_dev_hwstats_netdev *hwsdev;
     26	struct list_head *hwsdev_list;
     27
     28	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
     29	if (WARN_ON(!hwsdev_list))
     30		return;
     31
     32	list_for_each_entry(hwsdev, hwsdev_list, list) {
     33		if (hwsdev->enabled) {
     34			hwsdev->stats.rx_packets += 1;
     35			hwsdev->stats.tx_packets += 2;
     36			hwsdev->stats.rx_bytes += 100;
     37			hwsdev->stats.tx_bytes += 300;
     38		}
     39	}
     40}
     41
     42static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
     43{
     44	struct nsim_dev_hwstats *hwstats;
     45
     46	hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work);
     47	mutex_lock(&hwstats->hwsdev_list_lock);
     48	nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
     49	mutex_unlock(&hwstats->hwsdev_list_lock);
     50
     51	schedule_delayed_work(&hwstats->traffic_dw,
     52			      msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
     53}
     54
     55static struct nsim_dev_hwstats_netdev *
     56nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
     57			     int ifindex)
     58{
     59	struct nsim_dev_hwstats_netdev *hwsdev;
     60
     61	list_for_each_entry(hwsdev, hwsdev_list, list) {
     62		if (hwsdev->netdev->ifindex == ifindex)
     63			return hwsdev;
     64	}
     65
     66	return NULL;
     67}
     68
     69static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
     70				  struct netlink_ext_ack *extack)
     71{
     72	if (hwsdev->fail_enable) {
     73		hwsdev->fail_enable = false;
     74		NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
     75		return -ECANCELED;
     76	}
     77
     78	hwsdev->enabled = true;
     79	return 0;
     80}
     81
     82static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
     83{
     84	hwsdev->enabled = false;
     85	memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
     86}
     87
     88static int
     89nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
     90			     struct netdev_notifier_offload_xstats_info *info)
     91{
     92	netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
     93	memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
     94	return 0;
     95}
     96
     97static void
     98nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
     99			    struct netdev_notifier_offload_xstats_info *info)
    100{
    101	if (hwsdev->enabled)
    102		netdev_offload_xstats_report_used(info->report_used);
    103}
    104
    105static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
    106					     struct net_device *dev,
    107					     unsigned long event, void *ptr)
    108{
    109	struct netdev_notifier_offload_xstats_info *info;
    110	struct nsim_dev_hwstats_netdev *hwsdev;
    111	struct list_head *hwsdev_list;
    112	int err = 0;
    113
    114	info = ptr;
    115	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
    116	if (!hwsdev_list)
    117		return 0;
    118
    119	mutex_lock(&hwstats->hwsdev_list_lock);
    120
    121	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
    122	if (!hwsdev)
    123		goto out;
    124
    125	switch (event) {
    126	case NETDEV_OFFLOAD_XSTATS_ENABLE:
    127		err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
    128		break;
    129	case NETDEV_OFFLOAD_XSTATS_DISABLE:
    130		nsim_dev_hwsdev_disable(hwsdev);
    131		break;
    132	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
    133		nsim_dev_hwsdev_report_used(hwsdev, info);
    134		break;
    135	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
    136		err = nsim_dev_hwsdev_report_delta(hwsdev, info);
    137		break;
    138	}
    139
    140out:
    141	mutex_unlock(&hwstats->hwsdev_list_lock);
    142	return err;
    143}
    144
    145static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
    146{
    147	dev_put(hwsdev->netdev);
    148	kfree(hwsdev);
    149}
    150
    151static void
    152__nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
    153				    struct net_device *dev,
    154				    enum netdev_offload_xstats_type type)
    155{
    156	struct nsim_dev_hwstats_netdev *hwsdev;
    157	struct list_head *hwsdev_list;
    158
    159	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
    160	if (WARN_ON(!hwsdev_list))
    161		return;
    162
    163	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
    164	if (!hwsdev)
    165		return;
    166
    167	list_del(&hwsdev->list);
    168	nsim_dev_hwsdev_fini(hwsdev);
    169}
    170
    171static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
    172					      struct net_device *dev)
    173{
    174	mutex_lock(&hwstats->hwsdev_list_lock);
    175	__nsim_dev_hwstats_event_unregister(hwstats, dev,
    176					    NETDEV_OFFLOAD_XSTATS_TYPE_L3);
    177	mutex_unlock(&hwstats->hwsdev_list_lock);
    178}
    179
    180static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
    181				  struct net_device *dev,
    182				  unsigned long event, void *ptr)
    183{
    184	switch (event) {
    185	case NETDEV_OFFLOAD_XSTATS_ENABLE:
    186	case NETDEV_OFFLOAD_XSTATS_DISABLE:
    187	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
    188	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
    189		return nsim_dev_hwstats_event_off_xstats(hwstats, dev,
    190							 event, ptr);
    191	case NETDEV_UNREGISTER:
    192		nsim_dev_hwstats_event_unregister(hwstats, dev);
    193		break;
    194	}
    195
    196	return 0;
    197}
    198
    199static int nsim_dev_netdevice_event(struct notifier_block *nb,
    200				    unsigned long event, void *ptr)
    201{
    202	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
    203	struct nsim_dev_hwstats *hwstats;
    204	int err = 0;
    205
    206	hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
    207	err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
    208	if (err)
    209		return notifier_from_errno(err);
    210
    211	return NOTIFY_OK;
    212}
    213
    214static int
    215nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
    216				int ifindex,
    217				enum netdev_offload_xstats_type type,
    218				struct list_head *hwsdev_list)
    219{
    220	struct nsim_dev_hwstats_netdev *hwsdev;
    221	struct nsim_dev *nsim_dev;
    222	struct net_device *netdev;
    223	bool notify = false;
    224	struct net *net;
    225	int err = 0;
    226
    227	nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
    228	net = nsim_dev_net(nsim_dev);
    229
    230	rtnl_lock();
    231	mutex_lock(&hwstats->hwsdev_list_lock);
    232	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
    233	if (hwsdev)
    234		goto out_unlock_list;
    235
    236	netdev = dev_get_by_index(net, ifindex);
    237	if (!netdev) {
    238		err = -ENODEV;
    239		goto out_unlock_list;
    240	}
    241
    242	hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
    243	if (!hwsdev) {
    244		err = -ENOMEM;
    245		goto out_put_netdev;
    246	}
    247
    248	hwsdev->netdev = netdev;
    249	list_add_tail(&hwsdev->list, hwsdev_list);
    250	mutex_unlock(&hwstats->hwsdev_list_lock);
    251
    252	if (netdev_offload_xstats_enabled(netdev, type)) {
    253		nsim_dev_hwsdev_enable(hwsdev, NULL);
    254		notify = true;
    255	}
    256
    257	if (notify)
    258		rtnl_offload_xstats_notify(netdev);
    259	rtnl_unlock();
    260	return err;
    261
    262out_put_netdev:
    263	dev_put(netdev);
    264out_unlock_list:
    265	mutex_unlock(&hwstats->hwsdev_list_lock);
    266	rtnl_unlock();
    267	return err;
    268}
    269
    270static int
    271nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
    272				 int ifindex,
    273				 enum netdev_offload_xstats_type type,
    274				 struct list_head *hwsdev_list)
    275{
    276	struct nsim_dev_hwstats_netdev *hwsdev;
    277	int err = 0;
    278
    279	rtnl_lock();
    280	mutex_lock(&hwstats->hwsdev_list_lock);
    281	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
    282	if (hwsdev)
    283		list_del(&hwsdev->list);
    284	mutex_unlock(&hwstats->hwsdev_list_lock);
    285
    286	if (!hwsdev) {
    287		err = -ENOENT;
    288		goto unlock_out;
    289	}
    290
    291	if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
    292		netdev_offload_xstats_push_delta(hwsdev->netdev, type,
    293						 &hwsdev->stats);
    294		rtnl_offload_xstats_notify(hwsdev->netdev);
    295	}
    296	nsim_dev_hwsdev_fini(hwsdev);
    297
    298unlock_out:
    299	rtnl_unlock();
    300	return err;
    301}
    302
    303static int
    304nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
    305			      int ifindex,
    306			      enum netdev_offload_xstats_type type,
    307			      struct list_head *hwsdev_list)
    308{
    309	struct nsim_dev_hwstats_netdev *hwsdev;
    310	int err = 0;
    311
    312	mutex_lock(&hwstats->hwsdev_list_lock);
    313
    314	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
    315	if (!hwsdev) {
    316		err = -ENOENT;
    317		goto err_hwsdev_list_unlock;
    318	}
    319
    320	hwsdev->fail_enable = true;
    321
    322err_hwsdev_list_unlock:
    323	mutex_unlock(&hwstats->hwsdev_list_lock);
    324	return err;
    325}
    326
    327enum nsim_dev_hwstats_do {
    328	NSIM_DEV_HWSTATS_DO_DISABLE,
    329	NSIM_DEV_HWSTATS_DO_ENABLE,
    330	NSIM_DEV_HWSTATS_DO_FAIL,
    331};
    332
    333struct nsim_dev_hwstats_fops {
    334	const struct file_operations fops;
    335	enum nsim_dev_hwstats_do action;
    336	enum netdev_offload_xstats_type type;
    337};
    338
    339static ssize_t
    340nsim_dev_hwstats_do_write(struct file *file,
    341			  const char __user *data,
    342			  size_t count, loff_t *ppos)
    343{
    344	struct nsim_dev_hwstats *hwstats = file->private_data;
    345	struct nsim_dev_hwstats_fops *hwsfops;
    346	struct list_head *hwsdev_list;
    347	int ifindex;
    348	int err;
    349
    350	hwsfops = container_of(debugfs_real_fops(file),
    351			       struct nsim_dev_hwstats_fops, fops);
    352
    353	err = kstrtoint_from_user(data, count, 0, &ifindex);
    354	if (err)
    355		return err;
    356
    357	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
    358	if (WARN_ON(!hwsdev_list))
    359		return -EINVAL;
    360
    361	switch (hwsfops->action) {
    362	case NSIM_DEV_HWSTATS_DO_DISABLE:
    363		err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
    364						       hwsfops->type,
    365						       hwsdev_list);
    366		break;
    367	case NSIM_DEV_HWSTATS_DO_ENABLE:
    368		err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
    369						      hwsfops->type,
    370						      hwsdev_list);
    371		break;
    372	case NSIM_DEV_HWSTATS_DO_FAIL:
    373		err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
    374						    hwsfops->type,
    375						    hwsdev_list);
    376		break;
    377	}
    378	if (err)
    379		return err;
    380
    381	return count;
    382}
    383
    384#define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE)			\
    385	{							\
    386		.fops = {					\
    387			.open = simple_open,			\
    388			.write = nsim_dev_hwstats_do_write,	\
    389			.llseek = generic_file_llseek,		\
    390			.owner = THIS_MODULE,			\
    391		},						\
    392		.action = ACTION,				\
    393		.type = TYPE,					\
    394	}
    395
    396static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
    397	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
    398			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
    399
    400static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
    401	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
    402			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
    403
    404static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
    405	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
    406			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
    407
    408#undef NSIM_DEV_HWSTATS_FOPS
    409
    410int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
    411{
    412	struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
    413	struct net *net = nsim_dev_net(nsim_dev);
    414	int err;
    415
    416	mutex_init(&hwstats->hwsdev_list_lock);
    417	INIT_LIST_HEAD(&hwstats->l3_list);
    418
    419	hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
    420	err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
    421	if (err)
    422		goto err_mutex_destroy;
    423
    424	hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
    425	if (IS_ERR(hwstats->ddir)) {
    426		err = PTR_ERR(hwstats->ddir);
    427		goto err_unregister_notifier;
    428	}
    429
    430	hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
    431	if (IS_ERR(hwstats->l3_ddir)) {
    432		err = PTR_ERR(hwstats->l3_ddir);
    433		goto err_remove_hwstats_recursive;
    434	}
    435
    436	debugfs_create_file("enable_ifindex", 0600, hwstats->l3_ddir, hwstats,
    437			    &nsim_dev_hwstats_l3_enable_fops.fops);
    438	debugfs_create_file("disable_ifindex", 0600, hwstats->l3_ddir, hwstats,
    439			    &nsim_dev_hwstats_l3_disable_fops.fops);
    440	debugfs_create_file("fail_next_enable", 0600, hwstats->l3_ddir, hwstats,
    441			    &nsim_dev_hwstats_l3_fail_fops.fops);
    442
    443	INIT_DELAYED_WORK(&hwstats->traffic_dw,
    444			  &nsim_dev_hwstats_traffic_work);
    445	schedule_delayed_work(&hwstats->traffic_dw,
    446			      msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
    447	return 0;
    448
    449err_remove_hwstats_recursive:
    450	debugfs_remove_recursive(hwstats->ddir);
    451err_unregister_notifier:
    452	unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
    453err_mutex_destroy:
    454	mutex_destroy(&hwstats->hwsdev_list_lock);
    455	return err;
    456}
    457
    458static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
    459				      enum netdev_offload_xstats_type type)
    460{
    461	struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
    462	struct list_head *hwsdev_list;
    463
    464	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
    465	if (WARN_ON(!hwsdev_list))
    466		return;
    467
    468	mutex_lock(&hwstats->hwsdev_list_lock);
    469	list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
    470		list_del(&hwsdev->list);
    471		nsim_dev_hwsdev_fini(hwsdev);
    472	}
    473	mutex_unlock(&hwstats->hwsdev_list_lock);
    474}
    475
    476void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
    477{
    478	struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
    479	struct net *net = nsim_dev_net(nsim_dev);
    480
    481	cancel_delayed_work_sync(&hwstats->traffic_dw);
    482	debugfs_remove_recursive(hwstats->ddir);
    483	unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
    484	nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
    485	mutex_destroy(&hwstats->hwsdev_list_lock);
    486}