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