| // 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); |
| } |
| } |