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