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 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 {
15    fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_runner as frunner,
16    fidl_fuchsia_power_system as fpower, zx,
17};
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        let (kernel, on_stop) =
69            StarnixKernel::create(realm, KERNEL_URL, start_info, controller).await?;
70        let kernel_job = kernel.job.clone();
71        let kernel_koid = kernel.job.koid()?;
72
73        *kernel.wake_lease.lock() = wake_lease;
74        log::info!("Acquired wake lease for {:?}", kernel_job);
75
76        self.kernels.lock().insert(kernel_koid, kernel);
77
78        let kernels = self.kernels.clone();
79        self.background_tasks.spawn(async move {
80            on_stop.await;
81            _ = kernels.lock().remove(&kernel_koid);
82        });
83
84        Ok(())
85    }
86
87    /// Gets a momentary snapshot of all kernel jobs.
88    pub fn all_jobs(&self) -> Vec<Arc<zx::Job>> {
89        self.kernels.lock().iter().map(|(_, k)| Arc::clone(k.job())).collect()
90    }
91
92    /// Drops any active wake lease for the container running in the given `container_job`.
93    pub fn drop_wake_lease(&self, container_job: &zx::Job) -> Result<(), Error> {
94        // LINT.IfChange
95        fuchsia_trace::instant!(
96            "power",
97            "starnix-runner:drop-application-activity-lease",
98            fuchsia_trace::Scope::Process
99        );
100        // LINT.ThenChange(//src/performance/lib/trace_processing/metrics/suspend.py)
101        let job_koid = container_job.koid()?;
102        if let Some(kernel) = self.kernels.lock().get(&job_koid) {
103            kernel.wake_lease.lock().take();
104            log::info!("Dropped wake lease for {:?}", container_job);
105        }
106        Ok(())
107    }
108
109    /// Acquires a wake lease for the container running in the given `container_job`.
110    pub async fn acquire_wake_lease(&self, container_job: &zx::Job) -> Result<(), Error> {
111        // LINT.IfChange
112        fuchsia_trace::duration!("power", "starnix-runner:acquire-application-activity-lease");
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            let activity_governor = connect_to_protocol::<fpower::ActivityGovernorMarker>()?;
117            let wake_lease = match activity_governor
118                .take_application_activity_lease(&create_lease_name(&kernel.name))
119                .await
120            {
121                Ok(l) => l,
122                Err(e) => {
123                    log::warn!("Failed to acquire application activity lease for kernel: {:?}", e);
124                    return Ok(());
125                }
126            };
127            *kernel.wake_lease.lock() = Some(wake_lease);
128            log::info!("Acquired wake lease for {:?}", container_job);
129        }
130        Ok(())
131    }
132}
133
134impl Drop for Kernels {
135    fn drop(&mut self) {
136        self.background_tasks.shutdown();
137    }
138}