xref: /aosp_15_r20/external/crosvm/e2e_tests/tests/swap.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Testing vmm-swap
6 
7 #![cfg(any(target_os = "android", target_os = "linux"))]
8 
9 use std::time::Duration;
10 use std::time::Instant;
11 
12 use anyhow::bail;
13 use base::test_utils::call_test_with_sudo;
14 use fixture::vm::Config;
15 use fixture::vm::TestVm;
16 use swap::SwapState;
17 use swap::SwapStatus;
18 
19 const SWAP_STATE_CHANGE_TIMEOUT: Duration = Duration::from_secs(2);
20 const SWAP_FILE_PATH: &str = "/tmp/swap_test";
21 
get_swap_state(vm: &mut TestVm) -> SwapState22 fn get_swap_state(vm: &mut TestVm) -> SwapState {
23     let output = vm.swap_command("status").unwrap();
24     let status = serde_json::from_slice::<SwapStatus>(&output).unwrap();
25     status.state
26 }
27 
wait_until_swap_state_change( vm: &mut TestVm, state: SwapState, transition_state: &[SwapState], timeout: Duration, ) -> anyhow::Result<()>28 fn wait_until_swap_state_change(
29     vm: &mut TestVm,
30     state: SwapState,
31     transition_state: &[SwapState],
32     timeout: Duration,
33 ) -> anyhow::Result<()> {
34     let start = Instant::now();
35     loop {
36         let current_state = get_swap_state(vm);
37         if current_state == state {
38             return Ok(());
39         }
40         if start.elapsed() > timeout {
41             bail!(
42                 "state change timeout: current: {:?}, target: {:?}",
43                 current_state,
44                 state
45             );
46         }
47         if !transition_state.contains(&current_state) {
48             bail!(
49                 "unexpected state while waiting: current: {:?}, target: {:?}",
50                 current_state,
51                 state
52             );
53         }
54         std::thread::sleep(Duration::from_millis(100));
55     }
56 }
57 
create_tmpfs_file_in_guest(vm: &mut TestVm, size: usize)58 fn create_tmpfs_file_in_guest(vm: &mut TestVm, size: usize) {
59     vm.exec_in_guest("mount -t tmpfs -o size=64m /dev/shm /tmp")
60         .unwrap();
61     vm.exec_in_guest(&format!(
62         "head -c {} /dev/urandom > {}",
63         size, SWAP_FILE_PATH
64     ))
65     .unwrap();
66 }
67 
load_checksum_tmpfs_file(vm: &mut TestVm) -> String68 fn load_checksum_tmpfs_file(vm: &mut TestVm) -> String {
69     // Use checksum to validate that the RAM on the guest is not broken. Sending the whole content
70     // does not work due to the protocol of the connection between host and guest.
71     vm.exec_in_guest(&format!("cat {} | sha256sum", SWAP_FILE_PATH))
72         .unwrap()
73         .stdout
74 }
75 
76 #[ignore = "Only to be called by swap_enabled"]
77 #[test]
swap_enabled_impl()78 fn swap_enabled_impl() {
79     let mut config = Config::new();
80     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
81     let mut vm = TestVm::new_sudo(config).unwrap();
82 
83     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
84     vm.swap_command("enable").unwrap();
85     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
86     vm.swap_command("trim").unwrap();
87     wait_until_swap_state_change(
88         &mut vm,
89         SwapState::Pending,
90         &[SwapState::TrimInProgress],
91         SWAP_STATE_CHANGE_TIMEOUT,
92     )
93     .unwrap();
94     vm.swap_command("out").unwrap();
95     wait_until_swap_state_change(
96         &mut vm,
97         SwapState::Active,
98         &[SwapState::SwapOutInProgress],
99         SWAP_STATE_CHANGE_TIMEOUT,
100     )
101     .unwrap();
102     vm.swap_command("disable").unwrap();
103     wait_until_swap_state_change(
104         &mut vm,
105         SwapState::Ready,
106         &[SwapState::SwapInInProgress],
107         SWAP_STATE_CHANGE_TIMEOUT,
108     )
109     .unwrap();
110 }
111 
112 #[test]
swap_enabled()113 fn swap_enabled() {
114     call_test_with_sudo("swap_enabled_impl");
115 }
116 
117 #[ignore = "Only to be called by swap_out_multiple_times"]
118 #[test]
swap_out_multiple_times_impl()119 fn swap_out_multiple_times_impl() {
120     let mut config = Config::new();
121     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
122     let mut vm = TestVm::new_sudo(config).unwrap();
123 
124     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
125     vm.swap_command("enable").unwrap();
126     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
127     vm.swap_command("trim").unwrap();
128     wait_until_swap_state_change(
129         &mut vm,
130         SwapState::Pending,
131         &[SwapState::TrimInProgress],
132         SWAP_STATE_CHANGE_TIMEOUT,
133     )
134     .unwrap();
135     vm.swap_command("out").unwrap();
136     wait_until_swap_state_change(
137         &mut vm,
138         SwapState::Active,
139         &[SwapState::SwapOutInProgress],
140         SWAP_STATE_CHANGE_TIMEOUT,
141     )
142     .unwrap();
143     vm.swap_command("enable").unwrap();
144     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
145     vm.swap_command("trim").unwrap();
146     wait_until_swap_state_change(
147         &mut vm,
148         SwapState::Pending,
149         &[SwapState::TrimInProgress],
150         SWAP_STATE_CHANGE_TIMEOUT,
151     )
152     .unwrap();
153     vm.swap_command("out").unwrap();
154     wait_until_swap_state_change(
155         &mut vm,
156         SwapState::Active,
157         &[SwapState::SwapOutInProgress],
158         SWAP_STATE_CHANGE_TIMEOUT,
159     )
160     .unwrap();
161     vm.swap_command("enable").unwrap();
162     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
163     vm.swap_command("disable").unwrap();
164     wait_until_swap_state_change(
165         &mut vm,
166         SwapState::Ready,
167         &[SwapState::SwapInInProgress],
168         SWAP_STATE_CHANGE_TIMEOUT,
169     )
170     .unwrap();
171 }
172 
173 #[test]
swap_out_multiple_times()174 fn swap_out_multiple_times() {
175     call_test_with_sudo("swap_out_multiple_times_impl");
176 }
177 
178 #[ignore = "Only to be called by swap_disabled_without_swapped_out"]
179 #[test]
swap_disabled_without_swapped_out_impl()180 fn swap_disabled_without_swapped_out_impl() {
181     let mut config = Config::new();
182     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
183     let mut vm = TestVm::new_sudo(config).unwrap();
184 
185     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
186     vm.swap_command("enable").unwrap();
187     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
188     vm.swap_command("disable").unwrap();
189     wait_until_swap_state_change(
190         &mut vm,
191         SwapState::Ready,
192         &[SwapState::SwapInInProgress],
193         SWAP_STATE_CHANGE_TIMEOUT,
194     )
195     .unwrap();
196 }
197 
198 #[test]
swap_disabled_without_swapped_out()199 fn swap_disabled_without_swapped_out() {
200     call_test_with_sudo("swap_disabled_without_swapped_out_impl");
201 }
202 
203 #[ignore = "Only to be called by stopped_with_swap_enabled"]
204 #[test]
stopped_with_swap_enabled_impl()205 fn stopped_with_swap_enabled_impl() {
206     let mut config = Config::new();
207     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
208     let mut vm = TestVm::new_sudo(config).unwrap();
209 
210     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
211     vm.swap_command("enable").unwrap();
212     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
213     vm.swap_command("trim").unwrap();
214     wait_until_swap_state_change(
215         &mut vm,
216         SwapState::Pending,
217         &[SwapState::TrimInProgress],
218         SWAP_STATE_CHANGE_TIMEOUT,
219     )
220     .unwrap();
221     vm.swap_command("out").unwrap();
222     wait_until_swap_state_change(
223         &mut vm,
224         SwapState::Active,
225         &[SwapState::SwapOutInProgress],
226         SWAP_STATE_CHANGE_TIMEOUT,
227     )
228     .unwrap();
229     // dropping TestVm sends crosvm stop command and wait until the process exits.
230 }
231 
232 #[test]
stopped_with_swap_enabled()233 fn stopped_with_swap_enabled() {
234     call_test_with_sudo("stopped_with_swap_enabled_impl");
235 }
236 
237 #[ignore = "Only to be called by memory_contents_preserved_while_vmm_swap_enabled"]
238 #[test]
memory_contents_preserved_while_vmm_swap_enabled_impl()239 fn memory_contents_preserved_while_vmm_swap_enabled_impl() {
240     let mut config = Config::new();
241     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
242     let mut vm = TestVm::new_sudo(config).unwrap();
243     create_tmpfs_file_in_guest(&mut vm, 1024 * 1024);
244     let checksum = load_checksum_tmpfs_file(&mut vm);
245 
246     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
247     vm.swap_command("enable").unwrap();
248     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
249 
250     assert_eq!(load_checksum_tmpfs_file(&mut vm), checksum);
251 }
252 
253 #[test]
memory_contents_preserved_while_vmm_swap_enabled()254 fn memory_contents_preserved_while_vmm_swap_enabled() {
255     call_test_with_sudo("memory_contents_preserved_while_vmm_swap_enabled_impl");
256 }
257 
258 #[ignore = "Only to be called by memory_contents_preserved_after_vmm_swap_out"]
259 #[test]
memory_contents_preserved_after_vmm_swap_out_impl()260 fn memory_contents_preserved_after_vmm_swap_out_impl() {
261     let mut config = Config::new();
262     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
263     let mut vm = TestVm::new_sudo(config).unwrap();
264     create_tmpfs_file_in_guest(&mut vm, 1024 * 1024);
265     let checksum = load_checksum_tmpfs_file(&mut vm);
266 
267     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
268     vm.swap_command("enable").unwrap();
269     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
270     vm.swap_command("trim").unwrap();
271     wait_until_swap_state_change(
272         &mut vm,
273         SwapState::Pending,
274         &[SwapState::TrimInProgress],
275         SWAP_STATE_CHANGE_TIMEOUT,
276     )
277     .unwrap();
278     vm.swap_command("out").unwrap();
279     wait_until_swap_state_change(
280         &mut vm,
281         SwapState::Active,
282         &[SwapState::SwapOutInProgress],
283         SWAP_STATE_CHANGE_TIMEOUT,
284     )
285     .unwrap();
286 
287     assert_eq!(load_checksum_tmpfs_file(&mut vm), checksum);
288 }
289 
290 #[test]
memory_contents_preserved_after_vmm_swap_out()291 fn memory_contents_preserved_after_vmm_swap_out() {
292     call_test_with_sudo("memory_contents_preserved_after_vmm_swap_out_impl");
293 }
294 
295 #[ignore = "Only to be called by memory_contents_preserved_after_vmm_swap_disabled"]
296 #[test]
memory_contents_preserved_after_vmm_swap_disabled_impl()297 fn memory_contents_preserved_after_vmm_swap_disabled_impl() {
298     let mut config = Config::new();
299     config = config.extra_args(vec!["--swap".to_string(), ".".to_string()]);
300     let mut vm = TestVm::new_sudo(config).unwrap();
301     create_tmpfs_file_in_guest(&mut vm, 1024 * 1024);
302     let checksum = load_checksum_tmpfs_file(&mut vm);
303 
304     assert_eq!(get_swap_state(&mut vm), SwapState::Ready);
305     vm.swap_command("enable").unwrap();
306     assert_eq!(get_swap_state(&mut vm), SwapState::Pending);
307     vm.swap_command("trim").unwrap();
308     wait_until_swap_state_change(
309         &mut vm,
310         SwapState::Pending,
311         &[SwapState::TrimInProgress],
312         SWAP_STATE_CHANGE_TIMEOUT,
313     )
314     .unwrap();
315     vm.swap_command("out").unwrap();
316     wait_until_swap_state_change(
317         &mut vm,
318         SwapState::Active,
319         &[SwapState::SwapOutInProgress],
320         SWAP_STATE_CHANGE_TIMEOUT,
321     )
322     .unwrap();
323     vm.swap_command("disable").unwrap();
324     wait_until_swap_state_change(
325         &mut vm,
326         SwapState::Ready,
327         &[SwapState::SwapInInProgress],
328         SWAP_STATE_CHANGE_TIMEOUT,
329     )
330     .unwrap();
331 
332     assert_eq!(load_checksum_tmpfs_file(&mut vm), checksum);
333 }
334 
335 #[test]
memory_contents_preserved_after_vmm_swap_disabled()336 fn memory_contents_preserved_after_vmm_swap_disabled() {
337     call_test_with_sudo("memory_contents_preserved_after_vmm_swap_disabled_impl");
338 }
339