mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Worker threads spawned through spawn_virtio_thread previously wrote to exit_evt on any clean Err return, taking the whole VMM down on a single failed device worker. A guest induced fault in any virtio device thus propagated into a host wide failure. Route the Err return through the shared mark_device_needs_reset helper instead. The helper sets the DEVICE_NEEDS_RESET bit on device_status and triggers a config change interrupt, so the device goes idle and the guest is informed. The thread exits cleanly without killing the rest of the VMM. The panic and the seccomp filter apply paths keep writing to exit_evt. A panicked worker may have left poisoned locks or partially mutated state, so a hard exit remains the right policy there. spawn_virtio_thread now takes the device_status and the interrupt callback. Every native virtio and vhost-user call site is updated to pass them in. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
73 lines
2.3 KiB
Rust
73 lines
2.3 KiB
Rust
// Copyright © 2021 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
use std::panic::AssertUnwindSafe;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::AtomicU8;
|
|
use std::thread::{self, JoinHandle};
|
|
|
|
use log::error;
|
|
use seccompiler::{SeccompAction, apply_filter};
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
use crate::epoll_helper::EpollHelperError;
|
|
use crate::seccomp_filters::{Thread, get_seccomp_filter};
|
|
use crate::{ActivateError, VirtioInterrupt, mark_device_needs_reset};
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(crate) fn spawn_virtio_thread<F>(
|
|
name: &str,
|
|
seccomp_action: &SeccompAction,
|
|
thread_type: Thread,
|
|
epoll_threads: &mut Vec<JoinHandle<()>>,
|
|
exit_evt: &EventFd,
|
|
device_status: Arc<AtomicU8>,
|
|
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
|
f: F,
|
|
) -> Result<(), ActivateError>
|
|
where
|
|
F: FnOnce() -> std::result::Result<(), EpollHelperError>,
|
|
F: Send + 'static,
|
|
{
|
|
let seccomp_filter = get_seccomp_filter(seccomp_action, thread_type)
|
|
.map_err(ActivateError::CreateSeccompFilter)?;
|
|
|
|
let thread_exit_evt = exit_evt
|
|
.try_clone()
|
|
.map_err(ActivateError::CloneExitEventFd)?;
|
|
let thread_name = name.to_string();
|
|
|
|
thread::Builder::new()
|
|
.name(name.to_string())
|
|
.spawn(move || {
|
|
if !seccomp_filter.is_empty()
|
|
&& let Err(e) = apply_filter(&seccomp_filter)
|
|
{
|
|
error!("Error applying seccomp filter: {e:?}");
|
|
thread_exit_evt.write(1).ok();
|
|
return;
|
|
}
|
|
match std::panic::catch_unwind(AssertUnwindSafe(f)) {
|
|
Err(_) => {
|
|
error!("{thread_name} thread panicked");
|
|
thread_exit_evt.write(1).ok();
|
|
}
|
|
Ok(Err(e)) => {
|
|
mark_device_needs_reset(
|
|
&device_status,
|
|
interrupt_cb.as_ref(),
|
|
format_args!("{thread_name}: worker exited with error: {e:?}"),
|
|
);
|
|
}
|
|
Ok(Ok(())) => {}
|
|
}
|
|
})
|
|
.map(|thread| epoll_threads.push(thread))
|
|
.map_err(|e| {
|
|
error!("Failed to spawn thread for {name}: {e}");
|
|
ActivateError::ThreadSpawn(e)
|
|
})
|
|
}
|