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