blob: 71e0e4bcf7549da8dcccab87f66bfc7f7dc3a316 [file]
// Copyright 2024 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
mod wake_vector_ffi;
use crate::arch_rs::{blocking_disallowed, ints_disabled, set_blocking_disallowed};
use crate::kernel::idle_power_thread::IdlePowerThread;
use crate::kernel::thread::AutoPreemptDisabler;
use crate::platform_rs::timer::current_boot_time;
use crate::user_copy::UserOutPtr;
use core::mem::{align_of, offset_of, size_of};
use core::pin::Pin;
use core::ptr::NonNull;
use ksync::{KCell, LockToken};
use zx_status::Status;
use zx_types::{
ZX_KOID_INVALID, ZX_MAX_NAME_LEN, ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_PREVIOUSLY_REPORTED,
ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_SIGNALED, ZX_TIME_INFINITE, zx_instant_boot_t, zx_koid_t,
zx_wake_source_report_entry_t, zx_wake_source_report_header_t,
};
use self::lists::{
GlobalListLock, ListNodes, PendingCursor, PendingGuard, PendingListLock, global_list,
pending_list,
};
/// Diagnostic information about the wake vector managed by the implementor of [`WakeVector`].
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Diagnostics {
/// Indicates that the given wake vector is enabled and can generate wake events. Disabled wake
/// vectors are not listed in diagnostic logs.
pub enabled: bool,
/// The koid of the object implementing this interface, if any.
pub koid: zx_koid_t,
/// Extra information specific to the wake vector that can aid in determining the source of the
/// wake event and potentially its state.
pub extra: [u8; ZX_MAX_NAME_LEN],
}
impl Default for Diagnostics {
fn default() -> Self {
Self { enabled: false, koid: ZX_KOID_INVALID, extra: [0; ZX_MAX_NAME_LEN] }
}
}
zr::static_assert!(size_of::<Diagnostics>() == 48);
zr::static_assert!(align_of::<Diagnostics>() == 8);
zr::static_assert!(offset_of!(Diagnostics, enabled) == 0);
zr::static_assert!(offset_of!(Diagnostics, koid) == 8);
zr::static_assert!(offset_of!(Diagnostics, extra) == 16);
/// `WakeVector` is an interface implemented by objects that will generate system wake events using
/// the [`WakeEvent`] type. This interface provides diagnostic information about the wake vector to
/// the suspend subsystem.
///
/// The C++ base class `wake_vector::WakeVector` is represented as an opaque type in Rust and its
/// virtual `GetDiagnostics` method is invoked via an FFI shim.
#[repr(C)]
pub struct WakeVector(zr::Opaque<[u8; 0]>);
impl WakeVector {
/// Provides diagnostic information about the wake vector object implementing this interface.
pub fn get_diagnostics(&self, diagnostics_out: &mut Diagnostics) {
// SAFETY: `self` is a valid reference to a C++ `wake_vector::WakeVector` instance, and
// `diagnostics_out` is a valid mutable reference to `Diagnostics`.
unsafe {
wake_vector_ffi::cpp_wake_vector_get_diagnostics(self, diagnostics_out);
}
}
}
/// The result of a request to wake up the system.
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum WakeResult {
/// The system was not suspended at the time of the event.
Active = 0,
/// The system was suspended at the time of the wake event.
Resumed = 1,
/// The system was in the process of suspending at the time of the wake event.
SuspendAborted = 2,
/// An already pending wake event was triggered again.
BadState = 3,
}
/// Behavior when acknowledging a [`WakeEvent`].
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AckBehavior {
ClearSignaled = 0,
RemainSignaled = 1,
}
fn limit_size_t_u32(size: usize) -> u32 {
u32::try_from(size).unwrap_or(u32::MAX)
}
/// Encapsulates the global and pending intrusive `WakeEvent` lists, their singleton locks, and
/// `PendingCursor` so the rest of this module cannot violate list or lock invariants in safe Rust.
///
/// ## Lock Hierarchy and Guarded State
///
/// When both locks are acquired, `GlobalListLock` is always acquired before `PendingListLock`.
///
/// - **`GlobalListLock` (mutex)**: Guards `GLOBAL_LIST` and each `WakeEvent`'s `global_node`.
/// - Operations requiring only `GlobalListLock`: [`GlobalListRef::push_back`],
/// [`GlobalListRef::erase`], [`GlobalListRef::iter`], [`GlobalListRef::len`], and
/// [`ListNodes::in_global_list`].
/// - **`PendingListLock` (IRQ-safe spinlock)**: Guards `PENDING_LIST`, each `WakeEvent`'s
/// `pending_node`, and `WakeEvent::report_info`.
/// - Operations requiring only `PendingListLock`: reading/mutating `report_info`,
/// [`ListNodes::in_pending_list`], [`PendingListRef::is_empty`], [`PendingListRef::len`], and
/// [`PendingListRef::push_front`].
/// - **Both `GlobalListLock` (exclusive token) and `PendingListLock`**: Required to remove nodes
/// from `PENDING_LIST` ([`PendingListRef::erase`]) or to iterate `PENDING_LIST` across unlocked
/// windows ([`PendingCursor::new`], [`PendingCursor::next`], [`PendingCursor::erase_current`]).
/// Holding `GlobalListLock` for the lifetime of [`PendingCursor`] prevents any `WakeEvent` from
/// being destroyed or removed from `PENDING_LIST` while `PendingListLock` is temporarily dropped
/// via `call_unlocked`.
mod lists {
use super::WakeEvent;
use core::marker::PhantomPinned;
use core::pin::Pin;
use fbl::{DoublyLinkedList, DoublyLinkedListContainable, DoublyLinkedListNode, TrackingSize};
use ksync::{KMutexGuard, KOnceCell, LockToken, RawMutex, RawSpinlock};
use pin_init::{pin_data, pin_init};
struct GlobalListTag;
struct PendingListTag;
/// Intrusive list nodes embedded in [`WakeEvent`]. Fields are private to `lists` so only this
/// module can link or unlink a `WakeEvent`.
#[repr(C)]
pub struct ListNodes {
global_node: DoublyLinkedListNode<WakeEvent>,
pending_node: DoublyLinkedListNode<WakeEvent>,
_pin: PhantomPinned,
}
impl ListNodes {
/// Creates unlinked intrusive list nodes for a [`WakeEvent`].
///
/// # Safety
///
/// The caller must ensure that the containing [`WakeEvent`] is not moved while linked into
/// either list, that it is only linked into [`PENDING_LIST`] while also linked in
/// [`GLOBAL_LIST`], and that it is removed from both lists before being dropped.
pub const unsafe fn new() -> Self {
Self {
global_node: DoublyLinkedListNode::new(),
pending_node: DoublyLinkedListNode::new(),
_pin: PhantomPinned,
}
}
#[inline]
pub fn in_global_list(&self, _token: &LockToken<'_, GlobalListLock>) -> bool {
self.global_node.in_container()
}
#[inline]
pub fn in_pending_list(&self, _token: &LockToken<'_, PendingListLock>) -> bool {
self.pending_node.in_container()
}
#[inline]
pub fn in_global_list_exclusive(&mut self) -> bool {
self.global_node.in_container()
}
#[inline]
pub fn in_pending_list_exclusive(&mut self) -> bool {
self.pending_node.in_container()
}
}
impl DoublyLinkedListContainable<WakeEvent, GlobalListTag> for WakeEvent {
fn get_node(&self) -> &DoublyLinkedListNode<WakeEvent> {
&self.list_nodes.global_node
}
}
impl DoublyLinkedListContainable<WakeEvent, PendingListTag> for WakeEvent {
fn get_node(&self) -> &DoublyLinkedListNode<WakeEvent> {
&self.list_nodes.pending_node
}
}
type GlobalList = DoublyLinkedList<*mut WakeEvent, GlobalListTag, TrackingSize>;
type PendingList = DoublyLinkedList<*mut WakeEvent, PendingListTag, TrackingSize>;
// `fbl::DoublyLinkedList` uses a self-referential sentinel pointer and must be pin-initialized
// in place inside `KOnceCell`.
#[pin_data]
struct GlobalListHolder {
#[pin]
list: GlobalList,
}
// SAFETY: Access to `GlobalListHolder` is serialized by `GlobalListLock`.
unsafe impl Send for GlobalListHolder {}
#[pin_data]
struct PendingListHolder {
#[pin]
list: PendingList,
}
// SAFETY: Access to `PendingListHolder` is serialized by `PendingListLock`.
unsafe impl Send for PendingListHolder {}
ksync::declare_singleton_mutex!(pub GlobalListLock);
ksync::declare_singleton_spinlock!(pub PendingListLock);
pub type GlobalGuard<'a> = Pin<&'a mut KMutexGuard<'static, GlobalListLock, RawMutex>>;
pub type PendingGuard<'a> = Pin<&'a mut KMutexGuard<'static, PendingListLock, RawSpinlock>>;
static GLOBAL_LIST: KOnceCell<GlobalListHolder, GlobalListLock> = KOnceCell::new();
static PENDING_LIST: KOnceCell<PendingListHolder, PendingListLock> = KOnceCell::new();
pub fn global_list(guard: GlobalGuard<'_>) -> GlobalListRef<'_> {
// SAFETY: `guard.token_mut()` belongs to the `GlobalListLock` singleton that protects
// `GLOBAL_LIST`.
let holder = unsafe {
GLOBAL_LIST
.get_or_pin_init(
pin_init!(GlobalListHolder { list <- GlobalList::new() }),
guard.token_mut(),
)
.unwrap()
};
// SAFETY: `GLOBAL_LIST` is a `static` cell, so `holder.list` is pinned in static memory.
GlobalListRef { list: unsafe { Pin::new_unchecked(&mut holder.list) } }
}
pub fn pending_list(guard: PendingGuard<'_>) -> PendingListRef<'_> {
// SAFETY: `guard.token_mut()` belongs to the `PendingListLock` singleton that protects
// `PENDING_LIST`.
let holder = unsafe {
PENDING_LIST
.get_or_pin_init(
pin_init!(PendingListHolder { list <- PendingList::new() }),
guard.token_mut(),
)
.unwrap()
};
// SAFETY: `PENDING_LIST` is a `static` cell, so `holder.list` is pinned in static memory.
PendingListRef { list: unsafe { Pin::new_unchecked(&mut holder.list) } }
}
/// Safe handle to [`GLOBAL_LIST`] obtained while holding [`GlobalListLock`].
pub struct GlobalListRef<'a> {
list: Pin<&'a mut GlobalList>,
}
impl<'a> GlobalListRef<'a> {
pub fn push_back(&mut self, event: Pin<&WakeEvent>) {
assert!(!event.list_nodes.global_node.in_container());
// SAFETY: `!event.list_nodes.global_node.in_container()` is asserted above while
// holding `GlobalListLock`, `event` is pinned, and `WakeEvent::new`'s safety contract
// guarantees `event` remains valid until `destroy` removes it while holding
// `GlobalListLock`.
unsafe {
self.list.as_mut().push_back_raw(core::ptr::from_ref(event.get_ref()).cast_mut());
}
}
pub fn erase(&mut self, event: &WakeEvent) {
// SAFETY: `GlobalListTag` is private to `lists` and only ever used with the singleton
// `GLOBAL_LIST`. `DoublyLinkedList::erase` is a no-op if the node is not in the list.
unsafe {
self.list.as_mut().erase(event);
}
}
pub fn iter(&self) -> impl Iterator<Item = &WakeEvent> {
self.list.iter()
}
pub fn len(&self) -> usize {
self.list.len()
}
}
/// Safe handle to [`PENDING_LIST`] obtained while holding [`PendingListLock`].
pub struct PendingListRef<'a> {
list: Pin<&'a mut PendingList>,
}
impl<'a> PendingListRef<'a> {
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
pub fn len(&self) -> usize {
self.list.len()
}
/// Inserts `event` at the front of [`PENDING_LIST`] if it is not already in the list.
/// Pushing to the front ensures newly triggered events are not visited by an already
/// in-progress [`PendingCursor`] report pass.
pub fn push_front(&mut self, event: &WakeEvent) {
if !event.list_nodes.pending_node.in_container() {
// SAFETY: `!event.list_nodes.pending_node.in_container()` is checked above while
// holding `PendingListLock`, and `WakeEvent::new`'s safety contract guarantees
// `event` remains valid until `destroy` removes it.
unsafe {
self.list.as_mut().push_front_raw(core::ptr::from_ref(event).cast_mut());
}
}
}
/// Removes `event` from [`PENDING_LIST`]. Requires an exclusive `_global_guard` in
/// addition to `&mut self` ([`PendingListLock`]) so removal cannot occur on another thread
/// or on the same thread while a [`PendingCursor`] is active.
pub fn erase(&mut self, _global_guard: GlobalGuard<'_>, event: &WakeEvent) {
// SAFETY: `PendingListTag` is private to `lists` and only ever used with the singleton
// `PENDING_LIST`, and `_global_guard` proves exclusive access to `GlobalListLock` with
// no active `PendingCursor`. `DoublyLinkedList::erase` is a no-op if `event` is not in
// the list.
unsafe {
self.list.as_mut().erase(event);
}
}
}
/// Cursor over [`PENDING_LIST`] whose item lifetime `'g` is tied to an exclusive borrow of
/// [`GlobalListLock`].
///
/// Exclusively borrowing [`GlobalGuard`] for `'g` prevents both other threads and the current
/// thread from calling [`WakeEvent::destroy`] or [`PendingListRef::erase`] while the cursor is
/// alive, so `self.next` remains valid and linked in [`PENDING_LIST`] even if
/// [`PendingListLock`] is temporarily dropped between iterations. The current element can be
/// removed via [`PendingCursor::erase_current`], because `self.next` has already advanced past
/// `self.curr`.
pub struct PendingCursor<'g> {
curr: Option<&'g WakeEvent>,
next: Option<&'g WakeEvent>,
}
impl<'g> PendingCursor<'g> {
/// Creates a cursor positioned at the front of [`PENDING_LIST`].
pub fn new(_global_guard: GlobalGuard<'g>, pending_guard: PendingGuard<'_>) -> Self {
let list = pending_list(pending_guard).list;
// SAFETY: Any node in `PENDING_LIST` is also registered in `GLOBAL_LIST` and cannot be
// destroyed while `_global_guard` (`GlobalListLock`) is held.
let next = list.front().map(|front| unsafe { &*core::ptr::from_ref(front) });
Self { curr: None, next }
}
/// Returns the current [`WakeEvent`] and advances the cursor to the next element in
/// [`PENDING_LIST`].
pub fn next(&mut self, pending_guard: PendingGuard<'_>) -> Option<&'g WakeEvent> {
self.curr = self.next.take();
let curr = self.curr?;
let list = pending_list(pending_guard).list;
// SAFETY: `curr` is still in `PENDING_LIST` because `_global_guard` (`GlobalGuard<'g>`)
// has been exclusively borrowed since `curr` was observed in `PENDING_LIST`, preventing
// `PendingListRef::erase` and `WakeEvent::destroy`, and `erase_current` only ever
// removes `self.curr` (which is distinct from `self.next`).
let mut cursor = unsafe { list.cursor_at(curr) };
cursor.move_next();
// SAFETY: Any node in `PENDING_LIST` remains valid for `'g` while `GlobalListLock` is
// held.
self.next = cursor.get().map(|next| unsafe { &*core::ptr::from_ref(next) });
Some(curr)
}
/// Removes the element most recently yielded by [`PendingCursor::next`] from
/// [`PENDING_LIST`].
///
/// Safe because `self.next` has already advanced past `self.curr`, and `self.curr.take()`
/// ensures the element can only be removed once even if it is re-triggered after
/// [`PendingListLock`] is dropped.
pub fn erase_current(&mut self, pending_guard: PendingGuard<'_>) {
if let Some(curr) = self.curr.take() {
let list = pending_list(pending_guard).list;
// SAFETY: `PendingListTag` is private to `lists` and only ever used with the
// singleton `PENDING_LIST`. `self.next` has already moved past `curr`.
unsafe {
list.erase(curr);
}
}
}
}
}
/// `WakeEvent` manages the lifecycle of wake events triggered by wake vectors.
///
/// A system wake event may be triggered in response to an appropriately configured interrupt,
/// exception, timer, or other future wake source that should resume the system from a suspended
/// state. When a wake event is triggered, it enters the pending state and will prevent the system
/// from entering suspend until it has been acknowledged. A pending wake event is automatically
/// acknowledged when the `WakeEvent` instance is destroyed to prevent missing an acknowledgement
/// that would render the system unable to suspend.
///
/// `WakeEvent` maintains a global list of all instances for diagnostic purposes (i.e. logging wake
/// events that are pending before, during, and after suspend). `WakeEvent`s are added to and
/// removed from the global list using [`WakeEvent::initialize`] and [`WakeEvent::destroy`],
/// respectively. Because diagnostics access each `WakeEvent`, and its containing [`WakeVector`],
/// from the global list, care must be taken to avoid potential use-after-free hazards.
///
/// Users of `WakeEvent`s MUST adhere to the following rules:
/// 1. A `WakeEvent` object MUST be instantiated as a member of a type that implements the
/// [`WakeVector`] interface, such that the lifetime of the containing type encloses the
/// lifetime of the `WakeEvent`. DO NOT heap allocate `WakeEvent` instances separately from the
/// referenced `WakeVector`.
/// 2. The container of a `WakeEvent` instance SHOULD call [`WakeEvent::initialize`] during
/// construction / initialization to register the `WakeEvent` on the global list. The container
/// MAY skip the call to `WakeEvent::initialize` if initialization of the containing type fails.
/// 3. The container of a `WakeEvent` MUST call [`WakeEvent::destroy`] IFF
/// `WakeEvent::initialize` has been called previously on the same instance of `WakeEvent` AND
/// `WakeEvent::destroy` MUST be called BEFORE the containing type itself is destructed.
///
/// Calls to `WakeEvent::initialize` and `WakeEvent::destroy` must always be balanced. However, a
/// `WakeEvent` MAY be initialized and destroyed more than once, as long as it is destroyed before
/// its destructor is invoked.
///
/// ## Notes on the pending list, locks, and concurrency
///
/// It is a requirement that every wake source in the system which has become signaled since last
/// being reported be present in any wake source report generated for a caller of
/// `zx_system_suspend_enter`. They will continue to be reported to users until they have been
/// *both* acknowledged, and reported at least once.
///
/// The `PENDING_LIST` holds the current list of wake events waiting to be reported. Members of
/// the list should remain on the list provided that:
///
/// 1) They have been signaled at some point in the past, at least once.
/// 2) They are either not-yet-acknowledged, or not-yet-reported, or both.
///
/// The `PendingListLock` is a spinlock used to protect the integrity of `PENDING_LIST`, however
/// due to another requirement, it alone is not sufficient. Specifically, it is a requirement that
/// generating a report can never hold off interrupt processing for O(n) time. This requirement
/// would be violated if we had to hold `PendingListLock` for the duration of a report-generation
/// operation. To avoid violating this requirement, we drop `PendingListLock` each time through the
/// loop while iterating through the pending list during report generation.
///
/// The need to drop `PendingListLock` during iteration while generating a report leads to two
/// other potential bad behaviors which we need to protect against:
///
/// 1) During report generation, we are holding an iterator to an element in the list. This
/// iterator cannot become invalidated during the period where we don't hold the lock.
/// 2) We must never "double report" a wake source in a single report. IOW - if KOID X shows up
/// once in the report generated for the user, it must not show up any more times *in that
/// specific report*.
///
/// ### Avoiding iterator invalidation
///
/// Report generation holds `GlobalListLock` (a mutex) for the duration of a report generation
/// operation. Construction/Destruction operations must also hold `GlobalListLock` meaning that
/// they cannot invalidate a report operation's iterator as they cannot run concurrently with the
/// report generation. Trigger operations only add items, and therefore cannot invalidate an
/// intrusive list iterator.
///
/// Ack operations will never remove an element from the list. Instead, they merely mark the
/// element as ack'ed and depend on report generation to handle the removal for them, avoiding the
/// invalidation issue in the process.
///
/// ### Avoiding double reports
///
/// When an event becomes triggered, it is added to the *front* of the list instead of the back.
/// This ensures that once report generation has started and the initial iterator has been
/// computed, no newly triggered events can show up in this report. They will have to wait to ride
/// the next report-train.
#[repr(C)]
pub struct WakeEvent {
list_nodes: ListNodes,
wake_vector: NonNull<WakeVector>,
report_info: KCell<zx_wake_source_report_entry_t, PendingListLock>,
}
zr::static_assert!(size_of::<WakeEvent>() == 112);
zr::static_assert!(align_of::<WakeEvent>() == 8);
// SAFETY: `WakeEvent` synchronizes all mutable access to its intrusive nodes and `report_info`
// through `GlobalListLock` and `PendingListLock`.
unsafe impl Send for WakeEvent {}
// SAFETY: Shared references to `WakeEvent` only mutate state while holding `GlobalListLock` or
// `PendingListLock`.
unsafe impl Sync for WakeEvent {}
impl WakeEvent {
pub fn has_pending_wake_events() -> bool {
ksync::lock!(let mut guard = PendingListLock::get().lock());
!pending_list(guard.as_mut()).is_empty()
}
/// Construct a WakeEvent referencing the given wake_vector.
///
/// # Safety
///
/// The caller must ensure:
/// - `wake_vector` points to a valid, thread-safe `WakeVector` that outlives all calls to
/// `initialize` on this `WakeEvent`.
/// - `Self` is not moved while initialized (between `initialize` and `destroy`).
/// - `trigger`, `acknowledge`, and `strobe` are only called while `Self` is initialized.
/// - If `initialize` was called, `destroy` is called before `Self` is dropped.
pub const unsafe fn new(wake_vector: NonNull<WakeVector>) -> Self {
Self {
// SAFETY: Upheld by the caller of `WakeEvent::new`.
list_nodes: unsafe { ListNodes::new() },
wake_vector,
report_info: KCell::new(zx_wake_source_report_entry_t {
koid: 0,
name: [0; ZX_MAX_NAME_LEN],
initial_signal_time: 0,
last_signal_time: 0,
last_ack_time: 0,
signal_count: 0,
flags: 0,
}),
}
}
#[inline]
fn in_global_list(&self, token: &LockToken<'_, GlobalListLock>) -> bool {
self.list_nodes.in_global_list(token)
}
#[inline]
fn in_pending_list(&self, token: &LockToken<'_, PendingListLock>) -> bool {
self.list_nodes.in_pending_list(token)
}
#[inline]
fn report_info<'a>(
&'a self,
token: &'a LockToken<'_, PendingListLock>,
) -> &'a zx_wake_source_report_entry_t {
// SAFETY: `PendingListLock` is a singleton lock, so `token` proves `PendingListLock` is
// held.
unsafe { self.report_info.get(token) }
}
#[inline]
fn report_info_mut<'a>(
&'a self,
token: &'a mut LockToken<'_, PendingListLock>,
) -> &'a mut zx_wake_source_report_entry_t {
// SAFETY: `PendingListLock` is a singleton lock, so `token` proves exclusive ownership of
// `PendingListLock`.
unsafe { self.report_info.get_mut(token) }
}
#[inline]
fn is_signaled(&self, token: &LockToken<'_, PendingListLock>) -> bool {
(self.report_info(token).flags & ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_SIGNALED) != 0
}
#[inline]
fn has_been_reported(&self, token: &LockToken<'_, PendingListLock>) -> bool {
(self.report_info(token).flags & ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_PREVIOUSLY_REPORTED) != 0
}
#[inline]
fn assign_flag(&self, token: &mut LockToken<'_, PendingListLock>, value: bool, flag: u32) {
let report_info = self.report_info_mut(token);
if value {
report_info.flags |= flag;
} else {
report_info.flags &= !flag;
}
}
#[inline]
fn assign_signaled(&self, token: &mut LockToken<'_, PendingListLock>, value: bool) {
self.assign_flag(token, value, ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_SIGNALED);
}
#[inline]
fn assign_has_been_reported(&self, token: &mut LockToken<'_, PendingListLock>, value: bool) {
self.assign_flag(token, value, ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_PREVIOUSLY_REPORTED);
}
pub fn initialize(self: Pin<&Self>) {
// Stash our WakeVector constants in our report_info. These will never change over the
// lifetime of the WakeEvent, so we might as well just stash it all now.
let mut diagnostic_info = Diagnostics::default();
// SAFETY: `self.wake_vector` was provided at construction and remains valid whenever
// `initialize` is called.
unsafe { self.wake_vector.as_ref() }.get_diagnostics(&mut diagnostic_info);
ksync::lock!(let mut guard = GlobalListLock::get().lock());
debug_assert!(!self.in_global_list(guard.token()));
{
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
debug_assert!(!self.in_pending_list(pending_guard.token()));
let report_info = self.report_info_mut(pending_guard.as_mut().token_mut());
*report_info = zx_wake_source_report_entry_t::default();
report_info.koid = diagnostic_info.koid;
zr::static_assert!(size_of::<[u8; ZX_MAX_NAME_LEN]>() == ZX_MAX_NAME_LEN);
report_info.name.copy_from_slice(&diagnostic_info.extra);
// Initially flag our report entry as having been reported. This sets up
// our bookkeeping properly so the first time it becomes signaled, it will
// initialize all of the fields instead of simply bumping the signal count
// and updating the last signal time.
self.assign_has_been_reported(pending_guard.as_mut().token_mut(), true);
}
global_list(guard.as_mut()).push_back(self);
}
pub fn destroy(&self) {
ksync::lock!(let mut guard = GlobalListLock::get().lock());
debug_assert!(self.in_global_list(guard.token()));
{
// If we are in pending list right now, we need to remove ourselves, making
// certain to ack at the IdlePowerThread if needed.
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
if self.in_pending_list(pending_guard.token()) {
if self.is_signaled(pending_guard.token()) {
IdlePowerThread::acknowledge_system_wake_event();
self.assign_signaled(pending_guard.as_mut().token_mut(), false);
}
pending_list(pending_guard.as_mut()).erase(guard.as_mut(), self);
} else {
// It should not be possible to have a pending ack, but not be on the
// pending list.
debug_assert!(!self.is_signaled(pending_guard.token()));
}
}
global_list(guard.as_mut()).erase(self);
}
/// Triggers a wakeup that resumes the system, or aborts an incomplete suspend sequence, and
/// prevents the system from starting a new suspend sequence.
///
/// Must be called with interrupts and preempt disabled.
///
/// Returns:
/// - [`WakeResult::Active`] if this wake trigger occurred when the system was active.
/// - [`WakeResult::Resumed`] if this or another wake trigger resumed the system.
/// - [`WakeResult::SuspendAborted`] if this wake trigger occurred before suspend completed.
/// - [`WakeResult::BadState`] if this wake event is already pending.
///
/// Calls to `trigger` and `acknowledge` must be synchronized by the caller to guarantee that
/// updates are performed by a single actor at a time.
pub fn trigger(&self, trigger_time: zx_instant_boot_t) -> WakeResult {
let _preempt_disabler = AutoPreemptDisabler::new();
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
self.trigger_locked(pending_guard.as_mut(), trigger_time)
}
/// Acknowledges a pending wake event, allowing the system to enter suspend when all other
/// suspend conditions are met.
///
/// Calls to `trigger` and `acknowledge` must be synchronized by the caller to guarantee that
/// updates are performed by a single actor at a time.
pub fn acknowledge(&self, ack_behavior: AckBehavior) {
let _preempt_disabler = AutoPreemptDisabler::new();
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
self.acknowledge_locked(pending_guard.as_mut(), current_boot_time().0, ack_behavior);
}
/// WARNING : This is not the method you are looking for `<jedimindtrick/>`
///
/// `strobe` is an operation used only in a very specific situation; when a suspend operation
/// times out and the `ResumeTimerWakeVector` becomes signaled as a result. This object is
/// (currently) the only non-interrupt wake source/vector defined in the system, and it is not
/// directly exposed to user-mode as an object which becomes acknowledged by user-mode actions.
/// Instead, it is the synthetic wake source used to report suspend-operation timeouts, and is
/// (logically speaking) *always* immediately acked after being signaled.
///
/// `strobe` handles this operation, without needing to expose any locks to make it possible to
/// atomically `trigger`/`acknowledge` the object. For all other wake source objects in the
/// system, explicit calls to `trigger` and `acknowledge` are what should be used.
pub fn strobe(&self, trigger_time: zx_instant_boot_t) -> WakeResult {
let _preempt_disabler = AutoPreemptDisabler::new();
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
let result = self.trigger_locked(pending_guard.as_mut(), trigger_time);
self.acknowledge_locked(pending_guard.as_mut(), trigger_time, AckBehavior::ClearSignaled);
result
}
fn trigger_locked(
&self,
mut pending_guard: PendingGuard<'_>,
trigger_time: zx_instant_boot_t,
) -> WakeResult {
// There are three cases to consider here:
//
// 1) We are already signaled. This is an unusual state to be in as we are
// being triggered again, even though we have never received an ack.
// Regardless we should just bump our signal count, and record a new last
// triggered time.
// 2) We are not signaled, but we have not been reported yet. We must have
// been acknowledged previously, and are becoming triggered once
// again. Just like case #1, bump the signal count and record a new last
// triggered time.
// 3) We are not signaled, and have been reported (note; this is also the
// initial state of a WakeEvent). It is time to re-initialize our report
// entry state to record the first trigger event.
if !self.is_signaled(pending_guard.token()) && self.has_been_reported(pending_guard.token())
{
let report_info = self.report_info_mut(pending_guard.as_mut().token_mut());
report_info.initial_signal_time = trigger_time;
report_info.last_ack_time = ZX_TIME_INFINITE;
report_info.signal_count = 1;
self.assign_has_been_reported(pending_guard.as_mut().token_mut(), false);
} else {
let report_info = self.report_info_mut(pending_guard.as_mut().token_mut());
report_info.signal_count = report_info.signal_count.wrapping_add(1);
}
// Regardless of whether this was the first trigger, or a subsequent trigger,
// record a new last trigger time.
self.report_info_mut(pending_guard.as_mut().token_mut()).last_signal_time = trigger_time;
// If we are not in the pending list, add ourself now. Now, we add ourself to
// the front of the list instead of the back in order to prevent this one wake
// source from being double-reported in the case that it has been added to a
// report, and becomes re-triggered before report generation has finished.
// See the extensive comment on the WakeEvent struct for more details.
if !self.in_pending_list(pending_guard.token()) {
pending_list(pending_guard.as_mut()).push_front(self);
}
// If we are triggered multiple times without being ack'ed, make sure to
// report only one trigger to the IdlePowerThread level.
if !self.is_signaled(pending_guard.token()) {
self.assign_signaled(pending_guard.as_mut().token_mut(), true);
// WakeEvent triggering can happen starting from one of two places.
//
// 1) In a hard IRQ handler because a physical interrupt wake source was triggered.
// 2) During a user's call to zx_interrupt_trigger for a virtual interrupt.
//
// When we call TriggerSystemWakeEvent, if it needs to wake the boot CPU, it
// is going to eventually call PreemptSetPending for the boot CPU in order
// to make sure that it gets scheduled instead of simply returning to the
// idle thread. This function is going to demand that the per-cpu "blocking
// disallowed" flag has been set.
//
// In the case of #1, this will always be the case. We are in an IRQ
// handler, and the flag is going to be automatically set/cleared at the
// start of the handler (you are not allowed to block during an IRQ
// handler).
//
// In the case of #2, this is _effectively_ true, but the per-cpu flag will
// not have been set. By the time we make it to this point (in
// trigger_locked) we will have interrupts off, and will be holding the wake
// event pending list spinlock, both of which are Very Good Reasons to
// disallow blocking. This said, disabling interrupts and obtaining a
// spinlock does _not_ automatically set the blocking-disallowed flag. Why?
// Because when a thread actually blocks, it needs to hold (among other
// things) its scheduler's spinlock as it re-schedules after joining its
// WaitQueue.
//
// We already have interrupts off, are holding a spinlock, and are not going
// to call Scheduler::Block here. So go ahead and set the blocking
// disallowed flag while we call TriggerSystemWakeEvent, restoring the state
// to its previous value at the end of the sequence.
debug_assert!(ints_disabled());
let was_blocking_disallowed = blocking_disallowed();
set_blocking_disallowed(true);
let _cleanup = zr::defer(|| {
set_blocking_disallowed(was_blocking_disallowed);
});
return IdlePowerThread::trigger_system_wake_event();
}
WakeResult::BadState
}
fn acknowledge_locked(
&self,
mut pending_guard: PendingGuard<'_>,
trigger_time: zx_instant_boot_t,
ack_behavior: AckBehavior,
) {
// In order for an event to be waiting to be acknowledged, it must be in the
// pending list, and it needs to be signaled (we should not be getting spurious
// double acks).
debug_assert!(self.in_pending_list(pending_guard.token()));
debug_assert!(self.is_signaled(pending_guard.token()));
// Always record the last ack time.
self.report_info_mut(pending_guard.as_mut().token_mut()).last_ack_time = trigger_time;
if ack_behavior == AckBehavior::ClearSignaled {
// If our AckBehavior tells us to clear our signaled, then ack at the
// IdlePowerThread level, and clear the signaled flag.
IdlePowerThread::acknowledge_system_wake_event();
self.assign_signaled(pending_guard.as_mut().token_mut(), false);
} else {
// We have been told that we should remain signaled. Clear our "has been
// reported" flag. We are in a situation where the interrupt was triggered
// at least once more after the first time. The first trigger may have been
// reported, but the second and subsequent triggers have not been reported
// yet.
self.assign_has_been_reported(pending_guard.as_mut().token_mut(), false);
}
// Do NOT remove this event from the pending list, even if it has been
// reported already. If we remove the event from the list, we run the risk of
// invalidating the iterator for a report which is currently being generated.
// Instead, just leave the event in place, and let report generation handle
// removing the event from the list when the time comes.
//
// See the extensive comment on `WakeEvent` for a more comprehensive
// explanation of the pending list and the various ways that operations
// interact with it, and the things they need to do in order to remain safe.
}
/// Walks the global list of all instances and dumps diagnostic information to `f`. All events
/// that are currently pending OR that were triggered after `log_triggered_after_boot_time` are
/// logged.
///
/// Safe to call concurrently with any and all methods, including ctors and dtors.
pub fn dump(f: &mut impl core::fmt::Write, log_triggered_after_boot_time: zx_instant_boot_t) {
ksync::lock!(let mut guard = GlobalListLock::get().lock());
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
for event in global_list(guard.as_mut()).iter() {
if event.is_signaled(pending_guard.token())
|| event.report_info(pending_guard.token()).last_signal_time
>= log_triggered_after_boot_time
{
let entry = *event.report_info(pending_guard.token());
pending_guard.as_mut().call_unlocked(|| {
let name_len =
entry.name.iter().position(|&b| b == 0).unwrap_or(entry.name.len());
let extra = core::str::from_utf8(&entry.name[..name_len]).unwrap_or("");
let _ = write!(
f,
" koid : {}\n\
\x20 pending : {}\n\
\x20 prev reported : {}\n\
\x20 signal count : {}\n\
\x20 initial trigger : {}\n\
\x20 last trigger : {}\n\
\x20 last ack : {}\n\
\x20 extra : {}\n\n",
entry.koid,
if (entry.flags & ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_SIGNALED) != 0 {
"yes"
} else {
"no"
},
if (entry.flags & ZX_SYSTEM_WAKE_REPORT_ENTRY_FLAG_PREVIOUSLY_REPORTED) != 0
{
"yes"
} else {
"no"
},
entry.signal_count,
entry.initial_signal_time,
entry.last_signal_time,
entry.last_ack_time,
extra,
);
});
}
}
}
pub fn generate_wake_event_report(
suspend_start_time: zx_instant_boot_t,
out_header: UserOutPtr<zx_wake_source_report_header_t>,
out_entries: UserOutPtr<zx_wake_source_report_entry_t>,
num_entries: u32,
actual_entries: UserOutPtr<u32>,
) -> Result<(), Status> {
// These should have been verified for us already at the syscall layer.
debug_assert!(!out_header.is_null());
let do_report_entries = !out_entries.is_null();
debug_assert_eq!(do_report_entries, num_entries != 0);
debug_assert_eq!(do_report_entries, !actual_entries.is_null());
let mut hdr = zx_wake_source_report_header_t::default();
let mut reported_entries: u32 = 0;
hdr.suspend_start_time = suspend_start_time;
// We are going to need to heap allocate a side buffer to copy our results into. We cannot
// copy our results back out to user mode while holding any of our locks, and we cannot
// allocate an arbitrary amount of entries on our stack.
let mut entry_side_buffer = fbl::Array::<zx_wake_source_report_entry_t>::new();
let mut entry_side_buffer_len: usize = 0;
{
// Hold the global list lock for the entire O(n) time it takes to create the
// report. This prevents any wake sources from being created or destroyed
// while we iterate the pending list. This guarantees that our total wake
// source count cannot change while we generate the report, and also means
// that wake source destruction cannot invalidate our iterator out from
// under us when we drop the pending lock in order to copy data out to
// user-mode.
//
// See the extensive comment on `WakeEvent` for a more comprehensive
// explanation of the pending list and the various ways that operations
// interact with it, and the things they need to do in order to remain safe.
ksync::lock!(let mut guard = GlobalListLock::get().lock());
{
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
// Record the number of entries which are waiting to be reported at the
// start of report generation. We'll adjust this number later on to
// reflect the actual number of entries which were waiting at the start of
// the report which didn't actually fit into the user's buffer.
hdr.unreported_wake_report_entries =
limit_size_t_u32(pending_list(pending_guard.as_mut()).len());
if do_report_entries {
let mut cursor = PendingCursor::new(guard.as_mut(), pending_guard.as_mut());
entry_side_buffer_len =
(num_entries as usize).min(pending_list(pending_guard.as_mut()).len());
// Now that we have captured our initial iterator, and computed the
// maximum number of entries we might attempt to report, allocate a side
// buffer we can copy the information into. We cannot simply copy into
// a stack allocated single entry which we then copy to user mode after
// dropping the pending lock. We are not allowed to hold _any_ locks
// when we copy into user mode, so we will need to be able to buffer
// _all_ of our results before starting to do so.
//
// TODO(johngro): We don't necessarily have to do this every time we
// generate a report. We could also keep the allocation around as a
// static member of WakeEvent protected by the global lock, and growing
// it only when we needed to. At the end of the op (when we drop the
// global lock to copy out to user mode), we could move the buffer out,
// perform the copy, then lock and move the buffer back in again. We'd
// never have a second buffer sitting around unless there was a
// concurrent attempt to generate a report, something which is
// technically possible but should never happen in practice.
let mut alloc_result = Ok(());
pending_guard.as_mut().call_unlocked(|| {
if entry_side_buffer_len > 0 {
if let Ok(buf) =
fbl::Array::<zx_wake_source_report_entry_t>::try_new_zeroed_slice(
entry_side_buffer_len,
)
{
entry_side_buffer = buf;
} else {
alloc_result = Err(Status::NO_MEMORY);
}
}
});
// Bail out if we could not allocate a side buffer.
alloc_result?;
while (reported_entries as usize) < entry_side_buffer_len
&& let Some(curr) = cursor.next(pending_guard.as_mut())
{
let mut skip_reporting = false;
if !curr.is_signaled(pending_guard.token()) {
// The wake source is not currently signaled. If it has been
// reported already, then skip adding it to the user's report.
// Either way, remove it from the pending list.
if curr.has_been_reported(pending_guard.token()) {
skip_reporting = true;
}
cursor.erase_current(pending_guard.as_mut());
}
if !skip_reporting {
// Copy our result into our side buffer and mark it as having been
// reported. Then drop our lock briefly allowing pending IRQs
// to run, and wake sources to become signaled/acked.
entry_side_buffer[reported_entries as usize] =
*curr.report_info(pending_guard.token());
reported_entries += 1;
curr.assign_has_been_reported(pending_guard.as_mut().token_mut(), true);
pending_guard.as_mut().call_unlocked(|| {
core::hint::spin_loop();
});
}
// Whether we added the entry to the user's report, or skipped it
// because the entry was just waiting around to be cleaned up by us,
// we should drop the unreported count.
debug_assert!(hdr.unreported_wake_report_entries > 0);
hdr.unreported_wake_report_entries -= 1;
}
}
}
// Record the total number of wake sources in the system as well as our
// report time before dropping the global lock.
hdr.total_wake_sources = limit_size_t_u32(global_list(guard.as_mut()).len());
hdr.report_time = current_boot_time().0;
}
// We're done. Finish up by trying to copy everything back out to user-mode.
if do_report_entries {
debug_assert!((reported_entries as usize) <= entry_side_buffer_len);
out_entries.copy_slice_to_user(&entry_side_buffer[..reported_entries as usize])?;
actual_entries.copy_to_user(&reported_entries)?;
}
out_header.copy_to_user(&hdr)
}
pub fn discard_wake_event_report() {
ksync::lock!(let mut guard = GlobalListLock::get().lock());
ksync::lock!(let mut pending_guard = PendingListLock::get().lock());
let mut cursor = PendingCursor::new(guard.as_mut(), pending_guard.as_mut());
while let Some(curr) = cursor.next(pending_guard.as_mut()) {
// Remove the event from the pending list iff it has been acknowledged.
// Make sure the reported flag is set when we remove it from the list.
if !curr.is_signaled(pending_guard.token()) {
curr.assign_has_been_reported(pending_guard.as_mut().token_mut(), true);
cursor.erase_current(pending_guard.as_mut());
}
// Drop the lock and spin_loop to give any pending triggers or acks a chance to run.
pending_guard.as_mut().call_unlocked(|| {
core::hint::spin_loop();
});
}
}
}
impl Drop for WakeEvent {
fn drop(&mut self) {
// By the time that we destruct, we should have been Destroyed, meaning that
// we are no longer in any lists.
debug_assert!(!self.list_nodes.in_global_list_exclusive());
debug_assert!(!self.list_nodes.in_pending_list_exclusive());
}
}