mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
tests: Add an integration test to verify PCI device allocations
This commit adds an integration test to verify that the guest sees the correct BDF. Moreover, we check that we can allocate a random free BDF and that freeing BDFs works. Signed-off-by: Pascal Scholz <pascal.scholz@cyberus-technology.de> On-behalf-of: SAP pascal.scholz@sap.com Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
committed by
Rob Bradford
parent
8259f92909
commit
f82eebc0b0
@@ -1033,3 +1033,29 @@ pub(crate) fn make_guest_panic(guest: &Guest) {
|
||||
// Trigger guest a panic
|
||||
guest.ssh_command("screen -dmS reboot sh -c \"sleep 5; echo s | tee /proc/sysrq-trigger; echo c | sudo tee /proc/sysrq-trigger\"").unwrap();
|
||||
}
|
||||
|
||||
/// Extracts a BDF from a CHV returned response
|
||||
pub(crate) fn bdf_from_hotplug_response(
|
||||
s: &str,
|
||||
) -> (
|
||||
u16, /* Segment ID */
|
||||
u8, /* Bus ID */
|
||||
u8, /* Device ID */
|
||||
u8, /* Function ID */
|
||||
) {
|
||||
let json: serde_json::Value = serde_json::from_str(s).expect("should be valid JSON");
|
||||
let bdf_str = json["bdf"]
|
||||
.as_str()
|
||||
.expect("should contain string key `bdf`");
|
||||
|
||||
// BDF format: "SSSS:BB:DD.F"
|
||||
let parts: Vec<&str> = bdf_str.split(&[':', '.'][..]).collect();
|
||||
assert_eq!(parts.len(), 4, "unexpected BDF format: {bdf_str}");
|
||||
|
||||
let segment_id = u16::from_str_radix(parts[0], 16).unwrap();
|
||||
let bus_id = u8::from_str_radix(parts[1], 16).unwrap();
|
||||
let device_id = u8::from_str_radix(parts[2], 16).unwrap();
|
||||
let function_id = u8::from_str_radix(parts[3], 16).unwrap();
|
||||
|
||||
(segment_id, bus_id, device_id, function_id)
|
||||
}
|
||||
|
||||
@@ -5699,6 +5699,144 @@ mod common_parallel {
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pci_device_id() {
|
||||
let disk_config = UbuntuDiskConfig::new(JAMMY_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(disk_config));
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let kernel_path = direct_kernel_boot_path();
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let kernel_path = edk2_path();
|
||||
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
// Boot without network
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
cmd.args(["--api-socket", &api_socket])
|
||||
.default_cpus()
|
||||
.default_memory()
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_net()
|
||||
.default_disks()
|
||||
.capture_output();
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
|
||||
guest.wait_vm_boot().unwrap();
|
||||
|
||||
// Add a network device with non-static device id request
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
let (cmd_success, cmd_stdout, _) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-net",
|
||||
Some(
|
||||
format!(
|
||||
"id=test0,tap=,mac={},ip={},mask=255.255.255.128",
|
||||
guest.network.guest_mac1, guest.network.host_ip1,
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
// We now know the first free device ID on the bus
|
||||
let output = String::from_utf8(cmd_stdout).expect("should work");
|
||||
let (_, _, first_free_device_id, _) = bdf_from_hotplug_response(output.as_str());
|
||||
assert_ne!(first_free_device_id, 0);
|
||||
|
||||
// We expect a match from grep
|
||||
let _ = String::from(
|
||||
guest
|
||||
.ssh_command(&format!(
|
||||
"lspci -n | grep \"00:{first_free_device_id:02x}.0\""
|
||||
))
|
||||
.unwrap()
|
||||
.trim(),
|
||||
);
|
||||
// Calculate the succeeding device ID
|
||||
let device_id_to_allocate = first_free_device_id + 1;
|
||||
// We expect the succeeding device ID to be free
|
||||
assert!(matches!(
|
||||
guest.ssh_command(&format!(
|
||||
"lspci -n | grep \"00:{device_id_to_allocate:02x}.0\""
|
||||
)),
|
||||
Err(SshCommandError::NonZeroExitStatus(1))
|
||||
));
|
||||
|
||||
// Add a device to the next device slot explicitly
|
||||
let (cmd_success, cmd_stdout, _) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-net",
|
||||
Some(
|
||||
format!(
|
||||
"id=test1337,tap=,mac={},ip={},mask=255.255.255.128,pci_device_id={}",
|
||||
guest.network.guest_mac1, guest.network.host_ip1, device_id_to_allocate,
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
// Retrieve what BDF we actually reserved and assert it's equal to that we wanted to reserve
|
||||
let output = String::from_utf8(cmd_stdout).expect("should work");
|
||||
let (_, _, allocated_device_id, _) = bdf_from_hotplug_response(output.as_str());
|
||||
assert_eq!(device_id_to_allocate, allocated_device_id);
|
||||
// Check that the device ID is really in use
|
||||
let _ = String::from(
|
||||
guest
|
||||
.ssh_command(&format!(
|
||||
"lspci -n | grep \"00:{allocated_device_id:02x}.0\""
|
||||
))
|
||||
.unwrap()
|
||||
.trim(),
|
||||
);
|
||||
// Remove the first device to create a hole
|
||||
let cmd_success = remote_command(&api_socket, "remove-device", Some("test0"));
|
||||
assert!(cmd_success);
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
// We left a hole in the used PCI IDs. The guest sees no device on the respective ID
|
||||
assert!(matches!(
|
||||
guest.ssh_command(&format!(
|
||||
"lspci -n | grep \"00:{first_free_device_id:02x}.0\""
|
||||
)),
|
||||
Err(SshCommandError::NonZeroExitStatus(1))
|
||||
));
|
||||
// Reuse the device ID hole by dynamically coalescing with the first free ID
|
||||
let (cmd_success, cmd_stdout, _) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-net",
|
||||
Some(
|
||||
format!(
|
||||
"id=test0,tap=,mac={},ip={},mask=255.255.255.128",
|
||||
guest.network.guest_mac1, guest.network.host_ip1,
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
// Check that CHV reports that we added the same device to the same ID
|
||||
let output = String::from_utf8(cmd_stdout).expect("should work");
|
||||
let (_, _, allocated_device_id, _) = bdf_from_hotplug_response(output.as_str());
|
||||
assert_eq!(first_free_device_id, allocated_device_id);
|
||||
|
||||
// Check that guest sees the same device again at the same BDF
|
||||
let _ = String::from(
|
||||
guest
|
||||
.ssh_command(&format!(
|
||||
"lspci -n | grep \"00:{allocated_device_id:02x}.0\""
|
||||
))
|
||||
.unwrap()
|
||||
.trim(),
|
||||
);
|
||||
});
|
||||
|
||||
kill_child(&mut child);
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
}
|
||||
|
||||
mod dbus_api {
|
||||
|
||||
Reference in New Issue
Block a user