xref: /linux-6.15/drivers/net/netdevsim/hwstats.c (revision 1eb87004)
11a6d7ae7SPetr Machata // SPDX-License-Identifier: GPL-2.0
21a6d7ae7SPetr Machata 
31a6d7ae7SPetr Machata #include <linux/debugfs.h>
41a6d7ae7SPetr Machata 
51a6d7ae7SPetr Machata #include "netdevsim.h"
61a6d7ae7SPetr Machata 
71a6d7ae7SPetr Machata #define NSIM_DEV_HWSTATS_TRAFFIC_MS	100
81a6d7ae7SPetr Machata 
91a6d7ae7SPetr Machata static struct list_head *
nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats * hwstats,enum netdev_offload_xstats_type type)101a6d7ae7SPetr Machata nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
111a6d7ae7SPetr Machata 			       enum netdev_offload_xstats_type type)
121a6d7ae7SPetr Machata {
131a6d7ae7SPetr Machata 	switch (type) {
141a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
151a6d7ae7SPetr Machata 		return &hwstats->l3_list;
161a6d7ae7SPetr Machata 	}
171a6d7ae7SPetr Machata 
181a6d7ae7SPetr Machata 	WARN_ON_ONCE(1);
191a6d7ae7SPetr Machata 	return NULL;
201a6d7ae7SPetr Machata }
211a6d7ae7SPetr Machata 
nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats * hwstats,enum netdev_offload_xstats_type type)221a6d7ae7SPetr Machata static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
231a6d7ae7SPetr Machata 					  enum netdev_offload_xstats_type type)
241a6d7ae7SPetr Machata {
251a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
261a6d7ae7SPetr Machata 	struct list_head *hwsdev_list;
271a6d7ae7SPetr Machata 
281a6d7ae7SPetr Machata 	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
291a6d7ae7SPetr Machata 	if (WARN_ON(!hwsdev_list))
301a6d7ae7SPetr Machata 		return;
311a6d7ae7SPetr Machata 
321a6d7ae7SPetr Machata 	list_for_each_entry(hwsdev, hwsdev_list, list) {
331a6d7ae7SPetr Machata 		if (hwsdev->enabled) {
341a6d7ae7SPetr Machata 			hwsdev->stats.rx_packets += 1;
351a6d7ae7SPetr Machata 			hwsdev->stats.tx_packets += 2;
361a6d7ae7SPetr Machata 			hwsdev->stats.rx_bytes += 100;
371a6d7ae7SPetr Machata 			hwsdev->stats.tx_bytes += 300;
381a6d7ae7SPetr Machata 		}
391a6d7ae7SPetr Machata 	}
401a6d7ae7SPetr Machata }
411a6d7ae7SPetr Machata 
nsim_dev_hwstats_traffic_work(struct work_struct * work)421a6d7ae7SPetr Machata static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
431a6d7ae7SPetr Machata {
441a6d7ae7SPetr Machata 	struct nsim_dev_hwstats *hwstats;
451a6d7ae7SPetr Machata 
461a6d7ae7SPetr Machata 	hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work);
471a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
481a6d7ae7SPetr Machata 	nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
491a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
501a6d7ae7SPetr Machata 
511a6d7ae7SPetr Machata 	schedule_delayed_work(&hwstats->traffic_dw,
521a6d7ae7SPetr Machata 			      msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
531a6d7ae7SPetr Machata }
541a6d7ae7SPetr Machata 
551a6d7ae7SPetr Machata static struct nsim_dev_hwstats_netdev *
nsim_dev_hwslist_find_hwsdev(struct list_head * hwsdev_list,int ifindex)561a6d7ae7SPetr Machata nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
571a6d7ae7SPetr Machata 			     int ifindex)
581a6d7ae7SPetr Machata {
591a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
601a6d7ae7SPetr Machata 
611a6d7ae7SPetr Machata 	list_for_each_entry(hwsdev, hwsdev_list, list) {
621a6d7ae7SPetr Machata 		if (hwsdev->netdev->ifindex == ifindex)
631a6d7ae7SPetr Machata 			return hwsdev;
641a6d7ae7SPetr Machata 	}
651a6d7ae7SPetr Machata 
661a6d7ae7SPetr Machata 	return NULL;
671a6d7ae7SPetr Machata }
681a6d7ae7SPetr Machata 
nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev * hwsdev,struct netlink_ext_ack * extack)691a6d7ae7SPetr Machata static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
701a6d7ae7SPetr Machata 				  struct netlink_ext_ack *extack)
711a6d7ae7SPetr Machata {
721a6d7ae7SPetr Machata 	if (hwsdev->fail_enable) {
731a6d7ae7SPetr Machata 		hwsdev->fail_enable = false;
741a6d7ae7SPetr Machata 		NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
751a6d7ae7SPetr Machata 		return -ECANCELED;
761a6d7ae7SPetr Machata 	}
771a6d7ae7SPetr Machata 
781a6d7ae7SPetr Machata 	hwsdev->enabled = true;
791a6d7ae7SPetr Machata 	return 0;
801a6d7ae7SPetr Machata }
811a6d7ae7SPetr Machata 
nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev * hwsdev)821a6d7ae7SPetr Machata static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
831a6d7ae7SPetr Machata {
841a6d7ae7SPetr Machata 	hwsdev->enabled = false;
851a6d7ae7SPetr Machata 	memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
861a6d7ae7SPetr Machata }
871a6d7ae7SPetr Machata 
881a6d7ae7SPetr Machata static int
nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev * hwsdev,struct netdev_notifier_offload_xstats_info * info)891a6d7ae7SPetr Machata nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
901a6d7ae7SPetr Machata 			     struct netdev_notifier_offload_xstats_info *info)
911a6d7ae7SPetr Machata {
921a6d7ae7SPetr Machata 	netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
931a6d7ae7SPetr Machata 	memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
941a6d7ae7SPetr Machata 	return 0;
951a6d7ae7SPetr Machata }
961a6d7ae7SPetr Machata 
971a6d7ae7SPetr Machata static void
nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev * hwsdev,struct netdev_notifier_offload_xstats_info * info)981a6d7ae7SPetr Machata nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
991a6d7ae7SPetr Machata 			    struct netdev_notifier_offload_xstats_info *info)
1001a6d7ae7SPetr Machata {
1011a6d7ae7SPetr Machata 	if (hwsdev->enabled)
1021a6d7ae7SPetr Machata 		netdev_offload_xstats_report_used(info->report_used);
1031a6d7ae7SPetr Machata }
1041a6d7ae7SPetr Machata 
nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats * hwstats,struct net_device * dev,unsigned long event,void * ptr)1051a6d7ae7SPetr Machata static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
1061a6d7ae7SPetr Machata 					     struct net_device *dev,
1071a6d7ae7SPetr Machata 					     unsigned long event, void *ptr)
1081a6d7ae7SPetr Machata {
1091a6d7ae7SPetr Machata 	struct netdev_notifier_offload_xstats_info *info;
1101a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
1111a6d7ae7SPetr Machata 	struct list_head *hwsdev_list;
1121a6d7ae7SPetr Machata 	int err = 0;
1131a6d7ae7SPetr Machata 
1141a6d7ae7SPetr Machata 	info = ptr;
1151a6d7ae7SPetr Machata 	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
1161a6d7ae7SPetr Machata 	if (!hwsdev_list)
1171a6d7ae7SPetr Machata 		return 0;
1181a6d7ae7SPetr Machata 
1191a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
1201a6d7ae7SPetr Machata 
1211a6d7ae7SPetr Machata 	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
1221a6d7ae7SPetr Machata 	if (!hwsdev)
1231a6d7ae7SPetr Machata 		goto out;
1241a6d7ae7SPetr Machata 
1251a6d7ae7SPetr Machata 	switch (event) {
1261a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_ENABLE:
1271a6d7ae7SPetr Machata 		err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
1281a6d7ae7SPetr Machata 		break;
1291a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_DISABLE:
1301a6d7ae7SPetr Machata 		nsim_dev_hwsdev_disable(hwsdev);
1311a6d7ae7SPetr Machata 		break;
1321a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
1331a6d7ae7SPetr Machata 		nsim_dev_hwsdev_report_used(hwsdev, info);
1341a6d7ae7SPetr Machata 		break;
1351a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
1361a6d7ae7SPetr Machata 		err = nsim_dev_hwsdev_report_delta(hwsdev, info);
1371a6d7ae7SPetr Machata 		break;
1381a6d7ae7SPetr Machata 	}
1391a6d7ae7SPetr Machata 
1401a6d7ae7SPetr Machata out:
1411a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
1421a6d7ae7SPetr Machata 	return err;
1431a6d7ae7SPetr Machata }
1441a6d7ae7SPetr Machata 
nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev * hwsdev)1451a6d7ae7SPetr Machata static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
1461a6d7ae7SPetr Machata {
1471a6d7ae7SPetr Machata 	dev_put(hwsdev->netdev);
1481a6d7ae7SPetr Machata 	kfree(hwsdev);
1491a6d7ae7SPetr Machata }
1501a6d7ae7SPetr Machata 
1511a6d7ae7SPetr Machata static void
__nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats * hwstats,struct net_device * dev,enum netdev_offload_xstats_type type)1521a6d7ae7SPetr Machata __nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
1531a6d7ae7SPetr Machata 				    struct net_device *dev,
1541a6d7ae7SPetr Machata 				    enum netdev_offload_xstats_type type)
1551a6d7ae7SPetr Machata {
1561a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
1571a6d7ae7SPetr Machata 	struct list_head *hwsdev_list;
1581a6d7ae7SPetr Machata 
1591a6d7ae7SPetr Machata 	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
1601a6d7ae7SPetr Machata 	if (WARN_ON(!hwsdev_list))
1611a6d7ae7SPetr Machata 		return;
1621a6d7ae7SPetr Machata 
1631a6d7ae7SPetr Machata 	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
1641a6d7ae7SPetr Machata 	if (!hwsdev)
1651a6d7ae7SPetr Machata 		return;
1661a6d7ae7SPetr Machata 
1671a6d7ae7SPetr Machata 	list_del(&hwsdev->list);
1681a6d7ae7SPetr Machata 	nsim_dev_hwsdev_fini(hwsdev);
1691a6d7ae7SPetr Machata }
1701a6d7ae7SPetr Machata 
nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats * hwstats,struct net_device * dev)1711a6d7ae7SPetr Machata static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
1721a6d7ae7SPetr Machata 					      struct net_device *dev)
1731a6d7ae7SPetr Machata {
1741a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
1751a6d7ae7SPetr Machata 	__nsim_dev_hwstats_event_unregister(hwstats, dev,
1761a6d7ae7SPetr Machata 					    NETDEV_OFFLOAD_XSTATS_TYPE_L3);
1771a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
1781a6d7ae7SPetr Machata }
1791a6d7ae7SPetr Machata 
nsim_dev_hwstats_event(struct nsim_dev_hwstats * hwstats,struct net_device * dev,unsigned long event,void * ptr)1801a6d7ae7SPetr Machata static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
1811a6d7ae7SPetr Machata 				  struct net_device *dev,
1821a6d7ae7SPetr Machata 				  unsigned long event, void *ptr)
1831a6d7ae7SPetr Machata {
1841a6d7ae7SPetr Machata 	switch (event) {
1851a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_ENABLE:
1861a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_DISABLE:
1871a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
1881a6d7ae7SPetr Machata 	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
1891a6d7ae7SPetr Machata 		return nsim_dev_hwstats_event_off_xstats(hwstats, dev,
1901a6d7ae7SPetr Machata 							 event, ptr);
1911a6d7ae7SPetr Machata 	case NETDEV_UNREGISTER:
1921a6d7ae7SPetr Machata 		nsim_dev_hwstats_event_unregister(hwstats, dev);
1931a6d7ae7SPetr Machata 		break;
1941a6d7ae7SPetr Machata 	}
1951a6d7ae7SPetr Machata 
1961a6d7ae7SPetr Machata 	return 0;
1971a6d7ae7SPetr Machata }
1981a6d7ae7SPetr Machata 
nsim_dev_netdevice_event(struct notifier_block * nb,unsigned long event,void * ptr)1991a6d7ae7SPetr Machata static int nsim_dev_netdevice_event(struct notifier_block *nb,
2001a6d7ae7SPetr Machata 				    unsigned long event, void *ptr)
2011a6d7ae7SPetr Machata {
2021a6d7ae7SPetr Machata 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2031a6d7ae7SPetr Machata 	struct nsim_dev_hwstats *hwstats;
2041a6d7ae7SPetr Machata 	int err = 0;
2051a6d7ae7SPetr Machata 
2061a6d7ae7SPetr Machata 	hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
2071a6d7ae7SPetr Machata 	err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
2081a6d7ae7SPetr Machata 	if (err)
2091a6d7ae7SPetr Machata 		return notifier_from_errno(err);
2101a6d7ae7SPetr Machata 
2111a6d7ae7SPetr Machata 	return NOTIFY_OK;
2121a6d7ae7SPetr Machata }
2131a6d7ae7SPetr Machata 
2141a6d7ae7SPetr Machata static int
nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats * hwstats,int ifindex,enum netdev_offload_xstats_type type,struct list_head * hwsdev_list)2151a6d7ae7SPetr Machata nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
2161a6d7ae7SPetr Machata 				int ifindex,
2171a6d7ae7SPetr Machata 				enum netdev_offload_xstats_type type,
2181a6d7ae7SPetr Machata 				struct list_head *hwsdev_list)
2191a6d7ae7SPetr Machata {
2201a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
2211a6d7ae7SPetr Machata 	struct nsim_dev *nsim_dev;
2221a6d7ae7SPetr Machata 	struct net_device *netdev;
2231a6d7ae7SPetr Machata 	bool notify = false;
2241a6d7ae7SPetr Machata 	struct net *net;
2251a6d7ae7SPetr Machata 	int err = 0;
2261a6d7ae7SPetr Machata 
2271a6d7ae7SPetr Machata 	nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
2281a6d7ae7SPetr Machata 	net = nsim_dev_net(nsim_dev);
2291a6d7ae7SPetr Machata 
2301a6d7ae7SPetr Machata 	rtnl_lock();
2311a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
2321a6d7ae7SPetr Machata 	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
2331a6d7ae7SPetr Machata 	if (hwsdev)
2341a6d7ae7SPetr Machata 		goto out_unlock_list;
2351a6d7ae7SPetr Machata 
2361a6d7ae7SPetr Machata 	netdev = dev_get_by_index(net, ifindex);
2371a6d7ae7SPetr Machata 	if (!netdev) {
2381a6d7ae7SPetr Machata 		err = -ENODEV;
2391a6d7ae7SPetr Machata 		goto out_unlock_list;
2401a6d7ae7SPetr Machata 	}
2411a6d7ae7SPetr Machata 
2421a6d7ae7SPetr Machata 	hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
2431a6d7ae7SPetr Machata 	if (!hwsdev) {
2441a6d7ae7SPetr Machata 		err = -ENOMEM;
2451a6d7ae7SPetr Machata 		goto out_put_netdev;
2461a6d7ae7SPetr Machata 	}
2471a6d7ae7SPetr Machata 
2481a6d7ae7SPetr Machata 	hwsdev->netdev = netdev;
2491a6d7ae7SPetr Machata 	list_add_tail(&hwsdev->list, hwsdev_list);
2501a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
2511a6d7ae7SPetr Machata 
2521a6d7ae7SPetr Machata 	if (netdev_offload_xstats_enabled(netdev, type)) {
2531a6d7ae7SPetr Machata 		nsim_dev_hwsdev_enable(hwsdev, NULL);
2541a6d7ae7SPetr Machata 		notify = true;
2551a6d7ae7SPetr Machata 	}
2561a6d7ae7SPetr Machata 
2571a6d7ae7SPetr Machata 	if (notify)
2581a6d7ae7SPetr Machata 		rtnl_offload_xstats_notify(netdev);
2591a6d7ae7SPetr Machata 	rtnl_unlock();
2601a6d7ae7SPetr Machata 	return err;
2611a6d7ae7SPetr Machata 
2621a6d7ae7SPetr Machata out_put_netdev:
2631a6d7ae7SPetr Machata 	dev_put(netdev);
2641a6d7ae7SPetr Machata out_unlock_list:
2651a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
2661a6d7ae7SPetr Machata 	rtnl_unlock();
2671a6d7ae7SPetr Machata 	return err;
2681a6d7ae7SPetr Machata }
2691a6d7ae7SPetr Machata 
2701a6d7ae7SPetr Machata static int
nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats * hwstats,int ifindex,enum netdev_offload_xstats_type type,struct list_head * hwsdev_list)2711a6d7ae7SPetr Machata nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
2721a6d7ae7SPetr Machata 				 int ifindex,
2731a6d7ae7SPetr Machata 				 enum netdev_offload_xstats_type type,
2741a6d7ae7SPetr Machata 				 struct list_head *hwsdev_list)
2751a6d7ae7SPetr Machata {
2761a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
2771a6d7ae7SPetr Machata 	int err = 0;
2781a6d7ae7SPetr Machata 
2791a6d7ae7SPetr Machata 	rtnl_lock();
2801a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
2811a6d7ae7SPetr Machata 	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
2821a6d7ae7SPetr Machata 	if (hwsdev)
2831a6d7ae7SPetr Machata 		list_del(&hwsdev->list);
2841a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
2851a6d7ae7SPetr Machata 
2861a6d7ae7SPetr Machata 	if (!hwsdev) {
2871a6d7ae7SPetr Machata 		err = -ENOENT;
2881a6d7ae7SPetr Machata 		goto unlock_out;
2891a6d7ae7SPetr Machata 	}
2901a6d7ae7SPetr Machata 
2911a6d7ae7SPetr Machata 	if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
2921a6d7ae7SPetr Machata 		netdev_offload_xstats_push_delta(hwsdev->netdev, type,
2931a6d7ae7SPetr Machata 						 &hwsdev->stats);
2941a6d7ae7SPetr Machata 		rtnl_offload_xstats_notify(hwsdev->netdev);
2951a6d7ae7SPetr Machata 	}
2961a6d7ae7SPetr Machata 	nsim_dev_hwsdev_fini(hwsdev);
2971a6d7ae7SPetr Machata 
2981a6d7ae7SPetr Machata unlock_out:
2991a6d7ae7SPetr Machata 	rtnl_unlock();
3001a6d7ae7SPetr Machata 	return err;
3011a6d7ae7SPetr Machata }
3021a6d7ae7SPetr Machata 
3031a6d7ae7SPetr Machata static int
nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats * hwstats,int ifindex,enum netdev_offload_xstats_type type,struct list_head * hwsdev_list)3041a6d7ae7SPetr Machata nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
3051a6d7ae7SPetr Machata 			      int ifindex,
3061a6d7ae7SPetr Machata 			      enum netdev_offload_xstats_type type,
3071a6d7ae7SPetr Machata 			      struct list_head *hwsdev_list)
3081a6d7ae7SPetr Machata {
3091a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev;
3101a6d7ae7SPetr Machata 	int err = 0;
3111a6d7ae7SPetr Machata 
3121a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
3131a6d7ae7SPetr Machata 
3141a6d7ae7SPetr Machata 	hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
3151a6d7ae7SPetr Machata 	if (!hwsdev) {
3161a6d7ae7SPetr Machata 		err = -ENOENT;
3171a6d7ae7SPetr Machata 		goto err_hwsdev_list_unlock;
3181a6d7ae7SPetr Machata 	}
3191a6d7ae7SPetr Machata 
3201a6d7ae7SPetr Machata 	hwsdev->fail_enable = true;
3211a6d7ae7SPetr Machata 
3221a6d7ae7SPetr Machata err_hwsdev_list_unlock:
3231a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
3241a6d7ae7SPetr Machata 	return err;
3251a6d7ae7SPetr Machata }
3261a6d7ae7SPetr Machata 
3271a6d7ae7SPetr Machata enum nsim_dev_hwstats_do {
3281a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_DO_DISABLE,
3291a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_DO_ENABLE,
3301a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_DO_FAIL,
3311a6d7ae7SPetr Machata };
3321a6d7ae7SPetr Machata 
3331a6d7ae7SPetr Machata struct nsim_dev_hwstats_fops {
3341a6d7ae7SPetr Machata 	enum nsim_dev_hwstats_do action;
3351a6d7ae7SPetr Machata 	enum netdev_offload_xstats_type type;
3361a6d7ae7SPetr Machata };
3371a6d7ae7SPetr Machata 
3381a6d7ae7SPetr Machata static ssize_t
nsim_dev_hwstats_do_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)3391a6d7ae7SPetr Machata nsim_dev_hwstats_do_write(struct file *file,
3401a6d7ae7SPetr Machata 			  const char __user *data,
3411a6d7ae7SPetr Machata 			  size_t count, loff_t *ppos)
3421a6d7ae7SPetr Machata {
3431a6d7ae7SPetr Machata 	struct nsim_dev_hwstats *hwstats = file->private_data;
344*1eb87004SAl Viro 	const struct nsim_dev_hwstats_fops *hwsfops;
3451a6d7ae7SPetr Machata 	struct list_head *hwsdev_list;
3461a6d7ae7SPetr Machata 	int ifindex;
3471a6d7ae7SPetr Machata 	int err;
3481a6d7ae7SPetr Machata 
349*1eb87004SAl Viro 	hwsfops = debugfs_get_aux(file);
3501a6d7ae7SPetr Machata 
3511a6d7ae7SPetr Machata 	err = kstrtoint_from_user(data, count, 0, &ifindex);
3521a6d7ae7SPetr Machata 	if (err)
3531a6d7ae7SPetr Machata 		return err;
3541a6d7ae7SPetr Machata 
3551a6d7ae7SPetr Machata 	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
3561a6d7ae7SPetr Machata 	if (WARN_ON(!hwsdev_list))
3571a6d7ae7SPetr Machata 		return -EINVAL;
3581a6d7ae7SPetr Machata 
3591a6d7ae7SPetr Machata 	switch (hwsfops->action) {
3601a6d7ae7SPetr Machata 	case NSIM_DEV_HWSTATS_DO_DISABLE:
3611a6d7ae7SPetr Machata 		err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
3621a6d7ae7SPetr Machata 						       hwsfops->type,
3631a6d7ae7SPetr Machata 						       hwsdev_list);
3641a6d7ae7SPetr Machata 		break;
3651a6d7ae7SPetr Machata 	case NSIM_DEV_HWSTATS_DO_ENABLE:
3661a6d7ae7SPetr Machata 		err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
3671a6d7ae7SPetr Machata 						      hwsfops->type,
3681a6d7ae7SPetr Machata 						      hwsdev_list);
3691a6d7ae7SPetr Machata 		break;
3701a6d7ae7SPetr Machata 	case NSIM_DEV_HWSTATS_DO_FAIL:
3711a6d7ae7SPetr Machata 		err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
3721a6d7ae7SPetr Machata 						    hwsfops->type,
3731a6d7ae7SPetr Machata 						    hwsdev_list);
3741a6d7ae7SPetr Machata 		break;
3751a6d7ae7SPetr Machata 	}
3761a6d7ae7SPetr Machata 	if (err)
3771a6d7ae7SPetr Machata 		return err;
3781a6d7ae7SPetr Machata 
3791a6d7ae7SPetr Machata 	return count;
3801a6d7ae7SPetr Machata }
3811a6d7ae7SPetr Machata 
382*1eb87004SAl Viro static struct debugfs_short_fops debugfs_ops = {
383*1eb87004SAl Viro 	.write = nsim_dev_hwstats_do_write,
384*1eb87004SAl Viro 	.llseek = generic_file_llseek,
385*1eb87004SAl Viro };
386*1eb87004SAl Viro 
3871a6d7ae7SPetr Machata #define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE)			\
3881a6d7ae7SPetr Machata 	{							\
3891a6d7ae7SPetr Machata 		.action = ACTION,				\
3901a6d7ae7SPetr Machata 		.type = TYPE,					\
3911a6d7ae7SPetr Machata 	}
3921a6d7ae7SPetr Machata 
3931a6d7ae7SPetr Machata static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
3941a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
3951a6d7ae7SPetr Machata 			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
3961a6d7ae7SPetr Machata 
3971a6d7ae7SPetr Machata static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
3981a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
3991a6d7ae7SPetr Machata 			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
4001a6d7ae7SPetr Machata 
4011a6d7ae7SPetr Machata static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
4021a6d7ae7SPetr Machata 	NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
4031a6d7ae7SPetr Machata 			      NETDEV_OFFLOAD_XSTATS_TYPE_L3);
4041a6d7ae7SPetr Machata 
4051a6d7ae7SPetr Machata #undef NSIM_DEV_HWSTATS_FOPS
4061a6d7ae7SPetr Machata 
nsim_dev_hwstats_init(struct nsim_dev * nsim_dev)4071a6d7ae7SPetr Machata int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
4081a6d7ae7SPetr Machata {
4091a6d7ae7SPetr Machata 	struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
4101a6d7ae7SPetr Machata 	struct net *net = nsim_dev_net(nsim_dev);
4111a6d7ae7SPetr Machata 	int err;
4121a6d7ae7SPetr Machata 
4131a6d7ae7SPetr Machata 	mutex_init(&hwstats->hwsdev_list_lock);
4141a6d7ae7SPetr Machata 	INIT_LIST_HEAD(&hwstats->l3_list);
4151a6d7ae7SPetr Machata 
4161a6d7ae7SPetr Machata 	hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
4171a6d7ae7SPetr Machata 	err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
4181a6d7ae7SPetr Machata 	if (err)
4191a6d7ae7SPetr Machata 		goto err_mutex_destroy;
4201a6d7ae7SPetr Machata 
4211a6d7ae7SPetr Machata 	hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
4221a6d7ae7SPetr Machata 	if (IS_ERR(hwstats->ddir)) {
4231a6d7ae7SPetr Machata 		err = PTR_ERR(hwstats->ddir);
4241a6d7ae7SPetr Machata 		goto err_unregister_notifier;
4251a6d7ae7SPetr Machata 	}
4261a6d7ae7SPetr Machata 
4271a6d7ae7SPetr Machata 	hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
4281a6d7ae7SPetr Machata 	if (IS_ERR(hwstats->l3_ddir)) {
4291a6d7ae7SPetr Machata 		err = PTR_ERR(hwstats->l3_ddir);
4301a6d7ae7SPetr Machata 		goto err_remove_hwstats_recursive;
4311a6d7ae7SPetr Machata 	}
4321a6d7ae7SPetr Machata 
433*1eb87004SAl Viro 	debugfs_create_file_aux("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
434*1eb87004SAl Viro 			    &nsim_dev_hwstats_l3_enable_fops, &debugfs_ops);
435*1eb87004SAl Viro 	debugfs_create_file_aux("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
436*1eb87004SAl Viro 			    &nsim_dev_hwstats_l3_disable_fops, &debugfs_ops);
437*1eb87004SAl Viro 	debugfs_create_file_aux("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
438*1eb87004SAl Viro 			    &nsim_dev_hwstats_l3_fail_fops, &debugfs_ops);
4391a6d7ae7SPetr Machata 
4401a6d7ae7SPetr Machata 	INIT_DELAYED_WORK(&hwstats->traffic_dw,
4411a6d7ae7SPetr Machata 			  &nsim_dev_hwstats_traffic_work);
4421a6d7ae7SPetr Machata 	schedule_delayed_work(&hwstats->traffic_dw,
4431a6d7ae7SPetr Machata 			      msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
4441a6d7ae7SPetr Machata 	return 0;
4451a6d7ae7SPetr Machata 
4461a6d7ae7SPetr Machata err_remove_hwstats_recursive:
4471a6d7ae7SPetr Machata 	debugfs_remove_recursive(hwstats->ddir);
4481a6d7ae7SPetr Machata err_unregister_notifier:
4491a6d7ae7SPetr Machata 	unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
4501a6d7ae7SPetr Machata err_mutex_destroy:
4511a6d7ae7SPetr Machata 	mutex_destroy(&hwstats->hwsdev_list_lock);
4521a6d7ae7SPetr Machata 	return err;
4531a6d7ae7SPetr Machata }
4541a6d7ae7SPetr Machata 
nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats * hwstats,enum netdev_offload_xstats_type type)4551a6d7ae7SPetr Machata static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
4561a6d7ae7SPetr Machata 				      enum netdev_offload_xstats_type type)
4571a6d7ae7SPetr Machata {
4581a6d7ae7SPetr Machata 	struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
4591a6d7ae7SPetr Machata 	struct list_head *hwsdev_list;
4601a6d7ae7SPetr Machata 
4611a6d7ae7SPetr Machata 	hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
4621a6d7ae7SPetr Machata 	if (WARN_ON(!hwsdev_list))
4631a6d7ae7SPetr Machata 		return;
4641a6d7ae7SPetr Machata 
4651a6d7ae7SPetr Machata 	mutex_lock(&hwstats->hwsdev_list_lock);
4661a6d7ae7SPetr Machata 	list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
4671a6d7ae7SPetr Machata 		list_del(&hwsdev->list);
4681a6d7ae7SPetr Machata 		nsim_dev_hwsdev_fini(hwsdev);
4691a6d7ae7SPetr Machata 	}
4701a6d7ae7SPetr Machata 	mutex_unlock(&hwstats->hwsdev_list_lock);
4711a6d7ae7SPetr Machata }
4721a6d7ae7SPetr Machata 
nsim_dev_hwstats_exit(struct nsim_dev * nsim_dev)4731a6d7ae7SPetr Machata void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
4741a6d7ae7SPetr Machata {
4751a6d7ae7SPetr Machata 	struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
4761a6d7ae7SPetr Machata 	struct net *net = nsim_dev_net(nsim_dev);
4771a6d7ae7SPetr Machata 
4781a6d7ae7SPetr Machata 	cancel_delayed_work_sync(&hwstats->traffic_dw);
4791a6d7ae7SPetr Machata 	debugfs_remove_recursive(hwstats->ddir);
4801a6d7ae7SPetr Machata 	unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
4811a6d7ae7SPetr Machata 	nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
4821a6d7ae7SPetr Machata 	mutex_destroy(&hwstats->hwsdev_list_lock);
4831a6d7ae7SPetr Machata }
484