diff --git a/Cargo.lock b/Cargo.lock index 25c5654..544a995 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,7 @@ name = "cache_utils" version = "0.1.0" dependencies = [ "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "polling_serial 0.1.0", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -88,7 +89,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -115,7 +116,7 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -190,7 +191,7 @@ dependencies = [ "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" "checksum linked_list_allocator 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5825aea823c659d0fdcdbe8c9b78baf56f3a10365d783db874f6d360df72626f" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" diff --git a/cache_utils/Cargo.toml b/cache_utils/Cargo.toml index 276dfd1..7092c51 100644 --- a/cache_utils/Cargo.toml +++ b/cache_utils/Cargo.toml @@ -14,10 +14,10 @@ static_assertions = "1.1.0" itertools = { version = "0.9.0", default-features = false } nix = { version = "0.17.0", optional = true } - +libc = { version = "0.2.69", optional = true } [features] -std = ["nix", "itertools/use_std"] +std = ["nix", "itertools/use_std", "libc"] no_std = ["polling_serial", "vga_buffer"] default = ["std"] diff --git a/cache_utils/src/bin/frequency_test.rs b/cache_utils/src/bin/frequency_test.rs new file mode 100644 index 0000000..f533819 --- /dev/null +++ b/cache_utils/src/bin/frequency_test.rs @@ -0,0 +1,64 @@ +use cache_utils::frequency::get_freq_cpufreq_kernel; +use cache_utils::rdtsc_fence; +use libc::sched_getcpu; +use nix::sched::{sched_setaffinity, CpuSet}; +use nix::unistd::Pid; +use static_assertions::_core::time::Duration; +use std::thread::sleep; +use std::time::Instant; + +const NUM_SAMPLE: i32 = 1000000; +const NUM_SAMPLE_SLEEP: i32 = 10000; + +pub fn main() { + let mut core = CpuSet::new(); + core.set(unsafe { sched_getcpu() } as usize).unwrap(); + + sched_setaffinity(Pid::from_raw(0), &core); + let t0_pre = unsafe { rdtsc_fence() }; + let start = Instant::now(); + let t0_post = unsafe { rdtsc_fence() }; + + let mut tsc = t0_post; + + println!("TSC,Freq"); + for i in 0..NUM_SAMPLE { + //let t1 = unsafe { rdtsc_fence() }; + let frequency = get_freq_cpufreq_kernel(); + let t2 = unsafe { rdtsc_fence() }; + let delta = t2 - tsc; + tsc = t2; + if let Ok(freq) = frequency { + println!("{},{}", delta, freq); + } + } + println!("Idling"); + for i in 0..NUM_SAMPLE_SLEEP { + sleep(Duration::from_micros(1000)); + let frequency = get_freq_cpufreq_kernel(); + let t2 = unsafe { rdtsc_fence() }; + let delta = t2 - tsc; + tsc = t2; + if let Ok(freq) = frequency { + println!("{},{}", delta, freq); + } + } + + let tf_pre = unsafe { rdtsc_fence() }; + let elapsed = start.elapsed(); + let tf_post = unsafe { rdtsc_fence() }; + println!( + "Time elapsed: {} us, number of tsc tick: {} - {} - {}", + elapsed.as_micros(), + tf_pre - t0_pre, + (tf_pre - t0_pre + tf_post - t0_post) / 2, + tf_post - t0_post + ); + eprintln!( + "Time elapsed: {} us, number of tsc tick: {} - {} - {}", + elapsed.as_micros(), + tf_pre - t0_pre, + (tf_pre - t0_pre + tf_post - t0_post) / 2, + tf_post - t0_post + ); +} diff --git a/cache_utils/src/calibration.rs b/cache_utils/src/calibration.rs index c0e1c93..c709183 100644 --- a/cache_utils/src/calibration.rs +++ b/cache_utils/src/calibration.rs @@ -14,6 +14,12 @@ pub enum Verbosity { Debug, } +pub struct HistParams { + iterations: u32, + bucket_size: usize, + bucket_number: usize, +} + extern crate alloc; use crate::calibration::Verbosity::*; use crate::complex_addressing::AddressHasher; @@ -165,7 +171,7 @@ pub fn calibrate_flush( panic!("not aligned nicely"); } - calibrate_impl( + calibrate_impl_fixed_freq( pointer, cache_line_size, array.len() as isize, @@ -181,10 +187,13 @@ pub fn calibrate_flush( display_name: "clflush miss", }, ], - CFLUSH_BUCKET_NUMBER, - CFLUSH_BUCKET_SIZE, - CFLUSH_NUM_ITER, + HistParams { + bucket_number: CFLUSH_BUCKET_NUMBER, + bucket_size: CFLUSH_BUCKET_SIZE, + iterations: CFLUSH_NUM_ITER, + }, verbose_level, + None, ) } @@ -213,39 +222,41 @@ pub unsafe fn calibrate( num_iterations: u32, verbosity_level: Verbosity, ) -> Vec { - calibrate_impl( + calibrate_impl_fixed_freq( p, increment, len, operations, - buckets_num, - bucket_size, - num_iterations, + HistParams { + bucket_number: buckets_num, + bucket_size, + iterations: num_iterations, + }, verbosity_level, + None, ) } const SPURIOUS_THRESHOLD: u32 = 1; -fn calibrate_impl( +fn calibrate_impl_fixed_freq( p: *const u8, increment: usize, len: isize, operations: &[CalibrateOperation], - buckets_num: usize, - bucket_size: usize, - num_iterations: u32, + hist_params: HistParams, verbosity_level: Verbosity, + hasher: Option<&AddressHasher>, ) -> Vec { // TODO : adapt this to detect CPU generation and grab the correct masks. // These are the skylake masks. - let masks: [usize; 3] = [ - 0b1111_0011_0011_0011_0010_0100_1100_0100_000000, - 0b1011_1010_1101_0111_1110_1010_1010_0010_000000, - 0b0110_1101_0111_1101_0101_1101_0101_0001_000000, - ]; - - let hasher = AddressHasher::new(&masks); + /*let masks: [usize; 3] = [ + 0b1111_0011_0011_0011_0010_0100_1100_0100_000000, + 0b1011_1010_1101_0111_1110_1010_1010_0010_000000, + 0b0110_1101_0111_1101_0101_1101_0101_0001_000000, + ]; + let hasher = AddressHasher::new(&masks); + */ if verbosity_level >= Thresholds { println!( "Calibrating {}...", @@ -256,12 +267,16 @@ fn calibrate_impl( ); } - let to_bucket = |time: u64| -> usize { time as usize / bucket_size }; - let from_bucket = |bucket: usize| -> u64 { (bucket * bucket_size) as u64 }; + let to_bucket = |time: u64| -> usize { time as usize / hist_params.bucket_size }; + let from_bucket = |bucket: usize| -> u64 { (bucket * hist_params.bucket_size) as u64 }; let mut ret = Vec::new(); if verbosity_level >= Thresholds { + print!("CSV: address, "); + if hasher.is_some() { + print!("hash, "); + } println!( - "CSV: address, hash, {} min, {} median, {} max", + "{} min, {} median, {} max", operations .iter() .map(|operation| operation.name) @@ -277,8 +292,12 @@ fn calibrate_impl( ); } if verbosity_level >= RawResult { + print!("RESULT:address,"); + if hasher.is_some() { + print!("hash,"); + } println!( - "RESULT:address,hash,time,{}", + "time,{}", operations .iter() .map(|operation| operation.name) @@ -288,10 +307,14 @@ fn calibrate_impl( for i in (0..len).step_by(increment) { let pointer = unsafe { p.offset(i) }; - let hash = hasher.hash(pointer as usize); + let hash = hasher.map(|h| h.hash(pointer as usize)); if verbosity_level >= Thresholds { - println!("Calibration for {:p} (hash: {:x})", pointer, hash); + print!("Calibration for {:p}", pointer); + if let Some(h) = hash { + print!(" (hash: {:x})", h) + } + println!(); } // TODO add some useful impl to CalibrateResults @@ -305,10 +328,10 @@ fn calibrate_impl( calibrate_result.histogram.reserve(operations.len()); for op in operations { - let mut hist = vec![0; buckets_num]; - for _ in 0..num_iterations { + let mut hist = vec![0; hist_params.bucket_number]; + for _ in 0..hist_params.iterations { let time = unsafe { (op.op)(pointer) }; - let bucket = min(buckets_num - 1, to_bucket(time)); + let bucket = min(hist_params.bucket_number - 1, to_bucket(time)); hist[bucket] += 1; } calibrate_result.histogram.push(hist); @@ -319,12 +342,16 @@ fn calibrate_impl( let median_thresholds: Vec = calibrate_result .histogram .iter() - .map(|h| (num_iterations - h[buckets_num - 1]) / 2) + .map(|h| (hist_params.iterations - h[hist_params.bucket_number - 1]) / 2) .collect(); - for j in 0..buckets_num - 1 { + for j in 0..hist_params.bucket_number - 1 { if verbosity_level >= RawResult { - print!("RESULT:{:p},{:x},{}", pointer, hash, from_bucket(j)); + print!("RESULT:{:p},", pointer); + if let Some(h) = hash { + print!("{:x},", h); + } + print!("{}", from_bucket(j)); } // ignore the last bucket : spurious context switches etc. for op in 0..operations.len() { @@ -367,10 +394,12 @@ fn calibrate_impl( calibrate_result.max[j] ); } + print!("CSV: {:p}, ", pointer); + if let Some(h) = hash { + print!("{:x}, ", h) + } println!( - "CSV: {:p}, {:x}, {}, {}, {}", - pointer, - hash, + "{}, {}, {}", calibrate_result.min.iter().format(", "), calibrate_result.median.iter().format(", "), calibrate_result.max.iter().format(", ") @@ -392,7 +421,7 @@ pub fn calibrate_L3_miss_hit( } let pointer = (&array[0]) as *const u8; - let r = calibrate_impl( + let r = calibrate_impl_fixed_freq( pointer, cache_line_size, array.len() as isize, @@ -401,10 +430,13 @@ pub fn calibrate_L3_miss_hit( name: "l3_hit", display_name: "L3 hit", }], - 512, - 2, - 1 << 11, + HistParams { + bucket_number: 512, + bucket_size: 2, + iterations: 1 << 11, + }, verbose_level, + None, ); r.into_iter().next().unwrap() diff --git a/cache_utils/src/frequency.rs b/cache_utils/src/frequency.rs new file mode 100644 index 0000000..a27fcbc --- /dev/null +++ b/cache_utils/src/frequency.rs @@ -0,0 +1,67 @@ +use crate::frequency::Error::{Unimplemented, UnsupportedPlatform}; + +use crate::rdtsc_fence; +#[cfg(all(target_os = "linux", feature = "std"))] +use libc::sched_getcpu; +#[cfg(all(target_os = "linux", feature = "std"))] +use std::convert::TryInto; +#[cfg(all(target_os = "linux", feature = "std"))] +use std::os::raw::{c_uint, c_ulong}; + +pub enum Error { + InsufficentPrivileges, + UnsupportedPlatform, + Unimplemented, +} + +#[cfg(all(target_os = "linux", feature = "std"))] +#[link(name = "cpupower")] +extern "C" { + //unsigned long cpufreq_get_freq_kernel(unsigned int cpu); + fn cpufreq_get_freq_kernel(cpu: c_uint) -> c_ulong; +} + +pub fn get_freq_cpufreq_kernel() -> Result { + // TODO Add memorization + return match unsafe { sched_getcpu() }.try_into() { + Ok(cpu) => Ok(unsafe { cpufreq_get_freq_kernel(cpu) }), + Err(e) => Err(Unimplemented), + }; +} + +pub fn get_frequency() -> Result { + if cfg!(target_os = "linux") && cfg!(feature = "std") { + return get_freq_cpufreq_kernel(); + } + + if cfg!(target_os = "none") { + // TODO check CPL + // if sufficient privileges use rdmsr + // Otherwise return insufficent privileges + return Err(Unimplemented); + } + Err(UnsupportedPlatform) +} + +pub fn get_frequency_change_period(period: u64) -> Result { + let mut t: u64 = 0; + let mut freq: u64 = 0; + let mut last_freq_change: u64 = 0; + for _ in 0..period { + let f = get_frequency(); + let time = unsafe { rdtsc_fence() }; + match f { + Ok(f) => { + if f != freq { + t += time - last_freq_change; + last_freq_change = time; + freq = f; + } + } + Err(e) => { + return Err(e); + } + } + } + return Ok(t / period); +} diff --git a/cache_utils/src/lib.rs b/cache_utils/src/lib.rs index a48a6e4..9ccdfd5 100644 --- a/cache_utils/src/lib.rs +++ b/cache_utils/src/lib.rs @@ -19,6 +19,8 @@ pub mod complex_addressing; pub mod mmap; pub mod prefetcher; +pub mod frequency; + use core::arch::x86_64 as arch_x86; use core::ptr; diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs index dcc1864..ed289d3 100644 --- a/tests/basic_boot.rs +++ b/tests/basic_boot.rs @@ -8,7 +8,6 @@ use core::panic::PanicInfo; use polling_serial::{serial_print, serial_println}; use vga_buffer::println; - use bootloader::{entry_point, BootInfo}; entry_point!(test_kernel_main); diff --git a/tests/panic_test.rs b/tests/panic_test.rs index 1bcf2fa..16deb37 100644 --- a/tests/panic_test.rs +++ b/tests/panic_test.rs @@ -27,5 +27,4 @@ fn should_fail() { fn panic(_info: &PanicInfo) -> ! { serial_println!("[ok]"); exit_qemu(QemuExitCode::Success); - } diff --git a/tests/sse_interrupt_test.rs b/tests/sse_interrupt_test.rs index 026c52d..b7b26ac 100644 --- a/tests/sse_interrupt_test.rs +++ b/tests/sse_interrupt_test.rs @@ -16,7 +16,7 @@ use core::panic::PanicInfo; use lazy_static::lazy_static; use polling_serial::{serial_print, serial_println}; -use vga_buffer::{println}; +use vga_buffer::println; use volatile::Volatile; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};