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>
This commit is contained in:
Dmitry Torokhov 2018-02-21 13:10:53 -08:00 committed by chrome-bot
parent cd4053364d
commit 2cd14a1e46

View file

@ -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