xref: /aosp_15_r20/external/crosvm/swap/src/processes.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker #![deny(missing_docs)]
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::read_to_string;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::num::ParseIntError;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::str::FromStr;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::thread::sleep;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
13*bb4ee6a4SAndroid Build Coastguard Worker 
14*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
15*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
16*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
17*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
18*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::getpid;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::kill;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::Signal;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::Pid;
22*bb4ee6a4SAndroid Build Coastguard Worker 
23*bb4ee6a4SAndroid Build Coastguard Worker /// Stops all the crosvm device processes during moving the guest memory to the staging memory.
24*bb4ee6a4SAndroid Build Coastguard Worker ///
25*bb4ee6a4SAndroid Build Coastguard Worker /// While moving, we must guarantee that no one changes the guest memory contents. This supports
26*bb4ee6a4SAndroid Build Coastguard Worker /// devices in sandbox mode only.
27*bb4ee6a4SAndroid Build Coastguard Worker ///
28*bb4ee6a4SAndroid Build Coastguard Worker /// We stop all the crosvm processes instead of the alternatives.
29*bb4ee6a4SAndroid Build Coastguard Worker ///
30*bb4ee6a4SAndroid Build Coastguard Worker /// * Just stop vCPUs
31*bb4ee6a4SAndroid Build Coastguard Worker ///   * devices still may works in the child process and write something to the guest memory.
32*bb4ee6a4SAndroid Build Coastguard Worker /// * Use write protection of userfaultfd
33*bb4ee6a4SAndroid Build Coastguard Worker ///   * UFFDIO_REGISTER_MODE_WP for shmem is WIP and not supported yet.
34*bb4ee6a4SAndroid Build Coastguard Worker /// * `devices::Suspendable::sleep()`
35*bb4ee6a4SAndroid Build Coastguard Worker ///   * `Suspendable` is not supported by all devices yet.
36*bb4ee6a4SAndroid Build Coastguard Worker pub struct ProcessesGuard {
37*bb4ee6a4SAndroid Build Coastguard Worker     pids: Vec<Pid>,
38*bb4ee6a4SAndroid Build Coastguard Worker }
39*bb4ee6a4SAndroid Build Coastguard Worker 
40*bb4ee6a4SAndroid Build Coastguard Worker /// Stops all crosvm child processes except this monitor process using signals.
41*bb4ee6a4SAndroid Build Coastguard Worker ///
42*bb4ee6a4SAndroid Build Coastguard Worker /// The stopped processes are resumed when the freezer object is freed.
43*bb4ee6a4SAndroid Build Coastguard Worker ///
44*bb4ee6a4SAndroid Build Coastguard Worker /// This must be called from the main process.
freeze_child_processes(monitor_pid: Pid) -> Result<ProcessesGuard>45*bb4ee6a4SAndroid Build Coastguard Worker pub fn freeze_child_processes(monitor_pid: Pid) -> Result<ProcessesGuard> {
46*bb4ee6a4SAndroid Build Coastguard Worker     let mut guard = ProcessesGuard {
47*bb4ee6a4SAndroid Build Coastguard Worker         pids: load_descendants(getpid(), monitor_pid)?,
48*bb4ee6a4SAndroid Build Coastguard Worker     };
49*bb4ee6a4SAndroid Build Coastguard Worker 
50*bb4ee6a4SAndroid Build Coastguard Worker     for _ in 0..3 {
51*bb4ee6a4SAndroid Build Coastguard Worker         guard.stop_the_world().context("stop the world")?;
52*bb4ee6a4SAndroid Build Coastguard Worker         let pids_after = load_descendants(getpid(), monitor_pid)?;
53*bb4ee6a4SAndroid Build Coastguard Worker         if pids_after == guard.pids {
54*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(guard);
55*bb4ee6a4SAndroid Build Coastguard Worker         }
56*bb4ee6a4SAndroid Build Coastguard Worker         guard.pids = pids_after;
57*bb4ee6a4SAndroid Build Coastguard Worker     }
58*bb4ee6a4SAndroid Build Coastguard Worker 
59*bb4ee6a4SAndroid Build Coastguard Worker     bail!("new processes forked while freezing");
60*bb4ee6a4SAndroid Build Coastguard Worker }
61*bb4ee6a4SAndroid Build Coastguard Worker 
62*bb4ee6a4SAndroid Build Coastguard Worker impl ProcessesGuard {
63*bb4ee6a4SAndroid Build Coastguard Worker     /// Stops all the crosvm processes by sending SIGSTOP signal.
stop_the_world(&self) -> Result<()>64*bb4ee6a4SAndroid Build Coastguard Worker     fn stop_the_world(&self) -> Result<()> {
65*bb4ee6a4SAndroid Build Coastguard Worker         for pid in &self.pids {
66*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY:
67*bb4ee6a4SAndroid Build Coastguard Worker             // safe because pid in pids are crosvm processes except this monitor process.
68*bb4ee6a4SAndroid Build Coastguard Worker             unsafe { kill(*pid, Signal::Stop as i32) }.context("failed to stop process")?;
69*bb4ee6a4SAndroid Build Coastguard Worker         }
70*bb4ee6a4SAndroid Build Coastguard Worker         for pid in &self.pids {
71*bb4ee6a4SAndroid Build Coastguard Worker             wait_process_stopped(*pid).context("wait process stopped")?;
72*bb4ee6a4SAndroid Build Coastguard Worker         }
73*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
74*bb4ee6a4SAndroid Build Coastguard Worker     }
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker     /// Resumes all the crosvm processes by sending SIGCONT signal.
continue_the_world(&self)77*bb4ee6a4SAndroid Build Coastguard Worker     fn continue_the_world(&self) {
78*bb4ee6a4SAndroid Build Coastguard Worker         for pid in &self.pids {
79*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY:
80*bb4ee6a4SAndroid Build Coastguard Worker             // safe because pid in pids are crosvm processes except this monitor process and
81*bb4ee6a4SAndroid Build Coastguard Worker             // continue signal does not have side effects.
82*bb4ee6a4SAndroid Build Coastguard Worker             // ignore the result because we don't care whether it succeeds.
83*bb4ee6a4SAndroid Build Coastguard Worker             let _ = unsafe { kill(*pid, Signal::Continue as i32) };
84*bb4ee6a4SAndroid Build Coastguard Worker         }
85*bb4ee6a4SAndroid Build Coastguard Worker     }
86*bb4ee6a4SAndroid Build Coastguard Worker }
87*bb4ee6a4SAndroid Build Coastguard Worker 
88*bb4ee6a4SAndroid Build Coastguard Worker impl Drop for ProcessesGuard {
drop(&mut self)89*bb4ee6a4SAndroid Build Coastguard Worker     fn drop(&mut self) {
90*bb4ee6a4SAndroid Build Coastguard Worker         self.continue_the_world();
91*bb4ee6a4SAndroid Build Coastguard Worker     }
92*bb4ee6a4SAndroid Build Coastguard Worker }
93*bb4ee6a4SAndroid Build Coastguard Worker 
94*bb4ee6a4SAndroid Build Coastguard Worker /// Loads Pids of crosvm descendant processes except the monitor procesess.
load_descendants(current_pid: Pid, monitor_pid: Pid) -> Result<Vec<Pid>>95*bb4ee6a4SAndroid Build Coastguard Worker fn load_descendants(current_pid: Pid, monitor_pid: Pid) -> Result<Vec<Pid>> {
96*bb4ee6a4SAndroid Build Coastguard Worker     // children of the current process.
97*bb4ee6a4SAndroid Build Coastguard Worker     let children = read_to_string(format!("/proc/{0}/task/{0}/children", current_pid))
98*bb4ee6a4SAndroid Build Coastguard Worker         .context("read children")?;
99*bb4ee6a4SAndroid Build Coastguard Worker     let children = children.trim();
100*bb4ee6a4SAndroid Build Coastguard Worker     // str::split() to empty string results a iterator just returning 1 empty string.
101*bb4ee6a4SAndroid Build Coastguard Worker     if children.is_empty() {
102*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(Vec::new());
103*bb4ee6a4SAndroid Build Coastguard Worker     }
104*bb4ee6a4SAndroid Build Coastguard Worker     let pids: std::result::Result<Vec<i32>, ParseIntError> = children
105*bb4ee6a4SAndroid Build Coastguard Worker         .split(" ")
106*bb4ee6a4SAndroid Build Coastguard Worker         .map(i32::from_str)
107*bb4ee6a4SAndroid Build Coastguard Worker         // except this monitor process
108*bb4ee6a4SAndroid Build Coastguard Worker         .filter(|pid| match pid {
109*bb4ee6a4SAndroid Build Coastguard Worker             Ok(pid) => *pid != monitor_pid,
110*bb4ee6a4SAndroid Build Coastguard Worker             _ => true,
111*bb4ee6a4SAndroid Build Coastguard Worker         })
112*bb4ee6a4SAndroid Build Coastguard Worker         .collect();
113*bb4ee6a4SAndroid Build Coastguard Worker     let pids = pids.context("parse pids")?;
114*bb4ee6a4SAndroid Build Coastguard Worker     let mut result = Vec::new();
115*bb4ee6a4SAndroid Build Coastguard Worker     for pid in pids {
116*bb4ee6a4SAndroid Build Coastguard Worker         result.push(pid);
117*bb4ee6a4SAndroid Build Coastguard Worker         let pids = load_descendants(pid, monitor_pid)?;
118*bb4ee6a4SAndroid Build Coastguard Worker         result.extend(pids);
119*bb4ee6a4SAndroid Build Coastguard Worker     }
120*bb4ee6a4SAndroid Build Coastguard Worker     Ok(result)
121*bb4ee6a4SAndroid Build Coastguard Worker }
122*bb4ee6a4SAndroid Build Coastguard Worker 
123*bb4ee6a4SAndroid Build Coastguard Worker /// Extract process state from /proc/pid/stat.
124*bb4ee6a4SAndroid Build Coastguard Worker ///
125*bb4ee6a4SAndroid Build Coastguard Worker /// `/proc/<pid>/stat` file contains metadata for the process including the process state.
126*bb4ee6a4SAndroid Build Coastguard Worker ///
127*bb4ee6a4SAndroid Build Coastguard Worker /// See [proc(5)](https://man7.org/linux/man-pages/man5/proc.5.html) for the format.
parse_process_state(text: &str) -> Option<char>128*bb4ee6a4SAndroid Build Coastguard Worker fn parse_process_state(text: &str) -> Option<char> {
129*bb4ee6a4SAndroid Build Coastguard Worker     let chars = text.chars();
130*bb4ee6a4SAndroid Build Coastguard Worker     let mut chars = chars.peekable();
131*bb4ee6a4SAndroid Build Coastguard Worker     // skip to the end of "comm"
132*bb4ee6a4SAndroid Build Coastguard Worker     while match chars.next() {
133*bb4ee6a4SAndroid Build Coastguard Worker         Some(c) => c != ')',
134*bb4ee6a4SAndroid Build Coastguard Worker         None => false,
135*bb4ee6a4SAndroid Build Coastguard Worker     } {}
136*bb4ee6a4SAndroid Build Coastguard Worker     // skip the whitespace between "comm" and "state"
137*bb4ee6a4SAndroid Build Coastguard Worker     while match chars.peek() {
138*bb4ee6a4SAndroid Build Coastguard Worker         Some(c) => {
139*bb4ee6a4SAndroid Build Coastguard Worker             let is_whitespace = *c == ' ';
140*bb4ee6a4SAndroid Build Coastguard Worker             if is_whitespace {
141*bb4ee6a4SAndroid Build Coastguard Worker                 chars.next();
142*bb4ee6a4SAndroid Build Coastguard Worker             }
143*bb4ee6a4SAndroid Build Coastguard Worker             is_whitespace
144*bb4ee6a4SAndroid Build Coastguard Worker         }
145*bb4ee6a4SAndroid Build Coastguard Worker         None => false,
146*bb4ee6a4SAndroid Build Coastguard Worker     } {}
147*bb4ee6a4SAndroid Build Coastguard Worker     // the state
148*bb4ee6a4SAndroid Build Coastguard Worker     chars.next()
149*bb4ee6a4SAndroid Build Coastguard Worker }
150*bb4ee6a4SAndroid Build Coastguard Worker 
wait_for_task_stopped(task_path: &Path) -> Result<()>151*bb4ee6a4SAndroid Build Coastguard Worker fn wait_for_task_stopped(task_path: &Path) -> Result<()> {
152*bb4ee6a4SAndroid Build Coastguard Worker     for _ in 0..10 {
153*bb4ee6a4SAndroid Build Coastguard Worker         let stat = read_to_string(task_path.join("stat")).context("read process status")?;
154*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(state) = parse_process_state(&stat) {
155*bb4ee6a4SAndroid Build Coastguard Worker             if state == 'T' {
156*bb4ee6a4SAndroid Build Coastguard Worker                 return Ok(());
157*bb4ee6a4SAndroid Build Coastguard Worker             }
158*bb4ee6a4SAndroid Build Coastguard Worker         }
159*bb4ee6a4SAndroid Build Coastguard Worker         sleep(Duration::from_millis(50));
160*bb4ee6a4SAndroid Build Coastguard Worker     }
161*bb4ee6a4SAndroid Build Coastguard Worker     Err(anyhow!("time out"))
162*bb4ee6a4SAndroid Build Coastguard Worker }
163*bb4ee6a4SAndroid Build Coastguard Worker 
wait_process_stopped(pid: Pid) -> Result<()>164*bb4ee6a4SAndroid Build Coastguard Worker fn wait_process_stopped(pid: Pid) -> Result<()> {
165*bb4ee6a4SAndroid Build Coastguard Worker     let all_tasks = std::fs::read_dir(format!("/proc/{}/task", pid)).context("read tasks")?;
166*bb4ee6a4SAndroid Build Coastguard Worker     for task in all_tasks {
167*bb4ee6a4SAndroid Build Coastguard Worker         wait_for_task_stopped(&task.context("read task entry")?.path()).context("wait for task")?;
168*bb4ee6a4SAndroid Build Coastguard Worker     }
169*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
170*bb4ee6a4SAndroid Build Coastguard Worker }
171*bb4ee6a4SAndroid Build Coastguard Worker 
172*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
173*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
174*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
175*bb4ee6a4SAndroid Build Coastguard Worker 
176*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
parse_process_state_tests()177*bb4ee6a4SAndroid Build Coastguard Worker     fn parse_process_state_tests() {
178*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(parse_process_state("1234 (crosvm) T 0 0 0").unwrap(), 'T');
179*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(parse_process_state("1234 (crosvm) R 0 0 0").unwrap(), 'R');
180*bb4ee6a4SAndroid Build Coastguard Worker         // more than 1 white space
181*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(parse_process_state("1234 (crosvm)  T 0 0 0").unwrap(), 'T');
182*bb4ee6a4SAndroid Build Coastguard Worker         // no white space between comm and state
183*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(parse_process_state("1234 (crosvm)T 0 0 0").unwrap(), 'T');
184*bb4ee6a4SAndroid Build Coastguard Worker         // white space in the comm
185*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
186*bb4ee6a4SAndroid Build Coastguard Worker             parse_process_state("1234 (crosvm --test) T 0 0 0").unwrap(),
187*bb4ee6a4SAndroid Build Coastguard Worker             'T'
188*bb4ee6a4SAndroid Build Coastguard Worker         );
189*bb4ee6a4SAndroid Build Coastguard Worker         // no status
190*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(parse_process_state("1234 (crosvm)").is_none(), true);
191*bb4ee6a4SAndroid Build Coastguard Worker     }
192*bb4ee6a4SAndroid Build Coastguard Worker }
193