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