brintos

brintos / linux-shallow public Read only

0
0
Text · 12.3 KiB · 0e58aa7 Raw
487 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/debugfs.h>4 5#include "netdevsim.h"6 7#define NSIM_DEV_HWSTATS_TRAFFIC_MS	1008 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 int89nsim_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 void98nsim_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 void152__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 int215nsim_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 int271nsim_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 int304nsim_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_t340nsim_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_FOPS409 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", 0200, hwstats->l3_ddir, hwstats,437			    &nsim_dev_hwstats_l3_enable_fops.fops);438	debugfs_create_file("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,439			    &nsim_dev_hwstats_l3_disable_fops.fops);440	debugfs_create_file("fail_next_enable", 0200, 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}487