mshv_debugfs.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2026, Microsoft Corporation.
  4. *
  5. * The /sys/kernel/debug/mshv directory contents.
  6. * Contains various statistics data, provided by the hypervisor.
  7. *
  8. * Authors: Microsoft Linux virtualization team
  9. */
  10. #include <linux/debugfs.h>
  11. #include <linux/stringify.h>
  12. #include <asm/mshyperv.h>
  13. #include <linux/slab.h>
  14. #include "mshv.h"
  15. #include "mshv_root.h"
  16. /* Ensure this file is not used elsewhere by accident */
  17. #define MSHV_DEBUGFS_C
  18. #include "mshv_debugfs_counters.c"
  19. #define U32_BUF_SZ 11
  20. #define U64_BUF_SZ 21
  21. /* Only support SELF and PARENT areas */
  22. #define NUM_STATS_AREAS 2
  23. static_assert(HV_STATS_AREA_SELF == 0 && HV_STATS_AREA_PARENT == 1,
  24. "SELF and PARENT areas must be usable as indices into an array of size NUM_STATS_AREAS");
  25. /* HV_HYPERVISOR_COUNTER */
  26. #define HV_HYPERVISOR_COUNTER_LOGICAL_PROCESSORS 1
  27. static struct dentry *mshv_debugfs;
  28. static struct dentry *mshv_debugfs_partition;
  29. static struct dentry *mshv_debugfs_lp;
  30. static struct dentry **parent_vp_stats;
  31. static struct dentry *parent_partition_stats;
  32. static u64 mshv_lps_count;
  33. static struct hv_stats_page **mshv_lps_stats;
  34. static int lp_stats_show(struct seq_file *m, void *v)
  35. {
  36. const struct hv_stats_page *stats = m->private;
  37. int idx;
  38. for (idx = 0; idx < ARRAY_SIZE(hv_lp_counters); idx++) {
  39. char *name = hv_lp_counters[idx];
  40. if (!name)
  41. continue;
  42. seq_printf(m, "%-32s: %llu\n", name, stats->data[idx]);
  43. }
  44. return 0;
  45. }
  46. DEFINE_SHOW_ATTRIBUTE(lp_stats);
  47. static void mshv_lp_stats_unmap(u32 lp_index)
  48. {
  49. union hv_stats_object_identity identity = {
  50. .lp.lp_index = lp_index,
  51. .lp.stats_area_type = HV_STATS_AREA_SELF,
  52. };
  53. int err;
  54. err = hv_unmap_stats_page(HV_STATS_OBJECT_LOGICAL_PROCESSOR,
  55. mshv_lps_stats[lp_index], &identity);
  56. if (err)
  57. pr_err("%s: failed to unmap logical processor %u stats, err: %d\n",
  58. __func__, lp_index, err);
  59. mshv_lps_stats[lp_index] = NULL;
  60. }
  61. static struct hv_stats_page * __init mshv_lp_stats_map(u32 lp_index)
  62. {
  63. union hv_stats_object_identity identity = {
  64. .lp.lp_index = lp_index,
  65. .lp.stats_area_type = HV_STATS_AREA_SELF,
  66. };
  67. struct hv_stats_page *stats;
  68. int err;
  69. err = hv_map_stats_page(HV_STATS_OBJECT_LOGICAL_PROCESSOR, &identity,
  70. &stats);
  71. if (err) {
  72. pr_err("%s: failed to map logical processor %u stats, err: %d\n",
  73. __func__, lp_index, err);
  74. return ERR_PTR(err);
  75. }
  76. mshv_lps_stats[lp_index] = stats;
  77. return stats;
  78. }
  79. static struct hv_stats_page * __init lp_debugfs_stats_create(u32 lp_index,
  80. struct dentry *parent)
  81. {
  82. struct dentry *dentry;
  83. struct hv_stats_page *stats;
  84. stats = mshv_lp_stats_map(lp_index);
  85. if (IS_ERR(stats))
  86. return stats;
  87. dentry = debugfs_create_file("stats", 0400, parent,
  88. stats, &lp_stats_fops);
  89. if (IS_ERR(dentry)) {
  90. mshv_lp_stats_unmap(lp_index);
  91. return ERR_CAST(dentry);
  92. }
  93. return stats;
  94. }
  95. static int __init lp_debugfs_create(u32 lp_index, struct dentry *parent)
  96. {
  97. struct dentry *idx;
  98. char lp_idx_str[U32_BUF_SZ];
  99. struct hv_stats_page *stats;
  100. int err;
  101. sprintf(lp_idx_str, "%u", lp_index);
  102. idx = debugfs_create_dir(lp_idx_str, parent);
  103. if (IS_ERR(idx))
  104. return PTR_ERR(idx);
  105. stats = lp_debugfs_stats_create(lp_index, idx);
  106. if (IS_ERR(stats)) {
  107. err = PTR_ERR(stats);
  108. goto remove_debugfs_lp_idx;
  109. }
  110. return 0;
  111. remove_debugfs_lp_idx:
  112. debugfs_remove_recursive(idx);
  113. return err;
  114. }
  115. static void mshv_debugfs_lp_remove(void)
  116. {
  117. int lp_index;
  118. debugfs_remove_recursive(mshv_debugfs_lp);
  119. for (lp_index = 0; lp_index < mshv_lps_count; lp_index++)
  120. mshv_lp_stats_unmap(lp_index);
  121. kfree(mshv_lps_stats);
  122. mshv_lps_stats = NULL;
  123. }
  124. static int __init mshv_debugfs_lp_create(struct dentry *parent)
  125. {
  126. struct dentry *lp_dir;
  127. int err, lp_index;
  128. mshv_lps_stats = kzalloc_objs(*mshv_lps_stats, mshv_lps_count,
  129. GFP_KERNEL_ACCOUNT);
  130. if (!mshv_lps_stats)
  131. return -ENOMEM;
  132. lp_dir = debugfs_create_dir("lp", parent);
  133. if (IS_ERR(lp_dir)) {
  134. err = PTR_ERR(lp_dir);
  135. goto free_lp_stats;
  136. }
  137. for (lp_index = 0; lp_index < mshv_lps_count; lp_index++) {
  138. err = lp_debugfs_create(lp_index, lp_dir);
  139. if (err)
  140. goto remove_debugfs_lps;
  141. }
  142. mshv_debugfs_lp = lp_dir;
  143. return 0;
  144. remove_debugfs_lps:
  145. for (lp_index -= 1; lp_index >= 0; lp_index--)
  146. mshv_lp_stats_unmap(lp_index);
  147. debugfs_remove_recursive(lp_dir);
  148. free_lp_stats:
  149. kfree(mshv_lps_stats);
  150. mshv_lps_stats = NULL;
  151. return err;
  152. }
  153. static int vp_stats_show(struct seq_file *m, void *v)
  154. {
  155. const struct hv_stats_page **pstats = m->private;
  156. u64 parent_val, self_val;
  157. int idx;
  158. /*
  159. * For VP and partition stats, there may be two stats areas mapped,
  160. * SELF and PARENT. These refer to the privilege level of the data in
  161. * each page. Some fields may be 0 in SELF and nonzero in PARENT, or
  162. * vice versa.
  163. *
  164. * Hence, prioritize printing from the PARENT page (more privileged
  165. * data), but use the value from the SELF page if the PARENT value is
  166. * 0.
  167. */
  168. for (idx = 0; idx < ARRAY_SIZE(hv_vp_counters); idx++) {
  169. char *name = hv_vp_counters[idx];
  170. if (!name)
  171. continue;
  172. parent_val = pstats[HV_STATS_AREA_PARENT]->data[idx];
  173. self_val = pstats[HV_STATS_AREA_SELF]->data[idx];
  174. seq_printf(m, "%-43s: %llu\n", name,
  175. parent_val ? parent_val : self_val);
  176. }
  177. return 0;
  178. }
  179. DEFINE_SHOW_ATTRIBUTE(vp_stats);
  180. static void vp_debugfs_remove(struct dentry *vp_stats)
  181. {
  182. debugfs_remove_recursive(vp_stats->d_parent);
  183. }
  184. static int vp_debugfs_create(u64 partition_id, u32 vp_index,
  185. struct hv_stats_page **pstats,
  186. struct dentry **vp_stats_ptr,
  187. struct dentry *parent)
  188. {
  189. struct dentry *vp_idx_dir, *d;
  190. char vp_idx_str[U32_BUF_SZ];
  191. int err;
  192. sprintf(vp_idx_str, "%u", vp_index);
  193. vp_idx_dir = debugfs_create_dir(vp_idx_str, parent);
  194. if (IS_ERR(vp_idx_dir))
  195. return PTR_ERR(vp_idx_dir);
  196. d = debugfs_create_file("stats", 0400, vp_idx_dir,
  197. pstats, &vp_stats_fops);
  198. if (IS_ERR(d)) {
  199. err = PTR_ERR(d);
  200. goto remove_debugfs_vp_idx;
  201. }
  202. *vp_stats_ptr = d;
  203. return 0;
  204. remove_debugfs_vp_idx:
  205. debugfs_remove_recursive(vp_idx_dir);
  206. return err;
  207. }
  208. static int partition_stats_show(struct seq_file *m, void *v)
  209. {
  210. const struct hv_stats_page **pstats = m->private;
  211. u64 parent_val, self_val;
  212. int idx;
  213. for (idx = 0; idx < ARRAY_SIZE(hv_partition_counters); idx++) {
  214. char *name = hv_partition_counters[idx];
  215. if (!name)
  216. continue;
  217. parent_val = pstats[HV_STATS_AREA_PARENT]->data[idx];
  218. self_val = pstats[HV_STATS_AREA_SELF]->data[idx];
  219. seq_printf(m, "%-37s: %llu\n", name,
  220. parent_val ? parent_val : self_val);
  221. }
  222. return 0;
  223. }
  224. DEFINE_SHOW_ATTRIBUTE(partition_stats);
  225. static void mshv_partition_stats_unmap(u64 partition_id,
  226. struct hv_stats_page *stats_page,
  227. enum hv_stats_area_type stats_area_type)
  228. {
  229. union hv_stats_object_identity identity = {
  230. .partition.partition_id = partition_id,
  231. .partition.stats_area_type = stats_area_type,
  232. };
  233. int err;
  234. err = hv_unmap_stats_page(HV_STATS_OBJECT_PARTITION, stats_page,
  235. &identity);
  236. if (err)
  237. pr_err("%s: failed to unmap partition %lld %s stats, err: %d\n",
  238. __func__, partition_id,
  239. (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
  240. err);
  241. }
  242. static struct hv_stats_page *mshv_partition_stats_map(u64 partition_id,
  243. enum hv_stats_area_type stats_area_type)
  244. {
  245. union hv_stats_object_identity identity = {
  246. .partition.partition_id = partition_id,
  247. .partition.stats_area_type = stats_area_type,
  248. };
  249. struct hv_stats_page *stats;
  250. int err;
  251. err = hv_map_stats_page(HV_STATS_OBJECT_PARTITION, &identity, &stats);
  252. if (err) {
  253. pr_err("%s: failed to map partition %lld %s stats, err: %d\n",
  254. __func__, partition_id,
  255. (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
  256. err);
  257. return ERR_PTR(err);
  258. }
  259. return stats;
  260. }
  261. static int mshv_debugfs_partition_stats_create(u64 partition_id,
  262. struct dentry **partition_stats_ptr,
  263. struct dentry *parent)
  264. {
  265. struct dentry *dentry;
  266. struct hv_stats_page **pstats;
  267. int err;
  268. pstats = kzalloc_objs(struct hv_stats_page *, NUM_STATS_AREAS,
  269. GFP_KERNEL_ACCOUNT);
  270. if (!pstats)
  271. return -ENOMEM;
  272. pstats[HV_STATS_AREA_SELF] = mshv_partition_stats_map(partition_id,
  273. HV_STATS_AREA_SELF);
  274. if (IS_ERR(pstats[HV_STATS_AREA_SELF])) {
  275. err = PTR_ERR(pstats[HV_STATS_AREA_SELF]);
  276. goto cleanup;
  277. }
  278. /*
  279. * L1VH partition cannot access its partition stats in parent area.
  280. */
  281. if (is_l1vh_parent(partition_id)) {
  282. pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
  283. } else {
  284. pstats[HV_STATS_AREA_PARENT] = mshv_partition_stats_map(partition_id,
  285. HV_STATS_AREA_PARENT);
  286. if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) {
  287. err = PTR_ERR(pstats[HV_STATS_AREA_PARENT]);
  288. goto unmap_self;
  289. }
  290. if (!pstats[HV_STATS_AREA_PARENT])
  291. pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
  292. }
  293. dentry = debugfs_create_file("stats", 0400, parent,
  294. pstats, &partition_stats_fops);
  295. if (IS_ERR(dentry)) {
  296. err = PTR_ERR(dentry);
  297. goto unmap_partition_stats;
  298. }
  299. *partition_stats_ptr = dentry;
  300. return 0;
  301. unmap_partition_stats:
  302. if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF])
  303. mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_PARENT],
  304. HV_STATS_AREA_PARENT);
  305. unmap_self:
  306. mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_SELF],
  307. HV_STATS_AREA_SELF);
  308. cleanup:
  309. kfree(pstats);
  310. return err;
  311. }
  312. static void partition_debugfs_remove(u64 partition_id, struct dentry *dentry)
  313. {
  314. struct hv_stats_page **pstats = NULL;
  315. pstats = dentry->d_inode->i_private;
  316. debugfs_remove_recursive(dentry->d_parent);
  317. if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF]) {
  318. mshv_partition_stats_unmap(partition_id,
  319. pstats[HV_STATS_AREA_PARENT],
  320. HV_STATS_AREA_PARENT);
  321. }
  322. mshv_partition_stats_unmap(partition_id,
  323. pstats[HV_STATS_AREA_SELF],
  324. HV_STATS_AREA_SELF);
  325. kfree(pstats);
  326. }
  327. static int partition_debugfs_create(u64 partition_id,
  328. struct dentry **vp_dir_ptr,
  329. struct dentry **partition_stats_ptr,
  330. struct dentry *parent)
  331. {
  332. char part_id_str[U64_BUF_SZ];
  333. struct dentry *part_id_dir, *vp_dir;
  334. int err;
  335. if (is_l1vh_parent(partition_id))
  336. sprintf(part_id_str, "self");
  337. else
  338. sprintf(part_id_str, "%llu", partition_id);
  339. part_id_dir = debugfs_create_dir(part_id_str, parent);
  340. if (IS_ERR(part_id_dir))
  341. return PTR_ERR(part_id_dir);
  342. vp_dir = debugfs_create_dir("vp", part_id_dir);
  343. if (IS_ERR(vp_dir)) {
  344. err = PTR_ERR(vp_dir);
  345. goto remove_debugfs_partition_id;
  346. }
  347. err = mshv_debugfs_partition_stats_create(partition_id,
  348. partition_stats_ptr,
  349. part_id_dir);
  350. if (err)
  351. goto remove_debugfs_partition_id;
  352. *vp_dir_ptr = vp_dir;
  353. return 0;
  354. remove_debugfs_partition_id:
  355. debugfs_remove_recursive(part_id_dir);
  356. return err;
  357. }
  358. static void parent_vp_debugfs_remove(u32 vp_index,
  359. struct dentry *vp_stats_ptr)
  360. {
  361. struct hv_stats_page **pstats;
  362. pstats = vp_stats_ptr->d_inode->i_private;
  363. vp_debugfs_remove(vp_stats_ptr);
  364. mshv_vp_stats_unmap(hv_current_partition_id, vp_index, pstats);
  365. kfree(pstats);
  366. }
  367. static void mshv_debugfs_parent_partition_remove(void)
  368. {
  369. int idx;
  370. for_each_online_cpu(idx)
  371. parent_vp_debugfs_remove(hv_vp_index[idx],
  372. parent_vp_stats[idx]);
  373. partition_debugfs_remove(hv_current_partition_id,
  374. parent_partition_stats);
  375. kfree(parent_vp_stats);
  376. parent_vp_stats = NULL;
  377. parent_partition_stats = NULL;
  378. }
  379. static int __init parent_vp_debugfs_create(u32 vp_index,
  380. struct dentry **vp_stats_ptr,
  381. struct dentry *parent)
  382. {
  383. struct hv_stats_page **pstats;
  384. int err;
  385. pstats = kzalloc_objs(struct hv_stats_page *, NUM_STATS_AREAS,
  386. GFP_KERNEL_ACCOUNT);
  387. if (!pstats)
  388. return -ENOMEM;
  389. err = mshv_vp_stats_map(hv_current_partition_id, vp_index, pstats);
  390. if (err)
  391. goto cleanup;
  392. err = vp_debugfs_create(hv_current_partition_id, vp_index, pstats,
  393. vp_stats_ptr, parent);
  394. if (err)
  395. goto unmap_vp_stats;
  396. return 0;
  397. unmap_vp_stats:
  398. mshv_vp_stats_unmap(hv_current_partition_id, vp_index, pstats);
  399. cleanup:
  400. kfree(pstats);
  401. return err;
  402. }
  403. static int __init mshv_debugfs_parent_partition_create(void)
  404. {
  405. struct dentry *vp_dir;
  406. int err, idx, i;
  407. mshv_debugfs_partition = debugfs_create_dir("partition",
  408. mshv_debugfs);
  409. if (IS_ERR(mshv_debugfs_partition))
  410. return PTR_ERR(mshv_debugfs_partition);
  411. err = partition_debugfs_create(hv_current_partition_id,
  412. &vp_dir,
  413. &parent_partition_stats,
  414. mshv_debugfs_partition);
  415. if (err)
  416. goto remove_debugfs_partition;
  417. parent_vp_stats = kzalloc_objs(*parent_vp_stats, nr_cpu_ids);
  418. if (!parent_vp_stats) {
  419. err = -ENOMEM;
  420. goto remove_debugfs_partition;
  421. }
  422. for_each_online_cpu(idx) {
  423. err = parent_vp_debugfs_create(hv_vp_index[idx],
  424. &parent_vp_stats[idx],
  425. vp_dir);
  426. if (err)
  427. goto remove_debugfs_partition_vp;
  428. }
  429. return 0;
  430. remove_debugfs_partition_vp:
  431. for_each_online_cpu(i) {
  432. if (i >= idx)
  433. break;
  434. parent_vp_debugfs_remove(i, parent_vp_stats[i]);
  435. }
  436. partition_debugfs_remove(hv_current_partition_id,
  437. parent_partition_stats);
  438. kfree(parent_vp_stats);
  439. parent_vp_stats = NULL;
  440. parent_partition_stats = NULL;
  441. remove_debugfs_partition:
  442. debugfs_remove_recursive(mshv_debugfs_partition);
  443. mshv_debugfs_partition = NULL;
  444. return err;
  445. }
  446. static int hv_stats_show(struct seq_file *m, void *v)
  447. {
  448. const struct hv_stats_page *stats = m->private;
  449. int idx;
  450. for (idx = 0; idx < ARRAY_SIZE(hv_hypervisor_counters); idx++) {
  451. char *name = hv_hypervisor_counters[idx];
  452. if (!name)
  453. continue;
  454. seq_printf(m, "%-27s: %llu\n", name, stats->data[idx]);
  455. }
  456. return 0;
  457. }
  458. DEFINE_SHOW_ATTRIBUTE(hv_stats);
  459. static void mshv_hv_stats_unmap(void)
  460. {
  461. union hv_stats_object_identity identity = {
  462. .hv.stats_area_type = HV_STATS_AREA_SELF,
  463. };
  464. int err;
  465. err = hv_unmap_stats_page(HV_STATS_OBJECT_HYPERVISOR, NULL, &identity);
  466. if (err)
  467. pr_err("%s: failed to unmap hypervisor stats: %d\n",
  468. __func__, err);
  469. }
  470. static void * __init mshv_hv_stats_map(void)
  471. {
  472. union hv_stats_object_identity identity = {
  473. .hv.stats_area_type = HV_STATS_AREA_SELF,
  474. };
  475. struct hv_stats_page *stats;
  476. int err;
  477. err = hv_map_stats_page(HV_STATS_OBJECT_HYPERVISOR, &identity, &stats);
  478. if (err) {
  479. pr_err("%s: failed to map hypervisor stats: %d\n",
  480. __func__, err);
  481. return ERR_PTR(err);
  482. }
  483. return stats;
  484. }
  485. static int __init mshv_debugfs_hv_stats_create(struct dentry *parent)
  486. {
  487. struct dentry *dentry;
  488. u64 *stats;
  489. int err;
  490. stats = mshv_hv_stats_map();
  491. if (IS_ERR(stats))
  492. return PTR_ERR(stats);
  493. dentry = debugfs_create_file("stats", 0400, parent,
  494. stats, &hv_stats_fops);
  495. if (IS_ERR(dentry)) {
  496. err = PTR_ERR(dentry);
  497. pr_err("%s: failed to create hypervisor stats dentry: %d\n",
  498. __func__, err);
  499. goto unmap_hv_stats;
  500. }
  501. mshv_lps_count = stats[HV_HYPERVISOR_COUNTER_LOGICAL_PROCESSORS];
  502. return 0;
  503. unmap_hv_stats:
  504. mshv_hv_stats_unmap();
  505. return err;
  506. }
  507. int mshv_debugfs_vp_create(struct mshv_vp *vp)
  508. {
  509. struct mshv_partition *p = vp->vp_partition;
  510. if (!mshv_debugfs)
  511. return 0;
  512. return vp_debugfs_create(p->pt_id, vp->vp_index,
  513. vp->vp_stats_pages,
  514. &vp->vp_stats_dentry,
  515. p->pt_vp_dentry);
  516. }
  517. void mshv_debugfs_vp_remove(struct mshv_vp *vp)
  518. {
  519. if (!mshv_debugfs)
  520. return;
  521. vp_debugfs_remove(vp->vp_stats_dentry);
  522. }
  523. int mshv_debugfs_partition_create(struct mshv_partition *partition)
  524. {
  525. int err;
  526. if (!mshv_debugfs)
  527. return 0;
  528. err = partition_debugfs_create(partition->pt_id,
  529. &partition->pt_vp_dentry,
  530. &partition->pt_stats_dentry,
  531. mshv_debugfs_partition);
  532. if (err)
  533. return err;
  534. return 0;
  535. }
  536. void mshv_debugfs_partition_remove(struct mshv_partition *partition)
  537. {
  538. if (!mshv_debugfs)
  539. return;
  540. partition_debugfs_remove(partition->pt_id,
  541. partition->pt_stats_dentry);
  542. }
  543. int __init mshv_debugfs_init(void)
  544. {
  545. int err;
  546. mshv_debugfs = debugfs_create_dir("mshv", NULL);
  547. if (IS_ERR(mshv_debugfs)) {
  548. pr_err("%s: failed to create debugfs directory\n", __func__);
  549. return PTR_ERR(mshv_debugfs);
  550. }
  551. if (hv_root_partition()) {
  552. err = mshv_debugfs_hv_stats_create(mshv_debugfs);
  553. if (err)
  554. goto remove_mshv_dir;
  555. err = mshv_debugfs_lp_create(mshv_debugfs);
  556. if (err)
  557. goto unmap_hv_stats;
  558. }
  559. err = mshv_debugfs_parent_partition_create();
  560. if (err)
  561. goto unmap_lp_stats;
  562. return 0;
  563. unmap_lp_stats:
  564. if (hv_root_partition()) {
  565. mshv_debugfs_lp_remove();
  566. mshv_debugfs_lp = NULL;
  567. }
  568. unmap_hv_stats:
  569. if (hv_root_partition())
  570. mshv_hv_stats_unmap();
  571. remove_mshv_dir:
  572. debugfs_remove_recursive(mshv_debugfs);
  573. mshv_debugfs = NULL;
  574. return err;
  575. }
  576. void mshv_debugfs_exit(void)
  577. {
  578. mshv_debugfs_parent_partition_remove();
  579. if (hv_root_partition()) {
  580. mshv_debugfs_lp_remove();
  581. mshv_debugfs_lp = NULL;
  582. mshv_hv_stats_unmap();
  583. }
  584. debugfs_remove_recursive(mshv_debugfs);
  585. mshv_debugfs = NULL;
  586. mshv_debugfs_partition = NULL;
  587. }