#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[derive(Deserialize, Serialize)]
#[repr(transparent)]
pub struct HyperThread(u16);
bit_set_aware!(HyperThread);
impl Into<u16> for HyperThread
{
#[inline(always)]
fn into(self) -> u16
{
self.0
}
}
impl BitSetAware for HyperThread
{
const LinuxMaximum: u32 = 8192;
const InclusiveMinimum: Self = Self(0);
const InclusiveMaximum: Self = Self((Self::LinuxMaximum - 1) as u16);
const Prefix: &'static [u8] = b"cpu";
#[inline(always)]
fn from_validated_u16(value: u16) -> Self
{
debug_assert!((value as u32) < Self::LinuxMaximum);
Self(value)
}
}
impl HyperThread
{
#[inline(always)]
pub fn is_hyper_threading_active(sys_path: &SysPath) -> Option<bool>
{
let file_path = sys_path.hyper_thread_smt_file_path("active");
if file_path.exists()
{
Some(file_path.read_zero_or_one_bool().unwrap())
}
else
{
None
}
}
#[inline(always)]
pub fn hyper_threading_control(sys_path: &SysPath) -> Option<HyperThreadingStatus>
{
let file_path = HyperThread::smt_control_file_path(sys_path);
if file_path.exists()
{
Some(file_path.read_value().unwrap())
}
else
{
None
}
}
#[inline(always)]
pub fn try_to_enable_hyper_threading(sys_path: &SysPath, current_status: HyperThreadingStatus) -> HyperThreadingStatus
{
use self::HyperThreadingStatus::*;
match current_status
{
On | ForceOff | NotSupported | NotImplemented => current_status,
Off =>
{
Self::smt_control_file_path(sys_path).write_value(b"on\n" as &[u8]).unwrap();
Self::hyper_threading_control(sys_path).unwrap()
}
}
}
#[inline(always)]
pub fn try_to_disable_hyper_threading(sys_path: &SysPath, current_status: HyperThreadingStatus) -> HyperThreadingStatus
{
use self::HyperThreadingStatus::*;
match current_status
{
Off | ForceOff | NotSupported | NotImplemented => current_status,
On =>
{
Self::smt_control_file_path(sys_path).write_value(b"off\n" as &[u8]).unwrap();
Self::hyper_threading_control(sys_path).unwrap()
}
}
}
#[inline(always)]
pub fn current() -> (NumaNode, Self)
{
current_numa_node_and_hyper_thread()
}
#[deprecated]
pub fn current_hyper_thread() -> Self
{
let result = unsafe { sched_getcpu() };
debug_assert!(result >= 0, "sched_getcpu() was negative");
debug_assert!(result <= u16::MAX as i32, "sched_getcpu() was too large");
let result = result as u16;
debug_assert!((result as u32) < Self::LinuxMaximum);
Self(result as u16)
}
#[inline(always)]
pub fn kernel_exclusive_maximum(sys_path: &SysPath) -> u16
{
let value: u16 = sys_path.hyper_threads_folder_path("kernel_max").read_value().unwrap();
let this = Self::try_from(value).unwrap();
this.0 + 1
}
#[inline(always)]
pub fn sysconf_exclusive_maximum() -> u16
{
let result = unsafe { sysconf(_SC_NPROCESSORS_CONF) };
if result <= 0
{
1
}
else if result > Self::LinuxMaximum as c_long
{
Self::LinuxMaximum as u16
}
else
{
result as u16
}
}
#[inline(always)]
pub fn isolated(&self, linux_kernel_command_line: &LinuxKernelCommandLineParameters, isolated_cpus_required: bool) -> Result<HyperThreads, &'static str>
{
if let Some((isolated_cpu_flags, isolated_cpus)) = linux_kernel_command_line.isolcpus()
{
if !isolated_cpu_flags.contains(&IsolatedCpuFlags::Domain)
{
return Err("Kernel parameter `isolcpus` does not contain (or imply) the domain flag")
}
let rcu_nocbs = linux_kernel_command_line.rcu_nocbs().ok_or("Kernel parameter `rcu_nocbs` should be specified because isolcpus was specified")?;
let nohz_full = linux_kernel_command_line.nohz_full().ok_or("Kernel parameter `nohz_full` should be specified because isolcpus was specified")?;
if isolated_cpus != rcu_nocbs
{
return Err("Kernel parameters `isolcpus` and `rcu_nocbs` should match")
}
if isolated_cpus != nohz_full
{
return Err("Kernel parameters `isolcpus` and `nohz_full` should match")
}
Ok(isolated_cpus)
}
else
{
if isolated_cpus_required
{
return Err("Kernel parameter `isolcpus` should be specified")
}
else
{
Ok(HyperThreads(BitSet::empty()))
}
}
}
#[inline(always)]
pub fn numa_node(self, sys_path: &SysPath) -> Option<NumaNode>
{
match sys_path.hyper_thread_file_path(self, "node").canonicalize()
{
Err(_) => None,
Ok(canonical) => match canonical.file_name()
{
None => None,
Some(file_name) => NumaNode::parse_file_name(file_name).unwrap(),
},
}
}
#[inline(always)]
pub(crate) fn is_online(self, sys_path: &SysPath) -> io::Result<bool>
{
let file_path = self.online_file_path(sys_path);
if !file_path.exists()
{
return Ok(true)
}
file_path.read_zero_or_one_bool()
}
#[inline(always)]
pub fn set_online(self, sys_path: &SysPath, online: bool) -> io::Result<()>
{
assert_effective_user_id_is_root(&format!("Online/Offline HyperThread '{}'", self.0));
self.online_file_path(sys_path).write_value(online)
}
#[inline(always)]
pub fn siblings(self, sys_path: &SysPath) -> io::Result<HyperThreads>
{
sys_path.hyper_thread_topology_file_path(self, "core_siblings_list").read_hyper_thread_or_numa_node_list().map(HyperThreads)
}
#[inline(always)]
pub fn thread_siblings(self, sys_path: &SysPath) -> io::Result<HyperThreads>
{
sys_path.hyper_thread_topology_file_path(self, "thread_siblings_list").read_hyper_thread_or_numa_node_list().map(HyperThreads)
}
#[inline(always)]
pub fn level1_cache_hyper_thread_siblings_including_self(self, sys_path: &SysPath) -> io::Result<HyperThreads>
{
sys_path.hyper_thread_cache_file_path(self, "index0/shared_cpu_list").read_hyper_thread_or_numa_node_list().map(HyperThreads)
}
#[inline(always)]
pub fn level1_cache_hyper_thread_siblings_excluding_self(self, sys_path: &SysPath) -> io::Result<HyperThreads>
{
let mut hyper_threads = self.level1_cache_hyper_thread_siblings_including_self(sys_path)?.0;
hyper_threads.remove(self);
Ok(HyperThreads(hyper_threads))
}
#[inline(always)]
pub fn underlying_hardware_physical_core_identifier(self, sys_path: &SysPath) -> io::Result<u16>
{
sys_path.hyper_thread_topology_file_path(self, "core_id").read_value()
}
#[inline(always)]
pub fn underlying_hardware_physical_socket_identifier(self, sys_path: &SysPath) -> io::Result<u16>
{
sys_path.hyper_thread_topology_file_path(self, "physical_package_id").read_value()
}
#[inline(always)]
fn online_file_path(self, sys_path: &SysPath) -> PathBuf
{
sys_path.hyper_thread_file_path(self, "online")
}
#[inline(always)]
fn smt_control_file_path(sys_path: &SysPath) -> PathBuf
{
sys_path.hyper_thread_smt_file_path("control")
}
}