use crate::{Error, Result};
pub fn pin_to_core(core_id: usize) -> Result<()> {
#[cfg(target_os = "linux")]
{
let cpu_set_words = usize::try_from(libc::CPU_SETSIZE)
.unwrap_or(1024)
.div_ceil(usize::BITS as usize);
let max_core = cpu_set_words * usize::BITS as usize;
if core_id >= max_core {
return Err(Error::System {
operation: "sched_setaffinity",
source: std::io::Error::from(std::io::ErrorKind::InvalidInput),
});
}
let mut mask = vec![0usize; cpu_set_words];
mask[core_id / usize::BITS as usize] |= 1usize << (core_id % usize::BITS as usize);
let result = unsafe {
libc::sched_setaffinity(
0,
std::mem::size_of_val(mask.as_slice()),
mask.as_ptr().cast::<libc::cpu_set_t>(),
)
};
if result != 0 {
return Err(Error::System {
operation: "sched_setaffinity",
source: std::io::Error::last_os_error(),
});
}
}
#[cfg(not(target_os = "linux"))]
{
let _ = core_id;
}
Ok(())
}
fn parse_irq_affinity(content: &str) -> Result<Vec<u32>> {
let mut cores = Vec::new();
for (group_idx, chunk) in content.trim().split(',').rev().enumerate() {
let mask = u64::from_str_radix(chunk.trim(), 16).map_err(|_| Error::System {
operation: "read_irq_affinity",
source: std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("malformed affinity mask chunk: {chunk}"),
),
})?;
for bit in 0..32 {
if mask & (1 << bit) != 0 {
let core = u32::try_from(group_idx * 32 + bit).map_err(|_| Error::System {
operation: "read_irq_affinity",
source: std::io::Error::new(
std::io::ErrorKind::InvalidData,
"affinity mask core index overflow",
),
})?;
cores.push(core);
}
}
}
Ok(cores)
}
pub fn read_irq_affinity(irq: u32) -> Result<Vec<u32>> {
#[cfg(target_os = "linux")]
{
let path = format!("/proc/irq/{irq}/smp_affinity");
let content = std::fs::read_to_string(&path).map_err(|source| Error::System {
operation: "read_irq_affinity",
source,
})?;
parse_irq_affinity(&content)
}
#[cfg(not(target_os = "linux"))]
{
let _ = irq;
Ok(Vec::new())
}
}
pub fn pin_to_numa_node(node: u32) -> Result<()> {
#[cfg(target_os = "linux")]
{
crate::numa::pin_to_node(node)
}
#[cfg(not(target_os = "linux"))]
{
let _ = node;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{parse_irq_affinity, pin_to_core, pin_to_numa_node};
#[test]
fn affinity_helpers_are_non_fatal() {
let _ = pin_to_core(0);
let _ = pin_to_numa_node(0);
}
#[test]
fn parse_irq_affinity_handles_single_32bit_group() {
let cores = parse_irq_affinity("80000003").unwrap();
assert_eq!(cores, vec![0, 1, 31]);
}
#[test]
fn parse_irq_affinity_handles_multiple_32bit_groups() {
let cores = parse_irq_affinity("00000001,00000003").unwrap();
assert_eq!(cores, vec![0, 1, 32]);
}
#[test]
fn parse_irq_affinity_handles_96_cores() {
let cores = parse_irq_affinity("00000001,00000000,80000001").unwrap();
assert_eq!(cores, vec![0, 31, 64]);
}
}