// Copyright 2019 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Wrappers for CPU affinity functions.56use std::iter::FromIterator;7use std::mem;89use libc::cpu_set_t;10use libc::prctl;11use libc::sched_getaffinity;12use libc::sched_setaffinity;13use libc::CPU_ISSET;14use libc::CPU_SET;15use libc::CPU_SETSIZE;16use libc::CPU_ZERO;17use libc::EINVAL;1819use super::Error;20use super::Result;2122// This is needed because otherwise the compiler will complain that the23// impl doesn't reference any types from inside this crate.24struct CpuSet(cpu_set_t);2526impl CpuSet {27pub fn new() -> CpuSet {28// SAFETY:29// cpu_set_t is a C struct and can be safely initialized with zeroed memory.30let mut cpuset: cpu_set_t = unsafe { mem::MaybeUninit::zeroed().assume_init() };31// SAFETY:32// Safe because we pass a valid cpuset pointer.33unsafe { CPU_ZERO(&mut cpuset) };34CpuSet(cpuset)35}3637#[allow(clippy::unnecessary_cast)]38pub fn to_cpus(&self) -> Vec<usize> {39let mut cpus = Vec::new();40for i in 0..(CPU_SETSIZE as usize) {41// SAFETY: Safe because `i` and `self.0` are valid.42if unsafe { CPU_ISSET(i, &self.0) } {43cpus.push(i);44}45}46cpus47}48}4950impl FromIterator<usize> for CpuSet {51fn from_iter<I: IntoIterator<Item = usize>>(cpus: I) -> Self {52let mut cpuset = CpuSet::new();53for cpu in cpus {54// SAFETY:55// Safe because we pass a valid cpu index and cpuset.0 is a valid pointer.56unsafe { CPU_SET(cpu, &mut cpuset.0) };57}58cpuset59}60}6162/// Set the CPU affinity of the current thread to a given set of CPUs.63///64/// # Examples65///66/// Set the calling thread's CPU affinity so it will run on only CPUs67/// 0, 1, 5, and 6.68///69/// ```70/// # use base::linux::set_cpu_affinity;71/// set_cpu_affinity(vec![0, 1, 5, 6]).unwrap();72/// ```73#[allow(clippy::unnecessary_cast)]74pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<()> {75let CpuSet(cpuset) = cpus76.into_iter()77.map(|cpu| {78if cpu < CPU_SETSIZE as usize {79Ok(cpu)80} else {81Err(Error::new(EINVAL))82}83})84.collect::<Result<CpuSet>>()?;8586// SAFETY:87// Safe because we pass 0 for the current thread, and cpuset is a valid pointer and only88// used for the duration of this call.89crate::syscall!(unsafe { sched_setaffinity(0, mem::size_of_val(&cpuset), &cpuset) })?;9091Ok(())92}9394pub fn get_cpu_affinity() -> Result<Vec<usize>> {95let mut cpu_set = CpuSet::new();9697// SAFETY:98// Safe because we pass 0 for the current thread, and cpu_set.0 is a valid pointer and only99// used for the duration of this call.100crate::syscall!(unsafe { sched_getaffinity(0, mem::size_of_val(&cpu_set.0), &mut cpu_set.0) })?;101102Ok(cpu_set.to_cpus())103}104105/// Enable experimental core scheduling for the current thread.106///107/// If successful, the kernel should not schedule this thread with any other thread within the same108/// SMT core. Because this is experimental, this will return success on kernels which do not support109/// this function.110pub fn enable_core_scheduling() -> Result<()> {111const PR_SCHED_CORE: i32 = 62;112const PR_SCHED_CORE_CREATE: i32 = 1;113114#[allow(clippy::upper_case_acronyms, non_camel_case_types, dead_code)]115/// Specifies the scope of the pid parameter of `PR_SCHED_CORE`.116enum pid_type {117/// `PID` refers to threads.118PIDTYPE_PID,119/// `TGPID` refers to a process.120PIDTYPE_TGID,121/// `TGPID` refers to a process group.122PIDTYPE_PGID,123}124125// SAFETY: Safe because we check the return value to prctl.126let ret = unsafe {127prctl(128PR_SCHED_CORE,129PR_SCHED_CORE_CREATE,1300, // id of target task, 0 indicates current task131pid_type::PIDTYPE_PID as i32, // PID scopes to this thread only1320, // ignored by PR_SCHED_CORE_CREATE command133)134};135if ret == -1 {136let error = Error::last();137// prctl returns EINVAL for unknown functions, which we will ignore for now.138if error.errno() != libc::EINVAL {139return Err(error);140}141}142Ok(())143}144145146