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