blob: 4ab24dfaab88edf5131eda711c8834e7dded975d [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{
common_utils::common::macros::{fx_err_and_bail, with_line},
cpu_ctrl::types::SerializableCpuPerformanceStateInfo,
};
use anyhow::Error;
use fidl_fuchsia_hardware_cpu_ctrl::{DeviceMarker, DeviceProxy};
use fuchsia_syslog::macros::*;
use glob::glob;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
/// Perform cpu-ctrl operations.
///
/// Note this object is shared among all threads created
///
#[derive(Debug)]
pub struct CpuCtrlFacade {
proxy: RwLock<Option<DeviceProxy>>,
}
impl CpuCtrlFacade {
pub fn new() -> CpuCtrlFacade {
Self { proxy: RwLock::new(None) }
}
#[cfg(test)]
fn new_with_proxy(proxy: DeviceProxy) -> Self {
Self { proxy: RwLock::new(Some(proxy)) }
}
fn get_proxy(&self, device_number: String) -> Result<DeviceProxy, Error> {
let lock = self.proxy.upgradable_read();
if let Some(proxy) = lock.as_ref() {
Ok(proxy.clone())
} else {
let tag = "CpuCtrlFacade::get_proxy";
let (proxy, server) = match fidl::endpoints::create_proxy::<DeviceMarker>() {
Ok(r) => r,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to get cpu-ctrl proxy {:?}", e)
),
};
let mut path = String::from("/dev/class/cpu-ctrl/");
path.push_str(&device_number);
let found_path = glob(&path)?.filter_map(|entry| entry.ok()).next();
match found_path {
Some(path) => {
fdio::service_connect(path.to_string_lossy().as_ref(), server.into_channel())?;
*RwLockUpgradableReadGuard::upgrade(lock) = Some(proxy.clone());
Ok(proxy)
}
None => fx_err_and_bail!(&with_line!(tag), format_err!("Failed to find device")),
}
}
}
pub async fn get_performance_state_info(
&self,
device_number: String,
state: u32,
) -> Result<SerializableCpuPerformanceStateInfo, Error> {
let tag = "CpuCtrlFacade::get_performance_state_info";
match self.get_proxy(device_number)?.get_performance_state_info(state).await? {
Ok(r) => Ok(SerializableCpuPerformanceStateInfo::from(r)),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetPerformanceStateInfo failed: {:?}", e)
),
}
}
pub async fn get_num_logical_cores(&self, device_number: String) -> Result<u64, Error> {
Ok(self.get_proxy(device_number)?.get_num_logical_cores().await?)
}
pub async fn get_logical_core_id(
&self,
device_number: String,
index: u64,
) -> Result<u64, Error> {
Ok(self.get_proxy(device_number)?.get_logical_core_id(index).await?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_hardware_cpu_ctrl::{CpuPerformanceStateInfo, DeviceRequest};
use futures::{future::Future, join, stream::StreamExt};
use matches::assert_matches;
struct MockCpuCtrlBuilder {
expected: Vec<Box<dyn FnOnce(DeviceRequest) + Send + 'static>>,
}
impl MockCpuCtrlBuilder {
fn new() -> Self {
Self { expected: vec![] }
}
fn push(mut self, request: impl FnOnce(DeviceRequest) + Send + 'static) -> Self {
self.expected.push(Box::new(request));
self
}
fn expect_get_performance_state_info(
self,
_device_number: String,
expected_state: u32,
res: Result<CpuPerformanceStateInfo, Error>,
) -> Self {
self.push(move |req| match req {
DeviceRequest::GetPerformanceStateInfo { state, responder } => {
assert_eq!(expected_state, state);
responder
.send(
&mut res
.map(|info| CpuPerformanceStateInfo {
frequency_hz: info.frequency_hz,
voltage_uv: info.voltage_uv,
})
.map_err(|_status| -1),
)
.unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_num_logical_cores(
self,
_device_number: String,
res: Result<u64, Error>,
) -> Self {
self.push(move |req| match req {
DeviceRequest::GetNumLogicalCores { responder } => {
assert!(res.is_ok());
responder.send(res.unwrap()).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_logical_core_id(
self,
_device_number: String,
expected_index: u64,
res: Result<u64, Error>,
) -> Self {
self.push(move |req| match req {
DeviceRequest::GetLogicalCoreId { index, responder } => {
assert_eq!(expected_index, index);
assert!(res.is_ok());
responder.send(res.unwrap()).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn build(self) -> (CpuCtrlFacade, impl Future<Output = ()>) {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<DeviceMarker>().unwrap();
let fut = async move {
for expected in self.expected {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(CpuCtrlFacade::new_with_proxy(proxy), fut)
}
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_performance_state_info_ok() {
let (facade, expectations) = MockCpuCtrlBuilder::new()
.expect_get_performance_state_info(
"000".to_string(),
0,
Ok(CpuPerformanceStateInfo { frequency_hz: 1896000000, voltage_uv: 981000 }),
)
.build();
let test = async move {
assert_matches!(
facade.get_performance_state_info("000".to_string(), 0).await,
Ok(info) if info == SerializableCpuPerformanceStateInfo {
frequency_hz: 1896000000,
voltage_uv: 981000,
}
);
};
join!(expectations, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_num_logical_cores_ok() {
let (facade, expectations) = MockCpuCtrlBuilder::new()
.expect_get_num_logical_cores("000".to_string(), Ok(4))
.build();
let test = async move {
assert_matches!(facade.get_num_logical_cores("000".to_string()).await, Ok(4));
};
join!(expectations, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_logical_core_id_ok() {
let (facade, expectations) = MockCpuCtrlBuilder::new()
.expect_get_logical_core_id("000".to_string(), 0, Ok(0))
.build();
let test = async move {
assert_matches!(facade.get_logical_core_id("000".to_string(), 0).await, Ok(0));
};
join!(expectations, test);
}
}