Skip to main content

starnix_core/power/
manager.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::power::{SuspendState, SuspendStats};
6use crate::task::CurrentTask;
7
8use std::collections::{HashMap, HashSet};
9use std::future::Future;
10use std::sync::{Arc, Weak};
11
12use anyhow::{Context, anyhow};
13use fidl::endpoints::Proxy;
14use fidl_fuchsia_power_observability as fobs;
15use fidl_fuchsia_session_power as fpower;
16use fidl_fuchsia_starnix_runner as frunner;
17use fuchsia_component::client::connect_to_protocol_sync;
18use fuchsia_inspect as inspect;
19use fuchsia_inspect::ArrayProperty;
20use futures::stream::{FusedStream, Next};
21use futures::{FutureExt, StreamExt};
22use starnix_logging::{log_info, log_warn};
23use starnix_sync::{
24    EbpfSuspendLock, LockDepGuard, LockDepMutex, LockDepReadGuard, LockDepRwLock,
25    PowerMessageCountersLock, SuspendResumeManagerInnerLock,
26};
27use starnix_uapi::arc_key::WeakKey;
28use starnix_uapi::errors::Errno;
29use starnix_uapi::{errno, error};
30use std::collections::VecDeque;
31use std::fmt;
32use zx::Peered;
33
34/// Wake source persistent info, exposed in inspect diagnostics.
35#[derive(Debug, Default)]
36pub struct WakeupSource {
37    /// The last task command that activated this wakeup source.
38    pub last_actor: Option<starnix_task_command::TaskCommand>,
39
40    /// The number of times the wakeup source has been activated.
41    active_count: u64,
42
43    /// The number of events signaled to this source. Similar to active_count but can track
44    /// internal events causing the activation.
45    event_count: u64,
46
47    /// The number of times this source prevented suspension of the system, or woke the system from
48    /// a suspended state.
49    ///
50    /// Right now there is no way for wake locks to wake the Starnix container, because the
51    /// mechanism used for waking the container is not integrated into the wake source machinery.
52    wakeup_count: u64,
53
54    /// The number of times the timeout associated with this source expired.
55    expire_count: u64,
56
57    /// The timestamp relative to the monotonic clock when the lock became active. If 0, the lock
58    /// is currently inactive.
59    active_since: zx::MonotonicInstant,
60
61    /// The total duration this source has been held active since the system booted.
62    total_time: zx::MonotonicDuration,
63
64    /// The longest single duration this source was held active.
65    max_time: zx::MonotonicDuration,
66
67    /// The last time this source was either acquired or released.
68    last_change: zx::MonotonicInstant,
69}
70
71impl WakeupSource {
72    /// Lazily formats the display name for inspection and logging.
73    pub fn display_name(&self, origin: &WakeupSourceOrigin) -> String {
74        match &self.last_actor {
75            Some(actor) => format!("{} [{}]", origin.to_string(), actor),
76            None => origin.to_string(),
77        }
78    }
79
80    /// Returns the amount of time passed since this wake source was last
81    /// recorded as active. For active wake sources, this is exactly the time
82    /// since the source became active. For inactive sources it's zero.
83    pub fn active_duration(&self) -> zx::MonotonicDuration {
84        if self.active_since == zx::MonotonicInstant::ZERO {
85            zx::MonotonicDuration::default()
86        } else {
87            let now = zx::MonotonicInstant::get();
88            now - self.active_since
89        }
90    }
91}
92
93#[derive(Debug, Clone, Eq, PartialEq, Hash)]
94pub enum WakeupSourceOrigin {
95    WakeLock(String),
96    Epoll(crate::vfs::EpollKey),
97    HAL(String),
98}
99
100impl std::string::ToString for WakeupSourceOrigin {
101    fn to_string(&self) -> String {
102        match self {
103            WakeupSourceOrigin::WakeLock(lock) => lock.clone(),
104            WakeupSourceOrigin::Epoll(key) => format!("[epoll] {}", key),
105            WakeupSourceOrigin::HAL(lock) => format!("[HAL] {}", lock),
106        }
107    }
108}
109
110/// Manager for suspend and resume.
111pub struct SuspendResumeManager {
112    // The mutable state of [SuspendResumeManager].
113    inner: Arc<LockDepMutex<SuspendResumeManagerInner, SuspendResumeManagerInnerLock>>,
114
115    /// The currently registered message counters in the system whose values are exposed to inspect
116    /// via a lazy node.
117    message_counters:
118        Arc<LockDepMutex<HashSet<WeakKey<OwnedMessageCounter>>, PowerMessageCountersLock>>,
119
120    /// The lock used to to avoid suspension while holding eBPF locks.
121    ebpf_suspend_lock: LockDepRwLock<(), EbpfSuspendLock>,
122}
123
124/// Manager for suspend and resume.
125/// Manager for suspend and resume.
126pub struct SuspendResumeManagerInner {
127    /// The suspend counters and gauges.
128    suspend_stats: SuspendStats,
129    sync_on_suspend_enabled: bool,
130
131    suspend_events: VecDeque<SuspendEvent>,
132
133    /// The wake sources in the system, both active and inactive.
134    wakeup_sources: HashMap<WakeupSourceOrigin, WakeupSource>,
135
136    /// The event pair that is passed to the Starnix runner so it can observe whether
137    /// or not any wake locks are active before completing a suspend operation.
138    active_lock_reader: zx::EventPair,
139
140    /// The event pair that is used by the Starnix kernel to signal when there are
141    /// active wake locks in the container. Note that the peer of the writer is the
142    /// object that is signaled.
143    active_lock_writer: zx::EventPair,
144
145    /// The number of currently active wakeup sources.
146    active_wakeup_source_count: u64,
147
148    /// The total number of activate-deactivated cycles that have been seen across all wakeup
149    /// sources.
150    total_wakeup_source_event_count: u64,
151
152    /// The external wake sources that are registered with the runner.
153    external_wake_sources: HashMap<zx::Koid, ExternalWakeSource>,
154}
155
156#[derive(Debug)]
157struct ExternalWakeSource {
158    /// The handle that signals when the source is active.
159    handle: zx::NullableHandle,
160    /// The signals that indicate the source is active.
161    signals: zx::Signals,
162    /// The name of the wake source.
163    name: String,
164}
165
166impl SuspendResumeManager {
167    pub fn add_external_wake_source(
168        &self,
169        handle: zx::NullableHandle,
170        signals: zx::Signals,
171        name: String,
172    ) -> Result<(), Errno> {
173        let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
174            .map_err(|e| errno!(EINVAL, format!("Failed to connect to manager: {e:?}")))?;
175        manager
176            .add_wake_source(frunner::ManagerAddWakeSourceRequest {
177                container_job: Some(
178                    fuchsia_runtime::job_default()
179                        .duplicate_handle(zx::Rights::SAME_RIGHTS)
180                        .expect("Failed to dup handle"),
181                ),
182                name: Some(name.clone()),
183                handle: Some(
184                    handle.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|e| errno!(EIO, e))?,
185                ),
186                signals: Some(signals.bits()),
187                ..Default::default()
188            })
189            .map_err(|e| errno!(EIO, e))?;
190
191        let koid = handle.koid().map_err(|e| errno!(EINVAL, e))?;
192        self.lock().external_wake_sources.insert(
193            koid,
194            ExternalWakeSource {
195                handle: handle
196                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
197                    .map_err(|e| errno!(EIO, e))?,
198                signals,
199                name,
200            },
201        );
202        Ok(())
203    }
204
205    pub fn remove_external_wake_source(&self, handle: zx::NullableHandle) -> Result<(), Errno> {
206        let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
207            .map_err(|e| errno!(EINVAL, format!("Failed to connect to manager: {e:?}")))?;
208
209        let koid = handle.koid().map_err(|e| errno!(EINVAL, e))?;
210        self.lock().external_wake_sources.remove(&koid);
211
212        manager
213            .remove_wake_source(frunner::ManagerRemoveWakeSourceRequest {
214                container_job: Some(
215                    fuchsia_runtime::job_default()
216                        .duplicate_handle(zx::Rights::SAME_RIGHTS)
217                        .expect("Failed to dup handle"),
218                ),
219                handle: Some(handle),
220                ..Default::default()
221            })
222            .map_err(|e| errno!(EIO, e))?;
223
224        Ok(())
225    }
226}
227
228pub type EbpfSuspendGuard<'a> = LockDepReadGuard<'a, ()>;
229
230#[derive(Clone, Debug)]
231pub enum SuspendEvent {
232    Attempt { time: zx::BootInstant, state: String },
233    Resume { time: zx::BootInstant, reason: String },
234    Fail { time: zx::BootInstant, wakeup_sources: Option<Vec<String>> },
235}
236
237/// The inspect node ring buffer will keep at most this many entries.
238const INSPECT_RING_BUFFER_CAPACITY: usize = 128;
239
240impl Default for SuspendResumeManagerInner {
241    fn default() -> Self {
242        let (active_lock_reader, active_lock_writer) = zx::EventPair::create();
243        active_lock_writer
244            .signal_peer(zx::Signals::empty(), zx::Signals::USER_0)
245            .expect("Failed to signal peer");
246        Self {
247            suspend_stats: Default::default(),
248            sync_on_suspend_enabled: false,
249            suspend_events: VecDeque::with_capacity(INSPECT_RING_BUFFER_CAPACITY),
250            wakeup_sources: Default::default(),
251            active_lock_reader,
252            active_lock_writer,
253            active_wakeup_source_count: 0,
254            total_wakeup_source_event_count: 0,
255            external_wake_sources: Default::default(),
256        }
257    }
258}
259
260impl SuspendResumeManagerInner {
261    // Returns true if there are no wake locks preventing suspension.
262    pub fn can_suspend(&self) -> bool {
263        self.active_wakeup_source_count == 0
264    }
265
266    pub fn active_wake_locks(&self) -> Vec<WakeupSourceOrigin> {
267        self.wakeup_sources
268            .iter()
269            .filter_map(|(name, source)| match name {
270                WakeupSourceOrigin::WakeLock(_) => {
271                    if source.active_since > zx::MonotonicInstant::ZERO {
272                        Some(name.clone())
273                    } else {
274                        None
275                    }
276                }
277                _ => None,
278            })
279            .collect()
280    }
281
282    pub fn inactive_wake_locks(&self) -> Vec<WakeupSourceOrigin> {
283        self.wakeup_sources
284            .iter()
285            .filter_map(|(name, source)| match name {
286                WakeupSourceOrigin::WakeLock(_) => {
287                    if source.active_since == zx::MonotonicInstant::ZERO {
288                        Some(name.clone())
289                    } else {
290                        None
291                    }
292                }
293                _ => None,
294            })
295            .collect()
296    }
297
298    /// Signals whether or not there are currently any active wake locks in the kernel.
299    fn signal_wake_events(&mut self) {
300        let (clear_mask, set_mask) = if self.active_wakeup_source_count == 0 {
301            (zx::Signals::EVENT_SIGNALED, zx::Signals::USER_0)
302        } else {
303            (zx::Signals::USER_0, zx::Signals::EVENT_SIGNALED)
304        };
305        self.active_lock_writer.signal_peer(clear_mask, set_mask).expect("Failed to signal peer");
306    }
307
308    fn update_suspend_stats<UpdateFn>(&mut self, update: UpdateFn)
309    where
310        UpdateFn: FnOnce(&mut SuspendStats),
311    {
312        update(&mut self.suspend_stats);
313    }
314
315    fn add_suspend_event(&mut self, event: SuspendEvent) {
316        if self.suspend_events.len() >= INSPECT_RING_BUFFER_CAPACITY {
317            self.suspend_events.pop_front();
318        }
319        self.suspend_events.push_back(event);
320    }
321
322    fn record_suspend_events(&self, node: &inspect::Node) {
323        let events_node = node.create_child("suspend_events");
324        for (i, event) in self.suspend_events.iter().enumerate() {
325            let child = events_node.create_child(i.to_string());
326            match event {
327                SuspendEvent::Attempt { time, state } => {
328                    child.record_int(fobs::SUSPEND_ATTEMPTED_AT, time.into_nanos());
329                    child.record_string(fobs::SUSPEND_REQUESTED_STATE, state);
330                }
331                SuspendEvent::Resume { time, reason } => {
332                    child.record_int(fobs::SUSPEND_RESUMED_AT, time.into_nanos());
333                    child.record_string(fobs::SUSPEND_RESUME_REASON, reason);
334                }
335                SuspendEvent::Fail { time, wakeup_sources } => {
336                    child.record_int(fobs::SUSPEND_FAILED_AT, time.into_nanos());
337                    if let Some(names) = wakeup_sources {
338                        let names_array =
339                            child.create_string_array(fobs::WAKEUP_SOURCES_NAME, names.len());
340                        for (i, name) in names.iter().enumerate() {
341                            names_array.set(i, name);
342                        }
343                        child.record(names_array);
344                    }
345                }
346            }
347            events_node.record(child);
348        }
349        node.record(events_node);
350    }
351
352    fn record_wakeup_sources(&self, node: &inspect::Node) {
353        let wakeup_node = node.create_child("wakeup_sources");
354        for (origin, source) in self.wakeup_sources.iter() {
355            let child = wakeup_node.create_child(&source.display_name(origin));
356            child.record_uint("active_count", source.active_count);
357            child.record_uint("event_count", source.event_count);
358            child.record_uint("wakeup_count", source.wakeup_count);
359            child.record_uint("expire_count", source.expire_count);
360            child.record_int("active_since (ns)", source.active_since.into_nanos());
361            // Records how long has this wakeup source been active for. If the source is currently
362            // active, this is how long it's been currently active.
363            child.record_int("active_duration_mono (ns)", source.active_duration().into_nanos());
364            child.record_int("total_time (ms)", source.total_time.into_millis());
365            child.record_int("max_time (ms)", source.max_time.into_millis());
366            child.record_int("last_change (ns)", source.last_change.into_nanos());
367            wakeup_node.record(child);
368        }
369        node.record(wakeup_node);
370    }
371}
372
373pub type SuspendResumeManagerHandle = Arc<SuspendResumeManager>;
374
375impl Default for SuspendResumeManager {
376    fn default() -> Self {
377        let message_counters: Arc<
378            LockDepMutex<HashSet<WeakKey<OwnedMessageCounter>>, PowerMessageCountersLock>,
379        > = Default::default();
380        let message_counters_clone = message_counters.clone();
381        let root = inspect::component::inspector().root();
382        root.record_lazy_values("message_counters", move || {
383            let message_counters_clone = message_counters_clone.clone();
384            async move {
385                let inspector = fuchsia_inspect::Inspector::default();
386                let root = inspector.root();
387                let message_counters = message_counters_clone.lock();
388                let active_counter_names: Vec<String> = message_counters
389                    .iter()
390                    .filter_map(|c| c.0.upgrade())
391                    .map(|c| c.to_string())
392                    .collect();
393                let message_counters_inspect =
394                    root.create_string_array("message_counters", active_counter_names.len());
395                for (i, name) in active_counter_names.iter().enumerate() {
396                    message_counters_inspect.set(i, name);
397                }
398                root.record(message_counters_inspect);
399                Ok(inspector)
400            }
401            .boxed()
402        });
403        let inner = Arc::new(LockDepMutex::new(SuspendResumeManagerInner::default()));
404        let inner_clone = inner.clone();
405        root.record_lazy_child("wakeup_sources", move || {
406            let inner = inner_clone.clone();
407            async move {
408                let inspector = fuchsia_inspect::Inspector::default();
409                let root = inspector.root();
410                let state = inner.lock();
411
412                state.record_suspend_events(root);
413                state.record_wakeup_sources(root);
414
415                Ok(inspector)
416            }
417            .boxed()
418        });
419        Self { message_counters, inner, ebpf_suspend_lock: Default::default() }
420    }
421}
422
423impl SuspendResumeManager {
424    /// Locks and returns the inner state of the manager.
425    pub fn lock(&self) -> LockDepGuard<'_, SuspendResumeManagerInner> {
426        self.inner.lock()
427    }
428
429    /// Power on the PowerMode element and start listening to the suspend stats updates.
430    pub fn init(
431        self: &SuspendResumeManagerHandle,
432        system_task: &CurrentTask,
433    ) -> Result<(), anyhow::Error> {
434        let handoff = system_task
435            .kernel()
436            .connect_to_protocol_at_container_svc::<fpower::HandoffMarker>()?
437            .into_sync_proxy();
438        match handoff.take(zx::MonotonicInstant::INFINITE) {
439            Ok(parent_lease) => {
440                let parent_lease = parent_lease
441                    .map_err(|e| anyhow!("Failed to take lessor and lease from parent: {e:?}"))?;
442                drop(parent_lease)
443            }
444            Err(e) => {
445                if e.is_closed() {
446                    log_warn!(
447                        "Failed to send the fuchsia.session.power/Handoff.Take request. Assuming no Handoff protocol exists and moving on..."
448                    );
449                } else {
450                    return Err(e).context("Handoff::Take");
451                }
452            }
453        }
454        Ok(())
455    }
456
457    pub fn activate_wakeup_source(&self, origin: WakeupSourceOrigin) -> bool {
458        self.activate_wakeup_source_with_actor(origin, None)
459    }
460
461    pub fn activate_wakeup_source_with_actor(
462        &self,
463        origin: WakeupSourceOrigin,
464        actor: Option<starnix_task_command::TaskCommand>,
465    ) -> bool {
466        let mut state = self.lock();
467        let did_activate = {
468            let entry = state.wakeup_sources.entry(origin).or_default();
469            entry.last_actor = actor;
470            let now = zx::MonotonicInstant::get();
471            entry.active_count += 1;
472            entry.event_count += 1;
473            entry.last_change = now;
474            if entry.active_since == zx::MonotonicInstant::ZERO {
475                entry.active_since = now;
476                true
477            } else {
478                false
479            }
480        };
481        if did_activate {
482            state.active_wakeup_source_count += 1;
483            state.signal_wake_events();
484        }
485        did_activate
486    }
487
488    pub fn deactivate_wakeup_source(&self, origin: &WakeupSourceOrigin) -> bool {
489        self.remove_wakeup_source(origin, false)
490    }
491
492    pub fn timeout_wakeup_source(&self, origin: &WakeupSourceOrigin) -> bool {
493        self.remove_wakeup_source(origin, true)
494    }
495
496    fn remove_wakeup_source(&self, origin: &WakeupSourceOrigin, timed_out: bool) -> bool {
497        let mut state = self.lock();
498        let removed = match state.wakeup_sources.get_mut(origin) {
499            Some(entry) if entry.active_since != zx::MonotonicInstant::ZERO => {
500                if timed_out {
501                    entry.expire_count += 1;
502                }
503
504                let now = zx::MonotonicInstant::get();
505                let duration = now - entry.active_since;
506                entry.total_time += duration;
507                entry.max_time = std::cmp::max(duration, entry.max_time);
508                entry.last_change = now;
509                entry.active_since = zx::MonotonicInstant::ZERO;
510
511                true
512            }
513            _ => false,
514        };
515        if removed {
516            state.active_wakeup_source_count -= 1;
517            state.total_wakeup_source_event_count += 1;
518            state.signal_wake_events();
519        }
520        removed
521    }
522
523    pub fn add_message_counter(
524        &self,
525        name: &str,
526        counter: Option<zx::Counter>,
527    ) -> OwnedMessageCounterHandle {
528        let container_counter = OwnedMessageCounter::new(name, counter);
529        let mut message_counters = self.message_counters.lock();
530        message_counters.insert(WeakKey::from(&container_counter));
531        message_counters.retain(|c| c.0.upgrade().is_some());
532        container_counter
533    }
534
535    pub fn has_nonzero_message_counter(&self) -> bool {
536        self.message_counters.lock().iter().any(|c| {
537            let Some(c) = c.0.upgrade() else {
538                return false;
539            };
540            c.counter.as_ref().and_then(|counter| counter.read().ok()).map_or(false, |v| v != 0)
541        })
542    }
543
544    /// Returns a duplicate handle to the `EventPair` that is signaled when wake
545    /// locks are active.
546    pub fn duplicate_lock_event(&self) -> zx::EventPair {
547        let state = self.lock();
548        state
549            .active_lock_reader
550            .duplicate_handle(zx::Rights::SAME_RIGHTS)
551            .expect("Failed to duplicate handle")
552    }
553
554    /// Gets the suspend statistics.
555    pub fn suspend_stats(&self) -> SuspendStats {
556        self.lock().suspend_stats.clone()
557    }
558
559    pub fn total_wakeup_events(&self) -> u64 {
560        let state = self.lock();
561        state.total_wakeup_source_event_count + state.suspend_stats.success_count
562    }
563
564    /// Get the contents of the power "sync_on_suspend" file in the power
565    /// filesystem.  True will cause `1` to be reported, and false will cause
566    /// `0` to be reported.
567    pub fn sync_on_suspend_enabled(&self) -> bool {
568        self.lock().sync_on_suspend_enabled.clone()
569    }
570
571    /// Get the contents of the power "sync_on_suspend" file in the power
572    /// filesystem.  See also [sync_on_suspend_enabled].
573    pub fn set_sync_on_suspend(&self, enable: bool) {
574        self.lock().sync_on_suspend_enabled = enable;
575    }
576
577    /// Returns the supported suspend states.
578    pub fn suspend_states(&self) -> HashSet<SuspendState> {
579        // TODO(b/326470421): Remove the hardcoded supported state.
580        HashSet::from([SuspendState::Idle])
581    }
582
583    pub fn suspend(&self, suspend_state: SuspendState) -> Result<(), Errno> {
584        let suspend_start_time = zx::BootInstant::get();
585        let mut state = self.lock();
586        state.add_suspend_event(SuspendEvent::Attempt {
587            time: suspend_start_time,
588            state: suspend_state.to_string(),
589        });
590
591        // Check if any wake locks are active. If they are, short-circuit the suspend attempt.
592        if !state.can_suspend() {
593            self.report_failed_suspension(state, "kernel wake lock");
594            return error!(EINVAL);
595        }
596
597        // Check if any external wake sources are active.
598        let external_wake_source_abort = state.external_wake_sources.values().find_map(|source| {
599            if source.handle.wait_one(source.signals, zx::MonotonicInstant::INFINITE_PAST).is_ok() {
600                Some(source.name.clone())
601            } else {
602                None
603            }
604        });
605
606        if let Some(name) = external_wake_source_abort {
607            self.report_failed_suspension(state, &format!("external wake source: {}", name));
608            return error!(EINVAL);
609        }
610
611        // Drop the state lock. This allows programs to acquire wake locks again. The runner will
612        // check that no wake locks were acquired once all the container threads have been
613        // suspended, and thus honor any wake locks that were acquired during suspension.
614        std::mem::drop(state);
615
616        // Take the ebpf lock to ensure that ebpf is not preventing suspension. This is necessary
617        // because other components in the system might be executing ebpf programs on our behalf.
618        let _ebpf_lock = self.ebpf_suspend_lock.write();
619
620        let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
621            .expect("Failed to connect to manager");
622        fuchsia_trace::duration!("power", "suspend_container:fidl");
623
624        let container_job = Some(
625            fuchsia_runtime::job_default()
626                .duplicate_handle(zx::Rights::SAME_RIGHTS)
627                .expect("Failed to dup handle"),
628        );
629        let wake_lock_event = Some(self.duplicate_lock_event());
630
631        log_info!("Requesting container suspension.");
632        match manager.suspend_container(
633            frunner::ManagerSuspendContainerRequest {
634                container_job,
635                wake_locks: wake_lock_event,
636                ..Default::default()
637            },
638            zx::Instant::INFINITE,
639        ) {
640            Ok(Ok(res)) => {
641                self.report_container_resumed(suspend_start_time, res);
642            }
643            e => {
644                let state = self.lock();
645                self.report_failed_suspension(state, &format!("runner error {:?}", e));
646                return error!(EINVAL);
647            }
648        }
649        Ok(())
650    }
651
652    fn report_container_resumed(
653        &self,
654        suspend_start_time: zx::BootInstant,
655        res: frunner::ManagerSuspendContainerResponse,
656    ) {
657        let wake_time = zx::BootInstant::get();
658        // The "0" here is to mimic the expected power management success string,
659        // while we don't have IRQ numbers to report.
660        let resume_reason = res.resume_reason.clone().map(|s| format!("0 {}", s));
661        log_info!("Resuming from container suspension: {:?}", resume_reason);
662        let mut state = self.lock();
663        state.update_suspend_stats(|suspend_stats| {
664            suspend_stats.success_count += 1;
665            suspend_stats.last_time_in_suspend_operations = (wake_time - suspend_start_time).into();
666            suspend_stats.last_time_in_sleep =
667                zx::BootDuration::from_nanos(res.suspend_time.unwrap_or(0));
668            suspend_stats.last_resume_reason = resume_reason.clone();
669        });
670        state.add_suspend_event(SuspendEvent::Resume {
671            time: wake_time,
672            reason: resume_reason.unwrap_or_default(),
673        });
674        fuchsia_trace::instant!("power", "suspend_container:done", fuchsia_trace::Scope::Process);
675    }
676
677    fn report_failed_suspension(
678        &self,
679        mut state: LockDepGuard<'_, SuspendResumeManagerInner>,
680        failure_reason: &str,
681    ) {
682        let wake_time = zx::BootInstant::get();
683        state.update_suspend_stats(|suspend_stats| {
684            suspend_stats.fail_count += 1;
685            suspend_stats.last_failed_errno = Some(errno!(EINVAL));
686            suspend_stats.last_resume_reason = None;
687        });
688
689        let mut wakeup_sources: Vec<String> = state
690            .wakeup_sources
691            .iter_mut()
692            .filter_map(|(origin, source)| {
693                if source.active_since > zx::MonotonicInstant::ZERO {
694                    source.wakeup_count += 1;
695                    Some(source.display_name(origin))
696                } else {
697                    None
698                }
699            })
700            .collect();
701
702        for source in state.external_wake_sources.values() {
703            if source.handle.wait_one(source.signals, zx::MonotonicInstant::INFINITE_PAST).is_ok() {
704                wakeup_sources.push(source.name.clone());
705            }
706        }
707
708        let last_resume_reason = format!("Abort: {}", wakeup_sources.join(" "));
709        state.update_suspend_stats(|suspend_stats| {
710            // Power analysis tools require `Abort: ` in the case of failed suspends
711            suspend_stats.last_resume_reason = Some(last_resume_reason);
712        });
713
714        // LINT.IfChange(suspend_failed_tefmo)
715        log_warn!(
716            "Suspend failed due to {:?}. Here are the active wakeup sources: {:?}",
717            failure_reason,
718            wakeup_sources,
719        );
720        // LINT.ThenChange(//tools/testing/tefmocheck/nearby_string_check.go:suspend_failed_tefmo)
721        state.add_suspend_event(SuspendEvent::Fail {
722            time: wake_time,
723            wakeup_sources: Some(wakeup_sources),
724        });
725        fuchsia_trace::instant!("power", "suspend_container:error", fuchsia_trace::Scope::Process);
726    }
727
728    pub fn acquire_ebpf_suspend_lock<'a>(&'a self) -> EbpfSuspendGuard<'a> {
729        self.ebpf_suspend_lock.read()
730    }
731}
732
733/// Called when a wake happens resulting from a timer going off.
734pub trait OnWakeOps: Send + Sync {
735    /// Called on wake events.
736    ///
737    /// Must not block.
738    ///
739    /// # Args
740    /// - `current_task`: the currently active task
741    /// - `baton_lease`: the wake lease is provided if `on_wake` has critical
742    ///   work to do and needs to prevent suspend.
743    fn on_wake(&self, current_task: &CurrentTask, baton_lease: &zx::NullableHandle);
744}
745
746/// Creates a proxy between `remote_channel` and the returned `zx::Channel`.
747///
748/// The message counter's initial value will be set to 0.
749///
750/// The returned counter will be incremented each time there is an incoming message on the proxied
751/// channel. The starnix_kernel is expected to decrement the counter when that incoming message is
752/// handled.
753///
754/// Note that "message" in this context means channel message. This can be either a FIDL event, or
755/// a response to a FIDL message from the platform.
756///
757/// For example, the starnix_kernel may issue a hanging get to retrieve input events. When that
758/// hanging get returns, the counter will be incremented by 1. When the next hanging get has been
759/// scheduled, the input subsystem decrements the counter by 1.
760///
761/// The proxying is done by the Starnix runner, and allows messages on the channel to wake
762/// the container.
763pub fn create_proxy_for_wake_events_counter_zero(
764    remote_channel: zx::Channel,
765    name: String,
766) -> (zx::Channel, zx::Counter) {
767    let (local_proxy, kernel_channel) = zx::Channel::create();
768    let counter = zx::Counter::create();
769
770    let local_counter =
771        counter.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("Failed to duplicate counter");
772
773    let manager = fuchsia_component::client::connect_to_protocol_sync::<frunner::ManagerMarker>()
774        .expect("failed");
775    manager
776        .proxy_wake_channel(frunner::ManagerProxyWakeChannelRequest {
777            container_job: Some(
778                fuchsia_runtime::job_default()
779                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
780                    .expect("Failed to dup handle"),
781            ),
782            container_channel: Some(kernel_channel),
783            remote_channel: Some(remote_channel),
784            counter: Some(counter),
785            name: Some(name),
786            ..Default::default()
787        })
788        .expect("Failed to create proxy");
789
790    (local_proxy, local_counter)
791}
792
793/// Creates a proxy between `remote_channel` and the returned `zx::Channel`.
794///
795/// The message counter's initial value will be set to 1, which will prevent the container from
796/// suspending until the caller decrements the counter.
797///
798/// The returned counter will be incremented each time there is an incoming message on the proxied
799/// channel. The starnix_kernel is expected to decrement the counter when that incoming message is
800/// handled.
801///
802/// Note that "message" in this context means channel message. This can be either a FIDL event, or
803/// a response to a FIDL message from the platform.
804///
805/// For example, the starnix_kernel may issue a hanging get to retrieve input events. When that
806/// hanging get returns, the counter will be incremented by 1. When the next hanging get has been
807/// scheduled, the input subsystem decrements the counter by 1.
808///
809/// The proxying is done by the Starnix runner, and allows messages on the channel to wake
810/// the container.
811pub fn create_proxy_for_wake_events_counter(
812    remote_channel: zx::Channel,
813    name: String,
814) -> (zx::Channel, zx::Counter) {
815    let (proxy, counter) = create_proxy_for_wake_events_counter_zero(remote_channel, name);
816
817    // Increment the counter by one so that the initial incoming message to the container will
818    // set the count to 0, instead of -1.
819    counter.add(1).expect("Failed to add to counter");
820
821    (proxy, counter)
822}
823
824/// Marks a message handled by decrementing `counter`.
825///
826/// This should be called when a proxied channel message has been handled, and the caller would
827/// be ok letting the container suspend.
828pub fn mark_proxy_message_handled(counter: &zx::Counter) {
829    counter.add(-1).expect("Failed to decrement counter");
830}
831
832/// Marks all messages tracked by `counter` as handled.
833pub fn mark_all_proxy_messages_handled(counter: &zx::Counter) {
834    counter.write(0).expect("Failed to decrement counter");
835}
836
837/// Creates a watcher between clients and the Starnix runner.
838///
839/// Changes in the power state of the container are relayed by the event pair.
840pub fn create_watcher_for_wake_events(watcher: zx::EventPair) {
841    let manager = fuchsia_component::client::connect_to_protocol_sync::<frunner::ManagerMarker>()
842        .expect("failed");
843    manager
844        .register_wake_watcher(
845            frunner::ManagerRegisterWakeWatcherRequest {
846                watcher: Some(watcher),
847                ..Default::default()
848            },
849            zx::Instant::INFINITE,
850        )
851        .expect("Failed to register wake watcher");
852}
853
854/// Wrapper around a Weak `OwnedMessageCounter` that can be passed around to keep the container
855/// awake.
856///
857/// Each live `SharedMessageCounter` is responsible for a pending message while it in scope,
858/// and removes it from the counter when it goes out of scope.  Processes that need to cooperate
859/// can pass a `SharedMessageCounter` to each other to ensure that once the work is done, the lock
860/// goes out of scope as well. This allows for precise accounting of remaining work, and should
861/// give us control over container suspension which is guarded by the compiler, not conventions.
862#[derive(Debug)]
863pub struct SharedMessageCounter(Weak<OwnedMessageCounter>);
864
865impl Drop for SharedMessageCounter {
866    fn drop(&mut self) {
867        if let Some(message_counter) = self.0.upgrade() {
868            message_counter.mark_handled();
869        }
870    }
871}
872
873/// Owns a `zx::Counter` to track pending messages that prevent the container from suspending.
874///
875/// This struct ensures that the counter is reset to 0 when the last strong reference is dropped,
876/// effectively releasing any wake lock held by this counter.
877pub struct OwnedMessageCounter {
878    name: String,
879    counter: Option<zx::Counter>,
880}
881pub type OwnedMessageCounterHandle = Arc<OwnedMessageCounter>;
882
883impl Drop for OwnedMessageCounter {
884    /// Resets the underlying `zx::Counter` to 0 when the `OwnedMessageCounter` is dropped.
885    ///
886    /// This ensures that all pending messages are marked as handled, allowing the system to suspend
887    /// if no other wake locks are held.
888    fn drop(&mut self) {
889        self.counter.as_ref().map(mark_all_proxy_messages_handled);
890    }
891}
892
893impl OwnedMessageCounter {
894    pub fn new(name: &str, counter: Option<zx::Counter>) -> OwnedMessageCounterHandle {
895        Arc::new(Self { name: name.to_string(), counter })
896    }
897
898    /// Decrements the counter, signaling that a pending message or operation has been handled.
899    ///
900    /// This should be called when the work associated with a previous `mark_pending` call is
901    /// complete.
902    pub fn mark_handled(&self) {
903        self.counter.as_ref().map(mark_proxy_message_handled);
904    }
905
906    /// Increments the counter, signaling that a new message or operation is pending.
907    ///
908    /// This prevents the system from suspending until a corresponding `mark_handled` call is made.
909    pub fn mark_pending(&self) {
910        self.counter.as_ref().map(|c| c.add(1).expect("Failed to increment counter"));
911    }
912
913    /// Creates a `SharedMessageCounter` from this `OwnedMessageCounter`.
914    ///
915    /// `new_pending_message` - if a new pending message should be added
916    pub fn share(
917        self: &OwnedMessageCounterHandle,
918        new_pending_message: bool,
919    ) -> SharedMessageCounter {
920        if new_pending_message {
921            self.mark_pending();
922        }
923        SharedMessageCounter(Arc::downgrade(self))
924    }
925}
926
927impl fmt::Display for OwnedMessageCounter {
928    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929        write!(f, "Counter({}): {:?}", self.name, self.counter.as_ref().map(|c| c.read()))
930    }
931}
932
933/// A proxy wrapper that manages a `zx::Counter` to allow the container to suspend
934/// after events are being processed.
935///
936/// When the proxy is dropped, the counter is reset to 0 to release the wake-lock.
937pub struct ContainerWakingProxy<P: Proxy> {
938    counter: OwnedMessageCounterHandle,
939    proxy: P,
940}
941
942impl<P: Proxy> ContainerWakingProxy<P> {
943    pub fn new(counter: OwnedMessageCounterHandle, proxy: P) -> Self {
944        Self { counter, proxy }
945    }
946
947    /// Create a `Future` call on the proxy.
948    ///
949    /// The counter will be decremented as message handled after the future is created.
950    pub fn call<T, F, R>(&self, future: F) -> R
951    where
952        F: FnOnce(&P) -> R,
953        R: Future<Output = T>,
954    {
955        // The sequence for handling events MUST be:
956        //
957        // 1. create future
958        // 2. decrease counter
959        // 3. await future
960        //
961        // for allowing suspend - wake.
962        let f = future(&self.proxy);
963        self.counter.mark_handled();
964        f
965    }
966}
967
968/// A stream wrapper that manages a `zx::Counter` to allow the container to suspend
969/// after events are being processed.
970///
971/// When the stream is dropped, the counter is reset to 0 to release the wake-lock.
972pub struct ContainerWakingStream<S: FusedStream + Unpin> {
973    counter: OwnedMessageCounterHandle,
974    stream: S,
975}
976
977impl<S: FusedStream + Unpin> ContainerWakingStream<S> {
978    pub fn new(counter: OwnedMessageCounterHandle, stream: S) -> Self {
979        Self { counter, stream }
980    }
981
982    /// Create a `Next` call on the stream.poll_next().
983    ///
984    /// The counter will be decremented as message handled after the future is created.
985    pub fn next(&mut self) -> Next<'_, S> {
986        // See `ContainerWakingProxy::call` for sequence of handling events.
987        let is_terminated = self.stream.is_terminated();
988        let next = self.stream.next();
989        if !is_terminated {
990            self.counter.mark_handled();
991        }
992        next
993    }
994}
995
996#[cfg(test)]
997mod test {
998    use super::*;
999    use diagnostics_assertions::assert_data_tree;
1000    use fidl::endpoints::create_proxy_and_stream;
1001    use fidl_test_placeholders::{EchoMarker, EchoRequest};
1002    use fuchsia_async as fasync;
1003    use fuchsia_inspect as inspect;
1004    use futures::StreamExt;
1005
1006    #[::fuchsia::test]
1007    fn test_counter_zero_initialization() {
1008        let (_endpoint, endpoint) = zx::Channel::create();
1009        let (_channel, counter) =
1010            super::create_proxy_for_wake_events_counter_zero(endpoint, "test".into());
1011        assert_eq!(counter.read(), Ok(0));
1012    }
1013
1014    #[::fuchsia::test]
1015    fn test_counter_initialization() {
1016        let (_endpoint, endpoint) = zx::Channel::create();
1017        let (_channel, counter) =
1018            super::create_proxy_for_wake_events_counter(endpoint, "test".into());
1019        assert_eq!(counter.read(), Ok(1));
1020    }
1021
1022    #[::fuchsia::test]
1023    async fn test_container_waking_proxy() {
1024        let (proxy, mut stream) = create_proxy_and_stream::<EchoMarker>();
1025        let server_task = fasync::Task::spawn(async move {
1026            let request = stream.next().await.unwrap().unwrap();
1027            match request {
1028                EchoRequest::EchoString { value, responder } => {
1029                    responder.send(value.as_deref()).unwrap();
1030                }
1031            }
1032        });
1033
1034        let counter = zx::Counter::create();
1035        counter.add(5).unwrap();
1036        assert_eq!(counter.read(), Ok(5));
1037
1038        let waking_proxy = ContainerWakingProxy {
1039            counter: OwnedMessageCounter::new(
1040                "test_proxy",
1041                Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1042            ),
1043            proxy,
1044        };
1045
1046        let response_future = waking_proxy.call(|p| p.echo_string(Some("hello")));
1047
1048        // The `call` method decrements the counter.
1049        assert_eq!(counter.read(), Ok(4));
1050
1051        let response = response_future.await.unwrap();
1052        assert_eq!(response.as_deref(), Some("hello"));
1053
1054        server_task.await;
1055
1056        assert_eq!(counter.read(), Ok(4));
1057        drop(waking_proxy);
1058        assert_eq!(counter.read(), Ok(0));
1059    }
1060
1061    #[::fuchsia::test]
1062    async fn test_container_waking_stream() {
1063        let (proxy, stream) = create_proxy_and_stream::<EchoMarker>();
1064        let client_task = fasync::Task::spawn(async move {
1065            let response = proxy.echo_string(Some("hello")).await.unwrap();
1066            assert_eq!(response.as_deref(), Some("hello"));
1067        });
1068
1069        let counter = zx::Counter::create();
1070        counter.add(5).unwrap();
1071        assert_eq!(counter.read(), Ok(5));
1072
1073        let mut waking_stream = ContainerWakingStream {
1074            counter: OwnedMessageCounter::new(
1075                "test_stream",
1076                Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1077            ),
1078            stream,
1079        };
1080
1081        let request_future = waking_stream.next();
1082
1083        // The `next` method decrements the counter.
1084        assert_eq!(counter.read(), Ok(4));
1085
1086        let request = request_future.await.unwrap().unwrap();
1087        match request {
1088            EchoRequest::EchoString { value, responder } => {
1089                assert_eq!(value.as_deref(), Some("hello"));
1090                responder.send(value.as_deref()).unwrap();
1091            }
1092        }
1093
1094        client_task.await;
1095
1096        assert_eq!(counter.read(), Ok(4));
1097        drop(waking_stream);
1098        assert_eq!(counter.read(), Ok(0));
1099    }
1100
1101    #[::fuchsia::test]
1102    async fn test_message_counters_inspect() {
1103        let power_manager = SuspendResumeManager::default();
1104        let inspector = inspect::component::inspector();
1105
1106        let zx_counter = zx::Counter::create();
1107        let counter_handle = power_manager.add_message_counter(
1108            "test_counter",
1109            Some(zx_counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1110        );
1111
1112        zx_counter.add(1).unwrap();
1113
1114        assert_data_tree!(inspector, root: contains {
1115            message_counters: vec!["Counter(test_counter): Some(Ok(1))"],
1116        });
1117
1118        zx_counter.add(1).unwrap();
1119        assert_data_tree!(inspector, root: contains {
1120            message_counters: vec!["Counter(test_counter): Some(Ok(2))"],
1121        });
1122
1123        drop(counter_handle);
1124        assert_data_tree!(inspector, root: contains {
1125            message_counters: Vec::<String>::new(),
1126        });
1127    }
1128
1129    #[::fuchsia::test]
1130    fn test_shared_message_counter() {
1131        // Create an owned counter and set its value.
1132        let zx_counter = zx::Counter::create();
1133        let owned_counter = OwnedMessageCounter::new(
1134            "test_shared_counter",
1135            Some(zx_counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1136        );
1137        zx_counter.add(5).unwrap();
1138        assert_eq!(zx_counter.read(), Ok(5));
1139
1140        // Create a shared counter with no new message. The value should be unchanged.
1141        let shared_counter = owned_counter.share(false);
1142        assert_eq!(zx_counter.read(), Ok(5));
1143
1144        // Drop the shared counter. The value should be decremented.
1145        drop(shared_counter);
1146        assert_eq!(zx_counter.read(), Ok(4));
1147
1148        // Create a shared counter with a new message. The value should be incremented.
1149        let shared_counter_2 = owned_counter.share(true);
1150        assert_eq!(zx_counter.read(), Ok(5));
1151
1152        // Drop the shared counter. The value should be decremented.
1153        drop(shared_counter_2);
1154        assert_eq!(zx_counter.read(), Ok(4));
1155
1156        // Create another shared counter.
1157        let shared_counter_3 = owned_counter.share(false);
1158        assert_eq!(zx_counter.read(), Ok(4));
1159
1160        // Drop the owned counter. The value should be reset to 0.
1161        drop(owned_counter);
1162        assert_eq!(zx_counter.read(), Ok(0));
1163
1164        // Drop the shared counter. The value should remain 0, and it shouldn't panic.
1165        drop(shared_counter_3);
1166        assert_eq!(zx_counter.read(), Ok(0));
1167    }
1168
1169    #[::fuchsia::test]
1170    async fn test_container_waking_event_termination() {
1171        let stream = futures::stream::iter(vec![0]).fuse();
1172        let counter = zx::Counter::create();
1173        counter.add(2).unwrap();
1174        assert_eq!(counter.read(), Ok(2));
1175        let mut waking_stream = ContainerWakingStream {
1176            counter: OwnedMessageCounter::new(
1177                "test_stream",
1178                Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1179            ),
1180            stream,
1181        };
1182
1183        assert_eq!(waking_stream.next().await, Some(0));
1184        assert_eq!(counter.read(), Ok(1));
1185
1186        assert_eq!(waking_stream.next().await, None);
1187        assert_eq!(waking_stream.next().await, None);
1188        // The stream is already terminated, so the counter should remain 0.
1189        assert_eq!(counter.read(), Ok(0));
1190    }
1191
1192    #[::fuchsia::test]
1193    fn test_external_wake_source_aborts_suspend() {
1194        let manager = SuspendResumeManager::default();
1195        let event = zx::Event::create();
1196        let signals = zx::Signals::USER_0;
1197
1198        // We can't actually verify the runner call in this unit test environment easily
1199        // without a lot of mocking setup that might not be present.
1200        // However, we can verify that if it was registered, the suspend check respects it.
1201
1202        let res = manager.add_external_wake_source(
1203            event.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap().into_handle(),
1204            signals,
1205            "test_external".to_string(),
1206        );
1207
1208        if res.is_err() {
1209            println!(
1210                "Skipping test_external_wake_source_aborts_suspend because runner connection failed: {:?}",
1211                res
1212            );
1213            return;
1214        }
1215
1216        // Signal the event
1217        event.signal(zx::Signals::empty(), signals).unwrap();
1218
1219        let state = manager.lock();
1220        assert!(state.external_wake_sources.contains_key(&event.koid().unwrap()));
1221    }
1222}