runtime-rs: ch: Implement full thread/tid/pid handling

Add in the full details once cloud-hypervisor/cloud-hypervisor#6103
has been implemented, and the feature is available in a Cloud Hypervisor
release.

Fixes: #8799

Signed-off-by: David Esparza <david.esparza.borquez@intel.com>
This commit is contained in:
David Esparza
2024-03-08 09:12:12 -06:00
parent 1aec4f737a
commit b498e140a1
3 changed files with 135 additions and 6 deletions

View File

@@ -1654,6 +1654,7 @@ dependencies = [
"shim-interface",
"slog",
"slog-scope",
"tempdir",
"test-utils",
"tests_utils",
"thiserror",
@@ -2749,7 +2750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52"
dependencies = [
"libc",
"nix 0.26.2",
"nix 0.27.1",
]
[[package]]
@@ -3163,6 +3164,15 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.20"
@@ -3919,6 +3929,16 @@ dependencies = [
"xattr",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand 0.4.6",
"remove_dir_all",
]
[[package]]
name = "tempfile"
version = "3.7.0"

View File

@@ -41,6 +41,7 @@ tests_utils = { path = "../../tests/utils" }
futures = "0.3.25"
safe-path = "0.1.0"
crossbeam-channel = "0.5.6"
tempdir = "0.3.7"
[target.'cfg(not(target_arch = "s390x"))'.dependencies]
dragonball = { path = "../../../dragonball", features = ["atomic-guest-memory", "virtio-vsock", "hotplug", "virtio-blk", "virtio-net", "virtio-fs", "vhost-net", "dbs-upcall", "virtio-mem", "virtio-balloon", "vhost-user-net", "host-device"] }

View File

@@ -30,6 +30,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs;
use std::fs::create_dir_all;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixStream;
@@ -677,13 +678,10 @@ impl CloudHypervisorInner {
}
pub(crate) async fn get_thread_ids(&self) -> Result<VcpuThreadIds> {
let mut vcpus = HashMap::new();
let vcpu = 0;
let thread_id = self.get_vmm_master_tid().await?;
let proc_path = format!("/proc/{thread_id}");
vcpus.insert(vcpu, thread_id);
let vcpus = get_ch_vcpu_tids(&proc_path)?;
let vcpu_thread_ids = VcpuThreadIds { vcpus };
Ok(vcpu_thread_ids)
@@ -908,6 +906,59 @@ fn get_guest_protection() -> Result<GuestProtection> {
Ok(guest_protection)
}
// Return a TID/VCPU map from a specified /proc/{pid} path.
fn get_ch_vcpu_tids(proc_path: &str) -> Result<HashMap<u32, u32>> {
const VCPU_STR: &str = "vcpu";
let src = std::fs::canonicalize(proc_path)
.map_err(|e| anyhow!("Invalid proc path: {proc_path}: {e}"))?;
let tid_path = src.join("task");
let mut vcpus = HashMap::new();
for entry in fs::read_dir(&tid_path)? {
let entry = entry?;
let tid_str = match entry.file_name().into_string() {
Ok(id) => id,
Err(_) => continue,
};
let tid = tid_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid tid."))?;
let comm_path = tid_path.join(tid_str.clone()).join("comm");
if !comm_path.exists() {
return Err(anyhow!("comm path was not found."));
}
let p_name = fs::read_to_string(comm_path)?;
// The CH names it's threads with a vcpu${number} to identify them, where
// the thread name is located at /proc/${ch_pid}/task/${thread_id}/comm.
if !p_name.starts_with(VCPU_STR) {
continue;
}
let vcpu_id = p_name
.trim_start_matches(VCPU_STR)
.trim()
.parse::<u32>()
.map_err(|e| anyhow!(e).context("Invalid vcpu id."))?;
vcpus.insert(tid, vcpu_id);
}
if vcpus.is_empty() {
return Err(anyhow!("The contents of proc path are not available."));
}
Ok(vcpus)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -922,6 +973,9 @@ mod tests {
use std::path::PathBuf;
use test_utils::{assert_result, skip_if_not_root};
use std::fs::File;
use tempdir::TempDir;
fn set_fake_guest_protection(protection: Option<GuestProtection>) {
let existing_ref = FAKE_GUEST_PROTECTION.clone();
@@ -1333,4 +1387,58 @@ mod tests {
assert_eq!(d.level, level, "{}", msg);
}
}
#[actix_rt::test]
async fn test_get_thread_ids() {
let path_dir = "/tmp/proc";
let file_name = "1";
let tmp_dir = TempDir::new(path_dir).unwrap();
let file_path = tmp_dir.path().join(file_name);
let _tmp_file = File::create(file_path.as_os_str()).unwrap();
let file_path_name = file_path.as_path().to_str().map(|s| s.to_string());
let file_path_name_str = file_path_name.as_ref().unwrap().to_string();
#[derive(Debug)]
struct TestData<'a> {
proc_path: &'a str,
result: Result<HashMap<u32, u32>>,
}
let tests = &[
TestData {
// Test on a non-existent directory.
proc_path: path_dir,
result: Err(anyhow!(
"Invalid proc path: {path_dir}: No such file or directory (os error 2)"
)),
},
TestData {
// Test on an existing path, however it is not valid because it does not point to a pid.
proc_path: &file_path_name_str,
result: Err(anyhow!("Not a directory (os error 20)")),
},
TestData {
// Test on an existing proc/${pid} but that does not correspond to a CH pid.
proc_path: "/proc/1",
result: Err(anyhow!("The contents of proc path are not available.")),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test: [{}]: {:?}", i, d);
if std::env::var("DEBUG").is_ok() {
println!("DEBUG: {msg}");
}
let result = get_ch_vcpu_tids(d.proc_path);
let msg = format!("{}, result: {:?}", msg, result);
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
let actual_error = format!("{}", result.unwrap_err());
assert!(actual_error == expected_error, "{}", msg);
}
}
}