diff options
author | Dmitry Torokhov <dtor@chromium.org> | 2018-02-21 13:10:53 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-02-26 22:07:09 -0800 |
commit | 2cd14a1e4675e4917ab464df9ad8007f8bf28c0e (patch) | |
tree | 03f04f546eb83a16e2f94fef01498f13009db9f5 | |
parent | cd4053364d26029e4de560407803df842e96b7c0 (diff) | |
download | crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar.gz crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar.bz2 crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar.lz crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar.xz crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.tar.zst crosvm-2cd14a1e4675e4917ab464df9ad8007f8bf28c0e.zip |
sys_util: allow clearing given pending signal
We are planning on using KVM_SET_SIGNAL_MASK and have the signal that we use to kick VCPU permanently masked to close the race around handling pause requests, so we need a way to clear pending interrupts, otherwise VM will never run again. TEST=cargo test --features plugin; cargo test -p kvm; ./build_test BUG=chromium:800626 Change-Id: I2dfe6fcb129e4b8156f6a7ff842e171661c56440 Signed-off-by: Dmitry Torokhov <dtor@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/930462 Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r-- | sys_util/src/signal.rs | 58 |
1 files changed, 55 insertions, 3 deletions
diff --git a/sys_util/src/signal.rs b/sys_util/src/signal.rs index 2777613..b5360eb 100644 --- a/sys_util/src/signal.rs +++ b/sys_util/src/signal.rs @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use libc::{c_int, signal, - sigset_t, sigaddset, sigemptyset, sigismember, +use libc::{c_int, signal, timespec, + sigset_t, siginfo_t, + sigaddset, sigemptyset, sigismember, sigpending, sigtimedwait, pthread_t, pthread_kill, pthread_sigmask, - SIG_BLOCK, SIG_UNBLOCK, SIG_ERR, EINVAL}; + SIG_BLOCK, SIG_UNBLOCK, SIG_ERR, EAGAIN, EINTR, EINVAL }; use std::mem; use std::ptr::null_mut; @@ -27,6 +28,12 @@ pub enum Error { BlockSignal(errno::Error), /// The signal could not be unblocked. UnblockSignal(errno::Error), + /// Failed to wait for given signal. + ClearWaitPending(errno::Error), + /// Failed to get pending signals. + ClearGetPending(errno::Error), + /// Failed to check if given signal is in the set of pending signals. + ClearCheckPending(errno::Error), } pub type SignalResult<T> = result::Result<T, Error>; @@ -134,6 +141,51 @@ pub fn unblock_signal(num: c_int) -> SignalResult<()> { Ok(()) } +/// Clears pending signal. +pub fn clear_signal(num: c_int) -> SignalResult<()> { + let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?; + + while { + // This is safe as we are rigorously checking return values + // of libc calls. + unsafe { + let mut siginfo: siginfo_t = mem::zeroed(); + let ts = timespec { tv_sec: 0, tv_nsec: 0 }; + // Attempt to clear one instance of pending signal. If signal + // is not pending, the call will fail with EAGAIN or EINTR. + let ret = sigtimedwait(&sigset, &mut siginfo, &ts); + if ret < 0 { + let e = errno::Error::last(); + match e.errno() { + EAGAIN | EINTR => {} + _ => { + return Err(Error::ClearWaitPending(errno::Error::last())); + } + } + } + + // This sigset will be actually filled with `sigpending` call. + let mut chkset: sigset_t = mem::zeroed(); + // See if more instances of the signal are pending. + let ret = sigpending(&mut chkset); + if ret < 0 { + return Err(Error::ClearGetPending(errno::Error::last())); + } + + let ret = sigismember(&chkset, num); + if ret < 0 { + return Err(Error::ClearCheckPending(errno::Error::last())); + } + + // This is do-while loop condition. + ret != 0 + } + } + {} + + Ok(()) +} + /// 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 |