Skip to content

Commit c792ba7

Browse files
fabio-dCQ Bot
authored andcommitted
[starnix] Show ZX_INFO_MEMORY_STALL info in /proc/pressure/memory
This CL makes starnix generate /proc/pressure/memory with real stats, including averages over the last 10/60/300 seconds, that are computed by Starnix according to data periodically sampled from Zircon. The a dedicated service (child of the Starnix runner) is responsible for keeping track of the averages and, in general, mediate access to the new PSI services. This provides an injection point for test. Indeed, a new integration test uses this method to feed known data to a puppet program running within Starnix instance and verify that it gets back the expected readings. Note: The ProcfsTest.ProcPressure test is now embedded in the new integration test, which tests the cpu and io PSI files too (the expectation being that in the future they will also be served over the same protocol, like memory PSI is today). Bug: 319649348 Change-Id: I73818e4d66db9a3784da4fbbe7a06ebcf02f7bcf Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1176632 Commit-Queue: Fabio D'Urso <[email protected]> API-Review: Benjamin Lerman <[email protected]> Reviewed-by: Benjamin Lerman <[email protected]>
1 parent f32448f commit c792ba7

30 files changed

+1618
-124
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright 2025 The Fuchsia Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
import("//build/fidl/fidl.gni")
6+
7+
fidl("fuchsia.starnix.psi") {
8+
sources = [ "provider.fidl" ]
9+
public_deps = [ "//zircon/vdso/zx" ]
10+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# proto-file: tools/metadata/proto/metadata.proto
2+
# proto-message: Metadata
3+
# https://fuchsia.dev/fuchsia-src/development/source_code/metadata
4+
5+
last_reviewed_date: {
6+
year: 2025
7+
month: 1
8+
day: 27
9+
}
10+
11+
trackers: {
12+
# Buganizer component id for `starnix` public component.
13+
issue_tracker: {
14+
component_id: 1396624
15+
}
16+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include /src/starnix/OWNERS
2+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2025 The Fuchsia Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
@available(added=HEAD)
5+
library fuchsia.starnix.psi;
6+
7+
using zx;
8+
9+
type PsiStats = table {
10+
/// Average fraction of time spent stalling in the last 10 seconds (0-1).
11+
1: avg10 float64;
12+
13+
/// Average fraction of time spent stalling in the last 60 seconds (0-1).
14+
2: avg60 float64;
15+
16+
/// Average fraction of time spent stalling in the last 300 seconds (0-1).
17+
3: avg300 float64;
18+
19+
/// Cumulative time spent stalling since boot (using the monotonic clock as
20+
/// the time base).
21+
4: total zx.Duration;
22+
};
23+
24+
@discoverable
25+
open protocol PsiProvider {
26+
flexible GetMemoryPressureStats() -> (table {
27+
/// Stats about time spent with at least one thread stalling.
28+
1: some PsiStats;
29+
30+
/// Stats about time spent with all threads stalling.
31+
2: full PsiStats;
32+
}) error zx.Status;
33+
};

src/starnix/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ group("tests") {
2424
"kernel:tests",
2525
"lib:tests",
2626
"modules:tests",
27+
"psi-provider:tests",
2728
"runner:tests",
2829
"tests",
2930
"tools:tests",

src/starnix/kernel/BUILD.gn

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ rustc_library("starnix_core") {
145145
"fs/proc/fs.rs",
146146
"fs/proc/mod.rs",
147147
"fs/proc/pid_directory.rs",
148+
"fs/proc/pressure_directory.rs",
148149
"fs/proc/proc_directory.rs",
149150
"fs/proc/sysctl.rs",
150151
"fs/proc/sysrq.rs",
@@ -206,6 +207,7 @@ rustc_library("starnix_core") {
206207
"task/net.rs",
207208
"task/pid_table.rs",
208209
"task/process_group.rs",
210+
"task/psi_provider.rs",
209211
"task/ptrace.rs",
210212
"task/scheduler.rs",
211213
"task/seccomp.rs",
@@ -315,7 +317,7 @@ rustc_library("starnix_core") {
315317
"//sdk/fidl/fuchsia.scheduler:fuchsia.scheduler_rust",
316318
"//sdk/fidl/fuchsia.session.power:fuchsia.session.power_rust",
317319
"//sdk/fidl/fuchsia.starnix.binder:fuchsia.starnix.binder_rust",
318-
"//sdk/fidl/fuchsia.starnix.runner:fuchsia.starnix.runner_rust",
320+
"//sdk/fidl/fuchsia.starnix.psi:fuchsia.starnix.psi_rust",
319321
"//sdk/fidl/fuchsia.starnix.runner:fuchsia.starnix.runner_rust",
320322
"//sdk/fidl/fuchsia.sysinfo:fuchsia.sysinfo_rust",
321323
"//sdk/fidl/fuchsia.sysmem2:fuchsia.sysmem2_rust",

src/starnix/kernel/fs/proc/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
mod fs;
66
pub mod pid_directory;
7+
mod pressure_directory;
78
mod proc_directory;
89
mod sysctl;
910
mod sysrq;
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright 2025 The Fuchsia Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
use crate::task::{CurrentTask, EventHandler, WaitCanceler, Waiter};
6+
use crate::vfs::buffers::InputBuffer;
7+
use crate::vfs::{
8+
fileops_impl_delegate_read_and_seek, fileops_impl_noop_sync, DynamicFile, DynamicFileBuf,
9+
DynamicFileSource, FileObject, FileOps, FileSystemHandle, FsNodeHandle, FsNodeOps,
10+
SimpleFileNode, StaticDirectoryBuilder,
11+
};
12+
use fidl_fuchsia_starnix_psi::{
13+
PsiProviderGetMemoryPressureStatsResponse, PsiProviderSynchronousProxy,
14+
};
15+
16+
use starnix_logging::{log_error, track_stub};
17+
use starnix_sync::{FileOpsCore, Locked};
18+
use starnix_uapi::errno;
19+
use starnix_uapi::errors::Errno;
20+
use starnix_uapi::file_mode::mode;
21+
use starnix_uapi::vfs::FdEvents;
22+
use std::sync::Arc;
23+
24+
/// Creates the /proc/pressure directory. https://docs.kernel.org/accounting/psi.html
25+
pub fn pressure_directory(
26+
current_task: &CurrentTask,
27+
fs: &FileSystemHandle,
28+
) -> Option<FsNodeHandle> {
29+
let Some(psi_provider) = current_task.kernel().psi_provider.get() else {
30+
return None;
31+
};
32+
33+
let mut dir = StaticDirectoryBuilder::new(fs);
34+
dir.entry(
35+
current_task,
36+
"memory",
37+
MemoryPressureFile::new_node(psi_provider.clone()),
38+
mode!(IFREG, 0o666),
39+
);
40+
dir.entry(
41+
current_task,
42+
"cpu",
43+
StubPressureFile::new_node(StubPressureFileKind::CPU),
44+
mode!(IFREG, 0o666),
45+
);
46+
dir.entry(
47+
current_task,
48+
"io",
49+
StubPressureFile::new_node(StubPressureFileKind::IO),
50+
mode!(IFREG, 0o666),
51+
);
52+
Some(dir.build(current_task))
53+
}
54+
55+
struct MemoryPressureFileSource {
56+
psi_provider: Arc<PsiProviderSynchronousProxy>,
57+
}
58+
59+
impl DynamicFileSource for MemoryPressureFileSource {
60+
fn generate(&self, sink: &mut DynamicFileBuf) -> Result<(), Errno> {
61+
let PsiProviderGetMemoryPressureStatsResponse { some, full, .. } = self
62+
.psi_provider
63+
.get_memory_pressure_stats(zx::MonotonicInstant::INFINITE)
64+
.map_err(|e| {
65+
log_error!("FIDL error getting memory pressure stats: {e}");
66+
errno!(EIO)
67+
})?
68+
.map_err(|_| errno!(EIO))?;
69+
70+
let some = some.ok_or_else(|| errno!(EIO))?;
71+
writeln!(
72+
sink,
73+
"some avg10={:.2} avg60={:.2} avg300={:.2} total={}",
74+
some.avg10.ok_or_else(|| errno!(EIO))?,
75+
some.avg60.ok_or_else(|| errno!(EIO))?,
76+
some.avg300.ok_or_else(|| errno!(EIO))?,
77+
some.total.ok_or_else(|| errno!(EIO))? / 1000
78+
)?;
79+
80+
let full = full.ok_or_else(|| errno!(EIO))?;
81+
writeln!(
82+
sink,
83+
"full avg10={:.2} avg60={:.2} avg300={:.2} total={}",
84+
full.avg10.ok_or_else(|| errno!(EIO))?,
85+
full.avg60.ok_or_else(|| errno!(EIO))?,
86+
full.avg300.ok_or_else(|| errno!(EIO))?,
87+
full.total.ok_or_else(|| errno!(EIO))? / 1000
88+
)?;
89+
90+
Ok(())
91+
}
92+
}
93+
94+
struct MemoryPressureFile {
95+
source: DynamicFile<MemoryPressureFileSource>,
96+
}
97+
98+
impl MemoryPressureFile {
99+
pub fn new_node(psi_provider: Arc<PsiProviderSynchronousProxy>) -> impl FsNodeOps {
100+
SimpleFileNode::new(move || {
101+
Ok(Self {
102+
source: DynamicFile::new(MemoryPressureFileSource {
103+
psi_provider: psi_provider.clone(),
104+
}),
105+
})
106+
})
107+
}
108+
}
109+
110+
impl FileOps for MemoryPressureFile {
111+
fileops_impl_delegate_read_and_seek!(self, self.source);
112+
fileops_impl_noop_sync!();
113+
114+
/// Pressure notifications are configured by writing to the file.
115+
fn write(
116+
&self,
117+
_locked: &mut Locked<'_, FileOpsCore>,
118+
_file: &FileObject,
119+
_current_task: &CurrentTask,
120+
_offset: usize,
121+
data: &mut dyn InputBuffer,
122+
) -> Result<usize, Errno> {
123+
// Ignore the request for now.
124+
125+
track_stub!(TODO("https://fxbug.dev/322873423"), "memory pressure notification setup");
126+
Ok(data.drain())
127+
}
128+
129+
fn wait_async(
130+
&self,
131+
_locked: &mut Locked<'_, FileOpsCore>,
132+
_file: &FileObject,
133+
_current_task: &CurrentTask,
134+
waiter: &Waiter,
135+
_events: FdEvents,
136+
_handler: EventHandler,
137+
) -> Option<WaitCanceler> {
138+
Some(waiter.fake_wait())
139+
}
140+
141+
fn query_events(
142+
&self,
143+
_locked: &mut Locked<'_, FileOpsCore>,
144+
_file: &FileObject,
145+
_current_task: &CurrentTask,
146+
) -> Result<FdEvents, Errno> {
147+
Ok(FdEvents::empty())
148+
}
149+
}
150+
151+
struct StubPressureFileSource;
152+
153+
impl DynamicFileSource for StubPressureFileSource {
154+
fn generate(&self, sink: &mut DynamicFileBuf) -> Result<(), Errno> {
155+
writeln!(sink, "some avg10=0.00 avg60=0.00 avg300=0.00 total=0")?;
156+
writeln!(sink, "full avg10=0.00 avg60=0.00 avg300=0.00 total=0")?;
157+
Ok(())
158+
}
159+
}
160+
161+
#[derive(Clone, Copy)]
162+
enum StubPressureFileKind {
163+
CPU,
164+
IO,
165+
}
166+
167+
struct StubPressureFile {
168+
kind: StubPressureFileKind,
169+
source: DynamicFile<StubPressureFileSource>,
170+
}
171+
172+
impl StubPressureFile {
173+
pub fn new_node(kind: StubPressureFileKind) -> impl FsNodeOps {
174+
SimpleFileNode::new(move || {
175+
Ok(Self { kind, source: DynamicFile::new(StubPressureFileSource) })
176+
})
177+
}
178+
}
179+
180+
impl FileOps for StubPressureFile {
181+
fileops_impl_delegate_read_and_seek!(self, self.source);
182+
fileops_impl_noop_sync!();
183+
184+
/// Pressure notifications are configured by writing to the file.
185+
fn write(
186+
&self,
187+
_locked: &mut Locked<'_, FileOpsCore>,
188+
_file: &FileObject,
189+
_current_task: &CurrentTask,
190+
_offset: usize,
191+
data: &mut dyn InputBuffer,
192+
) -> Result<usize, Errno> {
193+
// Ignore the request for now.
194+
195+
track_stub!(
196+
TODO("https://fxbug.dev/322873423"),
197+
match self.kind {
198+
StubPressureFileKind::CPU => "cpu pressure notification setup",
199+
StubPressureFileKind::IO => "io pressure notification setup",
200+
}
201+
);
202+
Ok(data.drain())
203+
}
204+
205+
fn wait_async(
206+
&self,
207+
_locked: &mut Locked<'_, FileOpsCore>,
208+
_file: &FileObject,
209+
_current_task: &CurrentTask,
210+
waiter: &Waiter,
211+
_events: FdEvents,
212+
_handler: EventHandler,
213+
) -> Option<WaitCanceler> {
214+
Some(waiter.fake_wait())
215+
}
216+
217+
fn query_events(
218+
&self,
219+
_locked: &mut Locked<'_, FileOpsCore>,
220+
_file: &FileObject,
221+
_current_task: &CurrentTask,
222+
) -> Result<FdEvents, Errno> {
223+
Ok(FdEvents::empty())
224+
}
225+
}

0 commit comments

Comments
 (0)