Skip to main content

kernel_manager/
kernels.rs

1// Copyright 2024 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::{StarnixKernel, generate_kernel_name};
6use anyhow::Error;
7use fidl::endpoints::ServerEnd;
8use fidl_fuchsia_component as fcomponent;
9use fidl_fuchsia_component_runner as frunner;
10use fidl_fuchsia_power_system as fpower;
11use frunner::{ComponentControllerMarker, ComponentStartInfo};
12use fuchsia_component::client::connect_to_protocol;
13use fuchsia_sync::Mutex;
14use std::collections::HashMap;
15use std::sync::Arc;
16use vfs::execution_scope::ExecutionScope;
17use zx;
18
19/// The component URL of the Starnix kernel.
20const KERNEL_URL: &str = "starnix_kernel#meta/starnix_kernel.cm";
21
22/// Create the power lease name for better readability based on the Starnix kernel name.
23fn create_lease_name(kernel_name: &str) -> String {
24    format!("starnix-kernel-{}", kernel_name)
25}
26
27/// [`Kernels`] manages a collection of starnix kernels.
28pub struct Kernels {
29    kernels: Arc<Mutex<HashMap<zx::Koid, StarnixKernel>>>,
30    background_tasks: ExecutionScope,
31}
32
33impl Kernels {
34    /// Creates a new [`Kernels`] instance.
35    pub fn new() -> Self {
36        let kernels = Default::default();
37        Self { kernels, background_tasks: ExecutionScope::new() }
38    }
39
40    /// Runs a new starnix kernel and adds it to the collection.
41    pub async fn start(
42        &self,
43        start_info: ComponentStartInfo,
44        controller: ServerEnd<ComponentControllerMarker>,
45    ) -> Result<(), Error> {
46        let realm =
47            connect_to_protocol::<fcomponent::RealmMarker>().expect("Failed to connect to realm.");
48
49        let kernel_name = generate_kernel_name(&start_info)?;
50        let wake_lease = 'out: {
51            let Ok(activity_governor) = connect_to_protocol::<fpower::ActivityGovernorMarker>()
52            else {
53                break 'out None;
54            };
55
56            match activity_governor
57                .take_application_activity_lease(&create_lease_name(&kernel_name))
58                .await
59            {
60                Ok(l) => Some(l),
61                Err(e) => {
62                    log::warn!("Failed to acquire application activity lease for kernel: {:?}", e);
63                    None
64                }
65            }
66        };
67
68        if let Ok(boot_control) = connect_to_protocol::<fpower::BootControlMarker>() {
69            log::info!(
70                "Notifying Fuchsia SystemActivityGovernor that Starnix has booted to allow suspend"
71            );
72            let _ = boot_control.set_boot_complete().await;
73            if let Err(e) = boot_control.set_boot_complete().await {
74                log::warn!(
75                    "Failed to notify Fuchsia SystemActivityGovernor that Starnix has booted: {:?}",
76                    e
77                );
78            }
79        }
80
81        let (kernel, on_stop) =
82            StarnixKernel::create(realm, KERNEL_URL, start_info, controller).await?;
83        let kernel_job = kernel.job.clone();
84        let kernel_koid = kernel.job.koid()?;
85
86        *kernel.wake_lease.lock() = wake_lease;
87        log::info!("Acquired wake lease for {:?}", kernel_job);
88
89        self.kernels.lock().insert(kernel_koid, kernel);
90
91        let kernels = self.kernels.clone();
92        self.background_tasks.spawn(async move {
93            on_stop.await;
94            _ = kernels.lock().remove(&kernel_koid);
95        });
96
97        Ok(())
98    }
99
100    /// Gets a momentary snapshot of all kernel jobs.
101    pub fn all_jobs(&self) -> Vec<Arc<zx::Job>> {
102        self.kernels.lock().iter().map(|(_, k)| Arc::clone(k.job())).collect()
103    }
104
105    /// Drops any active wake lease for the container running in the given `container_job`.
106    pub fn drop_wake_lease(&self, container_job: &zx::Job) -> Result<(), Error> {
107        // LINT.IfChange
108        fuchsia_trace::instant!(
109            "power",
110            "starnix-runner:drop-application-activity-lease",
111            fuchsia_trace::Scope::Process
112        );
113        // LINT.ThenChange(//src/performance/lib/trace_processing/metrics/suspend.py)
114        let job_koid = container_job.koid()?;
115        if let Some(kernel) = self.kernels.lock().get(&job_koid) {
116            kernel.wake_lease.lock().take();
117            log::info!("Dropped wake lease for {:?}", container_job);
118        }
119        Ok(())
120    }
121
122    /// Acquires a wake lease for the container running in the given `container_job`.
123    pub async fn acquire_wake_lease(&self, container_job: &zx::Job) -> Result<(), Error> {
124        // LINT.IfChange
125        fuchsia_trace::duration!("power", "starnix-runner:acquire-application-activity-lease");
126        // LINT.ThenChange(//src/performance/lib/trace_processing/metrics/suspend.py)
127        let job_koid = container_job.koid()?;
128
129        let kernel_name = {
130            let guard = self.kernels.lock();
131            let Some(kernel) = guard.get(&job_koid) else {
132                return Ok(());
133            };
134            kernel.name.clone()
135        };
136
137        let activity_governor = connect_to_protocol::<fpower::ActivityGovernorMarker>()?;
138        let wake_lease = match activity_governor
139            .take_application_activity_lease(&create_lease_name(&kernel_name))
140            .await
141        {
142            Ok(l) => l,
143            Err(e) => {
144                log::warn!("Failed to acquire application activity lease for kernel: {:?}", e);
145                return Ok(());
146            }
147        };
148
149        if let Some(kernel) = self.kernels.lock().get(&job_koid) {
150            *kernel.wake_lease.lock() = Some(wake_lease);
151            log::info!("Acquired wake lease for {:?}", container_job);
152        }
153
154        Ok(())
155    }
156}
157
158impl Drop for Kernels {
159    fn drop(&mut self) {
160        self.background_tasks.shutdown();
161    }
162}