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