1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/mm/mmstat.h"
4
5#include "mos/filesystem/sysfs/sysfs.h"
6#include "mos/filesystem/sysfs/sysfs_autoinit.h"
7#include "mos/mm/paging/iterator.h"
8#include "mos/mm/physical/pmm.h"
9#include "mos/platform/platform.h"
10#include "mos/syslog/printk.h"
11#include "mos/tasks/process.h"
12#include "mos/tasks/task_types.h"
13
14#include <mos/mos_global.h>
15#include <mos/types.h>
16#include <mos_stdlib.h>
17
18typedef struct
19{
20 size_t npages;
21} vmap_global_mstat_t;
22
23static vmap_global_mstat_t stat[_MEM_MAX_TYPES] = { 0 };
24
25const char *mem_type_names[_MEM_MAX_TYPES] = {
26 [MEM_PAGETABLE] = "PageTable", //
27 [MEM_SLAB] = "Slab", //
28 [MEM_PAGECACHE] = "PageCache", //
29 [MEM_KERNEL] = "Kernel", //
30 [MEM_USER] = "User", //
31};
32
33void mmstat_inc(mmstat_type_t type, size_t size)
34{
35 MOS_ASSERT(type < _MEM_MAX_TYPES);
36 stat[type].npages += size;
37}
38
39void mmstat_dec(mmstat_type_t type, size_t size)
40{
41 MOS_ASSERT(type < _MEM_MAX_TYPES);
42 stat[type].npages -= size;
43}
44
45// ! sysfs support
46
47static bool mmstat_sysfs_stat(sysfs_file_t *f)
48{
49 char size_buf[32];
50 format_size(buf: size_buf, buf_size: sizeof(size_buf), size: pmm_total_frames * MOS_PAGE_SIZE);
51 sysfs_printf(file: f, fmt: "%-20s: %s, %zu pages\n", "Total", size_buf, pmm_total_frames);
52
53 format_size(buf: size_buf, buf_size: sizeof(size_buf), size: pmm_allocated_frames * MOS_PAGE_SIZE);
54 sysfs_printf(file: f, fmt: "%-20s: %s, %zu pages\n", "Allocated", size_buf, pmm_allocated_frames);
55
56 format_size(buf: size_buf, buf_size: sizeof(size_buf), size: pmm_reserved_frames * MOS_PAGE_SIZE);
57 sysfs_printf(file: f, fmt: "%-20s: %s, %zu pages\n", "Reserved", size_buf, pmm_reserved_frames);
58
59 for (u32 i = 0; i < _MEM_MAX_TYPES; i++)
60 {
61 format_size(buf: size_buf, buf_size: sizeof(size_buf), size: stat[i].npages * MOS_PAGE_SIZE);
62 sysfs_printf(file: f, fmt: "%-20s: %s, %zu pages\n", mem_type_names[i], size_buf, stat[i].npages);
63 }
64 return true;
65}
66
67static bool mmstat_sysfs_phyframe_stat_show(sysfs_file_t *f)
68{
69 const pfn_t pfn = (pfn_t) sysfs_file_get_data(file: f);
70 if (pfn >= pmm_total_frames)
71 {
72 pr_warn("mmstat: invalid pfn %llu", pfn);
73 return false;
74 }
75
76 const phyframe_t *frame = pfn_phyframe(pfn);
77 sysfs_printf(file: f, fmt: "pfn: %llu\n", pfn);
78 sysfs_printf(file: f, fmt: "type: %s\n", frame->state == PHYFRAME_FREE ? "free" : frame->state == PHYFRAME_ALLOCATED ? "allocated" : "reserved");
79 sysfs_printf(file: f, fmt: "order: %u\n", frame->order);
80 if (frame->state == PHYFRAME_ALLOCATED)
81 sysfs_printf(file: f, fmt: "refcnt: %zu\n", frame->allocated_refcount);
82
83 return true;
84}
85
86static size_t mmstat_sysfs_phyframe_stat_store(sysfs_file_t *f, const char *buf, size_t count, off_t offset)
87{
88 MOS_UNUSED(offset);
89
90 const pfn_t pfn = strntoll(str: buf, NULL, base: 10, n: count);
91 if (pfn >= pmm_total_frames)
92 {
93 pr_warn("mmstat: invalid pfn %llu", pfn);
94 return -EINVAL;
95 }
96
97 sysfs_file_set_data(file: f, data: (void *) pfn);
98 return count;
99}
100
101static bool mmstat_sysfs_pagetable_show(sysfs_file_t *f)
102{
103 const pid_t pid = (pid_t) (ptr_t) sysfs_file_get_data(file: f);
104 if (!pid)
105 {
106 pr_warn("mmstat: invalid pid %d", pid);
107 return false;
108 }
109
110 const process_t *proc = process_get(pid);
111 if (!proc)
112 {
113 pr_warn("mmstat: invalid pid %d", pid);
114 return false;
115 }
116
117 mm_context_t *mmctx = proc->mm;
118 spinlock_acquire(&mmctx->mm_lock);
119
120 pagetable_iter_t iter;
121 pagetable_iter_init(it: &iter, pgd: mmctx->pgd, vaddr: 0, MOS_USER_END_VADDR);
122
123 pagetable_iter_range_t *range;
124 while ((range = pagetable_iter_next(it: &iter)))
125 {
126 if (!range->present)
127 continue;
128
129 sysfs_printf(file: f, PTR_RANGE, range->vaddr, range->vaddr_end);
130 sysfs_printf(file: f, fmt: " %pvf " PFN_RANGE, (void *) &range->flags, range->pfn, range->pfn_end);
131 sysfs_printf(file: f, fmt: "\n");
132 }
133
134 spinlock_release(&mmctx->mm_lock);
135
136 return true;
137}
138
139static bool mmstat_sysfs_vmaps_show(sysfs_file_t *f)
140{
141 const pid_t pid = (pid_t) (ptr_t) sysfs_file_get_data(file: f);
142 if (!pid)
143 {
144 pr_warn("mmstat: invalid pid %d", pid);
145 return false;
146 }
147
148 process_t *proc = process_get(pid);
149 if (!proc)
150 {
151 pr_warn("mmstat: invalid pid %d", pid);
152 return false;
153 }
154
155 int i = 0;
156 spinlock_acquire(&proc->mm->mm_lock);
157 list_foreach(vmap_t, vmap, proc->mm->mmaps)
158 {
159 sysfs_printf(file: f, fmt: "%3d: ", i++);
160 sysfs_printf(file: f, fmt: "%pvm\n", (void *) vmap);
161 }
162 spinlock_release(&proc->mm->mm_lock);
163 return true;
164}
165
166static size_t mmstat_sysfs_store_pid(sysfs_file_t *f, const char *buf, size_t count, off_t offset)
167{
168 MOS_UNUSED(offset);
169
170 const pid_t pid = strntoll(str: buf, NULL, base: 10, n: count);
171 if (!pid)
172 {
173 pr_warn("mmstat: invalid pid %d", pid);
174 sysfs_file_set_data(file: f, data: (void *) 0);
175 return -EINVAL;
176 }
177
178 sysfs_file_set_data(file: f, data: (void *) (ptr_t) pid);
179 return count;
180}
181
182static sysfs_item_t mmstat_sysfs_items[] = {
183 SYSFS_RO_ITEM("stat", mmstat_sysfs_stat),
184 SYSFS_RW_ITEM("phyframe_stat", mmstat_sysfs_phyframe_stat_show, mmstat_sysfs_phyframe_stat_store),
185 SYSFS_RW_ITEM("pagetable", mmstat_sysfs_pagetable_show, mmstat_sysfs_store_pid),
186 SYSFS_RW_ITEM("vmaps", mmstat_sysfs_vmaps_show, mmstat_sysfs_store_pid),
187};
188
189SYSFS_AUTOREGISTER(mmstat, mmstat_sysfs_items);
190