Add `/proc/pid/oom_score_adj`
This commit is contained in:
parent
b42605af6b
commit
46aa437c87
|
|
@ -12,7 +12,8 @@ use crate::{
|
|||
stat::StatFileOps,
|
||||
task::{
|
||||
cmdline::CmdlineFileOps, comm::CommFileOps, environ::EnvironFileOps,
|
||||
exe::ExeSymOps, fd::FdDirOps, status::StatusFileOps,
|
||||
exe::ExeSymOps, fd::FdDirOps, oom_score_adj::OomScoreAdjFileOps,
|
||||
status::StatusFileOps,
|
||||
},
|
||||
},
|
||||
template::{DirOps, ProcDir, ProcDirBuilder},
|
||||
|
|
@ -29,6 +30,7 @@ mod comm;
|
|||
mod environ;
|
||||
mod exe;
|
||||
mod fd;
|
||||
mod oom_score_adj;
|
||||
mod status;
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/task`.
|
||||
|
|
@ -79,6 +81,7 @@ impl DirOps for TidDirOps {
|
|||
"environ" => EnvironFileOps::new_inode(self.process_ref.clone(), this_ptr),
|
||||
"exe" => ExeSymOps::new_inode(self.process_ref.clone(), this_ptr),
|
||||
"fd" => FdDirOps::new_inode(self.thread_ref.clone(), this_ptr),
|
||||
"oom_score_adj" => OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr),
|
||||
"stat" => StatFileOps::new_inode(
|
||||
self.process_ref.clone(),
|
||||
self.thread_ref.clone(),
|
||||
|
|
@ -126,6 +129,9 @@ impl TidDirOps {
|
|||
cached_children.put_entry_if_not_found("fd", || {
|
||||
FdDirOps::new_inode(self.thread_ref.clone(), this_ptr.clone())
|
||||
});
|
||||
cached_children.put_entry_if_not_found("oom_score_adj", || {
|
||||
OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
|
||||
});
|
||||
cached_children.put_entry_if_not_found("stat", || {
|
||||
StatFileOps::new_inode(
|
||||
self.process_ref.clone(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::{fmt::Write, sync::atomic::Ordering};
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
procfs::template::{FileOps, ProcFileBuilder},
|
||||
utils::{Inode, InodeMode},
|
||||
},
|
||||
prelude::*,
|
||||
process::Process,
|
||||
};
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/task/[tid]/oom_score_adj` (and also `/proc/[pid]/oom_score_adj`).
|
||||
pub struct OomScoreAdjFileOps(Arc<Process>);
|
||||
|
||||
impl OomScoreAdjFileOps {
|
||||
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3386>
|
||||
ProcFileBuilder::new(Self(process_ref), InodeMode::from_bits_truncate(0o644))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOps for OomScoreAdjFileOps {
|
||||
fn data(&self) -> Result<Vec<u8>> {
|
||||
let oom_score_adj = self.0.oom_score_adj().load(Ordering::Relaxed);
|
||||
|
||||
let mut output = String::new();
|
||||
writeln!(output, "{}", oom_score_adj).unwrap();
|
||||
Ok(output.into_bytes())
|
||||
}
|
||||
|
||||
fn write_at(&self, _offset: usize, reader: &mut VmReader) -> Result<usize> {
|
||||
// TODO: Extend the `ReadCString` trait to read a C string without
|
||||
// requiring the nul terminator.
|
||||
let (val, read_bytes) = read_i32_from(reader)?;
|
||||
if !(OOM_SCORE_ADJ_MIN..=OOM_SCORE_ADJ_MAX).contains(&val) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the OOM score adjustment is out of range");
|
||||
}
|
||||
|
||||
// TODO: If the new adjustment value is smaller than the smallest
|
||||
// adjustment value that the process has ever reached and the writer
|
||||
// does not have the `SYS_RESOURCE` capability, we should fail with
|
||||
// `EACCES`. See
|
||||
// <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L1152>.
|
||||
self.0.oom_score_adj().store(val as i16, Ordering::Relaxed);
|
||||
|
||||
Ok(read_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an `i32` from the given `VmReader`.
|
||||
///
|
||||
/// Returns the read value and the number of bytes read from the given `VmReader`.
|
||||
fn read_i32_from(reader: &mut VmReader) -> Result<(i32, usize)> {
|
||||
let mut buf = [0u8; BUF_SIZE_I32];
|
||||
|
||||
let read_bytes = reader.read_fallible(&mut (&mut buf[..BUF_SIZE_I32 - 1]).into())?;
|
||||
let val = CStr::from_bytes_until_nul(&buf)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(|str| str.parse::<i32>().ok())
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "the value is not a valid integer"))?;
|
||||
Ok((val, read_bytes))
|
||||
}
|
||||
|
||||
/// Worst case buffer size needed for holding an integer.
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/internal.h#L163>.
|
||||
const BUF_SIZE_I32: usize = 13;
|
||||
|
||||
// FIXME: Support OOM killer and move these constants to a more appropriate place.
|
||||
const OOM_SCORE_ADJ_MIN: i32 = -1000;
|
||||
const OOM_SCORE_ADJ_MAX: i32 = 1000;
|
||||
|
|
@ -401,6 +401,9 @@ fn clone_child_process(
|
|||
// Inherit the parent's nice value
|
||||
let child_nice = process.nice().load(Ordering::Relaxed);
|
||||
|
||||
// Inherit the parent's OOM score adjustment
|
||||
let child_oom_score_adj = process.oom_score_adj().load(Ordering::Relaxed);
|
||||
|
||||
let child_tid = allocate_posix_tid();
|
||||
|
||||
let child = {
|
||||
|
|
@ -437,6 +440,7 @@ fn clone_child_process(
|
|||
child_process_vm,
|
||||
child_resource_limits,
|
||||
child_nice,
|
||||
child_oom_score_adj,
|
||||
child_sig_dispositions,
|
||||
child_user_ns,
|
||||
child_thread_builder,
|
||||
|
|
@ -644,6 +648,7 @@ fn create_child_process(
|
|||
process_vm: ProcessVm,
|
||||
resource_limits: ResourceLimits,
|
||||
nice: Nice,
|
||||
oom_score_adj: i16,
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
user_ns: Arc<UserNamespace>,
|
||||
thread_builder: PosixThreadBuilder,
|
||||
|
|
@ -655,6 +660,7 @@ fn create_child_process(
|
|||
process_vm,
|
||||
resource_limits,
|
||||
nice,
|
||||
oom_score_adj,
|
||||
sig_dispositions,
|
||||
user_ns,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ fn create_init_process(
|
|||
let process_vm = ProcessVm::alloc();
|
||||
let resource_limits = ResourceLimits::default();
|
||||
let nice = Nice::default();
|
||||
let oom_score_adj = 0;
|
||||
let sig_dispositions = Arc::new(Mutex::new(SigDispositions::default()));
|
||||
let user_ns = UserNamespace::get_init_singleton().clone();
|
||||
|
||||
|
|
@ -61,6 +62,7 @@ fn create_init_process(
|
|||
process_vm,
|
||||
resource_limits,
|
||||
nice,
|
||||
oom_score_adj,
|
||||
sig_dispositions,
|
||||
user_ns,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use core::sync::atomic::{AtomicBool, AtomicI16, AtomicU32, Ordering};
|
||||
|
||||
use self::timer_manager::PosixTimerManager;
|
||||
use super::{
|
||||
|
|
@ -92,6 +92,9 @@ pub struct Process {
|
|||
/// According to POSIX.1, the nice value is a per-process attribute,
|
||||
/// the threads in a process should share a nice value.
|
||||
nice: AtomicNice,
|
||||
/// The adjustment value of the out-of-memory (OOM) killer score.
|
||||
// FIXME: Support OOM killer.
|
||||
oom_score_adj: AtomicI16,
|
||||
|
||||
// Child reaper attribute
|
||||
/// Whether the process is a child subreaper.
|
||||
|
|
@ -201,6 +204,7 @@ impl Process {
|
|||
|
||||
resource_limits: ResourceLimits,
|
||||
nice: Nice,
|
||||
oom_score_adj: i16,
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
user_ns: Arc<UserNamespace>,
|
||||
) -> Arc<Self> {
|
||||
|
|
@ -228,6 +232,7 @@ impl Process {
|
|||
exit_signal: AtomicSigNum::new_empty(),
|
||||
resource_limits,
|
||||
nice: AtomicNice::new(nice),
|
||||
oom_score_adj: AtomicI16::new(oom_score_adj),
|
||||
timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
|
||||
prof_clock,
|
||||
user_ns: Mutex::new(user_ns),
|
||||
|
|
@ -287,6 +292,10 @@ impl Process {
|
|||
self.tasks.lock().main().as_thread().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn oom_score_adj(&self) -> &AtomicI16 {
|
||||
&self.oom_score_adj
|
||||
}
|
||||
|
||||
// *********** Parent and child ***********
|
||||
|
||||
pub fn parent(&self) -> &ParentProcess {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ TESTS ?= \
|
|||
prctl_setuid_test \
|
||||
pread64_test \
|
||||
preadv2_test \
|
||||
proc_pid_oomscore_test \
|
||||
proc_test \
|
||||
pselect_test \
|
||||
pty_test \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
# TODO: Support `/proc/[pid]/oom_score`
|
||||
ProcPidOomscoreTest.BasicRead
|
||||
Loading…
Reference in New Issue