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 //! crate for the vmm-swap feature.
6*bb4ee6a4SAndroid Build Coastguard Worker
7*bb4ee6a4SAndroid Build Coastguard Worker #![deny(missing_docs)]
8*bb4ee6a4SAndroid Build Coastguard Worker
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::OpenOptions;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::io::stderr;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::io::stdout;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Range;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::fs::OpenOptionsExt;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::thread::Scope;
17*bb4ee6a4SAndroid Build Coastguard Worker use std::thread::ScopedJoinHandle;
18*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
19*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Instant;
20*bb4ee6a4SAndroid Build Coastguard Worker
21*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
22*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
23*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
24*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::info;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::FileDataIterator;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::syslog;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
29*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptor;
30*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptors;
31*bb4ee6a4SAndroid Build Coastguard Worker use base::EventToken;
32*bb4ee6a4SAndroid Build Coastguard Worker use base::RawDescriptor;
33*bb4ee6a4SAndroid Build Coastguard Worker use base::SendTube;
34*bb4ee6a4SAndroid Build Coastguard Worker use base::SharedMemory;
35*bb4ee6a4SAndroid Build Coastguard Worker use base::Tube;
36*bb4ee6a4SAndroid Build Coastguard Worker use base::TubeError;
37*bb4ee6a4SAndroid Build Coastguard Worker use base::WaitContext;
38*bb4ee6a4SAndroid Build Coastguard Worker use jail::create_base_minijail;
39*bb4ee6a4SAndroid Build Coastguard Worker use jail::create_sandbox_minijail;
40*bb4ee6a4SAndroid Build Coastguard Worker use jail::fork::fork_process;
41*bb4ee6a4SAndroid Build Coastguard Worker use jail::fork::Child;
42*bb4ee6a4SAndroid Build Coastguard Worker use jail::JailConfig;
43*bb4ee6a4SAndroid Build Coastguard Worker use jail::SandboxConfig;
44*bb4ee6a4SAndroid Build Coastguard Worker use jail::MAX_OPEN_FILES_DEFAULT;
45*bb4ee6a4SAndroid Build Coastguard Worker use once_cell::sync::Lazy;
46*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize;
47*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize;
48*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
49*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
50*bb4ee6a4SAndroid Build Coastguard Worker
51*bb4ee6a4SAndroid Build Coastguard Worker use crate::file_truncator::FileTruncator;
52*bb4ee6a4SAndroid Build Coastguard Worker use crate::page_handler::Error as PageHandlerError;
53*bb4ee6a4SAndroid Build Coastguard Worker use crate::page_handler::MoveToStaging;
54*bb4ee6a4SAndroid Build Coastguard Worker use crate::page_handler::PageHandler;
55*bb4ee6a4SAndroid Build Coastguard Worker use crate::page_handler::MLOCK_BUDGET;
56*bb4ee6a4SAndroid Build Coastguard Worker use crate::pagesize::bytes_to_pages;
57*bb4ee6a4SAndroid Build Coastguard Worker use crate::pagesize::THP_SIZE;
58*bb4ee6a4SAndroid Build Coastguard Worker use crate::processes::freeze_child_processes;
59*bb4ee6a4SAndroid Build Coastguard Worker use crate::processes::ProcessesGuard;
60*bb4ee6a4SAndroid Build Coastguard Worker use crate::uffd_list::Token as UffdListToken;
61*bb4ee6a4SAndroid Build Coastguard Worker use crate::uffd_list::UffdList;
62*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::register_regions;
63*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::unregister_regions;
64*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::DeadUffdCheckerImpl;
65*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::Error as UffdError;
66*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::Factory as UffdFactory;
67*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::UffdEvent;
68*bb4ee6a4SAndroid Build Coastguard Worker use crate::userfaultfd::Userfaultfd;
69*bb4ee6a4SAndroid Build Coastguard Worker use crate::worker::BackgroundJobControl;
70*bb4ee6a4SAndroid Build Coastguard Worker use crate::worker::Worker;
71*bb4ee6a4SAndroid Build Coastguard Worker use crate::SwapMetrics;
72*bb4ee6a4SAndroid Build Coastguard Worker use crate::SwapState;
73*bb4ee6a4SAndroid Build Coastguard Worker use crate::SwapStateTransition;
74*bb4ee6a4SAndroid Build Coastguard Worker use crate::SwapStatus;
75*bb4ee6a4SAndroid Build Coastguard Worker
76*bb4ee6a4SAndroid Build Coastguard Worker /// The max size of chunks to swap out/in at once.
77*bb4ee6a4SAndroid Build Coastguard Worker const MAX_SWAP_CHUNK_SIZE: usize = 2 * 1024 * 1024; // = 2MB
78*bb4ee6a4SAndroid Build Coastguard Worker /// The max pages to trim at once.
79*bb4ee6a4SAndroid Build Coastguard Worker const MAX_TRIM_PAGES: usize = 1024;
80*bb4ee6a4SAndroid Build Coastguard Worker
81*bb4ee6a4SAndroid Build Coastguard Worker /// Returns count of pages active on the guest memory.
count_resident_pages(guest_memory: &GuestMemory) -> usize82*bb4ee6a4SAndroid Build Coastguard Worker fn count_resident_pages(guest_memory: &GuestMemory) -> usize {
83*bb4ee6a4SAndroid Build Coastguard Worker let mut pages = 0;
84*bb4ee6a4SAndroid Build Coastguard Worker for region in guest_memory.regions() {
85*bb4ee6a4SAndroid Build Coastguard Worker let mut resident_bytes = 0u64;
86*bb4ee6a4SAndroid Build Coastguard Worker for range in FileDataIterator::new(region.shm, region.shm_offset, region.size as u64) {
87*bb4ee6a4SAndroid Build Coastguard Worker let range = match range {
88*bb4ee6a4SAndroid Build Coastguard Worker Ok(r) => r,
89*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
90*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to iterate data ranges: {e:?}");
91*bb4ee6a4SAndroid Build Coastguard Worker return 0;
92*bb4ee6a4SAndroid Build Coastguard Worker }
93*bb4ee6a4SAndroid Build Coastguard Worker };
94*bb4ee6a4SAndroid Build Coastguard Worker resident_bytes += range.end - range.start;
95*bb4ee6a4SAndroid Build Coastguard Worker }
96*bb4ee6a4SAndroid Build Coastguard Worker let resident_bytes = match resident_bytes.try_into() {
97*bb4ee6a4SAndroid Build Coastguard Worker Ok(n) => n,
98*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
99*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to load resident pages count: {:?}", e);
100*bb4ee6a4SAndroid Build Coastguard Worker return 0;
101*bb4ee6a4SAndroid Build Coastguard Worker }
102*bb4ee6a4SAndroid Build Coastguard Worker };
103*bb4ee6a4SAndroid Build Coastguard Worker
104*bb4ee6a4SAndroid Build Coastguard Worker pages += bytes_to_pages(resident_bytes);
105*bb4ee6a4SAndroid Build Coastguard Worker }
106*bb4ee6a4SAndroid Build Coastguard Worker pages
107*bb4ee6a4SAndroid Build Coastguard Worker }
108*bb4ee6a4SAndroid Build Coastguard Worker
109*bb4ee6a4SAndroid Build Coastguard Worker /// Commands used in vmm-swap feature internally sent to the monitor process from the main and other
110*bb4ee6a4SAndroid Build Coastguard Worker /// processes.
111*bb4ee6a4SAndroid Build Coastguard Worker ///
112*bb4ee6a4SAndroid Build Coastguard Worker /// This is mainly originated from the `crosvm swap <command>` command line.
113*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Serialize, Deserialize)]
114*bb4ee6a4SAndroid Build Coastguard Worker enum Command {
115*bb4ee6a4SAndroid Build Coastguard Worker Enable,
116*bb4ee6a4SAndroid Build Coastguard Worker Trim,
117*bb4ee6a4SAndroid Build Coastguard Worker SwapOut,
118*bb4ee6a4SAndroid Build Coastguard Worker Disable {
119*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup: bool,
120*bb4ee6a4SAndroid Build Coastguard Worker },
121*bb4ee6a4SAndroid Build Coastguard Worker Exit,
122*bb4ee6a4SAndroid Build Coastguard Worker Status,
123*bb4ee6a4SAndroid Build Coastguard Worker ProcessForked {
124*bb4ee6a4SAndroid Build Coastguard Worker #[serde(with = "base::with_as_descriptor")]
125*bb4ee6a4SAndroid Build Coastguard Worker uffd: Userfaultfd,
126*bb4ee6a4SAndroid Build Coastguard Worker reply_tube: Tube,
127*bb4ee6a4SAndroid Build Coastguard Worker },
128*bb4ee6a4SAndroid Build Coastguard Worker StaticDeviceSetupComplete(u32),
129*bb4ee6a4SAndroid Build Coastguard Worker }
130*bb4ee6a4SAndroid Build Coastguard Worker
131*bb4ee6a4SAndroid Build Coastguard Worker /// [SwapController] provides APIs to control vmm-swap.
132*bb4ee6a4SAndroid Build Coastguard Worker pub struct SwapController {
133*bb4ee6a4SAndroid Build Coastguard Worker child_process: Option<Child>,
134*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory: UffdFactory,
135*bb4ee6a4SAndroid Build Coastguard Worker command_tube: Tube,
136*bb4ee6a4SAndroid Build Coastguard Worker num_static_devices: u32,
137*bb4ee6a4SAndroid Build Coastguard Worker // Keep 1 page dummy mmap in the main process to make it present in all the descendant
138*bb4ee6a4SAndroid Build Coastguard Worker // processes.
139*bb4ee6a4SAndroid Build Coastguard Worker _dead_uffd_checker: DeadUffdCheckerImpl,
140*bb4ee6a4SAndroid Build Coastguard Worker // Keep the cloned [GuestMemory] in the main process not to free it before the monitor process
141*bb4ee6a4SAndroid Build Coastguard Worker // exits.
142*bb4ee6a4SAndroid Build Coastguard Worker _guest_memory: GuestMemory,
143*bb4ee6a4SAndroid Build Coastguard Worker }
144*bb4ee6a4SAndroid Build Coastguard Worker
145*bb4ee6a4SAndroid Build Coastguard Worker impl SwapController {
146*bb4ee6a4SAndroid Build Coastguard Worker /// Launch a monitor process for vmm-swap and return a controller.
147*bb4ee6a4SAndroid Build Coastguard Worker ///
148*bb4ee6a4SAndroid Build Coastguard Worker /// Pages on the [GuestMemory] are registered to userfaultfd to track pagefault events.
149*bb4ee6a4SAndroid Build Coastguard Worker ///
150*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
151*bb4ee6a4SAndroid Build Coastguard Worker ///
152*bb4ee6a4SAndroid Build Coastguard Worker /// * `guest_memory` - fresh new [GuestMemory]. Any pages on the [GuestMemory] must not be
153*bb4ee6a4SAndroid Build Coastguard Worker /// touched.
154*bb4ee6a4SAndroid Build Coastguard Worker /// * `swap_dir` - directory to store swap files.
launch( guest_memory: GuestMemory, swap_dir: &Path, jail_config: &Option<JailConfig>, ) -> anyhow::Result<Self>155*bb4ee6a4SAndroid Build Coastguard Worker pub fn launch(
156*bb4ee6a4SAndroid Build Coastguard Worker guest_memory: GuestMemory,
157*bb4ee6a4SAndroid Build Coastguard Worker swap_dir: &Path,
158*bb4ee6a4SAndroid Build Coastguard Worker jail_config: &Option<JailConfig>,
159*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<Self> {
160*bb4ee6a4SAndroid Build Coastguard Worker info!("vmm-swap is enabled. launch monitor process.");
161*bb4ee6a4SAndroid Build Coastguard Worker
162*bb4ee6a4SAndroid Build Coastguard Worker let preserved_guest_memory = guest_memory.clone();
163*bb4ee6a4SAndroid Build Coastguard Worker
164*bb4ee6a4SAndroid Build Coastguard Worker let uffd_factory = UffdFactory::new();
165*bb4ee6a4SAndroid Build Coastguard Worker let uffd = uffd_factory.create().context("create userfaultfd")?;
166*bb4ee6a4SAndroid Build Coastguard Worker
167*bb4ee6a4SAndroid Build Coastguard Worker // The swap file is created as `O_TMPFILE` from the specified directory. As benefits:
168*bb4ee6a4SAndroid Build Coastguard Worker //
169*bb4ee6a4SAndroid Build Coastguard Worker // * it has no chance to conflict.
170*bb4ee6a4SAndroid Build Coastguard Worker // * it has a security benefit that no one (except root) can access the swap file.
171*bb4ee6a4SAndroid Build Coastguard Worker // * it will be automatically deleted by the kernel when crosvm exits/dies or on reboot if
172*bb4ee6a4SAndroid Build Coastguard Worker // the device panics/hard-resets while crosvm is running.
173*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = OpenOptions::new()
174*bb4ee6a4SAndroid Build Coastguard Worker .read(true)
175*bb4ee6a4SAndroid Build Coastguard Worker .write(true)
176*bb4ee6a4SAndroid Build Coastguard Worker .custom_flags(libc::O_TMPFILE | libc::O_EXCL)
177*bb4ee6a4SAndroid Build Coastguard Worker .mode(0o000) // other processes with the same uid can't open the file
178*bb4ee6a4SAndroid Build Coastguard Worker .open(swap_dir)?;
179*bb4ee6a4SAndroid Build Coastguard Worker // The internal tube in which [Command]s sent from other processes than the monitor process
180*bb4ee6a4SAndroid Build Coastguard Worker // to the monitor process. The response is `Status` only.
181*bb4ee6a4SAndroid Build Coastguard Worker let (command_tube_main, command_tube_monitor) =
182*bb4ee6a4SAndroid Build Coastguard Worker Tube::pair().context("create swap command tube")?;
183*bb4ee6a4SAndroid Build Coastguard Worker
184*bb4ee6a4SAndroid Build Coastguard Worker // Allocate eventfd before creating sandbox.
185*bb4ee6a4SAndroid Build Coastguard Worker let bg_job_control = BackgroundJobControl::new().context("create background job event")?;
186*bb4ee6a4SAndroid Build Coastguard Worker
187*bb4ee6a4SAndroid Build Coastguard Worker let dead_uffd_checker = DeadUffdCheckerImpl::new().context("create dead uffd checker")?;
188*bb4ee6a4SAndroid Build Coastguard Worker
189*bb4ee6a4SAndroid Build Coastguard Worker let mut keep_rds = vec![
190*bb4ee6a4SAndroid Build Coastguard Worker stdout().as_raw_descriptor(),
191*bb4ee6a4SAndroid Build Coastguard Worker stderr().as_raw_descriptor(),
192*bb4ee6a4SAndroid Build Coastguard Worker uffd.as_raw_descriptor(),
193*bb4ee6a4SAndroid Build Coastguard Worker swap_file.as_raw_descriptor(),
194*bb4ee6a4SAndroid Build Coastguard Worker command_tube_monitor.as_raw_descriptor(),
195*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.get_completion_event().as_raw_descriptor(),
196*bb4ee6a4SAndroid Build Coastguard Worker ];
197*bb4ee6a4SAndroid Build Coastguard Worker
198*bb4ee6a4SAndroid Build Coastguard Worker syslog::push_descriptors(&mut keep_rds);
199*bb4ee6a4SAndroid Build Coastguard Worker cros_tracing::push_descriptors!(&mut keep_rds);
200*bb4ee6a4SAndroid Build Coastguard Worker metrics::push_descriptors(&mut keep_rds);
201*bb4ee6a4SAndroid Build Coastguard Worker keep_rds.extend(guest_memory.as_raw_descriptors());
202*bb4ee6a4SAndroid Build Coastguard Worker
203*bb4ee6a4SAndroid Build Coastguard Worker keep_rds.extend(uffd_factory.as_raw_descriptors());
204*bb4ee6a4SAndroid Build Coastguard Worker
205*bb4ee6a4SAndroid Build Coastguard Worker // Load and cache transparent hugepage size from sysfs before jumping into sandbox.
206*bb4ee6a4SAndroid Build Coastguard Worker Lazy::force(&THP_SIZE);
207*bb4ee6a4SAndroid Build Coastguard Worker
208*bb4ee6a4SAndroid Build Coastguard Worker let mut jail = if let Some(jail_config) = jail_config {
209*bb4ee6a4SAndroid Build Coastguard Worker let config = SandboxConfig::new(jail_config, "swap_monitor");
210*bb4ee6a4SAndroid Build Coastguard Worker create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)
211*bb4ee6a4SAndroid Build Coastguard Worker .context("create sandbox jail")?
212*bb4ee6a4SAndroid Build Coastguard Worker } else {
213*bb4ee6a4SAndroid Build Coastguard Worker create_base_minijail(Path::new("/"), MAX_OPEN_FILES_DEFAULT)
214*bb4ee6a4SAndroid Build Coastguard Worker .context("create minijail")?
215*bb4ee6a4SAndroid Build Coastguard Worker };
216*bb4ee6a4SAndroid Build Coastguard Worker jail.set_rlimit(
217*bb4ee6a4SAndroid Build Coastguard Worker libc::RLIMIT_MEMLOCK as libc::c_int,
218*bb4ee6a4SAndroid Build Coastguard Worker MLOCK_BUDGET as u64,
219*bb4ee6a4SAndroid Build Coastguard Worker MLOCK_BUDGET as u64,
220*bb4ee6a4SAndroid Build Coastguard Worker )
221*bb4ee6a4SAndroid Build Coastguard Worker .context("error setting RLIMIT_MEMLOCK")?;
222*bb4ee6a4SAndroid Build Coastguard Worker
223*bb4ee6a4SAndroid Build Coastguard Worker // Start a page fault monitoring process (this will be the first child process of the
224*bb4ee6a4SAndroid Build Coastguard Worker // current process)
225*bb4ee6a4SAndroid Build Coastguard Worker let child_process =
226*bb4ee6a4SAndroid Build Coastguard Worker fork_process(jail, keep_rds, Some(String::from("swap monitor")), || {
227*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = monitor_process(
228*bb4ee6a4SAndroid Build Coastguard Worker command_tube_monitor,
229*bb4ee6a4SAndroid Build Coastguard Worker guest_memory,
230*bb4ee6a4SAndroid Build Coastguard Worker uffd,
231*bb4ee6a4SAndroid Build Coastguard Worker swap_file,
232*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control,
233*bb4ee6a4SAndroid Build Coastguard Worker &dead_uffd_checker,
234*bb4ee6a4SAndroid Build Coastguard Worker ) {
235*bb4ee6a4SAndroid Build Coastguard Worker if let Some(PageHandlerError::Userfaultfd(UffdError::UffdClosed)) =
236*bb4ee6a4SAndroid Build Coastguard Worker e.downcast_ref::<PageHandlerError>()
237*bb4ee6a4SAndroid Build Coastguard Worker {
238*bb4ee6a4SAndroid Build Coastguard Worker // Userfaultfd can cause UffdError::UffdClosed if the main process
239*bb4ee6a4SAndroid Build Coastguard Worker // unexpectedly while it is swapping in. This is not a bug of swap monitor,
240*bb4ee6a4SAndroid Build Coastguard Worker // but the other feature on the main process.
241*bb4ee6a4SAndroid Build Coastguard Worker // Note that UffdError::UffdClosed from other processes than the main
242*bb4ee6a4SAndroid Build Coastguard Worker // process are derived from PageHandler::handle_page_fault() only and
243*bb4ee6a4SAndroid Build Coastguard Worker // handled in the loop of handle_vmm_swap().
244*bb4ee6a4SAndroid Build Coastguard Worker error!(
245*bb4ee6a4SAndroid Build Coastguard Worker "page_fault_handler_thread exited with userfaultfd closed error: {:#}",
246*bb4ee6a4SAndroid Build Coastguard Worker e
247*bb4ee6a4SAndroid Build Coastguard Worker );
248*bb4ee6a4SAndroid Build Coastguard Worker } else if e.is::<TubeError>() {
249*bb4ee6a4SAndroid Build Coastguard Worker // Tube can cause TubeError if the main process unexpectedly dies. This is
250*bb4ee6a4SAndroid Build Coastguard Worker // not a bug of swap monitor, but the other feature on the main process.
251*bb4ee6a4SAndroid Build Coastguard Worker // Even if the tube itself is broken and the main process is alive, the main
252*bb4ee6a4SAndroid Build Coastguard Worker // process catch that the swap monitor process exits unexpectedly and
253*bb4ee6a4SAndroid Build Coastguard Worker // terminates itself.
254*bb4ee6a4SAndroid Build Coastguard Worker error!("page_fault_handler_thread exited with tube error: {:#}", e);
255*bb4ee6a4SAndroid Build Coastguard Worker } else {
256*bb4ee6a4SAndroid Build Coastguard Worker panic!("page_fault_handler_thread exited with error: {:#}", e);
257*bb4ee6a4SAndroid Build Coastguard Worker }
258*bb4ee6a4SAndroid Build Coastguard Worker }
259*bb4ee6a4SAndroid Build Coastguard Worker })
260*bb4ee6a4SAndroid Build Coastguard Worker .context("fork monitor process")?;
261*bb4ee6a4SAndroid Build Coastguard Worker
262*bb4ee6a4SAndroid Build Coastguard Worker // send first status request to the monitor process and wait for the response until setup on
263*bb4ee6a4SAndroid Build Coastguard Worker // the monitor process completes.
264*bb4ee6a4SAndroid Build Coastguard Worker command_tube_main.send(&Command::Status)?;
265*bb4ee6a4SAndroid Build Coastguard Worker match command_tube_main
266*bb4ee6a4SAndroid Build Coastguard Worker .recv::<SwapStatus>()
267*bb4ee6a4SAndroid Build Coastguard Worker .context("recv initial status")?
268*bb4ee6a4SAndroid Build Coastguard Worker .state
269*bb4ee6a4SAndroid Build Coastguard Worker {
270*bb4ee6a4SAndroid Build Coastguard Worker SwapState::Ready => {
271*bb4ee6a4SAndroid Build Coastguard Worker // The initial state of swap status is Ready and this is a signal that the
272*bb4ee6a4SAndroid Build Coastguard Worker // monitoring process completes setup and is running.
273*bb4ee6a4SAndroid Build Coastguard Worker }
274*bb4ee6a4SAndroid Build Coastguard Worker status => {
275*bb4ee6a4SAndroid Build Coastguard Worker bail!("initial state is not Ready, but {:?}", status);
276*bb4ee6a4SAndroid Build Coastguard Worker }
277*bb4ee6a4SAndroid Build Coastguard Worker };
278*bb4ee6a4SAndroid Build Coastguard Worker
279*bb4ee6a4SAndroid Build Coastguard Worker Ok(Self {
280*bb4ee6a4SAndroid Build Coastguard Worker child_process: Some(child_process),
281*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory,
282*bb4ee6a4SAndroid Build Coastguard Worker command_tube: command_tube_main,
283*bb4ee6a4SAndroid Build Coastguard Worker num_static_devices: 0,
284*bb4ee6a4SAndroid Build Coastguard Worker _dead_uffd_checker: dead_uffd_checker,
285*bb4ee6a4SAndroid Build Coastguard Worker _guest_memory: preserved_guest_memory,
286*bb4ee6a4SAndroid Build Coastguard Worker })
287*bb4ee6a4SAndroid Build Coastguard Worker }
288*bb4ee6a4SAndroid Build Coastguard Worker
289*bb4ee6a4SAndroid Build Coastguard Worker /// Enable monitoring page faults and move guest memory to staging memory.
290*bb4ee6a4SAndroid Build Coastguard Worker ///
291*bb4ee6a4SAndroid Build Coastguard Worker /// The pages will be swapped in from the staging memory to the guest memory on page faults
292*bb4ee6a4SAndroid Build Coastguard Worker /// until pages are written into the swap file by [Self::swap_out()].
293*bb4ee6a4SAndroid Build Coastguard Worker ///
294*bb4ee6a4SAndroid Build Coastguard Worker /// This waits until enabling vmm-swap finishes on the monitor process.
295*bb4ee6a4SAndroid Build Coastguard Worker ///
296*bb4ee6a4SAndroid Build Coastguard Worker /// The caller must guarantee that any contents on the guest memory is not updated during
297*bb4ee6a4SAndroid Build Coastguard Worker /// enabling vmm-swap.
298*bb4ee6a4SAndroid Build Coastguard Worker ///
299*bb4ee6a4SAndroid Build Coastguard Worker /// # Note
300*bb4ee6a4SAndroid Build Coastguard Worker ///
301*bb4ee6a4SAndroid Build Coastguard Worker /// Enabling does not write pages to the swap file. User should call [Self::swap_out()]
302*bb4ee6a4SAndroid Build Coastguard Worker /// after a suitable time.
303*bb4ee6a4SAndroid Build Coastguard Worker ///
304*bb4ee6a4SAndroid Build Coastguard Worker /// Just after enabling vmm-swap, some amount of pages are swapped in as soon as guest resumes.
305*bb4ee6a4SAndroid Build Coastguard Worker /// By splitting the enable/swap_out operation and by delaying write to the swap file operation,
306*bb4ee6a4SAndroid Build Coastguard Worker /// it has a benefit of reducing file I/O for hot pages.
enable(&self) -> anyhow::Result<()>307*bb4ee6a4SAndroid Build Coastguard Worker pub fn enable(&self) -> anyhow::Result<()> {
308*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
309*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::Enable)
310*bb4ee6a4SAndroid Build Coastguard Worker .context("send swap enable request")?;
311*bb4ee6a4SAndroid Build Coastguard Worker
312*bb4ee6a4SAndroid Build Coastguard Worker let _ = self
313*bb4ee6a4SAndroid Build Coastguard Worker .command_tube
314*bb4ee6a4SAndroid Build Coastguard Worker .recv::<SwapStatus>()
315*bb4ee6a4SAndroid Build Coastguard Worker .context("receive swap status")?;
316*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
317*bb4ee6a4SAndroid Build Coastguard Worker }
318*bb4ee6a4SAndroid Build Coastguard Worker
319*bb4ee6a4SAndroid Build Coastguard Worker /// Trim pages in the staging memory which are needless to be written back to the swap file.
320*bb4ee6a4SAndroid Build Coastguard Worker ///
321*bb4ee6a4SAndroid Build Coastguard Worker /// * zero pages
322*bb4ee6a4SAndroid Build Coastguard Worker /// * pages which are the same as the pages in the swap file.
trim(&self) -> anyhow::Result<()>323*bb4ee6a4SAndroid Build Coastguard Worker pub fn trim(&self) -> anyhow::Result<()> {
324*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
325*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::Trim)
326*bb4ee6a4SAndroid Build Coastguard Worker .context("send swap trim request")?;
327*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
328*bb4ee6a4SAndroid Build Coastguard Worker }
329*bb4ee6a4SAndroid Build Coastguard Worker
330*bb4ee6a4SAndroid Build Coastguard Worker /// Swap out all the pages in the staging memory to the swap files.
331*bb4ee6a4SAndroid Build Coastguard Worker ///
332*bb4ee6a4SAndroid Build Coastguard Worker /// This returns as soon as it succeeds to send request to the monitor process.
333*bb4ee6a4SAndroid Build Coastguard Worker ///
334*bb4ee6a4SAndroid Build Coastguard Worker /// Users should call [Self::enable()] before this. See the comment of [Self::enable()] as well.
swap_out(&self) -> anyhow::Result<()>335*bb4ee6a4SAndroid Build Coastguard Worker pub fn swap_out(&self) -> anyhow::Result<()> {
336*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
337*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::SwapOut)
338*bb4ee6a4SAndroid Build Coastguard Worker .context("send swap out request")?;
339*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
340*bb4ee6a4SAndroid Build Coastguard Worker }
341*bb4ee6a4SAndroid Build Coastguard Worker
342*bb4ee6a4SAndroid Build Coastguard Worker /// Swap in all the guest memory and disable monitoring page faults.
343*bb4ee6a4SAndroid Build Coastguard Worker ///
344*bb4ee6a4SAndroid Build Coastguard Worker /// This returns as soon as it succeeds to send request to the monitor process.
disable(&self, slow_file_cleanup: bool) -> anyhow::Result<()>345*bb4ee6a4SAndroid Build Coastguard Worker pub fn disable(&self, slow_file_cleanup: bool) -> anyhow::Result<()> {
346*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
347*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::Disable { slow_file_cleanup })
348*bb4ee6a4SAndroid Build Coastguard Worker .context("send swap disable request")?;
349*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
350*bb4ee6a4SAndroid Build Coastguard Worker }
351*bb4ee6a4SAndroid Build Coastguard Worker
352*bb4ee6a4SAndroid Build Coastguard Worker /// Return current swap status.
353*bb4ee6a4SAndroid Build Coastguard Worker ///
354*bb4ee6a4SAndroid Build Coastguard Worker /// This blocks until response from the monitor process arrives to the main process.
status(&self) -> anyhow::Result<SwapStatus>355*bb4ee6a4SAndroid Build Coastguard Worker pub fn status(&self) -> anyhow::Result<SwapStatus> {
356*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
357*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::Status)
358*bb4ee6a4SAndroid Build Coastguard Worker .context("send swap status request")?;
359*bb4ee6a4SAndroid Build Coastguard Worker let status = self.command_tube.recv().context("receive swap status")?;
360*bb4ee6a4SAndroid Build Coastguard Worker Ok(status)
361*bb4ee6a4SAndroid Build Coastguard Worker }
362*bb4ee6a4SAndroid Build Coastguard Worker
363*bb4ee6a4SAndroid Build Coastguard Worker /// Suspend device processes using `SIGSTOP` signal.
364*bb4ee6a4SAndroid Build Coastguard Worker ///
365*bb4ee6a4SAndroid Build Coastguard Worker /// When the returned `ProcessesGuard` is dropped, the devices resume.
366*bb4ee6a4SAndroid Build Coastguard Worker ///
367*bb4ee6a4SAndroid Build Coastguard Worker /// This must be called from the main process.
suspend_devices(&self) -> anyhow::Result<ProcessesGuard>368*bb4ee6a4SAndroid Build Coastguard Worker pub fn suspend_devices(&self) -> anyhow::Result<ProcessesGuard> {
369*bb4ee6a4SAndroid Build Coastguard Worker // child_process become none on dropping SwapController.
370*bb4ee6a4SAndroid Build Coastguard Worker freeze_child_processes(
371*bb4ee6a4SAndroid Build Coastguard Worker self.child_process
372*bb4ee6a4SAndroid Build Coastguard Worker .as_ref()
373*bb4ee6a4SAndroid Build Coastguard Worker .expect("monitor process not exist")
374*bb4ee6a4SAndroid Build Coastguard Worker .pid,
375*bb4ee6a4SAndroid Build Coastguard Worker )
376*bb4ee6a4SAndroid Build Coastguard Worker }
377*bb4ee6a4SAndroid Build Coastguard Worker
378*bb4ee6a4SAndroid Build Coastguard Worker /// Notify the monitor process that all static devices are forked.
379*bb4ee6a4SAndroid Build Coastguard Worker ///
380*bb4ee6a4SAndroid Build Coastguard Worker /// Devices forked after this call are treated as dynamic devices which can die (e.g. hotplug
381*bb4ee6a4SAndroid Build Coastguard Worker /// devices).
on_static_devices_setup_complete(&self) -> anyhow::Result<()>382*bb4ee6a4SAndroid Build Coastguard Worker pub fn on_static_devices_setup_complete(&self) -> anyhow::Result<()> {
383*bb4ee6a4SAndroid Build Coastguard Worker // This sends the number of static devices counted on the main process because device
384*bb4ee6a4SAndroid Build Coastguard Worker // initializations are executed on child processes asynchronously.
385*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
386*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::StaticDeviceSetupComplete(self.num_static_devices))
387*bb4ee6a4SAndroid Build Coastguard Worker .context("send command")
388*bb4ee6a4SAndroid Build Coastguard Worker }
389*bb4ee6a4SAndroid Build Coastguard Worker
390*bb4ee6a4SAndroid Build Coastguard Worker /// Create [SwapDeviceHelper].
create_device_helper(&self) -> anyhow::Result<SwapDeviceHelper>391*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_device_helper(&self) -> anyhow::Result<SwapDeviceHelper> {
392*bb4ee6a4SAndroid Build Coastguard Worker let uffd_factory = self
393*bb4ee6a4SAndroid Build Coastguard Worker .uffd_factory
394*bb4ee6a4SAndroid Build Coastguard Worker .try_clone()
395*bb4ee6a4SAndroid Build Coastguard Worker .context("try clone uffd factory")?;
396*bb4ee6a4SAndroid Build Coastguard Worker let command_tube = self
397*bb4ee6a4SAndroid Build Coastguard Worker .command_tube
398*bb4ee6a4SAndroid Build Coastguard Worker .try_clone_send_tube()
399*bb4ee6a4SAndroid Build Coastguard Worker .context("try clone tube")?;
400*bb4ee6a4SAndroid Build Coastguard Worker Ok(SwapDeviceHelper {
401*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory,
402*bb4ee6a4SAndroid Build Coastguard Worker command_tube,
403*bb4ee6a4SAndroid Build Coastguard Worker })
404*bb4ee6a4SAndroid Build Coastguard Worker }
405*bb4ee6a4SAndroid Build Coastguard Worker }
406*bb4ee6a4SAndroid Build Coastguard Worker
407*bb4ee6a4SAndroid Build Coastguard Worker impl Drop for SwapController {
drop(&mut self)408*bb4ee6a4SAndroid Build Coastguard Worker fn drop(&mut self) {
409*bb4ee6a4SAndroid Build Coastguard Worker // Shutdown the monitor process.
410*bb4ee6a4SAndroid Build Coastguard Worker // This blocks until the monitor process exits.
411*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = self.command_tube.send(&Command::Exit) {
412*bb4ee6a4SAndroid Build Coastguard Worker error!(
413*bb4ee6a4SAndroid Build Coastguard Worker "failed to sent exit command to vmm-swap monitor process: {:#}",
414*bb4ee6a4SAndroid Build Coastguard Worker e
415*bb4ee6a4SAndroid Build Coastguard Worker );
416*bb4ee6a4SAndroid Build Coastguard Worker return;
417*bb4ee6a4SAndroid Build Coastguard Worker }
418*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = self
419*bb4ee6a4SAndroid Build Coastguard Worker .child_process
420*bb4ee6a4SAndroid Build Coastguard Worker .take()
421*bb4ee6a4SAndroid Build Coastguard Worker .expect("monitor process not exist")
422*bb4ee6a4SAndroid Build Coastguard Worker .wait()
423*bb4ee6a4SAndroid Build Coastguard Worker {
424*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to wait vmm-swap monitor process shutdown: {:#}", e);
425*bb4ee6a4SAndroid Build Coastguard Worker }
426*bb4ee6a4SAndroid Build Coastguard Worker }
427*bb4ee6a4SAndroid Build Coastguard Worker }
428*bb4ee6a4SAndroid Build Coastguard Worker
429*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new [SwapDeviceUffdSender] which is passed to the forked child process.
430*bb4ee6a4SAndroid Build Coastguard Worker pub trait PrepareFork {
431*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new [SwapDeviceUffdSender].
prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender>432*bb4ee6a4SAndroid Build Coastguard Worker fn prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender>;
433*bb4ee6a4SAndroid Build Coastguard Worker }
434*bb4ee6a4SAndroid Build Coastguard Worker
435*bb4ee6a4SAndroid Build Coastguard Worker impl PrepareFork for SwapController {
436*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new [SwapDeviceUffdSender].
437*bb4ee6a4SAndroid Build Coastguard Worker ///
438*bb4ee6a4SAndroid Build Coastguard Worker /// This should be called from the main process because creating a [Tube]s requires seccomp
439*bb4ee6a4SAndroid Build Coastguard Worker /// policy.
440*bb4ee6a4SAndroid Build Coastguard Worker ///
441*bb4ee6a4SAndroid Build Coastguard Worker /// This also counts the number of static devices which are created before booting.
prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender>442*bb4ee6a4SAndroid Build Coastguard Worker fn prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender> {
443*bb4ee6a4SAndroid Build Coastguard Worker let command_tube = self
444*bb4ee6a4SAndroid Build Coastguard Worker .command_tube
445*bb4ee6a4SAndroid Build Coastguard Worker .try_clone_send_tube()
446*bb4ee6a4SAndroid Build Coastguard Worker .context("try clone tube")?;
447*bb4ee6a4SAndroid Build Coastguard Worker self.num_static_devices += 1;
448*bb4ee6a4SAndroid Build Coastguard Worker SwapDeviceUffdSender::new(command_tube, &self.uffd_factory)
449*bb4ee6a4SAndroid Build Coastguard Worker }
450*bb4ee6a4SAndroid Build Coastguard Worker }
451*bb4ee6a4SAndroid Build Coastguard Worker
452*bb4ee6a4SAndroid Build Coastguard Worker /// Helper to create [SwapDeviceUffdSender] from child processes (e.g. JailWarden for hotplug
453*bb4ee6a4SAndroid Build Coastguard Worker /// devices).
454*bb4ee6a4SAndroid Build Coastguard Worker pub struct SwapDeviceHelper {
455*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory: UffdFactory,
456*bb4ee6a4SAndroid Build Coastguard Worker command_tube: SendTube,
457*bb4ee6a4SAndroid Build Coastguard Worker }
458*bb4ee6a4SAndroid Build Coastguard Worker
459*bb4ee6a4SAndroid Build Coastguard Worker impl PrepareFork for SwapDeviceHelper {
460*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new [SwapDeviceUffdSender].
prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender>461*bb4ee6a4SAndroid Build Coastguard Worker fn prepare_fork(&mut self) -> anyhow::Result<SwapDeviceUffdSender> {
462*bb4ee6a4SAndroid Build Coastguard Worker let command_tube = self.command_tube.try_clone().context("try clone tube")?;
463*bb4ee6a4SAndroid Build Coastguard Worker SwapDeviceUffdSender::new(command_tube, &self.uffd_factory)
464*bb4ee6a4SAndroid Build Coastguard Worker }
465*bb4ee6a4SAndroid Build Coastguard Worker }
466*bb4ee6a4SAndroid Build Coastguard Worker
467*bb4ee6a4SAndroid Build Coastguard Worker impl AsRawDescriptors for SwapDeviceHelper {
as_raw_descriptors(&self) -> Vec<RawDescriptor>468*bb4ee6a4SAndroid Build Coastguard Worker fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
469*bb4ee6a4SAndroid Build Coastguard Worker let mut rds = self.uffd_factory.as_raw_descriptors();
470*bb4ee6a4SAndroid Build Coastguard Worker rds.push(self.command_tube.as_raw_descriptor());
471*bb4ee6a4SAndroid Build Coastguard Worker rds
472*bb4ee6a4SAndroid Build Coastguard Worker }
473*bb4ee6a4SAndroid Build Coastguard Worker }
474*bb4ee6a4SAndroid Build Coastguard Worker
475*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new userfaultfd and send it to the monitor process.
476*bb4ee6a4SAndroid Build Coastguard Worker pub struct SwapDeviceUffdSender {
477*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory: UffdFactory,
478*bb4ee6a4SAndroid Build Coastguard Worker command_tube: SendTube,
479*bb4ee6a4SAndroid Build Coastguard Worker sender: Tube,
480*bb4ee6a4SAndroid Build Coastguard Worker receiver: Tube,
481*bb4ee6a4SAndroid Build Coastguard Worker }
482*bb4ee6a4SAndroid Build Coastguard Worker
483*bb4ee6a4SAndroid Build Coastguard Worker impl SwapDeviceUffdSender {
new(command_tube: SendTube, uffd_factory: &UffdFactory) -> anyhow::Result<Self>484*bb4ee6a4SAndroid Build Coastguard Worker fn new(command_tube: SendTube, uffd_factory: &UffdFactory) -> anyhow::Result<Self> {
485*bb4ee6a4SAndroid Build Coastguard Worker let uffd_factory = uffd_factory.try_clone().context("try clone uffd factory")?;
486*bb4ee6a4SAndroid Build Coastguard Worker let (sender, receiver) = Tube::pair().context("create tube")?;
487*bb4ee6a4SAndroid Build Coastguard Worker receiver
488*bb4ee6a4SAndroid Build Coastguard Worker .set_recv_timeout(Some(Duration::from_secs(60)))
489*bb4ee6a4SAndroid Build Coastguard Worker .context("set recv timeout")?;
490*bb4ee6a4SAndroid Build Coastguard Worker Ok(SwapDeviceUffdSender {
491*bb4ee6a4SAndroid Build Coastguard Worker uffd_factory,
492*bb4ee6a4SAndroid Build Coastguard Worker command_tube,
493*bb4ee6a4SAndroid Build Coastguard Worker sender,
494*bb4ee6a4SAndroid Build Coastguard Worker receiver,
495*bb4ee6a4SAndroid Build Coastguard Worker })
496*bb4ee6a4SAndroid Build Coastguard Worker }
497*bb4ee6a4SAndroid Build Coastguard Worker
498*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new userfaultfd and send it to the monitor process.
499*bb4ee6a4SAndroid Build Coastguard Worker ///
500*bb4ee6a4SAndroid Build Coastguard Worker /// This must be called as soon as a child process which may touch the guest memory is forked.
501*bb4ee6a4SAndroid Build Coastguard Worker ///
502*bb4ee6a4SAndroid Build Coastguard Worker /// Userfaultfd(2) originally has `UFFD_FEATURE_EVENT_FORK`. But it is not applicable to crosvm
503*bb4ee6a4SAndroid Build Coastguard Worker /// since it does not support non-root user namespace.
on_process_forked(self) -> anyhow::Result<()>504*bb4ee6a4SAndroid Build Coastguard Worker pub fn on_process_forked(self) -> anyhow::Result<()> {
505*bb4ee6a4SAndroid Build Coastguard Worker let uffd = self.uffd_factory.create().context("create userfaultfd")?;
506*bb4ee6a4SAndroid Build Coastguard Worker // The fd for Userfaultfd in this process is dropped when it is sent via Tube, but the
507*bb4ee6a4SAndroid Build Coastguard Worker // userfaultfd keeps alive in the monitor process which it is sent to.
508*bb4ee6a4SAndroid Build Coastguard Worker self.command_tube
509*bb4ee6a4SAndroid Build Coastguard Worker .send(&Command::ProcessForked {
510*bb4ee6a4SAndroid Build Coastguard Worker uffd,
511*bb4ee6a4SAndroid Build Coastguard Worker reply_tube: self.sender,
512*bb4ee6a4SAndroid Build Coastguard Worker })
513*bb4ee6a4SAndroid Build Coastguard Worker .context("send forked event")?;
514*bb4ee6a4SAndroid Build Coastguard Worker // Wait to proceeds the child process logic until the userfaultfd is set up.
515*bb4ee6a4SAndroid Build Coastguard Worker if !self.receiver.recv::<bool>().context("recv tube")? {
516*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to register a new userfaultfd");
517*bb4ee6a4SAndroid Build Coastguard Worker }
518*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
519*bb4ee6a4SAndroid Build Coastguard Worker }
520*bb4ee6a4SAndroid Build Coastguard Worker }
521*bb4ee6a4SAndroid Build Coastguard Worker
522*bb4ee6a4SAndroid Build Coastguard Worker impl AsRawDescriptors for SwapDeviceUffdSender {
as_raw_descriptors(&self) -> Vec<RawDescriptor>523*bb4ee6a4SAndroid Build Coastguard Worker fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
524*bb4ee6a4SAndroid Build Coastguard Worker let mut rds = self.uffd_factory.as_raw_descriptors();
525*bb4ee6a4SAndroid Build Coastguard Worker rds.push(self.command_tube.as_raw_descriptor());
526*bb4ee6a4SAndroid Build Coastguard Worker rds.push(self.sender.as_raw_descriptor());
527*bb4ee6a4SAndroid Build Coastguard Worker rds.push(self.receiver.as_raw_descriptor());
528*bb4ee6a4SAndroid Build Coastguard Worker rds
529*bb4ee6a4SAndroid Build Coastguard Worker }
530*bb4ee6a4SAndroid Build Coastguard Worker }
531*bb4ee6a4SAndroid Build Coastguard Worker
532*bb4ee6a4SAndroid Build Coastguard Worker #[derive(EventToken, Clone, Copy)]
533*bb4ee6a4SAndroid Build Coastguard Worker enum Token {
534*bb4ee6a4SAndroid Build Coastguard Worker UffdEvents(u32),
535*bb4ee6a4SAndroid Build Coastguard Worker Command,
536*bb4ee6a4SAndroid Build Coastguard Worker BackgroundJobCompleted,
537*bb4ee6a4SAndroid Build Coastguard Worker }
538*bb4ee6a4SAndroid Build Coastguard Worker
539*bb4ee6a4SAndroid Build Coastguard Worker impl UffdListToken for Token {
uffd_token(idx: u32) -> Self540*bb4ee6a4SAndroid Build Coastguard Worker fn uffd_token(idx: u32) -> Self {
541*bb4ee6a4SAndroid Build Coastguard Worker Token::UffdEvents(idx)
542*bb4ee6a4SAndroid Build Coastguard Worker }
543*bb4ee6a4SAndroid Build Coastguard Worker }
544*bb4ee6a4SAndroid Build Coastguard Worker
regions_from_guest_memory(guest_memory: &GuestMemory) -> Vec<Range<usize>>545*bb4ee6a4SAndroid Build Coastguard Worker fn regions_from_guest_memory(guest_memory: &GuestMemory) -> Vec<Range<usize>> {
546*bb4ee6a4SAndroid Build Coastguard Worker guest_memory
547*bb4ee6a4SAndroid Build Coastguard Worker .regions()
548*bb4ee6a4SAndroid Build Coastguard Worker .map(|region| region.host_addr..(region.host_addr + region.size))
549*bb4ee6a4SAndroid Build Coastguard Worker .collect()
550*bb4ee6a4SAndroid Build Coastguard Worker }
551*bb4ee6a4SAndroid Build Coastguard Worker
552*bb4ee6a4SAndroid Build Coastguard Worker /// The main thread of the monitor process.
monitor_process( command_tube: Tube, guest_memory: GuestMemory, uffd: Userfaultfd, swap_file: File, bg_job_control: BackgroundJobControl, dead_uffd_checker: &DeadUffdCheckerImpl, ) -> anyhow::Result<()>553*bb4ee6a4SAndroid Build Coastguard Worker fn monitor_process(
554*bb4ee6a4SAndroid Build Coastguard Worker command_tube: Tube,
555*bb4ee6a4SAndroid Build Coastguard Worker guest_memory: GuestMemory,
556*bb4ee6a4SAndroid Build Coastguard Worker uffd: Userfaultfd,
557*bb4ee6a4SAndroid Build Coastguard Worker swap_file: File,
558*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control: BackgroundJobControl,
559*bb4ee6a4SAndroid Build Coastguard Worker dead_uffd_checker: &DeadUffdCheckerImpl,
560*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<()> {
561*bb4ee6a4SAndroid Build Coastguard Worker info!("monitor_process started");
562*bb4ee6a4SAndroid Build Coastguard Worker
563*bb4ee6a4SAndroid Build Coastguard Worker let wait_ctx = WaitContext::build_with(&[
564*bb4ee6a4SAndroid Build Coastguard Worker (&command_tube, Token::Command),
565*bb4ee6a4SAndroid Build Coastguard Worker (
566*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.get_completion_event(),
567*bb4ee6a4SAndroid Build Coastguard Worker Token::BackgroundJobCompleted,
568*bb4ee6a4SAndroid Build Coastguard Worker ),
569*bb4ee6a4SAndroid Build Coastguard Worker ])
570*bb4ee6a4SAndroid Build Coastguard Worker .context("create wait context")?;
571*bb4ee6a4SAndroid Build Coastguard Worker
572*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file_opt = Some(swap_file);
573*bb4ee6a4SAndroid Build Coastguard Worker let mut truncate_worker: Option<FileTruncator> = None;
574*bb4ee6a4SAndroid Build Coastguard Worker
575*bb4ee6a4SAndroid Build Coastguard Worker let n_worker = num_cpus::get();
576*bb4ee6a4SAndroid Build Coastguard Worker info!("start {} workers for staging memory move", n_worker);
577*bb4ee6a4SAndroid Build Coastguard Worker // The worker threads are killed when the main thread of the monitor process dies.
578*bb4ee6a4SAndroid Build Coastguard Worker let worker = Worker::new(n_worker, n_worker);
579*bb4ee6a4SAndroid Build Coastguard Worker
580*bb4ee6a4SAndroid Build Coastguard Worker let mut uffd_list =
581*bb4ee6a4SAndroid Build Coastguard Worker UffdList::new(uffd, dead_uffd_checker, &wait_ctx).context("create uffd list")?;
582*bb4ee6a4SAndroid Build Coastguard Worker let mut state_transition = SwapStateTransition::default();
583*bb4ee6a4SAndroid Build Coastguard Worker let mut try_gc_uffds = false;
584*bb4ee6a4SAndroid Build Coastguard Worker
585*bb4ee6a4SAndroid Build Coastguard Worker loop {
586*bb4ee6a4SAndroid Build Coastguard Worker let events = wait_ctx.wait().context("wait poll events")?;
587*bb4ee6a4SAndroid Build Coastguard Worker
588*bb4ee6a4SAndroid Build Coastguard Worker for event in events.iter() {
589*bb4ee6a4SAndroid Build Coastguard Worker match event.token {
590*bb4ee6a4SAndroid Build Coastguard Worker Token::UffdEvents(id_uffd) => {
591*bb4ee6a4SAndroid Build Coastguard Worker let uffd = uffd_list
592*bb4ee6a4SAndroid Build Coastguard Worker .get(id_uffd)
593*bb4ee6a4SAndroid Build Coastguard Worker .with_context(|| format!("uffd is not found for idx: {}", id_uffd))?;
594*bb4ee6a4SAndroid Build Coastguard Worker // Userfaultfd does not work as level triggered but as edge triggered. We need
595*bb4ee6a4SAndroid Build Coastguard Worker // to read all the events in the userfaultfd here.
596*bb4ee6a4SAndroid Build Coastguard Worker while let Some(event) = uffd.read_event().context("read userfaultfd event")? {
597*bb4ee6a4SAndroid Build Coastguard Worker match event {
598*bb4ee6a4SAndroid Build Coastguard Worker UffdEvent::Remove { .. } => {
599*bb4ee6a4SAndroid Build Coastguard Worker // BUG(b/272620051): This is a bug of userfaultfd that
600*bb4ee6a4SAndroid Build Coastguard Worker // UFFD_EVENT_REMOVE can be read even after unregistering memory
601*bb4ee6a4SAndroid Build Coastguard Worker // from the userfaultfd.
602*bb4ee6a4SAndroid Build Coastguard Worker warn!("page remove event while vmm-swap disabled");
603*bb4ee6a4SAndroid Build Coastguard Worker }
604*bb4ee6a4SAndroid Build Coastguard Worker event => {
605*bb4ee6a4SAndroid Build Coastguard Worker bail!("unexpected uffd event: {:?}", event);
606*bb4ee6a4SAndroid Build Coastguard Worker }
607*bb4ee6a4SAndroid Build Coastguard Worker }
608*bb4ee6a4SAndroid Build Coastguard Worker }
609*bb4ee6a4SAndroid Build Coastguard Worker }
610*bb4ee6a4SAndroid Build Coastguard Worker Token::Command => match command_tube
611*bb4ee6a4SAndroid Build Coastguard Worker .recv::<Command>()
612*bb4ee6a4SAndroid Build Coastguard Worker .context("recv swap command")?
613*bb4ee6a4SAndroid Build Coastguard Worker {
614*bb4ee6a4SAndroid Build Coastguard Worker Command::ProcessForked { uffd, reply_tube } => {
615*bb4ee6a4SAndroid Build Coastguard Worker debug!("new fork uffd: {:?}", uffd);
616*bb4ee6a4SAndroid Build Coastguard Worker let result = match uffd_list.register(uffd) {
617*bb4ee6a4SAndroid Build Coastguard Worker Ok(is_dynamic_uffd) => {
618*bb4ee6a4SAndroid Build Coastguard Worker try_gc_uffds = is_dynamic_uffd;
619*bb4ee6a4SAndroid Build Coastguard Worker true
620*bb4ee6a4SAndroid Build Coastguard Worker }
621*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
622*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to register uffd to list: {:?}", e);
623*bb4ee6a4SAndroid Build Coastguard Worker false
624*bb4ee6a4SAndroid Build Coastguard Worker }
625*bb4ee6a4SAndroid Build Coastguard Worker };
626*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = reply_tube.send(&result) {
627*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to response to new process: {:?}", e);
628*bb4ee6a4SAndroid Build Coastguard Worker }
629*bb4ee6a4SAndroid Build Coastguard Worker }
630*bb4ee6a4SAndroid Build Coastguard Worker Command::StaticDeviceSetupComplete(num_static_devices) => {
631*bb4ee6a4SAndroid Build Coastguard Worker info!("static device setup complete: n={}", num_static_devices);
632*bb4ee6a4SAndroid Build Coastguard Worker if !uffd_list.set_num_static_devices(num_static_devices) {
633*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to set num_static_devices");
634*bb4ee6a4SAndroid Build Coastguard Worker }
635*bb4ee6a4SAndroid Build Coastguard Worker }
636*bb4ee6a4SAndroid Build Coastguard Worker Command::Enable => {
637*bb4ee6a4SAndroid Build Coastguard Worker info!("enabling vmm-swap");
638*bb4ee6a4SAndroid Build Coastguard Worker
639*bb4ee6a4SAndroid Build Coastguard Worker let staging_shmem =
640*bb4ee6a4SAndroid Build Coastguard Worker SharedMemory::new("swap staging memory", guest_memory.memory_size())
641*bb4ee6a4SAndroid Build Coastguard Worker .context("create staging shmem")?;
642*bb4ee6a4SAndroid Build Coastguard Worker
643*bb4ee6a4SAndroid Build Coastguard Worker let regions = regions_from_guest_memory(&guest_memory);
644*bb4ee6a4SAndroid Build Coastguard Worker
645*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = match (swap_file_opt.take(), truncate_worker.take()) {
646*bb4ee6a4SAndroid Build Coastguard Worker (Some(file), None) => file,
647*bb4ee6a4SAndroid Build Coastguard Worker (None, Some(worker)) => {
648*bb4ee6a4SAndroid Build Coastguard Worker worker.take_file().context("failed to get truncated swap")?
649*bb4ee6a4SAndroid Build Coastguard Worker }
650*bb4ee6a4SAndroid Build Coastguard Worker _ => bail!("Missing swap file"),
651*bb4ee6a4SAndroid Build Coastguard Worker };
652*bb4ee6a4SAndroid Build Coastguard Worker
653*bb4ee6a4SAndroid Build Coastguard Worker let page_handler = match PageHandler::create(
654*bb4ee6a4SAndroid Build Coastguard Worker &swap_file,
655*bb4ee6a4SAndroid Build Coastguard Worker &staging_shmem,
656*bb4ee6a4SAndroid Build Coastguard Worker ®ions,
657*bb4ee6a4SAndroid Build Coastguard Worker worker.channel.clone(),
658*bb4ee6a4SAndroid Build Coastguard Worker ) {
659*bb4ee6a4SAndroid Build Coastguard Worker Ok(page_handler) => page_handler,
660*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
661*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to create swap handler: {:?}", e);
662*bb4ee6a4SAndroid Build Coastguard Worker continue;
663*bb4ee6a4SAndroid Build Coastguard Worker }
664*bb4ee6a4SAndroid Build Coastguard Worker };
665*bb4ee6a4SAndroid Build Coastguard Worker
666*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/272634283): Should just disable vmm-swap without crash.
667*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY:
668*bb4ee6a4SAndroid Build Coastguard Worker // Safe because the regions are from guest memory and uffd_list contains all
669*bb4ee6a4SAndroid Build Coastguard Worker // the processes of crosvm.
670*bb4ee6a4SAndroid Build Coastguard Worker unsafe { register_regions(®ions, uffd_list.get_list()) }
671*bb4ee6a4SAndroid Build Coastguard Worker .context("register regions")?;
672*bb4ee6a4SAndroid Build Coastguard Worker
673*bb4ee6a4SAndroid Build Coastguard Worker // events may contain unprocessed entries, but those pending events will be
674*bb4ee6a4SAndroid Build Coastguard Worker // immediately re-created when handle_vmm_swap checks wait_ctx because
675*bb4ee6a4SAndroid Build Coastguard Worker // WaitContext is level triggered.
676*bb4ee6a4SAndroid Build Coastguard Worker drop(events);
677*bb4ee6a4SAndroid Build Coastguard Worker
678*bb4ee6a4SAndroid Build Coastguard Worker let mutex_transition = Mutex::new(state_transition);
679*bb4ee6a4SAndroid Build Coastguard Worker
680*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.reset()?;
681*bb4ee6a4SAndroid Build Coastguard Worker let swap_result = std::thread::scope(|scope| {
682*bb4ee6a4SAndroid Build Coastguard Worker let result = handle_vmm_swap(
683*bb4ee6a4SAndroid Build Coastguard Worker scope,
684*bb4ee6a4SAndroid Build Coastguard Worker &wait_ctx,
685*bb4ee6a4SAndroid Build Coastguard Worker &page_handler,
686*bb4ee6a4SAndroid Build Coastguard Worker &mut uffd_list,
687*bb4ee6a4SAndroid Build Coastguard Worker &guest_memory,
688*bb4ee6a4SAndroid Build Coastguard Worker ®ions,
689*bb4ee6a4SAndroid Build Coastguard Worker &command_tube,
690*bb4ee6a4SAndroid Build Coastguard Worker &worker,
691*bb4ee6a4SAndroid Build Coastguard Worker &mutex_transition,
692*bb4ee6a4SAndroid Build Coastguard Worker &bg_job_control,
693*bb4ee6a4SAndroid Build Coastguard Worker );
694*bb4ee6a4SAndroid Build Coastguard Worker // Abort background jobs to unblock ScopedJoinHandle eariler on a
695*bb4ee6a4SAndroid Build Coastguard Worker // failure.
696*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.abort();
697*bb4ee6a4SAndroid Build Coastguard Worker result
698*bb4ee6a4SAndroid Build Coastguard Worker })?;
699*bb4ee6a4SAndroid Build Coastguard Worker if swap_result.should_exit {
700*bb4ee6a4SAndroid Build Coastguard Worker return Ok(());
701*bb4ee6a4SAndroid Build Coastguard Worker }
702*bb4ee6a4SAndroid Build Coastguard Worker state_transition = mutex_transition.into_inner();
703*bb4ee6a4SAndroid Build Coastguard Worker
704*bb4ee6a4SAndroid Build Coastguard Worker unregister_regions(®ions, uffd_list.get_list())
705*bb4ee6a4SAndroid Build Coastguard Worker .context("unregister regions")?;
706*bb4ee6a4SAndroid Build Coastguard Worker
707*bb4ee6a4SAndroid Build Coastguard Worker // Truncate the swap file to hold minimum resources while disabled.
708*bb4ee6a4SAndroid Build Coastguard Worker if swap_result.slow_file_cleanup {
709*bb4ee6a4SAndroid Build Coastguard Worker truncate_worker = Some(
710*bb4ee6a4SAndroid Build Coastguard Worker FileTruncator::new(swap_file)
711*bb4ee6a4SAndroid Build Coastguard Worker .context("failed to start truncating")?,
712*bb4ee6a4SAndroid Build Coastguard Worker );
713*bb4ee6a4SAndroid Build Coastguard Worker } else {
714*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = swap_file.set_len(0) {
715*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to clear swap file: {:?}", e);
716*bb4ee6a4SAndroid Build Coastguard Worker };
717*bb4ee6a4SAndroid Build Coastguard Worker swap_file_opt = Some(swap_file);
718*bb4ee6a4SAndroid Build Coastguard Worker }
719*bb4ee6a4SAndroid Build Coastguard Worker
720*bb4ee6a4SAndroid Build Coastguard Worker info!("vmm-swap is disabled");
721*bb4ee6a4SAndroid Build Coastguard Worker // events are obsolete. Run `WaitContext::wait()` again
722*bb4ee6a4SAndroid Build Coastguard Worker break;
723*bb4ee6a4SAndroid Build Coastguard Worker }
724*bb4ee6a4SAndroid Build Coastguard Worker Command::Trim => {
725*bb4ee6a4SAndroid Build Coastguard Worker warn!("swap trim while disabled");
726*bb4ee6a4SAndroid Build Coastguard Worker }
727*bb4ee6a4SAndroid Build Coastguard Worker Command::SwapOut => {
728*bb4ee6a4SAndroid Build Coastguard Worker warn!("swap out while disabled");
729*bb4ee6a4SAndroid Build Coastguard Worker }
730*bb4ee6a4SAndroid Build Coastguard Worker Command::Disable { slow_file_cleanup } => {
731*bb4ee6a4SAndroid Build Coastguard Worker if !slow_file_cleanup {
732*bb4ee6a4SAndroid Build Coastguard Worker if let Some(worker) = truncate_worker.take() {
733*bb4ee6a4SAndroid Build Coastguard Worker swap_file_opt =
734*bb4ee6a4SAndroid Build Coastguard Worker Some(worker.take_file().context("failed to truncate swap")?);
735*bb4ee6a4SAndroid Build Coastguard Worker }
736*bb4ee6a4SAndroid Build Coastguard Worker }
737*bb4ee6a4SAndroid Build Coastguard Worker }
738*bb4ee6a4SAndroid Build Coastguard Worker Command::Exit => {
739*bb4ee6a4SAndroid Build Coastguard Worker return Ok(());
740*bb4ee6a4SAndroid Build Coastguard Worker }
741*bb4ee6a4SAndroid Build Coastguard Worker Command::Status => {
742*bb4ee6a4SAndroid Build Coastguard Worker let metrics = SwapMetrics {
743*bb4ee6a4SAndroid Build Coastguard Worker resident_pages: count_resident_pages(&guest_memory) as u64,
744*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default()
745*bb4ee6a4SAndroid Build Coastguard Worker };
746*bb4ee6a4SAndroid Build Coastguard Worker let status = SwapStatus {
747*bb4ee6a4SAndroid Build Coastguard Worker state: SwapState::Ready,
748*bb4ee6a4SAndroid Build Coastguard Worker metrics,
749*bb4ee6a4SAndroid Build Coastguard Worker state_transition,
750*bb4ee6a4SAndroid Build Coastguard Worker };
751*bb4ee6a4SAndroid Build Coastguard Worker command_tube.send(&status).context("send status response")?;
752*bb4ee6a4SAndroid Build Coastguard Worker debug!("swap status: {:?}", status);
753*bb4ee6a4SAndroid Build Coastguard Worker }
754*bb4ee6a4SAndroid Build Coastguard Worker },
755*bb4ee6a4SAndroid Build Coastguard Worker Token::BackgroundJobCompleted => {
756*bb4ee6a4SAndroid Build Coastguard Worker error!("unexpected background job completed event while swap is disabled");
757*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.reset()?;
758*bb4ee6a4SAndroid Build Coastguard Worker }
759*bb4ee6a4SAndroid Build Coastguard Worker };
760*bb4ee6a4SAndroid Build Coastguard Worker }
761*bb4ee6a4SAndroid Build Coastguard Worker if try_gc_uffds {
762*bb4ee6a4SAndroid Build Coastguard Worker uffd_list.gc_dead_uffds().context("gc dead uffds")?;
763*bb4ee6a4SAndroid Build Coastguard Worker try_gc_uffds = false;
764*bb4ee6a4SAndroid Build Coastguard Worker }
765*bb4ee6a4SAndroid Build Coastguard Worker }
766*bb4ee6a4SAndroid Build Coastguard Worker }
767*bb4ee6a4SAndroid Build Coastguard Worker
768*bb4ee6a4SAndroid Build Coastguard Worker enum State<'scope> {
769*bb4ee6a4SAndroid Build Coastguard Worker SwapOutPending,
770*bb4ee6a4SAndroid Build Coastguard Worker Trim(ScopedJoinHandle<'scope, anyhow::Result<()>>),
771*bb4ee6a4SAndroid Build Coastguard Worker SwapOutInProgress {
772*bb4ee6a4SAndroid Build Coastguard Worker started_time: Instant,
773*bb4ee6a4SAndroid Build Coastguard Worker },
774*bb4ee6a4SAndroid Build Coastguard Worker SwapOutCompleted,
775*bb4ee6a4SAndroid Build Coastguard Worker SwapInInProgress {
776*bb4ee6a4SAndroid Build Coastguard Worker join_handle: ScopedJoinHandle<'scope, anyhow::Result<()>>,
777*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup: bool,
778*bb4ee6a4SAndroid Build Coastguard Worker },
779*bb4ee6a4SAndroid Build Coastguard Worker Failed,
780*bb4ee6a4SAndroid Build Coastguard Worker }
781*bb4ee6a4SAndroid Build Coastguard Worker
782*bb4ee6a4SAndroid Build Coastguard Worker impl From<&State<'_>> for SwapState {
from(state: &State<'_>) -> Self783*bb4ee6a4SAndroid Build Coastguard Worker fn from(state: &State<'_>) -> Self {
784*bb4ee6a4SAndroid Build Coastguard Worker match state {
785*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutPending => SwapState::Pending,
786*bb4ee6a4SAndroid Build Coastguard Worker State::Trim(_) => SwapState::TrimInProgress,
787*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutInProgress { .. } => SwapState::SwapOutInProgress,
788*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutCompleted => SwapState::Active,
789*bb4ee6a4SAndroid Build Coastguard Worker State::SwapInInProgress { .. } => SwapState::SwapInInProgress,
790*bb4ee6a4SAndroid Build Coastguard Worker State::Failed => SwapState::Failed,
791*bb4ee6a4SAndroid Build Coastguard Worker }
792*bb4ee6a4SAndroid Build Coastguard Worker }
793*bb4ee6a4SAndroid Build Coastguard Worker }
794*bb4ee6a4SAndroid Build Coastguard Worker
handle_enable_command<'scope>( state: State, bg_job_control: &BackgroundJobControl, page_handler: &PageHandler, guest_memory: &GuestMemory, worker: &Worker<MoveToStaging>, state_transition: &Mutex<SwapStateTransition>, ) -> anyhow::Result<State<'scope>>795*bb4ee6a4SAndroid Build Coastguard Worker fn handle_enable_command<'scope>(
796*bb4ee6a4SAndroid Build Coastguard Worker state: State,
797*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control: &BackgroundJobControl,
798*bb4ee6a4SAndroid Build Coastguard Worker page_handler: &PageHandler,
799*bb4ee6a4SAndroid Build Coastguard Worker guest_memory: &GuestMemory,
800*bb4ee6a4SAndroid Build Coastguard Worker worker: &Worker<MoveToStaging>,
801*bb4ee6a4SAndroid Build Coastguard Worker state_transition: &Mutex<SwapStateTransition>,
802*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<State<'scope>> {
803*bb4ee6a4SAndroid Build Coastguard Worker match state {
804*bb4ee6a4SAndroid Build Coastguard Worker State::SwapInInProgress { join_handle, .. } => {
805*bb4ee6a4SAndroid Build Coastguard Worker info!("abort swap-in");
806*bb4ee6a4SAndroid Build Coastguard Worker abort_background_job(join_handle, bg_job_control).context("abort swap-in")?;
807*bb4ee6a4SAndroid Build Coastguard Worker }
808*bb4ee6a4SAndroid Build Coastguard Worker State::Trim(join_handle) => {
809*bb4ee6a4SAndroid Build Coastguard Worker info!("abort trim");
810*bb4ee6a4SAndroid Build Coastguard Worker abort_background_job(join_handle, bg_job_control).context("abort trim")?;
811*bb4ee6a4SAndroid Build Coastguard Worker }
812*bb4ee6a4SAndroid Build Coastguard Worker _ => {}
813*bb4ee6a4SAndroid Build Coastguard Worker }
814*bb4ee6a4SAndroid Build Coastguard Worker
815*bb4ee6a4SAndroid Build Coastguard Worker info!("start moving memory to staging");
816*bb4ee6a4SAndroid Build Coastguard Worker match move_guest_to_staging(page_handler, guest_memory, worker) {
817*bb4ee6a4SAndroid Build Coastguard Worker Ok(new_state_transition) => {
818*bb4ee6a4SAndroid Build Coastguard Worker info!(
819*bb4ee6a4SAndroid Build Coastguard Worker "move {} pages to staging in {} ms",
820*bb4ee6a4SAndroid Build Coastguard Worker new_state_transition.pages, new_state_transition.time_ms
821*bb4ee6a4SAndroid Build Coastguard Worker );
822*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = new_state_transition;
823*bb4ee6a4SAndroid Build Coastguard Worker Ok(State::SwapOutPending)
824*bb4ee6a4SAndroid Build Coastguard Worker }
825*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
826*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to move memory to staging: {}", e);
827*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
828*bb4ee6a4SAndroid Build Coastguard Worker Ok(State::Failed)
829*bb4ee6a4SAndroid Build Coastguard Worker }
830*bb4ee6a4SAndroid Build Coastguard Worker }
831*bb4ee6a4SAndroid Build Coastguard Worker }
832*bb4ee6a4SAndroid Build Coastguard Worker
move_guest_to_staging( page_handler: &PageHandler, guest_memory: &GuestMemory, worker: &Worker<MoveToStaging>, ) -> anyhow::Result<SwapStateTransition>833*bb4ee6a4SAndroid Build Coastguard Worker fn move_guest_to_staging(
834*bb4ee6a4SAndroid Build Coastguard Worker page_handler: &PageHandler,
835*bb4ee6a4SAndroid Build Coastguard Worker guest_memory: &GuestMemory,
836*bb4ee6a4SAndroid Build Coastguard Worker worker: &Worker<MoveToStaging>,
837*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<SwapStateTransition> {
838*bb4ee6a4SAndroid Build Coastguard Worker let start_time = std::time::Instant::now();
839*bb4ee6a4SAndroid Build Coastguard Worker
840*bb4ee6a4SAndroid Build Coastguard Worker let mut pages = 0;
841*bb4ee6a4SAndroid Build Coastguard Worker
842*bb4ee6a4SAndroid Build Coastguard Worker let result = guest_memory.regions().try_for_each(|region| {
843*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY:
844*bb4ee6a4SAndroid Build Coastguard Worker // safe because:
845*bb4ee6a4SAndroid Build Coastguard Worker // * all the regions are registered to all userfaultfd
846*bb4ee6a4SAndroid Build Coastguard Worker // * no process access the guest memory
847*bb4ee6a4SAndroid Build Coastguard Worker // * page fault events are handled by PageHandler
848*bb4ee6a4SAndroid Build Coastguard Worker // * wait for all the copy completed within _processes_guard
849*bb4ee6a4SAndroid Build Coastguard Worker pages += unsafe {
850*bb4ee6a4SAndroid Build Coastguard Worker page_handler.move_to_staging(region.host_addr, region.shm, region.shm_offset)
851*bb4ee6a4SAndroid Build Coastguard Worker }
852*bb4ee6a4SAndroid Build Coastguard Worker .context("move to staging")? as u64;
853*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
854*bb4ee6a4SAndroid Build Coastguard Worker });
855*bb4ee6a4SAndroid Build Coastguard Worker worker.channel.wait_complete();
856*bb4ee6a4SAndroid Build Coastguard Worker
857*bb4ee6a4SAndroid Build Coastguard Worker match result {
858*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) => {
859*bb4ee6a4SAndroid Build Coastguard Worker let resident_pages = count_resident_pages(guest_memory);
860*bb4ee6a4SAndroid Build Coastguard Worker if resident_pages > 0 {
861*bb4ee6a4SAndroid Build Coastguard Worker error!(
862*bb4ee6a4SAndroid Build Coastguard Worker "active page is not zero just after swap out but {} pages",
863*bb4ee6a4SAndroid Build Coastguard Worker resident_pages
864*bb4ee6a4SAndroid Build Coastguard Worker );
865*bb4ee6a4SAndroid Build Coastguard Worker }
866*bb4ee6a4SAndroid Build Coastguard Worker let time_ms = start_time.elapsed().as_millis().try_into()?;
867*bb4ee6a4SAndroid Build Coastguard Worker Ok(SwapStateTransition { pages, time_ms })
868*bb4ee6a4SAndroid Build Coastguard Worker }
869*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => Err(e),
870*bb4ee6a4SAndroid Build Coastguard Worker }
871*bb4ee6a4SAndroid Build Coastguard Worker }
872*bb4ee6a4SAndroid Build Coastguard Worker
abort_background_job<T>( join_handle: ScopedJoinHandle<'_, anyhow::Result<T>>, bg_job_control: &BackgroundJobControl, ) -> anyhow::Result<T>873*bb4ee6a4SAndroid Build Coastguard Worker fn abort_background_job<T>(
874*bb4ee6a4SAndroid Build Coastguard Worker join_handle: ScopedJoinHandle<'_, anyhow::Result<T>>,
875*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control: &BackgroundJobControl,
876*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<T> {
877*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.abort();
878*bb4ee6a4SAndroid Build Coastguard Worker // Wait until the background job is aborted and the thread finishes.
879*bb4ee6a4SAndroid Build Coastguard Worker let result = join_handle
880*bb4ee6a4SAndroid Build Coastguard Worker .join()
881*bb4ee6a4SAndroid Build Coastguard Worker .expect("panic on the background job thread");
882*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control.reset().context("reset swap in event")?;
883*bb4ee6a4SAndroid Build Coastguard Worker result.context("failure on background job thread")
884*bb4ee6a4SAndroid Build Coastguard Worker }
885*bb4ee6a4SAndroid Build Coastguard Worker
886*bb4ee6a4SAndroid Build Coastguard Worker struct VmmSwapResult {
887*bb4ee6a4SAndroid Build Coastguard Worker should_exit: bool,
888*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup: bool,
889*bb4ee6a4SAndroid Build Coastguard Worker }
890*bb4ee6a4SAndroid Build Coastguard Worker
handle_vmm_swap<'scope, 'env>( scope: &'scope Scope<'scope, 'env>, wait_ctx: &WaitContext<Token>, page_handler: &'env PageHandler<'env>, uffd_list: &'env mut UffdList<Token, DeadUffdCheckerImpl>, guest_memory: &GuestMemory, regions: &[Range<usize>], command_tube: &Tube, worker: &Worker<MoveToStaging>, state_transition: &'env Mutex<SwapStateTransition>, bg_job_control: &'env BackgroundJobControl, ) -> anyhow::Result<VmmSwapResult>891*bb4ee6a4SAndroid Build Coastguard Worker fn handle_vmm_swap<'scope, 'env>(
892*bb4ee6a4SAndroid Build Coastguard Worker scope: &'scope Scope<'scope, 'env>,
893*bb4ee6a4SAndroid Build Coastguard Worker wait_ctx: &WaitContext<Token>,
894*bb4ee6a4SAndroid Build Coastguard Worker page_handler: &'env PageHandler<'env>,
895*bb4ee6a4SAndroid Build Coastguard Worker uffd_list: &'env mut UffdList<Token, DeadUffdCheckerImpl>,
896*bb4ee6a4SAndroid Build Coastguard Worker guest_memory: &GuestMemory,
897*bb4ee6a4SAndroid Build Coastguard Worker regions: &[Range<usize>],
898*bb4ee6a4SAndroid Build Coastguard Worker command_tube: &Tube,
899*bb4ee6a4SAndroid Build Coastguard Worker worker: &Worker<MoveToStaging>,
900*bb4ee6a4SAndroid Build Coastguard Worker state_transition: &'env Mutex<SwapStateTransition>,
901*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control: &'env BackgroundJobControl,
902*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<VmmSwapResult> {
903*bb4ee6a4SAndroid Build Coastguard Worker let mut state = match move_guest_to_staging(page_handler, guest_memory, worker) {
904*bb4ee6a4SAndroid Build Coastguard Worker Ok(transition) => {
905*bb4ee6a4SAndroid Build Coastguard Worker info!(
906*bb4ee6a4SAndroid Build Coastguard Worker "move {} pages to staging in {} ms",
907*bb4ee6a4SAndroid Build Coastguard Worker transition.pages, transition.time_ms
908*bb4ee6a4SAndroid Build Coastguard Worker );
909*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = transition;
910*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutPending
911*bb4ee6a4SAndroid Build Coastguard Worker }
912*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
913*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to move memory to staging: {}", e);
914*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
915*bb4ee6a4SAndroid Build Coastguard Worker State::Failed
916*bb4ee6a4SAndroid Build Coastguard Worker }
917*bb4ee6a4SAndroid Build Coastguard Worker };
918*bb4ee6a4SAndroid Build Coastguard Worker command_tube
919*bb4ee6a4SAndroid Build Coastguard Worker .send(&SwapStatus::dummy())
920*bb4ee6a4SAndroid Build Coastguard Worker .context("send enable finish signal")?;
921*bb4ee6a4SAndroid Build Coastguard Worker
922*bb4ee6a4SAndroid Build Coastguard Worker let mut try_gc_uffds = false;
923*bb4ee6a4SAndroid Build Coastguard Worker loop {
924*bb4ee6a4SAndroid Build Coastguard Worker let events = match &state {
925*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutInProgress { started_time } => {
926*bb4ee6a4SAndroid Build Coastguard Worker let events = wait_ctx
927*bb4ee6a4SAndroid Build Coastguard Worker .wait_timeout(Duration::ZERO)
928*bb4ee6a4SAndroid Build Coastguard Worker .context("wait poll events")?;
929*bb4ee6a4SAndroid Build Coastguard Worker
930*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/273129441): swap out on a background thread.
931*bb4ee6a4SAndroid Build Coastguard Worker // Proceed swap out only when there is no page fault (or other) events.
932*bb4ee6a4SAndroid Build Coastguard Worker if events.is_empty() {
933*bb4ee6a4SAndroid Build Coastguard Worker match page_handler.swap_out(MAX_SWAP_CHUNK_SIZE) {
934*bb4ee6a4SAndroid Build Coastguard Worker Ok(num_pages) => {
935*bb4ee6a4SAndroid Build Coastguard Worker let mut state_transition = state_transition.lock();
936*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages += num_pages as u64;
937*bb4ee6a4SAndroid Build Coastguard Worker state_transition.time_ms =
938*bb4ee6a4SAndroid Build Coastguard Worker started_time.elapsed().as_millis().try_into()?;
939*bb4ee6a4SAndroid Build Coastguard Worker if num_pages == 0 {
940*bb4ee6a4SAndroid Build Coastguard Worker info!(
941*bb4ee6a4SAndroid Build Coastguard Worker "swap out all {} pages to file in {} ms",
942*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages, state_transition.time_ms
943*bb4ee6a4SAndroid Build Coastguard Worker );
944*bb4ee6a4SAndroid Build Coastguard Worker state = State::SwapOutCompleted;
945*bb4ee6a4SAndroid Build Coastguard Worker }
946*bb4ee6a4SAndroid Build Coastguard Worker }
947*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
948*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to swap out: {:?}", e);
949*bb4ee6a4SAndroid Build Coastguard Worker state = State::Failed;
950*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
951*bb4ee6a4SAndroid Build Coastguard Worker }
952*bb4ee6a4SAndroid Build Coastguard Worker }
953*bb4ee6a4SAndroid Build Coastguard Worker continue;
954*bb4ee6a4SAndroid Build Coastguard Worker }
955*bb4ee6a4SAndroid Build Coastguard Worker
956*bb4ee6a4SAndroid Build Coastguard Worker events
957*bb4ee6a4SAndroid Build Coastguard Worker }
958*bb4ee6a4SAndroid Build Coastguard Worker _ => wait_ctx.wait().context("wait poll events")?,
959*bb4ee6a4SAndroid Build Coastguard Worker };
960*bb4ee6a4SAndroid Build Coastguard Worker
961*bb4ee6a4SAndroid Build Coastguard Worker for event in events.iter() {
962*bb4ee6a4SAndroid Build Coastguard Worker match event.token {
963*bb4ee6a4SAndroid Build Coastguard Worker Token::UffdEvents(id_uffd) => {
964*bb4ee6a4SAndroid Build Coastguard Worker let uffd = uffd_list
965*bb4ee6a4SAndroid Build Coastguard Worker .get(id_uffd)
966*bb4ee6a4SAndroid Build Coastguard Worker .with_context(|| format!("uffd is not found for idx: {}", id_uffd))?;
967*bb4ee6a4SAndroid Build Coastguard Worker // Userfaultfd does not work as level triggered but as edge triggered. We need
968*bb4ee6a4SAndroid Build Coastguard Worker // to read all the events in the userfaultfd here.
969*bb4ee6a4SAndroid Build Coastguard Worker // TODO(kawasin): Use [userfaultfd::Uffd::read_events()] for performance.
970*bb4ee6a4SAndroid Build Coastguard Worker while let Some(event) = uffd.read_event().context("read userfaultfd event")? {
971*bb4ee6a4SAndroid Build Coastguard Worker match event {
972*bb4ee6a4SAndroid Build Coastguard Worker UffdEvent::Pagefault { addr, .. } => {
973*bb4ee6a4SAndroid Build Coastguard Worker match page_handler.handle_page_fault(uffd, addr as usize) {
974*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) => {}
975*bb4ee6a4SAndroid Build Coastguard Worker Err(PageHandlerError::Userfaultfd(UffdError::UffdClosed)) => {
976*bb4ee6a4SAndroid Build Coastguard Worker // Do nothing for the uffd. It will be garbage-collected
977*bb4ee6a4SAndroid Build Coastguard Worker // when a new uffd is registered.
978*bb4ee6a4SAndroid Build Coastguard Worker break;
979*bb4ee6a4SAndroid Build Coastguard Worker }
980*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
981*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to handle page fault: {:?}", e);
982*bb4ee6a4SAndroid Build Coastguard Worker }
983*bb4ee6a4SAndroid Build Coastguard Worker }
984*bb4ee6a4SAndroid Build Coastguard Worker }
985*bb4ee6a4SAndroid Build Coastguard Worker UffdEvent::Remove { start, end } => {
986*bb4ee6a4SAndroid Build Coastguard Worker page_handler
987*bb4ee6a4SAndroid Build Coastguard Worker .handle_page_remove(start as usize, end as usize)
988*bb4ee6a4SAndroid Build Coastguard Worker .context("handle fault")?;
989*bb4ee6a4SAndroid Build Coastguard Worker }
990*bb4ee6a4SAndroid Build Coastguard Worker event => {
991*bb4ee6a4SAndroid Build Coastguard Worker bail!("unsupported UffdEvent: {:?}", event);
992*bb4ee6a4SAndroid Build Coastguard Worker }
993*bb4ee6a4SAndroid Build Coastguard Worker }
994*bb4ee6a4SAndroid Build Coastguard Worker }
995*bb4ee6a4SAndroid Build Coastguard Worker }
996*bb4ee6a4SAndroid Build Coastguard Worker Token::Command => match command_tube
997*bb4ee6a4SAndroid Build Coastguard Worker .recv::<Command>()
998*bb4ee6a4SAndroid Build Coastguard Worker .context("recv swap command")?
999*bb4ee6a4SAndroid Build Coastguard Worker {
1000*bb4ee6a4SAndroid Build Coastguard Worker Command::ProcessForked { uffd, reply_tube } => {
1001*bb4ee6a4SAndroid Build Coastguard Worker debug!("new fork uffd: {:?}", uffd);
1002*bb4ee6a4SAndroid Build Coastguard Worker let result = if let Err(e) = {
1003*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY: regions is generated from the guest memory
1004*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY: the uffd is from a new process.
1005*bb4ee6a4SAndroid Build Coastguard Worker unsafe { register_regions(regions, std::array::from_ref(&uffd)) }
1006*bb4ee6a4SAndroid Build Coastguard Worker } {
1007*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to setup uffd: {:?}", e);
1008*bb4ee6a4SAndroid Build Coastguard Worker false
1009*bb4ee6a4SAndroid Build Coastguard Worker } else {
1010*bb4ee6a4SAndroid Build Coastguard Worker match uffd_list.register(uffd) {
1011*bb4ee6a4SAndroid Build Coastguard Worker Ok(is_dynamic_uffd) => {
1012*bb4ee6a4SAndroid Build Coastguard Worker try_gc_uffds = is_dynamic_uffd;
1013*bb4ee6a4SAndroid Build Coastguard Worker true
1014*bb4ee6a4SAndroid Build Coastguard Worker }
1015*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
1016*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to register uffd to list: {:?}", e);
1017*bb4ee6a4SAndroid Build Coastguard Worker false
1018*bb4ee6a4SAndroid Build Coastguard Worker }
1019*bb4ee6a4SAndroid Build Coastguard Worker }
1020*bb4ee6a4SAndroid Build Coastguard Worker };
1021*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = reply_tube.send(&result) {
1022*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to response to new process: {:?}", e);
1023*bb4ee6a4SAndroid Build Coastguard Worker }
1024*bb4ee6a4SAndroid Build Coastguard Worker }
1025*bb4ee6a4SAndroid Build Coastguard Worker Command::StaticDeviceSetupComplete(num_static_devices) => {
1026*bb4ee6a4SAndroid Build Coastguard Worker info!("static device setup complete: n={}", num_static_devices);
1027*bb4ee6a4SAndroid Build Coastguard Worker if !uffd_list.set_num_static_devices(num_static_devices) {
1028*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to set num_static_devices");
1029*bb4ee6a4SAndroid Build Coastguard Worker }
1030*bb4ee6a4SAndroid Build Coastguard Worker }
1031*bb4ee6a4SAndroid Build Coastguard Worker Command::Enable => {
1032*bb4ee6a4SAndroid Build Coastguard Worker let result = handle_enable_command(
1033*bb4ee6a4SAndroid Build Coastguard Worker state,
1034*bb4ee6a4SAndroid Build Coastguard Worker bg_job_control,
1035*bb4ee6a4SAndroid Build Coastguard Worker page_handler,
1036*bb4ee6a4SAndroid Build Coastguard Worker guest_memory,
1037*bb4ee6a4SAndroid Build Coastguard Worker worker,
1038*bb4ee6a4SAndroid Build Coastguard Worker state_transition,
1039*bb4ee6a4SAndroid Build Coastguard Worker );
1040*bb4ee6a4SAndroid Build Coastguard Worker command_tube
1041*bb4ee6a4SAndroid Build Coastguard Worker .send(&SwapStatus::dummy())
1042*bb4ee6a4SAndroid Build Coastguard Worker .context("send enable finish signal")?;
1043*bb4ee6a4SAndroid Build Coastguard Worker state = result?;
1044*bb4ee6a4SAndroid Build Coastguard Worker }
1045*bb4ee6a4SAndroid Build Coastguard Worker Command::Trim => match &state {
1046*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutPending => {
1047*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
1048*bb4ee6a4SAndroid Build Coastguard Worker let join_handle = scope.spawn(|| {
1049*bb4ee6a4SAndroid Build Coastguard Worker let mut ctx = page_handler.start_trim();
1050*bb4ee6a4SAndroid Build Coastguard Worker let job = bg_job_control.new_job();
1051*bb4ee6a4SAndroid Build Coastguard Worker let start_time = std::time::Instant::now();
1052*bb4ee6a4SAndroid Build Coastguard Worker
1053*bb4ee6a4SAndroid Build Coastguard Worker while !job.is_aborted() {
1054*bb4ee6a4SAndroid Build Coastguard Worker if let Some(trimmed_pages) =
1055*bb4ee6a4SAndroid Build Coastguard Worker ctx.trim_pages(MAX_TRIM_PAGES).context("trim pages")?
1056*bb4ee6a4SAndroid Build Coastguard Worker {
1057*bb4ee6a4SAndroid Build Coastguard Worker let mut state_transition = state_transition.lock();
1058*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages += trimmed_pages as u64;
1059*bb4ee6a4SAndroid Build Coastguard Worker state_transition.time_ms =
1060*bb4ee6a4SAndroid Build Coastguard Worker start_time.elapsed().as_millis().try_into()?;
1061*bb4ee6a4SAndroid Build Coastguard Worker } else {
1062*bb4ee6a4SAndroid Build Coastguard Worker // Traversed all pages.
1063*bb4ee6a4SAndroid Build Coastguard Worker break;
1064*bb4ee6a4SAndroid Build Coastguard Worker }
1065*bb4ee6a4SAndroid Build Coastguard Worker }
1066*bb4ee6a4SAndroid Build Coastguard Worker
1067*bb4ee6a4SAndroid Build Coastguard Worker if job.is_aborted() {
1068*bb4ee6a4SAndroid Build Coastguard Worker info!("trim is aborted");
1069*bb4ee6a4SAndroid Build Coastguard Worker } else {
1070*bb4ee6a4SAndroid Build Coastguard Worker info!(
1071*bb4ee6a4SAndroid Build Coastguard Worker "trimmed {} clean pages and {} zero pages",
1072*bb4ee6a4SAndroid Build Coastguard Worker ctx.trimmed_clean_pages(),
1073*bb4ee6a4SAndroid Build Coastguard Worker ctx.trimmed_zero_pages()
1074*bb4ee6a4SAndroid Build Coastguard Worker );
1075*bb4ee6a4SAndroid Build Coastguard Worker }
1076*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
1077*bb4ee6a4SAndroid Build Coastguard Worker });
1078*bb4ee6a4SAndroid Build Coastguard Worker
1079*bb4ee6a4SAndroid Build Coastguard Worker state = State::Trim(join_handle);
1080*bb4ee6a4SAndroid Build Coastguard Worker info!("start trimming staging memory");
1081*bb4ee6a4SAndroid Build Coastguard Worker }
1082*bb4ee6a4SAndroid Build Coastguard Worker state => {
1083*bb4ee6a4SAndroid Build Coastguard Worker warn!(
1084*bb4ee6a4SAndroid Build Coastguard Worker "swap trim is not ready. state: {:?}",
1085*bb4ee6a4SAndroid Build Coastguard Worker SwapState::from(state)
1086*bb4ee6a4SAndroid Build Coastguard Worker );
1087*bb4ee6a4SAndroid Build Coastguard Worker }
1088*bb4ee6a4SAndroid Build Coastguard Worker },
1089*bb4ee6a4SAndroid Build Coastguard Worker Command::SwapOut => match &state {
1090*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutPending => {
1091*bb4ee6a4SAndroid Build Coastguard Worker state = State::SwapOutInProgress {
1092*bb4ee6a4SAndroid Build Coastguard Worker started_time: std::time::Instant::now(),
1093*bb4ee6a4SAndroid Build Coastguard Worker };
1094*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
1095*bb4ee6a4SAndroid Build Coastguard Worker info!("start swapping out");
1096*bb4ee6a4SAndroid Build Coastguard Worker }
1097*bb4ee6a4SAndroid Build Coastguard Worker state => {
1098*bb4ee6a4SAndroid Build Coastguard Worker warn!("swap out is not ready. state: {:?}", SwapState::from(state));
1099*bb4ee6a4SAndroid Build Coastguard Worker }
1100*bb4ee6a4SAndroid Build Coastguard Worker },
1101*bb4ee6a4SAndroid Build Coastguard Worker Command::Disable { slow_file_cleanup } => {
1102*bb4ee6a4SAndroid Build Coastguard Worker match state {
1103*bb4ee6a4SAndroid Build Coastguard Worker State::Trim(join_handle) => {
1104*bb4ee6a4SAndroid Build Coastguard Worker info!("abort trim");
1105*bb4ee6a4SAndroid Build Coastguard Worker abort_background_job(join_handle, bg_job_control)
1106*bb4ee6a4SAndroid Build Coastguard Worker .context("abort trim")?;
1107*bb4ee6a4SAndroid Build Coastguard Worker }
1108*bb4ee6a4SAndroid Build Coastguard Worker State::SwapOutInProgress { .. } => {
1109*bb4ee6a4SAndroid Build Coastguard Worker info!("swap out is aborted");
1110*bb4ee6a4SAndroid Build Coastguard Worker }
1111*bb4ee6a4SAndroid Build Coastguard Worker State::SwapInInProgress { join_handle, .. } => {
1112*bb4ee6a4SAndroid Build Coastguard Worker info!("swap in is in progress");
1113*bb4ee6a4SAndroid Build Coastguard Worker state = State::SwapInInProgress {
1114*bb4ee6a4SAndroid Build Coastguard Worker join_handle,
1115*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup,
1116*bb4ee6a4SAndroid Build Coastguard Worker };
1117*bb4ee6a4SAndroid Build Coastguard Worker continue;
1118*bb4ee6a4SAndroid Build Coastguard Worker }
1119*bb4ee6a4SAndroid Build Coastguard Worker _ => {}
1120*bb4ee6a4SAndroid Build Coastguard Worker }
1121*bb4ee6a4SAndroid Build Coastguard Worker *state_transition.lock() = SwapStateTransition::default();
1122*bb4ee6a4SAndroid Build Coastguard Worker
1123*bb4ee6a4SAndroid Build Coastguard Worker let uffd = uffd_list.clone_main_uffd().context("clone main uffd")?;
1124*bb4ee6a4SAndroid Build Coastguard Worker let join_handle = scope.spawn(move || {
1125*bb4ee6a4SAndroid Build Coastguard Worker let mut ctx = page_handler.start_swap_in();
1126*bb4ee6a4SAndroid Build Coastguard Worker let job = bg_job_control.new_job();
1127*bb4ee6a4SAndroid Build Coastguard Worker let start_time = std::time::Instant::now();
1128*bb4ee6a4SAndroid Build Coastguard Worker while !job.is_aborted() {
1129*bb4ee6a4SAndroid Build Coastguard Worker match ctx.swap_in(&uffd, MAX_SWAP_CHUNK_SIZE) {
1130*bb4ee6a4SAndroid Build Coastguard Worker Ok(num_pages) => {
1131*bb4ee6a4SAndroid Build Coastguard Worker if num_pages == 0 {
1132*bb4ee6a4SAndroid Build Coastguard Worker break;
1133*bb4ee6a4SAndroid Build Coastguard Worker }
1134*bb4ee6a4SAndroid Build Coastguard Worker let mut state_transition = state_transition.lock();
1135*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages += num_pages as u64;
1136*bb4ee6a4SAndroid Build Coastguard Worker state_transition.time_ms =
1137*bb4ee6a4SAndroid Build Coastguard Worker start_time.elapsed().as_millis().try_into()?;
1138*bb4ee6a4SAndroid Build Coastguard Worker }
1139*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
1140*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to swap in: {:?}", e);
1141*bb4ee6a4SAndroid Build Coastguard Worker }
1142*bb4ee6a4SAndroid Build Coastguard Worker }
1143*bb4ee6a4SAndroid Build Coastguard Worker }
1144*bb4ee6a4SAndroid Build Coastguard Worker if job.is_aborted() {
1145*bb4ee6a4SAndroid Build Coastguard Worker info!("swap in is aborted");
1146*bb4ee6a4SAndroid Build Coastguard Worker }
1147*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
1148*bb4ee6a4SAndroid Build Coastguard Worker });
1149*bb4ee6a4SAndroid Build Coastguard Worker state = State::SwapInInProgress {
1150*bb4ee6a4SAndroid Build Coastguard Worker join_handle,
1151*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup,
1152*bb4ee6a4SAndroid Build Coastguard Worker };
1153*bb4ee6a4SAndroid Build Coastguard Worker
1154*bb4ee6a4SAndroid Build Coastguard Worker info!("start swapping in");
1155*bb4ee6a4SAndroid Build Coastguard Worker }
1156*bb4ee6a4SAndroid Build Coastguard Worker Command::Exit => {
1157*bb4ee6a4SAndroid Build Coastguard Worker match state {
1158*bb4ee6a4SAndroid Build Coastguard Worker State::SwapInInProgress { join_handle, .. } => {
1159*bb4ee6a4SAndroid Build Coastguard Worker // Wait until swap-in finishes.
1160*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = join_handle.join() {
1161*bb4ee6a4SAndroid Build Coastguard Worker bail!("failed to join swap in thread: {:?}", e);
1162*bb4ee6a4SAndroid Build Coastguard Worker }
1163*bb4ee6a4SAndroid Build Coastguard Worker return Ok(VmmSwapResult {
1164*bb4ee6a4SAndroid Build Coastguard Worker should_exit: true,
1165*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup: false,
1166*bb4ee6a4SAndroid Build Coastguard Worker });
1167*bb4ee6a4SAndroid Build Coastguard Worker }
1168*bb4ee6a4SAndroid Build Coastguard Worker State::Trim(join_handle) => {
1169*bb4ee6a4SAndroid Build Coastguard Worker abort_background_job(join_handle, bg_job_control)
1170*bb4ee6a4SAndroid Build Coastguard Worker .context("abort trim")?;
1171*bb4ee6a4SAndroid Build Coastguard Worker }
1172*bb4ee6a4SAndroid Build Coastguard Worker _ => {}
1173*bb4ee6a4SAndroid Build Coastguard Worker }
1174*bb4ee6a4SAndroid Build Coastguard Worker let mut ctx = page_handler.start_swap_in();
1175*bb4ee6a4SAndroid Build Coastguard Worker // Swap-in all before exit.
1176*bb4ee6a4SAndroid Build Coastguard Worker while ctx
1177*bb4ee6a4SAndroid Build Coastguard Worker .swap_in(uffd_list.main_uffd(), MAX_SWAP_CHUNK_SIZE)
1178*bb4ee6a4SAndroid Build Coastguard Worker .context("swap in")?
1179*bb4ee6a4SAndroid Build Coastguard Worker > 0
1180*bb4ee6a4SAndroid Build Coastguard Worker {}
1181*bb4ee6a4SAndroid Build Coastguard Worker return Ok(VmmSwapResult {
1182*bb4ee6a4SAndroid Build Coastguard Worker should_exit: true,
1183*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup: false,
1184*bb4ee6a4SAndroid Build Coastguard Worker });
1185*bb4ee6a4SAndroid Build Coastguard Worker }
1186*bb4ee6a4SAndroid Build Coastguard Worker Command::Status => {
1187*bb4ee6a4SAndroid Build Coastguard Worker let mut metrics = SwapMetrics {
1188*bb4ee6a4SAndroid Build Coastguard Worker resident_pages: count_resident_pages(guest_memory) as u64,
1189*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default()
1190*bb4ee6a4SAndroid Build Coastguard Worker };
1191*bb4ee6a4SAndroid Build Coastguard Worker page_handler.load_metrics(&mut metrics);
1192*bb4ee6a4SAndroid Build Coastguard Worker let status = SwapStatus {
1193*bb4ee6a4SAndroid Build Coastguard Worker state: (&state).into(),
1194*bb4ee6a4SAndroid Build Coastguard Worker metrics,
1195*bb4ee6a4SAndroid Build Coastguard Worker state_transition: *state_transition.lock(),
1196*bb4ee6a4SAndroid Build Coastguard Worker };
1197*bb4ee6a4SAndroid Build Coastguard Worker command_tube.send(&status).context("send status response")?;
1198*bb4ee6a4SAndroid Build Coastguard Worker debug!("swap status: {:?}", status);
1199*bb4ee6a4SAndroid Build Coastguard Worker }
1200*bb4ee6a4SAndroid Build Coastguard Worker },
1201*bb4ee6a4SAndroid Build Coastguard Worker Token::BackgroundJobCompleted => {
1202*bb4ee6a4SAndroid Build Coastguard Worker // Reset the completed event.
1203*bb4ee6a4SAndroid Build Coastguard Worker if !bg_job_control
1204*bb4ee6a4SAndroid Build Coastguard Worker .reset()
1205*bb4ee6a4SAndroid Build Coastguard Worker .context("reset background job event")?
1206*bb4ee6a4SAndroid Build Coastguard Worker {
1207*bb4ee6a4SAndroid Build Coastguard Worker // When the job is aborted and the event is comsumed by reset(), the token
1208*bb4ee6a4SAndroid Build Coastguard Worker // `Token::BackgroundJobCompleted` may remain in the `events`. Just ignore
1209*bb4ee6a4SAndroid Build Coastguard Worker // the obsolete token here.
1210*bb4ee6a4SAndroid Build Coastguard Worker continue;
1211*bb4ee6a4SAndroid Build Coastguard Worker }
1212*bb4ee6a4SAndroid Build Coastguard Worker match state {
1213*bb4ee6a4SAndroid Build Coastguard Worker State::SwapInInProgress {
1214*bb4ee6a4SAndroid Build Coastguard Worker join_handle,
1215*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup,
1216*bb4ee6a4SAndroid Build Coastguard Worker } => {
1217*bb4ee6a4SAndroid Build Coastguard Worker join_handle
1218*bb4ee6a4SAndroid Build Coastguard Worker .join()
1219*bb4ee6a4SAndroid Build Coastguard Worker .expect("panic on the background job thread")
1220*bb4ee6a4SAndroid Build Coastguard Worker .context("swap in finish")?;
1221*bb4ee6a4SAndroid Build Coastguard Worker let state_transition = state_transition.lock();
1222*bb4ee6a4SAndroid Build Coastguard Worker info!(
1223*bb4ee6a4SAndroid Build Coastguard Worker "swap in all {} pages in {} ms.",
1224*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages, state_transition.time_ms
1225*bb4ee6a4SAndroid Build Coastguard Worker );
1226*bb4ee6a4SAndroid Build Coastguard Worker return Ok(VmmSwapResult {
1227*bb4ee6a4SAndroid Build Coastguard Worker should_exit: false,
1228*bb4ee6a4SAndroid Build Coastguard Worker slow_file_cleanup,
1229*bb4ee6a4SAndroid Build Coastguard Worker });
1230*bb4ee6a4SAndroid Build Coastguard Worker }
1231*bb4ee6a4SAndroid Build Coastguard Worker State::Trim(join_handle) => {
1232*bb4ee6a4SAndroid Build Coastguard Worker join_handle
1233*bb4ee6a4SAndroid Build Coastguard Worker .join()
1234*bb4ee6a4SAndroid Build Coastguard Worker .expect("panic on the background job thread")
1235*bb4ee6a4SAndroid Build Coastguard Worker .context("trim finish")?;
1236*bb4ee6a4SAndroid Build Coastguard Worker let state_transition = state_transition.lock();
1237*bb4ee6a4SAndroid Build Coastguard Worker info!(
1238*bb4ee6a4SAndroid Build Coastguard Worker "trimmed {} pages in {} ms.",
1239*bb4ee6a4SAndroid Build Coastguard Worker state_transition.pages, state_transition.time_ms
1240*bb4ee6a4SAndroid Build Coastguard Worker );
1241*bb4ee6a4SAndroid Build Coastguard Worker state = State::SwapOutPending;
1242*bb4ee6a4SAndroid Build Coastguard Worker }
1243*bb4ee6a4SAndroid Build Coastguard Worker state => {
1244*bb4ee6a4SAndroid Build Coastguard Worker bail!(
1245*bb4ee6a4SAndroid Build Coastguard Worker "background job completed but the actual state is {:?}",
1246*bb4ee6a4SAndroid Build Coastguard Worker SwapState::from(&state)
1247*bb4ee6a4SAndroid Build Coastguard Worker );
1248*bb4ee6a4SAndroid Build Coastguard Worker }
1249*bb4ee6a4SAndroid Build Coastguard Worker }
1250*bb4ee6a4SAndroid Build Coastguard Worker }
1251*bb4ee6a4SAndroid Build Coastguard Worker };
1252*bb4ee6a4SAndroid Build Coastguard Worker }
1253*bb4ee6a4SAndroid Build Coastguard Worker if try_gc_uffds {
1254*bb4ee6a4SAndroid Build Coastguard Worker uffd_list.gc_dead_uffds().context("gc dead uffds")?;
1255*bb4ee6a4SAndroid Build Coastguard Worker try_gc_uffds = false;
1256*bb4ee6a4SAndroid Build Coastguard Worker }
1257*bb4ee6a4SAndroid Build Coastguard Worker }
1258*bb4ee6a4SAndroid Build Coastguard Worker }
1259