blob: 4161966845562df8cfa389abf7984d0e22913e2c [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Pressure stall information (PSI) utilities.
//!
//! PSI documentation: https://docs.kernel.org/accounting/psi.html
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::time::Duration;
use tokio::io::unix::AsyncFd;
use tokio::io::Interest;
/// The target of the PSI. Either "some" or "full".
pub enum Target {
Some,
Full,
}
impl Target {
fn to_str(&self) -> &'static str {
match self {
Target::Some => "some",
Target::Full => "full",
}
}
}
/// A watcher for PSI events.
pub struct PsiWatcher {
fd: AsyncFd<File>,
}
impl PsiWatcher {
/// Creates a new PSI watcher for memory pressure.
pub fn new_memory_pressure(
target: Target,
stall: Duration,
window: Duration,
) -> io::Result<Self> {
Self::new("/proc/pressure/memory", target, stall, window)
}
fn new(path: &str, target: Target, stall: Duration, window: Duration) -> io::Result<Self> {
let mut file = OpenOptions::new().read(true).write(true).open(path)?;
let monitor_config = format!(
"{} {} {}\0",
target.to_str(),
stall.as_micros(),
window.as_micros()
);
file.write_all(monitor_config.as_bytes())?;
// Monitor POLLPRI for PSI events.
// https://docs.kernel.org/accounting/psi.html#userspace-monitor-usage-example
let fd = AsyncFd::with_interest(file, Interest::PRIORITY)?;
Ok(Self { fd })
}
/// Waits for a PSI event to occur.
pub async fn wait(&mut self) -> io::Result<()> {
self.fd.readable().await?.clear_ready();
Ok(())
}
}