|
| 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