Skip to content

Latest commit

 

History

History
81 lines (77 loc) · 2.83 KB

vm-shutdown.md

File metadata and controls

81 lines (77 loc) · 2.83 KB

Rust threads do not support killing whereas pthreads do. We first overload killing in vmm-sys-utils.

/// Trait for threads that can be signalled via `pthread_kill`.
///
/// Note that this is only useful for signals between `SIGRTMIN()` and
/// `SIGRTMAX()` because these are guaranteed to not be used by the C
/// runtime.
///
/// # Safety
///
/// This is marked unsafe because the implementation of this trait must
/// guarantee that the returned `pthread_t` is valid and has a lifetime at
/// least that of the trait object.
pub unsafe trait Killable {
    /// Cast this killable thread as `pthread_t`.
    fn pthread_handle(&self) -> pthread_t;

    /// Send a signal to this killable thread.
    ///
    /// # Arguments
    ///
    /// * `num`: specify the signal
    fn kill(&self, num: c_int) -> errno::Result<()> {
        validate_signal_num(num)?;

        // Safe because we ensure we are using a valid pthread handle,
        // a valid signal number, and check the return result.
        let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
        if ret < 0 {
            return errno::errno_result();
        }
        Ok(())
    }
}

// Safe because we fulfill our contract of returning a genuine pthread handle.
unsafe impl<T> Killable for JoinHandle<T> {
    fn pthread_handle(&self) -> pthread_t {
        // JoinHandleExt::as_pthread_t gives c_ulong, convert it to the
        // type that the libc crate expects
        assert_eq!(mem::size_of::<pthread_t>(), mem::size_of::<usize>());
        self.as_pthread_t() as usize as pthread_t
    }
}

Next, we setup the kill handler in

  • KvmVcpu.run
    • KvmVcpu::setup_signal_handler().unwrap();
   pub(crate) fn setup_signal_handler() -> Result<()> {
        extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
            KvmVcpu::set_local_immediate_exit(1);
        }
        #[allow(clippy::identity_op)]
        register_signal_handler(SIGRTMIN() + 0, handle_signal)
            .map_err(Error::RegisterSignalHandler)?;
        Ok(())
    }

This calls set_local_immediate_exit on receiving a termination signal:

fn set_local_immediate_exit(value: u8) {
    Self::TLS_VCPU_PTR.with(|v| {
        if let Some(vcpu) = *v.borrow() {
            // The block below modifies a mmaped memory region (`kvm_run` struct) which is valid
            // as long as the `VMM` is still in scope. This function is called in response to
            // SIGRTMIN(), while the vCPU threads are still active. Their termination are
            // strictly bound to the lifespan of the `VMM` and it precedes the `VMM` dropping.
            unsafe {
                let vcpu_ref = &*vcpu;
                vcpu_ref.vcpu_fd.set_kvm_immediate_exit(value);
            };
        }
    });
}