1use std::sync::{Arc, atomic::AtomicU64, atomic::Ordering::Relaxed};
3
4use crate::{BytePageSize, size::PAGE_SIZES};
5
6const CLASSES: usize = PAGE_SIZES.len();
7
8#[derive(Debug, Default)]
25pub struct PageCacheMetrics {
26 classes: [Class; CLASSES],
27}
28
29#[derive(Debug, Default)]
30struct Class {
31 hits: AtomicU64,
32 misses: AtomicU64,
33 cached: AtomicU64,
34 freed: AtomicU64,
35 added: AtomicU64,
38 removed: AtomicU64,
39}
40
41#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43pub struct PageCacheSnapshot {
44 sizes: [PageCacheStats; CLASSES],
45}
46
47#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
49#[non_exhaustive]
50pub struct PageCacheStats {
51 pub size: BytePageSize,
53 pub hits: u64,
55 pub misses: u64,
57 pub cached: u64,
59 pub freed: u64,
61 pub pages: usize,
63 pub bytes: usize,
65}
66
67impl PageCacheMetrics {
68 pub fn new() -> Arc<Self> {
70 Arc::default()
71 }
72
73 pub fn current() -> Arc<Self> {
78 crate::stvec::cache_metrics().unwrap_or_default()
79 }
80
81 pub fn set_current(metrics: Arc<Self>) {
87 crate::stvec::set_cache_metrics(metrics);
88 }
89
90 pub fn snapshot(&self) -> PageCacheSnapshot {
92 let mut snapshot = PageCacheSnapshot::default();
93 for (stats, class) in snapshot.sizes.iter_mut().zip(&self.classes) {
94 stats.hits = class.hits.load(Relaxed);
95 stats.misses = class.misses.load(Relaxed);
96 stats.cached = class.cached.load(Relaxed);
97 stats.freed = class.freed.load(Relaxed);
98 let removed = class.removed.load(Relaxed);
99 let hits = stats.hits;
100 stats.pages =
102 (stats.cached + class.added.load(Relaxed)).saturating_sub(hits + removed) as usize;
103 stats.bytes = stats.pages * stats.size.alloc_size();
104 }
105 snapshot
106 }
107
108 pub(crate) fn hit(&self, class: usize) {
109 inc(&self.classes[class].hits, 1);
110 }
111
112 pub(crate) fn miss(&self, class: usize) {
113 inc(&self.classes[class].misses, 1);
114 }
115
116 pub(crate) fn cached(&self, class: usize) {
117 inc(&self.classes[class].cached, 1);
118 }
119
120 pub(crate) fn freed(&self, class: usize) {
121 inc(&self.classes[class].freed, 1);
122 }
123
124 pub(crate) fn remove_pages(&self, class: usize, pages: usize) {
126 if pages != 0 {
127 inc(&self.classes[class].removed, pages as u64);
128 }
129 }
130
131 pub(crate) fn add_pages(&self, class: usize, pages: usize) {
132 if pages != 0 {
133 inc(&self.classes[class].added, pages as u64);
134 }
135 }
136}
137
138#[inline]
140fn inc(counter: &AtomicU64, n: u64) {
141 counter.store(counter.load(Relaxed).wrapping_add(n), Relaxed);
142}
143
144impl PageCacheSnapshot {
145 pub fn get(&self, size: BytePageSize) -> Option<&PageCacheStats> {
148 self.sizes.get(size as usize)
149 }
150
151 pub fn iter(&self) -> impl Iterator<Item = &PageCacheStats> {
153 self.sizes.iter()
154 }
155
156 pub fn total(&self) -> PageCacheStats {
158 self.sizes.iter().fold(
159 PageCacheStats {
160 size: BytePageSize::Unset,
161 ..PageCacheStats::default()
162 },
163 |mut total, s| {
164 total.hits += s.hits;
165 total.misses += s.misses;
166 total.cached += s.cached;
167 total.freed += s.freed;
168 total.pages += s.pages;
169 total.bytes += s.bytes;
170 total
171 },
172 )
173 }
174}
175
176impl Default for PageCacheSnapshot {
177 fn default() -> Self {
178 Self {
179 sizes: PAGE_SIZES.map(|size| PageCacheStats {
180 size,
181 ..PageCacheStats::default()
182 }),
183 }
184 }
185}