summary refs log tree commit diff
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@chromium.org>2018-02-21 13:10:53 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-02-26 22:07:09 -0800
commit2cd14a1e4675e4917ab464df9ad8007f8bf28c0e (patch)
tree03f04f546eb83a16e2f94fef01498f13009db9f5
parentcd4053364d26029e4de560407803df842e96b7c0 (diff)
downloadcrosvm-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.rs58
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