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 //! Integration test for scsi devices as virtio-scsi.
6
7 use std::path::Path;
8
9 use fixture::utils::prepare_disk_img;
10 use fixture::vm::Config;
11 use fixture::vm::TestVm;
12
13 // Mount the scsi device, and then check if simple read, write, and sync operations work.
mount_scsi_devices(mut config: Config, count: usize) -> anyhow::Result<()>14 fn mount_scsi_devices(mut config: Config, count: usize) -> anyhow::Result<()> {
15 let disks = (0..count).map(|_| prepare_disk_img()).collect::<Vec<_>>();
16 for disk in &disks {
17 let scsi_disk = disk.path().to_str().unwrap();
18 println!("scsi-disk={scsi_disk}");
19 config = config.extra_args(vec!["--scsi-block".to_string(), scsi_disk.to_string()]);
20 }
21
22 let mut vm = TestVm::new(config).unwrap();
23 for (i, disk) in disks.iter().enumerate() {
24 let dev = format!("/dev/sd{}", char::from(b'a' + i as u8));
25 let dest = Path::new("/mnt").join(disk.path().file_name().unwrap());
26 vm.exec_in_guest("mount -t tmpfs none /mnt")?;
27 vm.exec_in_guest(&format!("mkdir -p {}", dest.display()))?;
28 vm.exec_in_guest(&format!("mount -t ext4 {dev} {}", dest.display()))?;
29
30 let output = dest.join("tmp");
31 vm.exec_in_guest(&format!("echo 42 > {}", output.display()))?;
32 vm.exec_in_guest(&format!("sync -d {}", output.display()))?;
33 assert_eq!(
34 vm.exec_in_guest(&format!("cat {}", output.display()))?
35 .stdout
36 .trim(),
37 "42"
38 );
39 }
40 Ok(())
41 }
42
43 #[test]
test_scsi_mount()44 fn test_scsi_mount() {
45 let config = Config::new();
46 mount_scsi_devices(config, 1).unwrap();
47 }
48
49 #[test]
test_scsi_mount_disable_sandbox()50 fn test_scsi_mount_disable_sandbox() {
51 let config = Config::new().disable_sandbox();
52 mount_scsi_devices(config, 1).unwrap();
53 }
54
55 #[test]
test_scsi_mount_multi_devices()56 fn test_scsi_mount_multi_devices() {
57 let config = Config::new();
58 mount_scsi_devices(config, 3).unwrap();
59 }
60
61 #[test]
test_scsi_mount_multi_devices_disable_sandbox()62 fn test_scsi_mount_multi_devices_disable_sandbox() {
63 let config = Config::new().disable_sandbox();
64 mount_scsi_devices(config, 3).unwrap();
65 }
66
67 // This test is for commands in controlq.
68 // First check if the resetting behavior is supported by `sg_opcodes` commands, and then issue the
69 // `sg_reset` command.
reset_scsi(config: Config) -> anyhow::Result<()>70 fn reset_scsi(config: Config) -> anyhow::Result<()> {
71 let disk = prepare_disk_img();
72 let scsi_disk = disk.path().to_str().unwrap();
73 let config = config.extra_args(vec!["--scsi-block".to_string(), scsi_disk.to_string()]);
74 println!("scsi-disk={scsi_disk}");
75
76 let mut vm = TestVm::new(config).unwrap();
77 let cmd = vm.exec_in_guest("sg_opcodes --tmf /dev/sda")?;
78 let stdout = cmd.stdout.trim();
79 assert!(stdout.contains("Logical unit reset"));
80 assert!(stdout.contains("Target reset"));
81
82 assert!(vm.exec_in_guest("sg_reset -d /dev/sda").is_ok());
83 Ok(())
84 }
85
86 #[test]
test_scsi_reset()87 fn test_scsi_reset() {
88 let config = Config::new();
89 reset_scsi(config).unwrap();
90 }
91
92 #[test]
test_scsi_reset_disable_sandbox()93 fn test_scsi_reset_disable_sandbox() {
94 let config = Config::new().disable_sandbox();
95 reset_scsi(config).unwrap();
96 }
97
write_same_scsi(config: Config) -> anyhow::Result<()>98 fn write_same_scsi(config: Config) -> anyhow::Result<()> {
99 let disk = prepare_disk_img();
100 let scsi_disk = disk.path().to_str().unwrap();
101 let config = config.extra_args(vec!["--scsi-block".to_string(), scsi_disk.to_string()]);
102 println!("scsi-disk={scsi_disk}");
103
104 let mut vm = TestVm::new(config)?;
105 assert!(vm
106 .exec_in_guest("sg_write_same --16 --lba=0 --num=1 --unmap /dev/sda")
107 .is_ok());
108 assert!(vm
109 .exec_in_guest("sg_write_same --16 --lba=0 --num=1 /dev/sda")
110 .is_ok());
111 Ok(())
112 }
113
114 #[test]
test_scsi_write_same()115 fn test_scsi_write_same() {
116 let config = Config::new();
117 write_same_scsi(config).unwrap();
118 }
119
120 #[test]
test_scsi_write_same_disable_sandbox()121 fn test_scsi_write_same_disable_sandbox() {
122 let config = Config::new().disable_sandbox();
123 write_same_scsi(config).unwrap();
124 }
125
unmap_scsi(config: Config) -> anyhow::Result<()>126 fn unmap_scsi(config: Config) -> anyhow::Result<()> {
127 let disk = prepare_disk_img();
128 let scsi_disk = disk.path().to_str().unwrap();
129 let config = config.extra_args(vec!["--scsi-block".to_string(), scsi_disk.to_string()]);
130 println!("scsi-disk={scsi_disk}");
131
132 let mut vm = TestVm::new(config)?;
133 assert!(vm
134 .exec_in_guest("sg_unmap --lba=0 --num=1 -f /dev/sda")
135 .is_ok());
136 Ok(())
137 }
138
139 #[test]
test_scsi_unmap()140 fn test_scsi_unmap() {
141 let config = Config::new();
142 unmap_scsi(config).unwrap();
143 }
144
145 #[test]
test_scsi_unmap_disable_sandbox()146 fn test_scsi_unmap_disable_sandbox() {
147 let config = Config::new().disable_sandbox();
148 unmap_scsi(config).unwrap();
149 }
150