hwstats.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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. bool notify = false;
  187. struct net *net;
  188. int err = 0;
  189. nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
  190. net = nsim_dev_net(nsim_dev);
  191. rtnl_lock();
  192. mutex_lock(&hwstats->hwsdev_list_lock);
  193. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  194. if (hwsdev)
  195. goto out_unlock_list;
  196. netdev = dev_get_by_index(net, ifindex);
  197. if (!netdev) {
  198. err = -ENODEV;
  199. goto out_unlock_list;
  200. }
  201. hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
  202. if (!hwsdev) {
  203. err = -ENOMEM;
  204. goto out_put_netdev;
  205. }
  206. hwsdev->netdev = netdev;
  207. list_add_tail(&hwsdev->list, hwsdev_list);
  208. mutex_unlock(&hwstats->hwsdev_list_lock);
  209. if (netdev_offload_xstats_enabled(netdev, type)) {
  210. nsim_dev_hwsdev_enable(hwsdev, NULL);
  211. notify = true;
  212. }
  213. if (notify)
  214. rtnl_offload_xstats_notify(netdev);
  215. rtnl_unlock();
  216. return err;
  217. out_put_netdev:
  218. dev_put(netdev);
  219. out_unlock_list:
  220. mutex_unlock(&hwstats->hwsdev_list_lock);
  221. rtnl_unlock();
  222. return err;
  223. }
  224. static int
  225. nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
  226. int ifindex,
  227. enum netdev_offload_xstats_type type,
  228. struct list_head *hwsdev_list)
  229. {
  230. struct nsim_dev_hwstats_netdev *hwsdev;
  231. int err = 0;
  232. rtnl_lock();
  233. mutex_lock(&hwstats->hwsdev_list_lock);
  234. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  235. if (hwsdev)
  236. list_del(&hwsdev->list);
  237. mutex_unlock(&hwstats->hwsdev_list_lock);
  238. if (!hwsdev) {
  239. err = -ENOENT;
  240. goto unlock_out;
  241. }
  242. if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
  243. netdev_offload_xstats_push_delta(hwsdev->netdev, type,
  244. &hwsdev->stats);
  245. rtnl_offload_xstats_notify(hwsdev->netdev);
  246. }
  247. nsim_dev_hwsdev_fini(hwsdev);
  248. unlock_out:
  249. rtnl_unlock();
  250. return err;
  251. }
  252. static int
  253. nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
  254. int ifindex,
  255. enum netdev_offload_xstats_type type,
  256. struct list_head *hwsdev_list)
  257. {
  258. struct nsim_dev_hwstats_netdev *hwsdev;
  259. int err = 0;
  260. mutex_lock(&hwstats->hwsdev_list_lock);
  261. hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
  262. if (!hwsdev) {
  263. err = -ENOENT;
  264. goto err_hwsdev_list_unlock;
  265. }
  266. hwsdev->fail_enable = true;
  267. err_hwsdev_list_unlock:
  268. mutex_unlock(&hwstats->hwsdev_list_lock);
  269. return err;
  270. }
  271. enum nsim_dev_hwstats_do {
  272. NSIM_DEV_HWSTATS_DO_DISABLE,
  273. NSIM_DEV_HWSTATS_DO_ENABLE,
  274. NSIM_DEV_HWSTATS_DO_FAIL,
  275. };
  276. struct nsim_dev_hwstats_fops {
  277. const struct file_operations fops;
  278. enum nsim_dev_hwstats_do action;
  279. enum netdev_offload_xstats_type type;
  280. };
  281. static ssize_t
  282. nsim_dev_hwstats_do_write(struct file *file,
  283. const char __user *data,
  284. size_t count, loff_t *ppos)
  285. {
  286. struct nsim_dev_hwstats *hwstats = file->private_data;
  287. struct nsim_dev_hwstats_fops *hwsfops;
  288. struct list_head *hwsdev_list;
  289. int ifindex;
  290. int err;
  291. hwsfops = container_of(debugfs_real_fops(file),
  292. struct nsim_dev_hwstats_fops, fops);
  293. err = kstrtoint_from_user(data, count, 0, &ifindex);
  294. if (err)
  295. return err;
  296. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
  297. if (WARN_ON(!hwsdev_list))
  298. return -EINVAL;
  299. switch (hwsfops->action) {
  300. case NSIM_DEV_HWSTATS_DO_DISABLE:
  301. err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
  302. hwsfops->type,
  303. hwsdev_list);
  304. break;
  305. case NSIM_DEV_HWSTATS_DO_ENABLE:
  306. err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
  307. hwsfops->type,
  308. hwsdev_list);
  309. break;
  310. case NSIM_DEV_HWSTATS_DO_FAIL:
  311. err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
  312. hwsfops->type,
  313. hwsdev_list);
  314. break;
  315. }
  316. if (err)
  317. return err;
  318. return count;
  319. }
  320. #define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \
  321. { \
  322. .fops = { \
  323. .open = simple_open, \
  324. .write = nsim_dev_hwstats_do_write, \
  325. .llseek = generic_file_llseek, \
  326. .owner = THIS_MODULE, \
  327. }, \
  328. .action = ACTION, \
  329. .type = TYPE, \
  330. }
  331. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
  332. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
  333. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  334. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
  335. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
  336. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  337. static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
  338. NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
  339. NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  340. #undef NSIM_DEV_HWSTATS_FOPS
  341. int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
  342. {
  343. struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
  344. struct net *net = nsim_dev_net(nsim_dev);
  345. int err;
  346. mutex_init(&hwstats->hwsdev_list_lock);
  347. INIT_LIST_HEAD(&hwstats->l3_list);
  348. hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
  349. err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  350. if (err)
  351. goto err_mutex_destroy;
  352. hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
  353. if (IS_ERR(hwstats->ddir)) {
  354. err = PTR_ERR(hwstats->ddir);
  355. goto err_unregister_notifier;
  356. }
  357. hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
  358. if (IS_ERR(hwstats->l3_ddir)) {
  359. err = PTR_ERR(hwstats->l3_ddir);
  360. goto err_remove_hwstats_recursive;
  361. }
  362. debugfs_create_file("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
  363. &nsim_dev_hwstats_l3_enable_fops.fops);
  364. debugfs_create_file("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
  365. &nsim_dev_hwstats_l3_disable_fops.fops);
  366. debugfs_create_file("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
  367. &nsim_dev_hwstats_l3_fail_fops.fops);
  368. INIT_DELAYED_WORK(&hwstats->traffic_dw,
  369. &nsim_dev_hwstats_traffic_work);
  370. schedule_delayed_work(&hwstats->traffic_dw,
  371. msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
  372. return 0;
  373. err_remove_hwstats_recursive:
  374. debugfs_remove_recursive(hwstats->ddir);
  375. err_unregister_notifier:
  376. unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  377. err_mutex_destroy:
  378. mutex_destroy(&hwstats->hwsdev_list_lock);
  379. return err;
  380. }
  381. static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
  382. enum netdev_offload_xstats_type type)
  383. {
  384. struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
  385. struct list_head *hwsdev_list;
  386. hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
  387. if (WARN_ON(!hwsdev_list))
  388. return;
  389. mutex_lock(&hwstats->hwsdev_list_lock);
  390. list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
  391. list_del(&hwsdev->list);
  392. nsim_dev_hwsdev_fini(hwsdev);
  393. }
  394. mutex_unlock(&hwstats->hwsdev_list_lock);
  395. }
  396. void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
  397. {
  398. struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
  399. struct net *net = nsim_dev_net(nsim_dev);
  400. cancel_delayed_work_sync(&hwstats->traffic_dw);
  401. debugfs_remove_recursive(hwstats->ddir);
  402. unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
  403. nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
  404. mutex_destroy(&hwstats->hwsdev_list_lock);
  405. }