@@ -14,6 +14,14 @@ use super::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0};
1414use super :: vdso;
1515#[ cfg( target_arch = "x86" ) ]
1616use core:: arch:: global_asm;
17+ #[ cfg( feature = "process" ) ]
18+ #[ cfg( any(
19+ target_arch = "x86_64" ,
20+ target_arch = "x86" ,
21+ target_arch = "riscv64" ,
22+ target_arch = "powerpc64"
23+ ) ) ]
24+ use core:: ffi:: c_void;
1725use core:: mem:: transmute;
1826use core:: ptr:: null_mut;
1927use core:: sync:: atomic:: AtomicPtr ;
@@ -96,6 +104,30 @@ pub(crate) fn clock_gettime_dynamic(which_clock: DynamicClockId<'_>) -> io::Resu
96104 }
97105}
98106
107+ #[ cfg( feature = "process" ) ]
108+ #[ cfg( any(
109+ target_arch = "x86_64" ,
110+ target_arch = "x86" ,
111+ target_arch = "riscv64" ,
112+ target_arch = "powerpc64"
113+ ) ) ]
114+ #[ inline]
115+ pub ( crate ) fn sched_getcpu ( ) -> usize {
116+ // SAFETY: `GETCPU` contains either null or the address of a function with
117+ // an ABI like libc `getcpu`, and calling it has the side effect of writing
118+ // to the result buffers, and no others.
119+ unsafe {
120+ let mut cpu = MaybeUninit :: < u32 > :: uninit ( ) ;
121+ let callee = match transmute ( GETCPU . load ( Relaxed ) ) {
122+ Some ( callee) => callee,
123+ None => init_getcpu ( ) ,
124+ } ;
125+ let r0 = callee ( cpu. as_mut_ptr ( ) , null_mut ( ) , null_mut ( ) ) ;
126+ debug_assert_eq ! ( r0, 0 ) ;
127+ cpu. assume_init ( ) as usize
128+ }
129+ }
130+
99131#[ cfg( target_arch = "x86" ) ]
100132pub ( super ) mod x86_via_vdso {
101133 use super :: { transmute, ArgReg , Relaxed , RetReg , SyscallNumber , A0 , A1 , A2 , A3 , A4 , A5 , R0 } ;
@@ -223,6 +255,15 @@ pub(super) mod x86_via_vdso {
223255#[ cfg( feature = "time" ) ]
224256type ClockGettimeType = unsafe extern "C" fn ( c:: c_int , * mut Timespec ) -> c:: c_int ;
225257
258+ #[ cfg( feature = "process" ) ]
259+ #[ cfg( any(
260+ target_arch = "x86_64" ,
261+ target_arch = "x86" ,
262+ target_arch = "riscv64" ,
263+ target_arch = "powerpc64"
264+ ) ) ]
265+ type GetcpuType = unsafe extern "C" fn ( * mut u32 , * mut u32 , * mut c_void ) -> c:: c_int ;
266+
226267/// The underlying syscall functions are only called from asm, using the
227268/// special syscall calling convention to pass arguments and return values,
228269/// which the signature here doesn't reflect.
@@ -239,6 +280,22 @@ fn init_clock_gettime() -> ClockGettimeType {
239280 unsafe { transmute ( CLOCK_GETTIME . load ( Relaxed ) ) }
240281}
241282
283+ /// Initialize `GETCPU` and return its value.
284+ #[ cfg( feature = "process" ) ]
285+ #[ cfg( any(
286+ target_arch = "x86_64" ,
287+ target_arch = "x86" ,
288+ target_arch = "riscv64" ,
289+ target_arch = "powerpc64"
290+ ) ) ]
291+ #[ cold]
292+ fn init_getcpu ( ) -> GetcpuType {
293+ init ( ) ;
294+ // SAFETY: Load the function address from static storage that we just
295+ // initialized.
296+ unsafe { transmute ( GETCPU . load ( Relaxed ) ) }
297+ }
298+
242299/// Initialize `SYSCALL` and return its value.
243300#[ cfg( target_arch = "x86" ) ]
244301#[ cold]
@@ -254,6 +311,14 @@ fn init_syscall() -> SyscallType {
254311struct Function ;
255312#[ cfg( feature = "time" ) ]
256313static mut CLOCK_GETTIME : AtomicPtr < Function > = AtomicPtr :: new ( null_mut ( ) ) ;
314+ #[ cfg( feature = "process" ) ]
315+ #[ cfg( any(
316+ target_arch = "x86_64" ,
317+ target_arch = "x86" ,
318+ target_arch = "riscv64" ,
319+ target_arch = "powerpc64"
320+ ) ) ]
321+ static mut GETCPU : AtomicPtr < Function > = AtomicPtr :: new ( null_mut ( ) ) ;
257322#[ cfg( target_arch = "x86" ) ]
258323static mut SYSCALL : AtomicPtr < Function > = AtomicPtr :: new ( null_mut ( ) ) ;
259324
@@ -315,6 +380,24 @@ unsafe fn _rustix_clock_gettime_via_syscall(
315380 ret ( syscall ! ( __NR_clock_gettime, c_int( clockid) , res) )
316381}
317382
383+ #[ cfg( feature = "process" ) ]
384+ #[ cfg( any(
385+ target_arch = "x86_64" ,
386+ target_arch = "x86" ,
387+ target_arch = "riscv64" ,
388+ target_arch = "powerpc64"
389+ ) ) ]
390+ unsafe extern "C" fn rustix_getcpu_via_syscall (
391+ cpu : * mut u32 ,
392+ node : * mut u32 ,
393+ unused : * mut c_void ,
394+ ) -> c:: c_int {
395+ match ret ( syscall ! ( __NR_getcpu, cpu, node, unused) ) {
396+ Ok ( ( ) ) => 0 ,
397+ Err ( err) => err. raw_os_error ( ) . wrapping_neg ( ) ,
398+ }
399+ }
400+
318401#[ cfg( target_arch = "x86" ) ]
319402extern "C" {
320403 /// A symbol pointing to an `int 0x80` instruction. This “function” is only
@@ -361,6 +444,24 @@ fn minimal_init() {
361444 . ok ( ) ;
362445 }
363446
447+ #[ cfg( feature = "process" ) ]
448+ #[ cfg( any(
449+ target_arch = "x86_64" ,
450+ target_arch = "x86" ,
451+ target_arch = "riscv64" ,
452+ target_arch = "powerpc64"
453+ ) ) ]
454+ {
455+ GETCPU
456+ . compare_exchange (
457+ null_mut ( ) ,
458+ rustix_getcpu_via_syscall as * mut Function ,
459+ Relaxed ,
460+ Relaxed ,
461+ )
462+ . ok ( ) ;
463+ }
464+
364465 #[ cfg( target_arch = "x86" ) ]
365466 {
366467 SYSCALL
@@ -430,6 +531,60 @@ fn init() {
430531 }
431532 }
432533
534+ #[ cfg( feature = "process" ) ]
535+ #[ cfg( any(
536+ target_arch = "x86_64" ,
537+ target_arch = "x86" ,
538+ target_arch = "riscv64" ,
539+ target_arch = "powerpc64"
540+ ) ) ]
541+ {
542+ // Look up the platform-specific `getcpu` symbol as documented
543+ // [here].
544+ //
545+ // [here]: https://man7.org/linux/man-pages/man7/vdso.7.html
546+ #[ cfg( target_arch = "x86_64" ) ]
547+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_getcpu" ) ) ;
548+ #[ cfg( target_arch = "x86" ) ]
549+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_getcpu" ) ) ;
550+ #[ cfg( target_arch = "riscv64" ) ]
551+ let ptr = vdso. sym ( cstr ! ( "LINUX_4.15" ) , cstr ! ( "__kernel_getcpu" ) ) ;
552+ #[ cfg( target_arch = "powerpc64" ) ]
553+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6.15" ) , cstr ! ( "__kernel_getcpu" ) ) ;
554+
555+ #[ cfg( any(
556+ target_arch = "x86_64" ,
557+ target_arch = "riscv64" ,
558+ target_arch = "powerpc64"
559+ ) ) ]
560+ let ok = true ;
561+
562+ // On 32-bit x86, the symbol doesn't appear present sometimes.
563+ #[ cfg( target_arch = "x86" ) ]
564+ let ok = !ptr. is_null ( ) ;
565+
566+ #[ cfg( any(
567+ target_arch = "aarch64" ,
568+ target_arch = "arm" ,
569+ target_arch = "mips" ,
570+ target_arch = "mips32r6" ,
571+ target_arch = "mips64" ,
572+ target_arch = "mips64r6"
573+ ) ) ]
574+ let ok = false ;
575+
576+ if ok {
577+ assert ! ( !ptr. is_null( ) ) ;
578+
579+ // SAFETY: Store the computed function addresses in static
580+ // storage so that we don't need to compute it again (but if
581+ // we do, it doesn't hurt anything).
582+ unsafe {
583+ GETCPU . store ( ptr. cast ( ) , Relaxed ) ;
584+ }
585+ }
586+ }
587+
433588 // On x86, also look up the vsyscall entry point.
434589 #[ cfg( target_arch = "x86" ) ]
435590 {
0 commit comments