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