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
lp_stats_show(struct seq_file * m,void * v)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
mshv_lp_stats_unmap(u32 lp_index)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
mshv_lp_stats_map(u32 lp_index)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
lp_debugfs_stats_create(u32 lp_index,struct dentry * parent)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
lp_debugfs_create(u32 lp_index,struct dentry * parent)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
mshv_debugfs_lp_remove(void)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
mshv_debugfs_lp_create(struct dentry * parent)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
vp_stats_show(struct seq_file * m,void * v)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
vp_debugfs_remove(struct dentry * vp_stats)225 static void vp_debugfs_remove(struct dentry *vp_stats)
226 {
227 debugfs_remove_recursive(vp_stats->d_parent);
228 }
229
vp_debugfs_create(u64 partition_id,u32 vp_index,struct hv_stats_page ** pstats,struct dentry ** vp_stats_ptr,struct dentry * parent)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
partition_stats_show(struct seq_file * m,void * v)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
mshv_partition_stats_unmap(u64 partition_id,struct hv_stats_page * stats_page,enum hv_stats_area_type stats_area_type)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
mshv_partition_stats_map(u64 partition_id,enum hv_stats_area_type stats_area_type)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
mshv_debugfs_partition_stats_create(u64 partition_id,struct dentry ** partition_stats_ptr,struct dentry * parent)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
partition_debugfs_remove(u64 partition_id,struct dentry * dentry)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
partition_debugfs_create(u64 partition_id,struct dentry ** vp_dir_ptr,struct dentry ** partition_stats_ptr,struct dentry * parent)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
parent_vp_debugfs_remove(u32 vp_index,struct dentry * vp_stats_ptr)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
mshv_debugfs_parent_partition_remove(void)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
parent_vp_debugfs_create(u32 vp_index,struct dentry ** vp_stats_ptr,struct dentry * parent)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
mshv_debugfs_parent_partition_create(void)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
hv_stats_show(struct seq_file * m,void * v)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
mshv_hv_stats_unmap(void)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
mshv_hv_stats_map(void)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
mshv_debugfs_hv_stats_create(struct dentry * parent)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
mshv_debugfs_vp_create(struct mshv_vp * vp)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
mshv_debugfs_vp_remove(struct mshv_vp * vp)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
mshv_debugfs_partition_create(struct mshv_partition * partition)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
mshv_debugfs_partition_remove(struct mshv_partition * partition)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
mshv_debugfs_init(void)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
mshv_debugfs_exit(void)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