1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/debugfs.h>
7 #define NSIM_DEV_HWSTATS_TRAFFIC_MS 100
9 static struct list_head *
10 nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
11 enum netdev_offload_xstats_type type)
14 case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
15 return &hwstats->l3_list;
22 static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
23 enum netdev_offload_xstats_type type)
25 struct nsim_dev_hwstats_netdev *hwsdev;
26 struct list_head *hwsdev_list;
28 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
29 if (WARN_ON(!hwsdev_list))
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;
42 static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
44 struct nsim_dev_hwstats *hwstats;
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);
51 schedule_delayed_work(&hwstats->traffic_dw,
52 msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
55 static struct nsim_dev_hwstats_netdev *
56 nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
59 struct nsim_dev_hwstats_netdev *hwsdev;
61 list_for_each_entry(hwsdev, hwsdev_list, list) {
62 if (hwsdev->netdev->ifindex == ifindex)
69 static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
70 struct netlink_ext_ack *extack)
72 if (hwsdev->fail_enable) {
73 hwsdev->fail_enable = false;
74 NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
78 hwsdev->enabled = true;
82 static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
84 hwsdev->enabled = false;
85 memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
89 nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
90 struct netdev_notifier_offload_xstats_info *info)
92 netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
93 memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
98 nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
99 struct netdev_notifier_offload_xstats_info *info)
102 netdev_offload_xstats_report_used(info->report_used);
105 static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
106 struct net_device *dev,
107 unsigned long event, void *ptr)
109 struct netdev_notifier_offload_xstats_info *info;
110 struct nsim_dev_hwstats_netdev *hwsdev;
111 struct list_head *hwsdev_list;
115 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
119 mutex_lock(&hwstats->hwsdev_list_lock);
121 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
126 case NETDEV_OFFLOAD_XSTATS_ENABLE:
127 err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
129 case NETDEV_OFFLOAD_XSTATS_DISABLE:
130 nsim_dev_hwsdev_disable(hwsdev);
132 case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
133 nsim_dev_hwsdev_report_used(hwsdev, info);
135 case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
136 err = nsim_dev_hwsdev_report_delta(hwsdev, info);
141 mutex_unlock(&hwstats->hwsdev_list_lock);
145 static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
147 dev_put(hwsdev->netdev);
152 __nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
153 struct net_device *dev,
154 enum netdev_offload_xstats_type type)
156 struct nsim_dev_hwstats_netdev *hwsdev;
157 struct list_head *hwsdev_list;
159 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
160 if (WARN_ON(!hwsdev_list))
163 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
167 list_del(&hwsdev->list);
168 nsim_dev_hwsdev_fini(hwsdev);
171 static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
172 struct net_device *dev)
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);
180 static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
181 struct net_device *dev,
182 unsigned long event, void *ptr)
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,
191 case NETDEV_UNREGISTER:
192 nsim_dev_hwstats_event_unregister(hwstats, dev);
199 static int nsim_dev_netdevice_event(struct notifier_block *nb,
200 unsigned long event, void *ptr)
202 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
203 struct nsim_dev_hwstats *hwstats;
206 hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
207 err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
209 return notifier_from_errno(err);
215 nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
217 enum netdev_offload_xstats_type type,
218 struct list_head *hwsdev_list)
220 struct nsim_dev_hwstats_netdev *hwsdev;
221 struct nsim_dev *nsim_dev;
222 struct net_device *netdev;
227 nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
228 net = nsim_dev_net(nsim_dev);
231 mutex_lock(&hwstats->hwsdev_list_lock);
232 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
234 goto out_unlock_list;
236 netdev = dev_get_by_index(net, ifindex);
239 goto out_unlock_list;
242 hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
248 hwsdev->netdev = netdev;
249 list_add_tail(&hwsdev->list, hwsdev_list);
250 mutex_unlock(&hwstats->hwsdev_list_lock);
252 if (netdev_offload_xstats_enabled(netdev, type)) {
253 nsim_dev_hwsdev_enable(hwsdev, NULL);
258 rtnl_offload_xstats_notify(netdev);
265 mutex_unlock(&hwstats->hwsdev_list_lock);
271 nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
273 enum netdev_offload_xstats_type type,
274 struct list_head *hwsdev_list)
276 struct nsim_dev_hwstats_netdev *hwsdev;
280 mutex_lock(&hwstats->hwsdev_list_lock);
281 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
283 list_del(&hwsdev->list);
284 mutex_unlock(&hwstats->hwsdev_list_lock);
291 if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
292 netdev_offload_xstats_push_delta(hwsdev->netdev, type,
294 rtnl_offload_xstats_notify(hwsdev->netdev);
296 nsim_dev_hwsdev_fini(hwsdev);
304 nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
306 enum netdev_offload_xstats_type type,
307 struct list_head *hwsdev_list)
309 struct nsim_dev_hwstats_netdev *hwsdev;
312 mutex_lock(&hwstats->hwsdev_list_lock);
314 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
317 goto err_hwsdev_list_unlock;
320 hwsdev->fail_enable = true;
322 err_hwsdev_list_unlock:
323 mutex_unlock(&hwstats->hwsdev_list_lock);
327 enum nsim_dev_hwstats_do {
328 NSIM_DEV_HWSTATS_DO_DISABLE,
329 NSIM_DEV_HWSTATS_DO_ENABLE,
330 NSIM_DEV_HWSTATS_DO_FAIL,
333 struct nsim_dev_hwstats_fops {
334 enum nsim_dev_hwstats_do action;
335 enum netdev_offload_xstats_type type;
339 nsim_dev_hwstats_do_write(struct file *file,
340 const char __user *data,
341 size_t count, loff_t *ppos)
343 struct nsim_dev_hwstats *hwstats = file->private_data;
344 const struct nsim_dev_hwstats_fops *hwsfops;
345 struct list_head *hwsdev_list;
349 hwsfops = debugfs_get_aux(file);
351 err = kstrtoint_from_user(data, count, 0, &ifindex);
355 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
356 if (WARN_ON(!hwsdev_list))
359 switch (hwsfops->action) {
360 case NSIM_DEV_HWSTATS_DO_DISABLE:
361 err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
365 case NSIM_DEV_HWSTATS_DO_ENABLE:
366 err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
370 case NSIM_DEV_HWSTATS_DO_FAIL:
371 err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
382 static struct debugfs_short_fops debugfs_ops = {
383 .write = nsim_dev_hwstats_do_write,
384 .llseek = generic_file_llseek,
387 #define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \
393 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
394 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
395 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
397 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
398 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
399 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
401 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
402 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
403 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
405 #undef NSIM_DEV_HWSTATS_FOPS
407 int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
409 struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
410 struct net *net = nsim_dev_net(nsim_dev);
413 mutex_init(&hwstats->hwsdev_list_lock);
414 INIT_LIST_HEAD(&hwstats->l3_list);
416 hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
417 err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
419 goto err_mutex_destroy;
421 hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
422 if (IS_ERR(hwstats->ddir)) {
423 err = PTR_ERR(hwstats->ddir);
424 goto err_unregister_notifier;
427 hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
428 if (IS_ERR(hwstats->l3_ddir)) {
429 err = PTR_ERR(hwstats->l3_ddir);
430 goto err_remove_hwstats_recursive;
433 debugfs_create_file_aux("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
434 &nsim_dev_hwstats_l3_enable_fops, &debugfs_ops);
435 debugfs_create_file_aux("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
436 &nsim_dev_hwstats_l3_disable_fops, &debugfs_ops);
437 debugfs_create_file_aux("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
438 &nsim_dev_hwstats_l3_fail_fops, &debugfs_ops);
440 INIT_DELAYED_WORK(&hwstats->traffic_dw,
441 &nsim_dev_hwstats_traffic_work);
442 schedule_delayed_work(&hwstats->traffic_dw,
443 msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
446 err_remove_hwstats_recursive:
447 debugfs_remove_recursive(hwstats->ddir);
448 err_unregister_notifier:
449 unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
451 mutex_destroy(&hwstats->hwsdev_list_lock);
455 static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
456 enum netdev_offload_xstats_type type)
458 struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
459 struct list_head *hwsdev_list;
461 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
462 if (WARN_ON(!hwsdev_list))
465 mutex_lock(&hwstats->hwsdev_list_lock);
466 list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
467 list_del(&hwsdev->list);
468 nsim_dev_hwsdev_fini(hwsdev);
470 mutex_unlock(&hwstats->hwsdev_list_lock);
473 void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
475 struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
476 struct net *net = nsim_dev_net(nsim_dev);
478 cancel_delayed_work_sync(&hwstats->traffic_dw);
479 debugfs_remove_recursive(hwstats->ddir);
480 unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
481 nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
482 mutex_destroy(&hwstats->hwsdev_list_lock);