162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/debugfs.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "netdevsim.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define NSIM_DEV_HWSTATS_TRAFFIC_MS 100 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic struct list_head * 1062306a36Sopenharmony_cinsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats, 1162306a36Sopenharmony_ci enum netdev_offload_xstats_type type) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci switch (type) { 1462306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_TYPE_L3: 1562306a36Sopenharmony_ci return &hwstats->l3_list; 1662306a36Sopenharmony_ci } 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci WARN_ON_ONCE(1); 1962306a36Sopenharmony_ci return NULL; 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats, 2362306a36Sopenharmony_ci enum netdev_offload_xstats_type type) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 2662306a36Sopenharmony_ci struct list_head *hwsdev_list; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); 2962306a36Sopenharmony_ci if (WARN_ON(!hwsdev_list)) 3062306a36Sopenharmony_ci return; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci list_for_each_entry(hwsdev, hwsdev_list, list) { 3362306a36Sopenharmony_ci if (hwsdev->enabled) { 3462306a36Sopenharmony_ci hwsdev->stats.rx_packets += 1; 3562306a36Sopenharmony_ci hwsdev->stats.tx_packets += 2; 3662306a36Sopenharmony_ci hwsdev->stats.rx_bytes += 100; 3762306a36Sopenharmony_ci hwsdev->stats.tx_bytes += 300; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void nsim_dev_hwstats_traffic_work(struct work_struct *work) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct nsim_dev_hwstats *hwstats; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work); 4762306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 4862306a36Sopenharmony_ci nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3); 4962306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci schedule_delayed_work(&hwstats->traffic_dw, 5262306a36Sopenharmony_ci msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS)); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct nsim_dev_hwstats_netdev * 5662306a36Sopenharmony_cinsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list, 5762306a36Sopenharmony_ci int ifindex) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci list_for_each_entry(hwsdev, hwsdev_list, list) { 6262306a36Sopenharmony_ci if (hwsdev->netdev->ifindex == ifindex) 6362306a36Sopenharmony_ci return hwsdev; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return NULL; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev, 7062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (hwsdev->fail_enable) { 7362306a36Sopenharmony_ci hwsdev->fail_enable = false; 7462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail"); 7562306a36Sopenharmony_ci return -ECANCELED; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci hwsdev->enabled = true; 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci hwsdev->enabled = false; 8562306a36Sopenharmony_ci memset(&hwsdev->stats, 0, sizeof(hwsdev->stats)); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int 8962306a36Sopenharmony_cinsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev, 9062306a36Sopenharmony_ci struct netdev_notifier_offload_xstats_info *info) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats); 9362306a36Sopenharmony_ci memset(&hwsdev->stats, 0, sizeof(hwsdev->stats)); 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void 9862306a36Sopenharmony_cinsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev, 9962306a36Sopenharmony_ci struct netdev_notifier_offload_xstats_info *info) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci if (hwsdev->enabled) 10262306a36Sopenharmony_ci netdev_offload_xstats_report_used(info->report_used); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats, 10662306a36Sopenharmony_ci struct net_device *dev, 10762306a36Sopenharmony_ci unsigned long event, void *ptr) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct netdev_notifier_offload_xstats_info *info; 11062306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 11162306a36Sopenharmony_ci struct list_head *hwsdev_list; 11262306a36Sopenharmony_ci int err = 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci info = ptr; 11562306a36Sopenharmony_ci hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type); 11662306a36Sopenharmony_ci if (!hwsdev_list) 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex); 12262306a36Sopenharmony_ci if (!hwsdev) 12362306a36Sopenharmony_ci goto out; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci switch (event) { 12662306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_ENABLE: 12762306a36Sopenharmony_ci err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_DISABLE: 13062306a36Sopenharmony_ci nsim_dev_hwsdev_disable(hwsdev); 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_REPORT_USED: 13362306a36Sopenharmony_ci nsim_dev_hwsdev_report_used(hwsdev, info); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: 13662306a36Sopenharmony_ci err = nsim_dev_hwsdev_report_delta(hwsdev, info); 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciout: 14162306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci dev_put(hwsdev->netdev); 14862306a36Sopenharmony_ci kfree(hwsdev); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void 15262306a36Sopenharmony_ci__nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats, 15362306a36Sopenharmony_ci struct net_device *dev, 15462306a36Sopenharmony_ci enum netdev_offload_xstats_type type) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 15762306a36Sopenharmony_ci struct list_head *hwsdev_list; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); 16062306a36Sopenharmony_ci if (WARN_ON(!hwsdev_list)) 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex); 16462306a36Sopenharmony_ci if (!hwsdev) 16562306a36Sopenharmony_ci return; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci list_del(&hwsdev->list); 16862306a36Sopenharmony_ci nsim_dev_hwsdev_fini(hwsdev); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats, 17262306a36Sopenharmony_ci struct net_device *dev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 17562306a36Sopenharmony_ci __nsim_dev_hwstats_event_unregister(hwstats, dev, 17662306a36Sopenharmony_ci NETDEV_OFFLOAD_XSTATS_TYPE_L3); 17762306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats, 18162306a36Sopenharmony_ci struct net_device *dev, 18262306a36Sopenharmony_ci unsigned long event, void *ptr) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci switch (event) { 18562306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_ENABLE: 18662306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_DISABLE: 18762306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_REPORT_USED: 18862306a36Sopenharmony_ci case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: 18962306a36Sopenharmony_ci return nsim_dev_hwstats_event_off_xstats(hwstats, dev, 19062306a36Sopenharmony_ci event, ptr); 19162306a36Sopenharmony_ci case NETDEV_UNREGISTER: 19262306a36Sopenharmony_ci nsim_dev_hwstats_event_unregister(hwstats, dev); 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int nsim_dev_netdevice_event(struct notifier_block *nb, 20062306a36Sopenharmony_ci unsigned long event, void *ptr) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 20362306a36Sopenharmony_ci struct nsim_dev_hwstats *hwstats; 20462306a36Sopenharmony_ci int err = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb); 20762306a36Sopenharmony_ci err = nsim_dev_hwstats_event(hwstats, dev, event, ptr); 20862306a36Sopenharmony_ci if (err) 20962306a36Sopenharmony_ci return notifier_from_errno(err); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return NOTIFY_OK; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int 21562306a36Sopenharmony_cinsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats, 21662306a36Sopenharmony_ci int ifindex, 21762306a36Sopenharmony_ci enum netdev_offload_xstats_type type, 21862306a36Sopenharmony_ci struct list_head *hwsdev_list) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 22162306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 22262306a36Sopenharmony_ci struct net_device *netdev; 22362306a36Sopenharmony_ci bool notify = false; 22462306a36Sopenharmony_ci struct net *net; 22562306a36Sopenharmony_ci int err = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci nsim_dev = container_of(hwstats, struct nsim_dev, hwstats); 22862306a36Sopenharmony_ci net = nsim_dev_net(nsim_dev); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci rtnl_lock(); 23162306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 23262306a36Sopenharmony_ci hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); 23362306a36Sopenharmony_ci if (hwsdev) 23462306a36Sopenharmony_ci goto out_unlock_list; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci netdev = dev_get_by_index(net, ifindex); 23762306a36Sopenharmony_ci if (!netdev) { 23862306a36Sopenharmony_ci err = -ENODEV; 23962306a36Sopenharmony_ci goto out_unlock_list; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL); 24362306a36Sopenharmony_ci if (!hwsdev) { 24462306a36Sopenharmony_ci err = -ENOMEM; 24562306a36Sopenharmony_ci goto out_put_netdev; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci hwsdev->netdev = netdev; 24962306a36Sopenharmony_ci list_add_tail(&hwsdev->list, hwsdev_list); 25062306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (netdev_offload_xstats_enabled(netdev, type)) { 25362306a36Sopenharmony_ci nsim_dev_hwsdev_enable(hwsdev, NULL); 25462306a36Sopenharmony_ci notify = true; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (notify) 25862306a36Sopenharmony_ci rtnl_offload_xstats_notify(netdev); 25962306a36Sopenharmony_ci rtnl_unlock(); 26062306a36Sopenharmony_ci return err; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ciout_put_netdev: 26362306a36Sopenharmony_ci dev_put(netdev); 26462306a36Sopenharmony_ciout_unlock_list: 26562306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 26662306a36Sopenharmony_ci rtnl_unlock(); 26762306a36Sopenharmony_ci return err; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int 27162306a36Sopenharmony_cinsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats, 27262306a36Sopenharmony_ci int ifindex, 27362306a36Sopenharmony_ci enum netdev_offload_xstats_type type, 27462306a36Sopenharmony_ci struct list_head *hwsdev_list) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 27762306a36Sopenharmony_ci int err = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci rtnl_lock(); 28062306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 28162306a36Sopenharmony_ci hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); 28262306a36Sopenharmony_ci if (hwsdev) 28362306a36Sopenharmony_ci list_del(&hwsdev->list); 28462306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!hwsdev) { 28762306a36Sopenharmony_ci err = -ENOENT; 28862306a36Sopenharmony_ci goto unlock_out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) { 29262306a36Sopenharmony_ci netdev_offload_xstats_push_delta(hwsdev->netdev, type, 29362306a36Sopenharmony_ci &hwsdev->stats); 29462306a36Sopenharmony_ci rtnl_offload_xstats_notify(hwsdev->netdev); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci nsim_dev_hwsdev_fini(hwsdev); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciunlock_out: 29962306a36Sopenharmony_ci rtnl_unlock(); 30062306a36Sopenharmony_ci return err; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int 30462306a36Sopenharmony_cinsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats, 30562306a36Sopenharmony_ci int ifindex, 30662306a36Sopenharmony_ci enum netdev_offload_xstats_type type, 30762306a36Sopenharmony_ci struct list_head *hwsdev_list) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev; 31062306a36Sopenharmony_ci int err = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex); 31562306a36Sopenharmony_ci if (!hwsdev) { 31662306a36Sopenharmony_ci err = -ENOENT; 31762306a36Sopenharmony_ci goto err_hwsdev_list_unlock; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci hwsdev->fail_enable = true; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cierr_hwsdev_list_unlock: 32362306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 32462306a36Sopenharmony_ci return err; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cienum nsim_dev_hwstats_do { 32862306a36Sopenharmony_ci NSIM_DEV_HWSTATS_DO_DISABLE, 32962306a36Sopenharmony_ci NSIM_DEV_HWSTATS_DO_ENABLE, 33062306a36Sopenharmony_ci NSIM_DEV_HWSTATS_DO_FAIL, 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistruct nsim_dev_hwstats_fops { 33462306a36Sopenharmony_ci const struct file_operations fops; 33562306a36Sopenharmony_ci enum nsim_dev_hwstats_do action; 33662306a36Sopenharmony_ci enum netdev_offload_xstats_type type; 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic ssize_t 34062306a36Sopenharmony_cinsim_dev_hwstats_do_write(struct file *file, 34162306a36Sopenharmony_ci const char __user *data, 34262306a36Sopenharmony_ci size_t count, loff_t *ppos) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct nsim_dev_hwstats *hwstats = file->private_data; 34562306a36Sopenharmony_ci struct nsim_dev_hwstats_fops *hwsfops; 34662306a36Sopenharmony_ci struct list_head *hwsdev_list; 34762306a36Sopenharmony_ci int ifindex; 34862306a36Sopenharmony_ci int err; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci hwsfops = container_of(debugfs_real_fops(file), 35162306a36Sopenharmony_ci struct nsim_dev_hwstats_fops, fops); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci err = kstrtoint_from_user(data, count, 0, &ifindex); 35462306a36Sopenharmony_ci if (err) 35562306a36Sopenharmony_ci return err; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type); 35862306a36Sopenharmony_ci if (WARN_ON(!hwsdev_list)) 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (hwsfops->action) { 36262306a36Sopenharmony_ci case NSIM_DEV_HWSTATS_DO_DISABLE: 36362306a36Sopenharmony_ci err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex, 36462306a36Sopenharmony_ci hwsfops->type, 36562306a36Sopenharmony_ci hwsdev_list); 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case NSIM_DEV_HWSTATS_DO_ENABLE: 36862306a36Sopenharmony_ci err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex, 36962306a36Sopenharmony_ci hwsfops->type, 37062306a36Sopenharmony_ci hwsdev_list); 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci case NSIM_DEV_HWSTATS_DO_FAIL: 37362306a36Sopenharmony_ci err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex, 37462306a36Sopenharmony_ci hwsfops->type, 37562306a36Sopenharmony_ci hwsdev_list); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (err) 37962306a36Sopenharmony_ci return err; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return count; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci#define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \ 38562306a36Sopenharmony_ci { \ 38662306a36Sopenharmony_ci .fops = { \ 38762306a36Sopenharmony_ci .open = simple_open, \ 38862306a36Sopenharmony_ci .write = nsim_dev_hwstats_do_write, \ 38962306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 39062306a36Sopenharmony_ci .owner = THIS_MODULE, \ 39162306a36Sopenharmony_ci }, \ 39262306a36Sopenharmony_ci .action = ACTION, \ 39362306a36Sopenharmony_ci .type = TYPE, \ 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops = 39762306a36Sopenharmony_ci NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE, 39862306a36Sopenharmony_ci NETDEV_OFFLOAD_XSTATS_TYPE_L3); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops = 40162306a36Sopenharmony_ci NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE, 40262306a36Sopenharmony_ci NETDEV_OFFLOAD_XSTATS_TYPE_L3); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops = 40562306a36Sopenharmony_ci NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL, 40662306a36Sopenharmony_ci NETDEV_OFFLOAD_XSTATS_TYPE_L3); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci#undef NSIM_DEV_HWSTATS_FOPS 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint nsim_dev_hwstats_init(struct nsim_dev *nsim_dev) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats; 41362306a36Sopenharmony_ci struct net *net = nsim_dev_net(nsim_dev); 41462306a36Sopenharmony_ci int err; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci mutex_init(&hwstats->hwsdev_list_lock); 41762306a36Sopenharmony_ci INIT_LIST_HEAD(&hwstats->l3_list); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event; 42062306a36Sopenharmony_ci err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb); 42162306a36Sopenharmony_ci if (err) 42262306a36Sopenharmony_ci goto err_mutex_destroy; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir); 42562306a36Sopenharmony_ci if (IS_ERR(hwstats->ddir)) { 42662306a36Sopenharmony_ci err = PTR_ERR(hwstats->ddir); 42762306a36Sopenharmony_ci goto err_unregister_notifier; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir); 43162306a36Sopenharmony_ci if (IS_ERR(hwstats->l3_ddir)) { 43262306a36Sopenharmony_ci err = PTR_ERR(hwstats->l3_ddir); 43362306a36Sopenharmony_ci goto err_remove_hwstats_recursive; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci debugfs_create_file("enable_ifindex", 0200, hwstats->l3_ddir, hwstats, 43762306a36Sopenharmony_ci &nsim_dev_hwstats_l3_enable_fops.fops); 43862306a36Sopenharmony_ci debugfs_create_file("disable_ifindex", 0200, hwstats->l3_ddir, hwstats, 43962306a36Sopenharmony_ci &nsim_dev_hwstats_l3_disable_fops.fops); 44062306a36Sopenharmony_ci debugfs_create_file("fail_next_enable", 0200, hwstats->l3_ddir, hwstats, 44162306a36Sopenharmony_ci &nsim_dev_hwstats_l3_fail_fops.fops); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci INIT_DELAYED_WORK(&hwstats->traffic_dw, 44462306a36Sopenharmony_ci &nsim_dev_hwstats_traffic_work); 44562306a36Sopenharmony_ci schedule_delayed_work(&hwstats->traffic_dw, 44662306a36Sopenharmony_ci msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS)); 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cierr_remove_hwstats_recursive: 45062306a36Sopenharmony_ci debugfs_remove_recursive(hwstats->ddir); 45162306a36Sopenharmony_cierr_unregister_notifier: 45262306a36Sopenharmony_ci unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb); 45362306a36Sopenharmony_cierr_mutex_destroy: 45462306a36Sopenharmony_ci mutex_destroy(&hwstats->hwsdev_list_lock); 45562306a36Sopenharmony_ci return err; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats, 45962306a36Sopenharmony_ci enum netdev_offload_xstats_type type) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct nsim_dev_hwstats_netdev *hwsdev, *tmp; 46262306a36Sopenharmony_ci struct list_head *hwsdev_list; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type); 46562306a36Sopenharmony_ci if (WARN_ON(!hwsdev_list)) 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci mutex_lock(&hwstats->hwsdev_list_lock); 46962306a36Sopenharmony_ci list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) { 47062306a36Sopenharmony_ci list_del(&hwsdev->list); 47162306a36Sopenharmony_ci nsim_dev_hwsdev_fini(hwsdev); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci mutex_unlock(&hwstats->hwsdev_list_lock); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_civoid nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats; 47962306a36Sopenharmony_ci struct net *net = nsim_dev_net(nsim_dev); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci cancel_delayed_work_sync(&hwstats->traffic_dw); 48262306a36Sopenharmony_ci debugfs_remove_recursive(hwstats->ddir); 48362306a36Sopenharmony_ci unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb); 48462306a36Sopenharmony_ci nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3); 48562306a36Sopenharmony_ci mutex_destroy(&hwstats->hwsdev_list_lock); 48662306a36Sopenharmony_ci} 487