diff --git a/src/linux.rs b/src/linux.rs index d56f32d25f..71c7ae7b6e 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -44,6 +44,7 @@ use aarch64::AArch64 as Arch; pub enum Error { BalloonDeviceNew(devices::virtio::BalloonError), BlockDeviceNew(sys_util::Error), + BlockSignal(sys_util::signal::Error), ChownWaylandRoot(sys_util::Error), CloneEventFd(sys_util::Error), Cmdline(kernel_cmdline::Error), @@ -74,6 +75,7 @@ pub enum Error { RegisterIrqfd(sys_util::Error), RegisterNet(device_manager::Error), RegisterRng(device_manager::Error), + RegisterSignalHandler(sys_util::Error), RegisterVsock(device_manager::Error), RegisterWayland(device_manager::Error), RngDeviceNew(devices::virtio::RngError), @@ -98,6 +100,7 @@ impl fmt::Display for Error { match self { &Error::BalloonDeviceNew(ref e) => write!(f, "failed to create balloon: {:?}", e), &Error::BlockDeviceNew(ref e) => write!(f, "failed to create block device: {:?}", e), + &Error::BlockSignal(ref e) => write!(f, "failed to block signal: {:?}", e), &Error::ChownWaylandRoot(ref e) => { write!(f, "error chowning wayland root directory: {:?}", e) } @@ -142,6 +145,9 @@ impl fmt::Display for Error { &Error::RegisterIrqfd(ref e) => write!(f, "error registering irqfd: {:?}", e), &Error::RegisterNet(ref e) => write!(f, "error registering net device: {:?}", e), &Error::RegisterRng(ref e) => write!(f, "error registering rng device: {:?}", e), + &Error::RegisterSignalHandler(ref e) => { + write!(f, "error registering signal handler: {:?}", e) + } &Error::RegisterVsock(ref e) => { write!(f, "error registering virtual socket device: {:?}", e) } @@ -459,6 +465,17 @@ fn setup_vcpu(kvm: &Kvm, Ok(vcpu) } +fn setup_vcpu_signal_handler() -> Result<()> { + unsafe { + extern "C" fn handle_signal() {} + // Our signal handler does nothing and is trivially async signal safe. + register_signal_handler(SIGRTMIN() + 0, handle_signal) + .map_err(Error::RegisterSignalHandler)?; + } + block_signal(SIGRTMIN() + 0).map_err(Error::BlockSignal)?; + Ok(()) +} + fn run_vcpu(vcpu: Vcpu, cpu_id: u32, start_barrier: Arc, @@ -469,15 +486,30 @@ fn run_vcpu(vcpu: Vcpu, thread::Builder::new() .name(format!("crosvm_vcpu{}", cpu_id)) .spawn(move || { - unsafe { - extern "C" fn handle_signal() {} - // Our signal handler does nothing and is trivially async signal safe. - register_signal_handler(SIGRTMIN() + 0, handle_signal) - .expect("failed to register vcpu signal handler"); - } + let mut sig_ok = true; + match get_blocked_signals() { + Ok(mut v) => { + v.retain(|&x| x != SIGRTMIN() + 0); + if let Err(e) = vcpu.set_signal_mask(&v) { + error!( + "Failed to set the KVM_SIGNAL_MASK for vcpu {} : {:?}", + cpu_id, e + ); + sig_ok = false; + } + } + Err(e) => { + error!( + "Failed to retrieve signal mask for vcpu {} : {:?}", + cpu_id, e + ); + sig_ok = false; + } + }; start_barrier.wait(); - loop { + + while sig_ok { let run_res = vcpu.run(); match run_res { Ok(run) => { @@ -515,6 +547,10 @@ fn run_vcpu(vcpu: Vcpu, if kill_signaled.load(Ordering::SeqCst) { break; } + + // Try to clear the signal that we use to kick VCPU if it is + // pending before attempting to handle pause requests. + clear_signal(SIGRTMIN() + 0).expect("failed to clear pending signal"); } exit_evt .write(1) @@ -745,6 +781,7 @@ pub fn run_config(cfg: Config) -> Result<()> { &CString::new(cmdline).unwrap()). map_err(|e| Error::SetupSystemMemory(e))?; + setup_vcpu_signal_handler()?; for (cpu_id, vcpu) in vcpus.into_iter().enumerate() { let handle = run_vcpu(vcpu, cpu_id as u32, diff --git a/sys_util/src/signal.rs b/sys_util/src/signal.rs index 5196e20acb..0bba761e48 100644 --- a/sys_util/src/signal.rs +++ b/sys_util/src/signal.rs @@ -9,7 +9,7 @@ use libc::{c_int, sigaction, SA_RESTART, timespec, SIG_BLOCK, SIG_UNBLOCK, EAGAIN, EINTR, EINVAL }; use std::mem; -use std::ptr::null_mut; +use std::ptr::{null, null_mut}; use std::result; use std::thread::JoinHandle; use std::os::unix::thread::JoinHandleExt; @@ -26,6 +26,8 @@ pub enum Error { CompareBlockedSignals(errno::Error), /// The signal could not be blocked. BlockSignal(errno::Error), + /// The signal mask could not be retrieved. + RetrieveSignalMask(i32), /// The signal could not be unblocked. UnblockSignal(errno::Error), /// Failed to wait for given signal. @@ -110,6 +112,28 @@ pub fn create_sigset(signals: &[c_int]) -> errno::Result { Ok(sigset) } +/// Retrieves the signal mask of the current thread as a vector of c_ints. +pub fn get_blocked_signals() -> SignalResult> { + let mut mask = Vec::new(); + + // Safe - return values are checked. + unsafe { + let mut old_sigset: sigset_t = mem::zeroed(); + let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t); + if ret < 0 { + return Err(Error::RetrieveSignalMask(ret)); + } + + for num in 0..(SIGRTMAX() + 1) { + if sigismember(&old_sigset, num) > 0 { + mask.push(num); + } + } + } + + Ok(mask) +} + /// Masks given signal. /// /// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked