cgroups_rs/fs/
events.rs

1// Copyright (c) 2020 Ant Group
2//
3// SPDX-License-Identifier: Apache-2.0 or MIT
4//
5
6use eventfd::{eventfd, EfdFlags};
7use nix::sys::eventfd;
8use std::fs::{self, File};
9use std::io::Read;
10use std::os::unix::io::{AsRawFd, FromRawFd};
11use std::path::Path;
12use std::sync::mpsc::{self, Receiver};
13use std::thread;
14
15use crate::fs::error::ErrorKind::*;
16use crate::fs::error::*;
17
18// notify_on_oom returns channel on which you can expect event about OOM,
19// if process died without OOM this channel will be closed.
20pub fn notify_on_oom_v2(key: &str, dir: &Path) -> Result<Receiver<String>> {
21    register_memory_event(key, dir, "memory.oom_control", "")
22}
23
24// notify_on_oom returns channel on which you can expect event about OOM,
25// if process died without OOM this channel will be closed.
26pub fn notify_on_oom_v1(key: &str, dir: &Path) -> Result<Receiver<String>> {
27    register_memory_event(key, dir, "memory.oom_control", "")
28}
29
30// level is one of "low", "medium", or "critical"
31pub fn notify_memory_pressure(key: &str, dir: &Path, level: &str) -> Result<Receiver<String>> {
32    if level != "low" && level != "medium" && level != "critical" {
33        return Err(Error::from_string(format!(
34            "invalid pressure level {}",
35            level
36        )));
37    }
38
39    register_memory_event(key, dir, "memory.pressure_level", level)
40}
41
42fn register_memory_event(
43    key: &str,
44    cg_dir: &Path,
45    event_name: &str,
46    arg: &str,
47) -> Result<Receiver<String>> {
48    let path = cg_dir.join(event_name);
49    let event_file = File::open(path.clone())
50        .map_err(|e| Error::with_cause(ReadFailed(path.display().to_string()), e))?;
51
52    let eventfd = eventfd(0, EfdFlags::EFD_CLOEXEC)
53        .map_err(|e| Error::with_cause(ReadFailed("eventfd".to_string()), e))?;
54
55    let event_control_path = cg_dir.join("cgroup.event_control");
56    let data = if arg.is_empty() {
57        format!("{} {}", eventfd, event_file.as_raw_fd())
58    } else {
59        format!("{} {} {}", eventfd, event_file.as_raw_fd(), arg)
60    };
61
62    // write to file and set mode to 0700(FIXME)
63    fs::write(&event_control_path, data.clone()).map_err(|e| {
64        Error::with_cause(
65            WriteFailed(event_control_path.display().to_string(), data),
66            e,
67        )
68    })?;
69
70    let mut eventfd_file = unsafe { File::from_raw_fd(eventfd) };
71
72    let (sender, receiver) = mpsc::channel();
73    let key = key.to_string();
74
75    thread::spawn(move || {
76        loop {
77            let mut buf = [0; 8];
78            if eventfd_file.read(&mut buf).is_err() {
79                return;
80            }
81
82            // When a cgroup is destroyed, an event is sent to eventfd.
83            // So if the control path is gone, return instead of notifying.
84            if !Path::new(&event_control_path).exists() {
85                return;
86            }
87            sender.send(key.clone()).unwrap();
88        }
89    });
90
91    Ok(receiver)
92}