Skip to content

Commit 5f0a05b

Browse files
Add getcpu system call support for getting a NUMA node of the current thread
1 parent 3832726 commit 5f0a05b

4 files changed

Lines changed: 102 additions & 10 deletions

File tree

src/backend/linux_raw/thread/syscalls.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,40 @@ pub(crate) fn setgroups_thread(gids: &[crate::ugid::Gid]) -> io::Result<()> {
457457
))]
458458
pub(crate) use crate::backend::vdso_wrappers::sched_getcpu;
459459

460+
// `getcpu` has special optimizations via the vDSO on some architectures.
461+
#[cfg(any(
462+
target_arch = "x86_64",
463+
target_arch = "x86",
464+
target_arch = "riscv64",
465+
target_arch = "powerpc",
466+
target_arch = "powerpc64",
467+
target_arch = "s390x"
468+
))]
469+
pub(crate) use crate::backend::vdso_wrappers::getcpu;
470+
471+
// `getcpu` on platforms without a vDSO entry for it.
472+
#[cfg(not(any(
473+
target_arch = "x86_64",
474+
target_arch = "x86",
475+
target_arch = "riscv64",
476+
target_arch = "powerpc",
477+
target_arch = "powerpc64",
478+
target_arch = "s390x"
479+
)))]
480+
#[inline]
481+
pub(crate) fn getcpu() -> (usize, usize) {
482+
let mut cpu = MaybeUninit::<u32>::uninit();
483+
let mut numa_node = MaybeUninit::<u32>::uninit();
484+
485+
unsafe {
486+
let r = ret(syscall!(__NR_getcpu, &mut cpu, &mut numa_node, zero()));
487+
488+
debug_assert!(r.is_ok());
489+
490+
(cpu.assume_init() as usize, numa_node.assume_init() as usize)
491+
}
492+
}
493+
460494
// `sched_getcpu` on platforms without a vDSO entry for it.
461495
#[cfg(not(any(
462496
target_arch = "x86_64",
@@ -468,6 +502,9 @@ pub(crate) use crate::backend::vdso_wrappers::sched_getcpu;
468502
)))]
469503
#[inline]
470504
pub(crate) fn sched_getcpu() -> usize {
505+
// We should not implement this function by using the `getcpu` function definded above
506+
// because we want to provide exactly one pointer to the system call.
507+
471508
let mut cpu = MaybeUninit::<u32>::uninit();
472509
unsafe {
473510
let r = ret(syscall!(__NR_getcpu, &mut cpu, zero(), zero()));

src/backend/linux_raw/vdso_wrappers.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,35 @@ pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timesp
117117
}
118118
}
119119

120+
#[cfg(feature = "thread")]
121+
#[cfg(any(
122+
target_arch = "x86_64",
123+
target_arch = "x86",
124+
target_arch = "riscv64",
125+
target_arch = "powerpc",
126+
target_arch = "powerpc64",
127+
target_arch = "s390x",
128+
))]
129+
#[inline]
130+
pub(crate) fn getcpu() -> (usize, usize) {
131+
// SAFETY: `GETCPU` contains either null or the address of a function with
132+
// an ABI like libc `getcpu`, and calling it has the side effect of writing
133+
// to the result buffers, and no others.
134+
unsafe {
135+
let mut cpu = MaybeUninit::<u32>::uninit();
136+
let mut numa_node = MaybeUninit::<u32>::uninit();
137+
let callee = match transmute(GETCPU.load(Relaxed)) {
138+
Some(callee) => callee,
139+
None => init_getcpu(),
140+
};
141+
let r0 = callee(cpu.as_mut_ptr(), numa_node.as_mut_ptr(), null_mut());
142+
143+
debug_assert_eq!(r0, 0);
144+
145+
(cpu.assume_init() as usize, numa_node.assume_init() as usize)
146+
}
147+
}
148+
120149
#[cfg(feature = "thread")]
121150
#[cfg(any(
122151
target_arch = "x86_64",
@@ -128,6 +157,9 @@ pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timesp
128157
))]
129158
#[inline]
130159
pub(crate) fn sched_getcpu() -> usize {
160+
// We should not implement this function by using the `getcpu` function definded above
161+
// because we want to provide exactly one pointer to the system call.
162+
131163
// SAFETY: `GETCPU` contains either null or the address of a function with
132164
// an ABI like libc `getcpu`, and calling it has the side effect of writing
133165
// to the result buffers, and no others.
@@ -308,6 +340,7 @@ fn init_clock_gettime() -> ClockGettimeType {
308340
target_arch = "s390x",
309341
))]
310342
#[cold]
343+
#[inline(never)]
311344
fn init_getcpu() -> GetcpuType {
312345
init();
313346
// SAFETY: Load the function address from static storage that we just

src/buffer.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,8 @@ mod tests {
381381
let nread = read(&input, &mut buf).unwrap();
382382
assert_eq!(nread, buf.len());
383383
assert_eq!(
384-
&buf[..58],
385-
b"//! Utilities for functions that return data via buffers.\n"
384+
&buf[..57],
385+
b"//! Utilities for functions that return data via buffers."
386386
);
387387
input.seek(SeekFrom::End(-1)).unwrap();
388388
let nread = read(&input, &mut buf).unwrap();
@@ -407,13 +407,13 @@ mod tests {
407407
let (init, uninit) = read(&input, &mut buf).unwrap();
408408
assert_eq!(uninit.len(), 0);
409409
assert_eq!(
410-
&init[..58],
411-
b"//! Utilities for functions that return data via buffers.\n"
410+
&init[..57],
411+
b"//! Utilities for functions that return data via buffers."
412412
);
413413
assert_eq!(init.len(), buf.len());
414414
assert_eq!(
415-
unsafe { core::mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(&mut buf[..58]) },
416-
b"//! Utilities for functions that return data via buffers.\n"
415+
unsafe { core::mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(&mut buf[..57]) },
416+
b"//! Utilities for functions that return data via buffers."
417417
);
418418
input.seek(SeekFrom::End(-1)).unwrap();
419419
let (init, uninit) = read(&input, &mut buf).unwrap();
@@ -440,8 +440,8 @@ mod tests {
440440
assert_eq!(nread, buf.capacity());
441441
assert_eq!(nread, buf.len());
442442
assert_eq!(
443-
&buf[..58],
444-
b"//! Utilities for functions that return data via buffers.\n"
443+
&buf[..57],
444+
b"//! Utilities for functions that return data via buffers."
445445
);
446446
buf.clear();
447447
input.seek(SeekFrom::End(-1)).unwrap();

src/thread/sched.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use core::{fmt, hash};
1111
/// - [Linux]
1212
///
1313
/// [Linux]: https://man7.org/linux/man-pages/man3/CPU_SET.3.html
14-
/// [`sched_setaffinity`]: crate::thread::sched_setaffinity
15-
/// [`sched_getaffinity`]: crate::thread::sched_getaffinity
14+
/// [`sched_setaffinity`]: sched_setaffinity
15+
/// [`sched_getaffinity`]: sched_getaffinity
1616
#[repr(transparent)]
1717
#[derive(Clone, Copy)]
1818
pub struct CpuSet {
@@ -159,3 +159,25 @@ pub fn sched_getaffinity(pid: Option<Pid>) -> io::Result<CpuSet> {
159159
pub fn sched_getcpu() -> usize {
160160
backend::thread::syscalls::sched_getcpu()
161161
}
162+
163+
/// `sched_getcpu()`—Get the CPU and NUMA node that the current thread is currently on.
164+
///
165+
/// # Example
166+
///
167+
/// ```rust
168+
/// use rustix::thread::getcpu;
169+
///
170+
/// let (core, numa_node) = getcpu();
171+
///
172+
/// println!("The current thread was on the {core} core and {numa_node} numa node.");
173+
/// ```
174+
///
175+
/// # References
176+
/// - [Linux]
177+
///
178+
/// [Linux]: https://man7.org/linux/man-pages/man2/getcpu.2.html
179+
#[cfg(linux_kernel)]
180+
#[inline]
181+
pub fn getcpu() -> (usize, usize) {
182+
backend::thread::syscalls::getcpu()
183+
}

0 commit comments

Comments
 (0)