From b3d59c83dd9e99acc7a89df395a280986dbaefe1 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Mon, 28 Aug 2023 15:01:23 +0800 Subject: [PATCH 1/2] fix: remove support for AMD tsc counter Signed-off-by: Andy Lok --- src/tsc_now.rs | 227 ++----------------------------------------------- 1 file changed, 7 insertions(+), 220 deletions(-) diff --git a/src/tsc_now.rs b/src/tsc_now.rs index 68deb85..4eaf08f 100644 --- a/src/tsc_now.rs +++ b/src/tsc_now.rs @@ -2,10 +2,6 @@ //! This module will be compiled when it's either linux_x86 or linux_x86_64. -use libc::{cpu_set_t, sched_setaffinity, CPU_SET}; -use std::io::prelude::*; -use std::io::BufReader; -use std::mem::{size_of, zeroed, MaybeUninit}; use std::time::Instant; use std::{cell::UnsafeCell, fs::read_to_string}; @@ -30,7 +26,6 @@ unsafe fn init() { let tsc_level = TSCLevel::get(); let is_tsc_available = match &tsc_level { TSCLevel::Stable { .. } => true, - TSCLevel::PerCPUStable { .. } => true, TSCLevel::Unstable => false, }; if is_tsc_available { @@ -57,13 +52,6 @@ pub(crate) fn current_cycle() -> u64 { TSCLevel::Stable { cycles_from_anchor, .. } => tsc().wrapping_sub(*cycles_from_anchor), - TSCLevel::PerCPUStable { - cycles_from_anchor, .. - } => { - let (tsc, cpuid) = tsc_with_cpuid(); - let anchor = cycles_from_anchor[cpuid]; - tsc.wrapping_sub(anchor) - } TSCLevel::Unstable => panic!("tsc is unstable"), } } @@ -73,93 +61,21 @@ enum TSCLevel { cycles_per_second: u64, cycles_from_anchor: u64, }, - PerCPUStable { - cycles_per_second: u64, - cycles_from_anchor: Vec, - }, Unstable, } impl TSCLevel { fn get() -> TSCLevel { - let anchor = Instant::now(); - if is_tsc_stable() { - let (cps, cfa) = cycles_per_sec(anchor); - return TSCLevel::Stable { - cycles_per_second: cps, - cycles_from_anchor: cfa, - }; + if !is_tsc_stable() { + return TSCLevel::Unstable; } - if is_tsc_percpu_stable() { - // Retrieve the IDs of all active CPUs. - let cpuids = if let Ok(cpuids) = available_cpus() { - if cpuids.is_empty() { - return TSCLevel::Unstable; - } - - cpuids - } else { - return TSCLevel::Unstable; - }; - - let max_cpu_id = *cpuids.iter().max().unwrap(); - - // Spread the threads to all CPUs and calculate - // cycles from anchor separately - let handles = cpuids.into_iter().map(|id| { - std::thread::spawn(move || { - set_affinity(id).unwrap(); - - // check if cpu id matches IA32_TSC_AUX - let (_, cpuid) = tsc_with_cpuid(); - assert_eq!(cpuid, id); - - let (cps, cfa) = cycles_per_sec(anchor); - - (id, cps, cfa) - }) - }); - - // Block and wait for all threads finished - let results = handles.map(|h| h.join()).collect::, _>>(); - - let results = if let Ok(results) = results { - results - } else { - return TSCLevel::Unstable; - }; - - // Indexed by CPU ID - let mut cycles_from_anchor = vec![0; max_cpu_id + 1]; - - // Rates of TSCs on different CPUs won't be a big gap - // or it's unstable. - let mut max_cps = std::u64::MIN; - let mut min_cps = std::u64::MAX; - let mut sum_cps = 0; - let len = results.len(); - for (cpuid, cps, cfa) in results { - if cps > max_cps { - max_cps = cps; - } - if cps < min_cps { - min_cps = cps; - } - sum_cps += cps; - cycles_from_anchor[cpuid] = cfa; - } - if (max_cps - min_cps) as f64 / min_cps as f64 > 0.0005 { - return TSCLevel::Unstable; - } - - return TSCLevel::PerCPUStable { - cycles_per_second: sum_cps / len as u64, - cycles_from_anchor, - }; + let anchor = Instant::now(); + let (cps, cfa) = cycles_per_sec(anchor); + TSCLevel::Stable { + cycles_per_second: cps, + cycles_from_anchor: cfa, } - - TSCLevel::Unstable } #[inline] @@ -168,9 +84,6 @@ impl TSCLevel { TSCLevel::Stable { cycles_per_second, .. } => *cycles_per_second, - TSCLevel::PerCPUStable { - cycles_per_second, .. - } => *cycles_per_second, TSCLevel::Unstable => panic!("tsc is unstable"), } } @@ -186,36 +99,6 @@ fn is_tsc_stable() -> bool { clock_source.map(|s| s.contains("tsc")).unwrap_or(false) } -/// Checks if CPU flag contains `constant_tsc`, `nonstop_tsc` and -/// `rdtscp`. With these features, TSCs can be synced via offsets -/// between them and CPUID extracted from `IA32_TSC_AUX`. -fn is_tsc_percpu_stable() -> bool { - let f = || { - let cpuinfo = std::fs::File::open("/proc/cpuinfo").ok()?; - let mut cpuinfo = BufReader::new(cpuinfo); - - let mut buf = String::with_capacity(1024); - loop { - if cpuinfo.read_line(&mut buf).ok()? == 0 { - break; - } - - if buf.starts_with("flags") { - break; - } - - buf.clear(); - } - - let constant_tsc = buf.contains("constant_tsc"); - let nonstop_tsc = buf.contains("nonstop_tsc"); - let rdtscp = buf.contains("rdtscp"); - Some(constant_tsc && nonstop_tsc && rdtscp) - }; - - f().unwrap_or(false) -} - /// Returns (1) cycles per second and (2) cycles from anchor. /// The result of subtracting `cycles_from_anchor` from newly fetched TSC /// can be used to @@ -275,99 +158,3 @@ fn tsc() -> u64 { unsafe { _rdtsc() } } - -#[inline] -fn tsc_with_cpuid() -> (u64, usize) { - #[cfg(target_arch = "x86")] - use core::arch::x86::__rdtscp; - #[cfg(target_arch = "x86_64")] - use core::arch::x86_64::__rdtscp; - - let mut aux = MaybeUninit::::uninit(); - let tsc = unsafe { __rdtscp(aux.as_mut_ptr()) }; - let aux = unsafe { aux.assume_init() }; - - // IA32_TSC_AUX are encoded by Linux kernel as follow format: - // - // 31 12 11 0 - // [ node id ][ cpu id ] - // - // See: https://elixir.bootlin.com/linux/v5.7.7/source/arch/x86/include/asm/segment.h#L249 - - // extract cpu id and check the same - (tsc, (aux & 0xfff) as usize) -} - -// Retrieve available CPUs from `/sys` filesystem. -fn available_cpus() -> Result, Error> { - let s = read_to_string("/sys/devices/system/cpu/online")?; - parse_cpu_list_format(&s) -} - -/// A wrapper function of sched_setaffinity(2) -fn set_affinity(cpuid: usize) -> Result<(), Error> { - let mut set = unsafe { zeroed::() }; - - unsafe { CPU_SET(cpuid, &mut set) }; - - // Set the current thread's core affinity. - if unsafe { - sched_setaffinity( - 0, // Defaults to current thread - size_of::(), - &set as *const _, - ) - } != 0 - { - Err(std::io::Error::last_os_error().into()) - } else { - Ok(()) - } -} - -/// List format -/// The List Format for cpus and mems is a comma-separated list of CPU or -/// memory-node numbers and ranges of numbers, in ASCII decimal. -/// -/// Examples of the List Format: -/// 0-4,9 # bits 0, 1, 2, 3, 4, and 9 set -/// 0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set -fn parse_cpu_list_format(list: &str) -> Result, Error> { - let mut res = vec![]; - let list = list.trim(); - for set in list.split(',') { - if set.contains('-') { - let mut ft = set.splitn(2, '-'); - let from = ft.next().ok_or("expected from")?.parse::()?; - let to = ft.next().ok_or("expected to")?.parse::()?; - for i in from..=to { - res.push(i); - } - } else { - res.push(set.parse()?) - } - } - - Ok(res) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_list_format() { - assert_eq!( - parse_cpu_list_format("0-2,7,12-14\n").unwrap(), - &[0, 1, 2, 7, 12, 13, 14] - ); - assert_eq!( - parse_cpu_list_format("0-4,9\n").unwrap(), - &[0, 1, 2, 3, 4, 9] - ); - assert_eq!( - parse_cpu_list_format("0-15\n").unwrap(), - (0..=15).collect::>() - ); - } -} From 5cd04dddb7443bcf86febda5608c8f86ad16a651 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Mon, 28 Aug 2023 15:30:21 +0800 Subject: [PATCH 2/2] bump version Signed-off-by: Andy Lok --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4aff190..90c5d8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "minstant" -version = "0.1.2" +version = "0.1.3" authors = ["The TiKV Authors"] -edition = "2018" +edition = "2021" license = "MIT" description = "A drop-in replacement for `std::time::Instant` that measures time with high performance and high accuracy powered by TSC" homepage = "https://github.com/tikv/minstant"