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