// Copyright 2020 Arm Limited (or its affiliates). All rights reserved. // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. use std::fs; use std::path::Path; use log::warn; #[derive(Copy, Clone)] pub enum CacheLevel { /// L1 data cache L1D = 0, /// L1 instruction cache L1I = 1, /// L2 cache L2 = 2, /// L3 cache L3 = 3, } /// NOTE: cache size file directory example, /// "/sys/devices/system/cpu/cpu0/cache/index0/size". pub fn get_cache_size(cache_level: CacheLevel) -> u32 { let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); match cache_level { CacheLevel::L1D => file_directory += "/index0/size", CacheLevel::L1I => file_directory += "/index1/size", CacheLevel::L2 => file_directory += "/index2/size", CacheLevel::L3 => file_directory += "/index3/size", } let file_path = Path::new(&file_directory); if file_path.exists() { let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); // The content of the file is as simple as a size, like: "32K" let src = src.trim(); let src_digits: u32 = src[0..src.len() - 1].parse().unwrap(); let src_unit = &src[src.len() - 1..]; src_digits * match src_unit { "K" => 1u32 << 10, "M" => 1u32 << 20, "G" => 1u32 << 30, _ => 1, } } else { 0 } } /// NOTE: coherency_line_size file directory example, /// "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size". pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 { let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); match cache_level { CacheLevel::L1D => file_directory += "/index0/coherency_line_size", CacheLevel::L1I => file_directory += "/index1/coherency_line_size", CacheLevel::L2 => file_directory += "/index2/coherency_line_size", CacheLevel::L3 => file_directory += "/index3/coherency_line_size", } let file_path = Path::new(&file_directory); if file_path.exists() { let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); src.trim().parse::().unwrap() } else { 0 } } /// NOTE: number_of_sets file directory example, /// "/sys/devices/system/cpu/cpu0/cache/index0/number_of_sets". pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 { let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); match cache_level { CacheLevel::L1D => file_directory += "/index0/number_of_sets", CacheLevel::L1I => file_directory += "/index1/number_of_sets", CacheLevel::L2 => file_directory += "/index2/number_of_sets", CacheLevel::L3 => file_directory += "/index3/number_of_sets", } let file_path = Path::new(&file_directory); if file_path.exists() { let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); src.trim().parse::().unwrap() } else { 0 } } /// NOTE: shared_cpu_list file directory example, /// "/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_list". pub fn get_cache_shared(cache_level: CacheLevel) -> bool { let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); let mut result = true; match cache_level { CacheLevel::L1D | CacheLevel::L1I => result = false, CacheLevel::L2 => file_directory += "/index2/shared_cpu_list", CacheLevel::L3 => file_directory += "/index3/shared_cpu_list", } if !result { return false; } let file_path = Path::new(&file_directory); if file_path.exists() { let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); let src = src.trim(); if src.is_empty() { result = false; } else { result = src.contains('-') || src.contains(','); } } else { result = false; } result } #[derive(Default, Copy, Clone, Debug)] pub struct CacheTopologyInfo { pub l1_d_cache_size: u32, pub l1_d_cache_line_size: u32, pub l1_d_cache_sets: u32, pub l1_i_cache_size: u32, pub l1_i_cache_line_size: u32, pub l1_i_cache_sets: u32, pub l2_cache_size: u32, pub l2_cache_line_size: u32, pub l2_cache_sets: u32, pub l3_cache_size: u32, pub l3_cache_line_size: u32, pub l3_cache_sets: u32, pub l2_cache_shared: bool, pub l3_cache_shared: bool, } /// Reads cache topology information from sysfs for cpu0. pub fn read_cache_topology() -> Option { let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache"); if !cache_path.exists() { warn!("Cache topology information is not available in sysfs."); return None; } let mut info = CacheTopologyInfo { l1_d_cache_size: get_cache_size(CacheLevel::L1D), l1_d_cache_line_size: get_cache_coherency_line_size(CacheLevel::L1D), l1_d_cache_sets: get_cache_number_of_sets(CacheLevel::L1D), l1_i_cache_size: get_cache_size(CacheLevel::L1I), l1_i_cache_line_size: get_cache_coherency_line_size(CacheLevel::L1I), l1_i_cache_sets: get_cache_number_of_sets(CacheLevel::L1I), l2_cache_size: get_cache_size(CacheLevel::L2), l2_cache_line_size: get_cache_coherency_line_size(CacheLevel::L2), l2_cache_sets: get_cache_number_of_sets(CacheLevel::L2), l3_cache_size: get_cache_size(CacheLevel::L3), l3_cache_line_size: get_cache_coherency_line_size(CacheLevel::L3), l3_cache_sets: get_cache_number_of_sets(CacheLevel::L3), l2_cache_shared: false, l3_cache_shared: false, }; if info.l2_cache_size != 0 { info.l2_cache_shared = get_cache_shared(CacheLevel::L2); } if info.l3_cache_size != 0 { info.l3_cache_shared = get_cache_shared(CacheLevel::L3); } Some(info) }