hwstats.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/debugfs.h>
  3. #include "netdevsim.h"
  4. #define NSIM_DEV_HWSTATS_TRAFFIC_MS 100
  5. static struct list_head *
  6. nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
  7. enum netdev_offload_xstats_type type)
  8. {
  9. switch (type) {
  10. case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
  11. return &hwstats->l3_list;
  12. }
  13. WARN_ON_ONCE(1);
  14. return NULL;
  15. }
  16. static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
  17. enum netdev_offload_xstats_type type)
  18. {
  19. struct nsim_dev_hwstats_netdev *hwsdev;
  20. struct list_head *hwsdev_list;
  21. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
  22. if (WARN_ON(!hwsdev_list))
  23. return;
  24. list_for_each_entry(hwsdev, hwsdev_list, list) {
  25. if (hwsdev->enabled) {
  26. hwsdev->stats.rx_packets += 1;
  27. hwsdev->stats.tx_packets += 2;
  28. hwsdev->stats.rx_bytes += 100;
  29. hwsdev->stats.tx_bytes += 300;
  30. }
  31. }
  32. }
  33. static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
  34. {
  35. struct nsim_dev_hwstats *hwstats;
  36. hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work);
  37. mutex_lock(&hwstats->hwsdev_list_lock);
  38. nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  39. mutex_unlock(&hwstats->hwsdev_list_lock);
  40. schedule_delayed_work(&hwstats->traffic_dw,
  41. msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
  42. }
  43. static struct nsim_dev_hwstats_netdev *
  44. nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
  45. int ifindex)
  46. {
  47. struct nsim_dev_hwstats_netdev *hwsdev;
  48. list_for_each_entry(hwsdev, hwsdev_list, list) {
  49. if (hwsdev->netdev->ifindex == ifindex)
  50. return hwsdev;
  51. }
  52. return NULL;
  53. }
  54. static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
  55. struct netlink_ext_ack *extack)
  56. {
  57. if (hwsdev->fail_enable) {
  58. hwsdev->fail_enable = false;
  59. NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
  60. return -ECANCELED;
  61. }
  62. hwsdev->enabled = true;
  63. return 0;
  64. }
  65. static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
  66. {
  67. hwsdev->enabled = false;
  68. memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
  69. }
  70. static int
  71. nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
  72. struct netdev_notifier_offload_xstats_info *info)
  73. {
  74. netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
  75. memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
  76. return 0;
  77. }
  78. static void
  79. nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
  80. struct netdev_notifier_offload_xstats_info *info)
  81. {
  82. if (hwsdev->enabled)
  83. netdev_offload_xstats_report_used(info->report_used);
  84. }
  85. static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
  86. struct net_device *dev,
  87. unsigned long event, void *ptr)
  88. {
  89. struct netdev_notifier_offload_xstats_info *info;
  90. struct nsim_dev_hwstats_netdev *hwsdev;
  91. struct list_head *hwsdev_list;
  92. int err = 0;
  93. info = ptr;
  94. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
  95. if (!hwsdev_list)
  96. return 0;
  97. mutex_lock(&hwstats->hwsdev_list_lock);
  98. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
  99. if (!hwsdev)
  100. goto out;
  101. switch (event) {
  102. case NETDEV_OFFLOAD_XSTATS_ENABLE:
  103. err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
  104. break;
  105. case NETDEV_OFFLOAD_XSTATS_DISABLE:
  106. nsim_dev_hwsdev_disable(hwsdev);
  107. break;
  108. case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
  109. nsim_dev_hwsdev_report_used(hwsdev, info);
  110. break;
  111. case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
  112. err = nsim_dev_hwsdev_report_delta(hwsdev, info);
  113. break;
  114. }
  115. out:
  116. mutex_unlock(&hwstats->hwsdev_list_lock);
  117. return err;
  118. }
  119. static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
  120. {
  121. dev_put(hwsdev->netdev);
  122. kfree(hwsdev);
  123. }
  124. static void
  125. __nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
  126. struct net_device *dev,
  127. enum netdev_offload_xstats_type type)
  128. {
  129. struct nsim_dev_hwstats_netdev *hwsdev;
  130. struct list_head *hwsdev_list;
  131. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
  132. if (WARN_ON(!hwsdev_list))
  133. return;
  134. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
  135. if (!hwsdev)
  136. return;
  137. list_del(&hwsdev->list);
  138. nsim_dev_hwsdev_fini(hwsdev);
  139. }
  140. static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
  141. struct net_device *dev)
  142. {
  143. mutex_lock(&hwstats->hwsdev_list_lock);
  144. __nsim_dev_hwstats_event_unregister(hwstats, dev,
  145. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  146. mutex_unlock(&hwstats->hwsdev_list_lock);
  147. }
  148. static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
  149. struct net_device *dev,
  150. unsigned long event, void *ptr)
  151. {
  152. switch (event) {
  153. case NETDEV_OFFLOAD_XSTATS_ENABLE:
  154. case NETDEV_OFFLOAD_XSTATS_DISABLE:
  155. case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
  156. case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
  157. return nsim_dev_hwstats_event_off_xstats(hwstats, dev,
  158. event, ptr);
  159. case NETDEV_UNREGISTER:
  160. nsim_dev_hwstats_event_unregister(hwstats, dev);
  161. break;
  162. }
  163. return 0;
  164. }
  165. static int nsim_dev_netdevice_event(struct notifier_block *nb,
  166. unsigned long event, void *ptr)
  167. {
  168. struct net_device *dev = netdev_notifier_info_to_dev(ptr);
  169. struct nsim_dev_hwstats *hwstats;
  170. int err = 0;
  171. hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
  172. err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
  173. if (err)
  174. return notifier_from_errno(err);
  175. return NOTIFY_OK;
  176. }
  177. static int
  178. nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
  179. int ifindex,
  180. enum netdev_offload_xstats_type type,
  181. struct list_head *hwsdev_list)
  182. {
  183. struct nsim_dev_hwstats_netdev *hwsdev;
  184. struct nsim_dev *nsim_dev;
  185. struct net_device *netdev;
  186. struct net *net;
  187. int err = 0;
  188. nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
  189. net = nsim_dev_net(nsim_dev);
  190. rtnl_lock();
  191. mutex_lock(&hwstats->hwsdev_list_lock);
  192. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  193. if (hwsdev)
  194. goto out_unlock_list;
  195. netdev = dev_get_by_index(net, ifindex);
  196. if (!netdev) {
  197. err = -ENODEV;
  198. goto out_unlock_list;
  199. }
  200. hwsdev = kzalloc_obj(*hwsdev);
  201. if (!hwsdev) {
  202. err = -ENOMEM;
  203. goto out_put_netdev;
  204. }
  205. hwsdev->netdev = netdev;
  206. list_add_tail(&hwsdev->list, hwsdev_list);
  207. mutex_unlock(&hwstats->hwsdev_list_lock);
  208. if (netdev_offload_xstats_enabled(netdev, type)) {
  209. nsim_dev_hwsdev_enable(hwsdev, NULL);
  210. rtnl_offload_xstats_notify(netdev);
  211. }
  212. rtnl_unlock();
  213. return err;
  214. out_put_netdev:
  215. dev_put(netdev);
  216. out_unlock_list:
  217. mutex_unlock(&hwstats->hwsdev_list_lock);
  218. rtnl_unlock();
  219. return err;
  220. }
  221. static int
  222. nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
  223. int ifindex,
  224. enum netdev_offload_xstats_type type,
  225. struct list_head *hwsdev_list)
  226. {
  227. struct nsim_dev_hwstats_netdev *hwsdev;
  228. int err = 0;
  229. rtnl_lock();
  230. mutex_lock(&hwstats->hwsdev_list_lock);
  231. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  232. if (hwsdev)
  233. list_del(&hwsdev->list);
  234. mutex_unlock(&hwstats->hwsdev_list_lock);
  235. if (!hwsdev) {
  236. err = -ENOENT;
  237. goto unlock_out;
  238. }
  239. if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
  240. netdev_offload_xstats_push_delta(hwsdev->netdev, type,
  241. &hwsdev->stats);
  242. rtnl_offload_xstats_notify(hwsdev->netdev);
  243. }
  244. nsim_dev_hwsdev_fini(hwsdev);
  245. unlock_out:
  246. rtnl_unlock();
  247. return err;
  248. }
  249. static int
  250. nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
  251. int ifindex,
  252. enum netdev_offload_xstats_type type,
  253. struct list_head *hwsdev_list)
  254. {
  255. struct nsim_dev_hwstats_netdev *hwsdev;
  256. int err = 0;
  257. mutex_lock(&hwstats->hwsdev_list_lock);
  258. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  259. if (!hwsdev) {
  260. err = -ENOENT;
  261. goto err_hwsdev_list_unlock;
  262. }
  263. hwsdev->fail_enable = true;
  264. err_hwsdev_list_unlock:
  265. mutex_unlock(&hwstats->hwsdev_list_lock);
  266. return err;
  267. }
  268. enum nsim_dev_hwstats_do {
  269. NSIM_DEV_HWSTATS_DO_DISABLE,
  270. NSIM_DEV_HWSTATS_DO_ENABLE,
  271. NSIM_DEV_HWSTATS_DO_FAIL,
  272. };
  273. struct nsim_dev_hwstats_fops {
  274. enum nsim_dev_hwstats_do action;
  275. enum netdev_offload_xstats_type type;
  276. };
  277. static ssize_t
  278. nsim_dev_hwstats_do_write(struct file *file,
  279. const char __user *data,
  280. size_t count, loff_t *ppos)
  281. {
  282. struct nsim_dev_hwstats *hwstats = file->private_data;
  283. const struct nsim_dev_hwstats_fops *hwsfops;
  284. struct list_head *hwsdev_list;
  285. int ifindex;
  286. int err;
  287. hwsfops = debugfs_get_aux(file);
  288. err = kstrtoint_from_user(data, count, 0, &ifindex);
  289. if (err)
  290. return err;
  291. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
  292. if (WARN_ON(!hwsdev_list))
  293. return -EINVAL;
  294. switch (hwsfops->action) {
  295. case NSIM_DEV_HWSTATS_DO_DISABLE:
  296. err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
  297. hwsfops->type,
  298. hwsdev_list);
  299. break;
  300. case NSIM_DEV_HWSTATS_DO_ENABLE:
  301. err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
  302. hwsfops->type,
  303. hwsdev_list);
  304. break;
  305. case NSIM_DEV_HWSTATS_DO_FAIL:
  306. err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
  307. hwsfops->type,
  308. hwsdev_list);
  309. break;
  310. }
  311. if (err)
  312. return err;
  313. return count;
  314. }
  315. static struct debugfs_short_fops debugfs_ops = {
  316. .write = nsim_dev_hwstats_do_write,
  317. .llseek = generic_file_llseek,
  318. };
  319. #define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \
  320. { \
  321. .action = ACTION, \
  322. .type = TYPE, \
  323. }
  324. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
  325. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
  326. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  327. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
  328. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
  329. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  330. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
  331. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
  332. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  333. #undef NSIM_DEV_HWSTATS_FOPS
  334. int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
  335. {
  336. struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
  337. struct net *net = nsim_dev_net(nsim_dev);
  338. int err;
  339. mutex_init(&hwstats->hwsdev_list_lock);
  340. INIT_LIST_HEAD(&hwstats->l3_list);
  341. hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
  342. err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  343. if (err)
  344. goto err_mutex_destroy;
  345. hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
  346. if (IS_ERR(hwstats->ddir)) {
  347. err = PTR_ERR(hwstats->ddir);
  348. goto err_unregister_notifier;
  349. }
  350. hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
  351. if (IS_ERR(hwstats->l3_ddir)) {
  352. err = PTR_ERR(hwstats->l3_ddir);
  353. goto err_remove_hwstats_recursive;
  354. }
  355. debugfs_create_file_aux("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
  356. &nsim_dev_hwstats_l3_enable_fops, &debugfs_ops);
  357. debugfs_create_file_aux("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
  358. &nsim_dev_hwstats_l3_disable_fops, &debugfs_ops);
  359. debugfs_create_file_aux("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
  360. &nsim_dev_hwstats_l3_fail_fops, &debugfs_ops);
  361. INIT_DELAYED_WORK(&hwstats->traffic_dw,
  362. &nsim_dev_hwstats_traffic_work);
  363. schedule_delayed_work(&hwstats->traffic_dw,
  364. msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
  365. return 0;
  366. err_remove_hwstats_recursive:
  367. debugfs_remove_recursive(hwstats->ddir);
  368. err_unregister_notifier:
  369. unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  370. err_mutex_destroy:
  371. mutex_destroy(&hwstats->hwsdev_list_lock);
  372. return err;
  373. }
  374. static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
  375. enum netdev_offload_xstats_type type)
  376. {
  377. struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
  378. struct list_head *hwsdev_list;
  379. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
  380. if (WARN_ON(!hwsdev_list))
  381. return;
  382. mutex_lock(&hwstats->hwsdev_list_lock);
  383. list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
  384. list_del(&hwsdev->list);
  385. nsim_dev_hwsdev_fini(hwsdev);
  386. }
  387. mutex_unlock(&hwstats->hwsdev_list_lock);
  388. }
  389. void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
  390. {
  391. struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
  392. struct net *net = nsim_dev_net(nsim_dev);
  393. cancel_delayed_work_sync(&hwstats->traffic_dw);
  394. debugfs_remove_recursive(hwstats->ddir);
  395. unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  396. nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  397. mutex_destroy(&hwstats->hwsdev_list_lock);
  398. }