From 6d900f3270425b66b75771b6bc119e5a39b3e057 Mon Sep 17 00:00:00 2001 From: wuzheng Date: Wed, 13 Sep 2023 11:34:42 +0800 Subject: [PATCH] implement syscall `syscall` and add new feature `random-hw` --- api/arceos_posix_api/Cargo.toml | 1 + api/arceos_posix_api/build.rs | 2 + api/arceos_posix_api/ctypes.h | 1 + api/arceos_posix_api/src/imp/io_mpx/mod.rs | 5 + api/arceos_posix_api/src/imp/io_mpx/poll.rs | 74 +++++++++ api/arceos_posix_api/src/imp/mod.rs | 2 +- api/arceos_posix_api/src/lib.rs | 2 + .../parallel/expect_info_smp4_fifo.out | 1 - .../pthread/parallel/expect_info_smp4_rr.out | 1 - apps/c/redis/features.txt | 1 + apps/task/parallel/expect_info_smp1_fifo.out | 1 - apps/task/parallel/expect_info_smp4_cfs.out | 1 - apps/task/parallel/expect_info_smp4_rr.out | 1 - scripts/make/features.mk | 4 +- ulib/axlibc/Cargo.toml | 2 + ulib/axlibc/c/poll.c | 18 --- ulib/axlibc/src/io_mpx.rs | 13 ++ ulib/axlibc/src/lib.rs | 4 +- ulib/axlibc/src/rand.rs | 144 +++++++++++++++--- 19 files changed, 234 insertions(+), 44 deletions(-) create mode 100644 api/arceos_posix_api/src/imp/io_mpx/poll.rs delete mode 100644 ulib/axlibc/c/poll.c diff --git a/api/arceos_posix_api/Cargo.toml b/api/arceos_posix_api/Cargo.toml index 03081419b..fcc1daace 100644 --- a/api/arceos_posix_api/Cargo.toml +++ b/api/arceos_posix_api/Cargo.toml @@ -26,6 +26,7 @@ net = ["dep:axnet", "axfeat/net", "fd"] pipe = ["fd"] select = ["fd"] epoll = ["fd"] +poll = ["fd"] [dependencies] # ArceOS modules diff --git a/api/arceos_posix_api/build.rs b/api/arceos_posix_api/build.rs index 46556bf37..4706fbee8 100644 --- a/api/arceos_posix_api/build.rs +++ b/api/arceos_posix_api/build.rs @@ -57,6 +57,8 @@ typedef struct {{ "pthread_attr_t", "pthread_mutex_t", "pthread_mutexattr_t", + "pollfd", + "nfds_t", "epoll_event", "iovec", "clockid_t", diff --git a/api/arceos_posix_api/ctypes.h b/api/arceos_posix_api/ctypes.h index b1131bced..49fa1b5b9 100644 --- a/api/arceos_posix_api/ctypes.h +++ b/api/arceos_posix_api/ctypes.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/api/arceos_posix_api/src/imp/io_mpx/mod.rs b/api/arceos_posix_api/src/imp/io_mpx/mod.rs index f0c44b0ab..93bda10f5 100644 --- a/api/arceos_posix_api/src/imp/io_mpx/mod.rs +++ b/api/arceos_posix_api/src/imp/io_mpx/mod.rs @@ -10,16 +10,21 @@ //! I/O multiplexing: //! //! * [`select`](select::sys_select) +//! * [`poll`](poll::sys_poll) //! * [`epoll_create`](epoll::sys_epoll_create) //! * [`epoll_ctl`](epoll::sys_epoll_ctl) //! * [`epoll_wait`](epoll::sys_epoll_wait) #[cfg(feature = "epoll")] mod epoll; +#[cfg(feature = "poll")] +mod poll; #[cfg(feature = "select")] mod select; #[cfg(feature = "epoll")] pub use self::epoll::{sys_epoll_create, sys_epoll_ctl, sys_epoll_wait}; +#[cfg(feature = "poll")] +pub use self::poll::sys_poll; #[cfg(feature = "select")] pub use self::select::sys_select; diff --git a/api/arceos_posix_api/src/imp/io_mpx/poll.rs b/api/arceos_posix_api/src/imp/io_mpx/poll.rs new file mode 100644 index 000000000..e1249f566 --- /dev/null +++ b/api/arceos_posix_api/src/imp/io_mpx/poll.rs @@ -0,0 +1,74 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Rukos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/// Add `poll` feature to use poll() interface. +/// poll() is a system call function used to monitor I/O events across multiple file descriptors. +/// poll() is a blocking type of interface, make sure that this does not cost too much. +/// To monitor I/O events, you can also use `select` or `epoll` instead. +use crate::{ctypes, imp::fd_ops::get_file_like}; +use axerrno::{LinuxError, LinuxResult}; +use axhal::time::current_time; + +use core::{ffi::c_int, time::Duration}; + +fn poll_all(fds: &mut [ctypes::pollfd]) -> LinuxResult { + let mut events_num = 0; + + for pollfd_item in fds.iter_mut() { + let intfd = pollfd_item.fd; + let events = pollfd_item.events; + let revents = &mut pollfd_item.revents; + match get_file_like(intfd as c_int)?.poll() { + Err(_) => { + if (events & ctypes::EPOLLERR as i16) != 0 { + *revents |= ctypes::EPOLLERR as i16; + } + } + Ok(state) => { + if state.readable && (events & ctypes::EPOLLIN as i16 != 0) { + *revents |= ctypes::EPOLLIN as i16; + } + + if state.writable && (events & ctypes::EPOLLOUT as i16 != 0) { + *revents |= ctypes::EPOLLOUT as i16; + } + } + } + events_num += 1; + } + Ok(events_num) +} + +/// Used to monitor multiple file descriptors for events +pub unsafe fn sys_poll(fds: *mut ctypes::pollfd, nfds: ctypes::nfds_t, timeout: c_int) -> c_int { + debug!("ax_poll <= nfds: {} timeout: {}", nfds, timeout); + + syscall_body!(ax_poll, { + if nfds == 0 { + return Err(LinuxError::EINVAL); + } + let fds = core::slice::from_raw_parts_mut(fds, nfds as usize); + let deadline = (!timeout.is_negative()) + .then(|| current_time() + Duration::from_millis(timeout as u64)); + loop { + #[cfg(feature = "net")] + axnet::poll_interfaces(); + let fds_num = poll_all(fds)?; + if fds_num > 0 { + return Ok(fds_num as c_int); + } + + if deadline.map_or(false, |ddl| current_time() >= ddl) { + debug!(" timeout!"); + return Ok(0); + } + crate::sys_sched_yield(); + } + }) +} diff --git a/api/arceos_posix_api/src/imp/mod.rs b/api/arceos_posix_api/src/imp/mod.rs index ac0fe02aa..adc7da948 100644 --- a/api/arceos_posix_api/src/imp/mod.rs +++ b/api/arceos_posix_api/src/imp/mod.rs @@ -19,7 +19,7 @@ pub mod time; pub mod fd_ops; #[cfg(feature = "fs")] pub mod fs; -#[cfg(any(feature = "select", feature = "epoll"))] +#[cfg(any(feature = "select", feature = "poll", feature = "epoll"))] pub mod io_mpx; #[cfg(feature = "net")] pub mod net; diff --git a/api/arceos_posix_api/src/lib.rs b/api/arceos_posix_api/src/lib.rs index 71d469dd4..49c0593b3 100644 --- a/api/arceos_posix_api/src/lib.rs +++ b/api/arceos_posix_api/src/lib.rs @@ -51,6 +51,8 @@ pub use imp::time::{sys_clock_gettime, sys_nanosleep}; pub use imp::fd_ops::{sys_close, sys_dup, sys_dup2, sys_fcntl}; #[cfg(feature = "fs")] pub use imp::fs::{sys_fstat, sys_getcwd, sys_lseek, sys_lstat, sys_open, sys_rename, sys_stat}; +#[cfg(feature = "poll")] +pub use imp::io_mpx::sys_poll; #[cfg(feature = "select")] pub use imp::io_mpx::sys_select; #[cfg(feature = "epoll")] diff --git a/apps/c/pthread/parallel/expect_info_smp4_fifo.out b/apps/c/pthread/parallel/expect_info_smp4_fifo.out index cb930640c..2861e291b 100644 --- a/apps/c/pthread/parallel/expect_info_smp4_fifo.out +++ b/apps/c/pthread/parallel/expect_info_smp4_fifo.out @@ -55,6 +55,5 @@ part 12 finished part 13 finished part 14 finished part 15 finished -actual sum = 61783189038 (C)Pthread parallel run OK! Shutting down... diff --git a/apps/c/pthread/parallel/expect_info_smp4_rr.out b/apps/c/pthread/parallel/expect_info_smp4_rr.out index 5c3fc839b..2e016df97 100644 --- a/apps/c/pthread/parallel/expect_info_smp4_rr.out +++ b/apps/c/pthread/parallel/expect_info_smp4_rr.out @@ -56,6 +56,5 @@ part 12 finished part 13 finished part 14 finished part 15 finished -actual sum = 61783189038 (C)Pthread parallel run OK! Shutting down... diff --git a/apps/c/redis/features.txt b/apps/c/redis/features.txt index 8422cb83c..0191f1029 100644 --- a/apps/c/redis/features.txt +++ b/apps/c/redis/features.txt @@ -7,3 +7,4 @@ fs net pipe epoll +poll diff --git a/apps/task/parallel/expect_info_smp1_fifo.out b/apps/task/parallel/expect_info_smp1_fifo.out index 6e8097768..7c38a8829 100644 --- a/apps/task/parallel/expect_info_smp1_fifo.out +++ b/apps/task/parallel/expect_info_smp1_fifo.out @@ -34,6 +34,5 @@ part 13: ThreadId(17) \[1625000, 1750000) part 14: ThreadId(18) \[1750000, 1875000) part 15: ThreadId(19) \[1875000, 2000000) part 15: ThreadId(19) finished -sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/apps/task/parallel/expect_info_smp4_cfs.out b/apps/task/parallel/expect_info_smp4_cfs.out index 7f91a26d0..f062d0790 100644 --- a/apps/task/parallel/expect_info_smp4_cfs.out +++ b/apps/task/parallel/expect_info_smp4_cfs.out @@ -55,6 +55,5 @@ part 11: ThreadId([0-9]\+) finished part 12: ThreadId([0-9]\+) finished part 13: ThreadId([0-9]\+) finished part 14: ThreadId([0-9]\+) finished -sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/apps/task/parallel/expect_info_smp4_rr.out b/apps/task/parallel/expect_info_smp4_rr.out index 771d4bb5d..bc8350554 100644 --- a/apps/task/parallel/expect_info_smp4_rr.out +++ b/apps/task/parallel/expect_info_smp4_rr.out @@ -55,6 +55,5 @@ part 11: ThreadId([0-9]\+) finished part 12: ThreadId([0-9]\+) finished part 13: ThreadId([0-9]\+) finished part 14: ThreadId([0-9]\+) finished -sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/scripts/make/features.mk b/scripts/make/features.mk index 2ae6276ad..0b250d7cd 100644 --- a/scripts/make/features.mk +++ b/scripts/make/features.mk @@ -14,7 +14,7 @@ ifeq ($(APP_TYPE),c) ax_feat_prefix := axfeat/ lib_feat_prefix := axlibc/ - lib_features := fp_simd alloc multitask fs net fd pipe select epoll + lib_features := fp_simd alloc multitask fs net fd pipe select poll epoll random-hw else # TODO: it's better to use `axfeat/` as `ax_feat_prefix`, but all apps need to have `axfeat` as a dependency ax_feat_prefix := axstd/ @@ -28,7 +28,7 @@ ifeq ($(APP_TYPE), c) ifneq ($(wildcard $(APP)/features.txt),) # check features.txt exists override FEATURES += $(shell cat $(APP)/features.txt) endif - ifneq ($(filter fs net pipe select epoll,$(FEATURES)),) + ifneq ($(filter fs net pipe select poll epoll,$(FEATURES)),) override FEATURES += fd endif endif diff --git a/ulib/axlibc/Cargo.toml b/ulib/axlibc/Cargo.toml index 703e79e01..b3eb705c9 100644 --- a/ulib/axlibc/Cargo.toml +++ b/ulib/axlibc/Cargo.toml @@ -45,7 +45,9 @@ net = ["arceos_posix_api/net", "fd"] fd = [] pipe = ["arceos_posix_api/pipe"] select = ["arceos_posix_api/select"] +poll = ["arceos_posix_api/poll"] epoll = ["arceos_posix_api/epoll"] +random-hw = [] [dependencies] axfeat = { path = "../../api/axfeat" } diff --git a/ulib/axlibc/c/poll.c b/ulib/axlibc/c/poll.c deleted file mode 100644 index 988628618..000000000 --- a/ulib/axlibc/c/poll.c +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright (c) [2023] [Syswonder Community] - * [Rukos] is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include -#include - -// TODO -int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout) -{ - unimplemented(); - return 0; -} diff --git a/ulib/axlibc/src/io_mpx.rs b/ulib/axlibc/src/io_mpx.rs index 522b8d7d9..2565196b3 100644 --- a/ulib/axlibc/src/io_mpx.rs +++ b/ulib/axlibc/src/io_mpx.rs @@ -11,6 +11,8 @@ use crate::{ctypes, utils::e}; use core::ffi::c_int; +#[cfg(feature = "poll")] +use arceos_posix_api::sys_poll; #[cfg(feature = "select")] use arceos_posix_api::sys_select; #[cfg(feature = "epoll")] @@ -61,3 +63,14 @@ pub unsafe extern "C" fn select( ) -> c_int { e(sys_select(nfds, readfds, writefds, exceptfds, timeout)) } + +/// Monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation +#[cfg(feature = "poll")] +#[no_mangle] +pub unsafe extern "C" fn poll( + fds: *mut ctypes::pollfd, + nfds: ctypes::nfds_t, + timeout: c_int, +) -> c_int { + e(sys_poll(fds, nfds, timeout)) +} diff --git a/ulib/axlibc/src/lib.rs b/ulib/axlibc/src/lib.rs index b7d718580..4b1202bfd 100644 --- a/ulib/axlibc/src/lib.rs +++ b/ulib/axlibc/src/lib.rs @@ -62,7 +62,7 @@ mod utils; mod fd_ops; #[cfg(feature = "fs")] mod fs; -#[cfg(any(feature = "select", feature = "epoll"))] +#[cfg(any(feature = "select", feature = "poll", feature = "epoll"))] mod io_mpx; #[cfg(feature = "alloc")] mod malloc; @@ -125,6 +125,8 @@ pub use self::pthread::{pthread_mutex_init, pthread_mutex_lock, pthread_mutex_un #[cfg(feature = "pipe")] pub use self::pipe::pipe; +#[cfg(feature = "poll")] +pub use self::io_mpx::poll; #[cfg(feature = "select")] pub use self::io_mpx::select; #[cfg(feature = "epoll")] diff --git a/ulib/axlibc/src/rand.rs b/ulib/axlibc/src/rand.rs index f28c337d6..61f478479 100644 --- a/ulib/axlibc/src/rand.rs +++ b/ulib/axlibc/src/rand.rs @@ -7,33 +7,143 @@ * See the Mulan PSL v2 for more details. */ -//! Random number generator. +/// If adding `random-hw` to features, rand()/random() will try to use CPU instruction to generate the random. +/// If CPU doesn't support instructions for random Generation, rand()/random() will return persudo random using LCG algorithm instead; +/// Without feature `random-hw`, rand()/random() will simply return 64-bit persudo random generated from LCG algorithm by default. +/// For x86_64, intel's CPU support `rdrand` instruction since IvyBridge, AMD's CPU support `rdrand` since Ryzen. +/// For aarch64, resigter `rndr` is supported for part of CPU core since ARMv8.5. For more information, you can read: https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/RNDR--Random-Number?lang=en +/// We can determine whether the CPU supports this instruction by CPUID(x86_64) or ID_AA64ISAR0_EL1(aarch), which is implement in function `has_rdrand()`. +/// As of now, riscv64 does not support generating random numbers through instructions. +use core::ffi::{c_int, c_long, c_uint}; -use core::{ - ffi::{c_int, c_long, c_uint}, - sync::atomic::{AtomicU64, Ordering::SeqCst}, -}; +use core::sync::atomic::{AtomicU64, Ordering::SeqCst}; -static SEED: AtomicU64 = AtomicU64::new(0xa2ce_a2ce); +static SEED: AtomicU64 = AtomicU64::new(0xae_f3); -/// Sets the seed for the random number generator. +/// Returns a 32-bit unsigned pseudo random interger using LCG. +fn rand_lcg32() -> u32 { + let new_seed = SEED + .load(SeqCst) + .wrapping_mul(6364136223846793005) + .wrapping_add(1); + SEED.store(new_seed, SeqCst); + (new_seed >> 33) as u32 +} + +/// Returns a 64-bit unsigned pseudo random interger using LCG. +fn random_lcg64() -> u64 { + let new_seed = SEED + .load(SeqCst) + .wrapping_mul(6364136223846793005) + .wrapping_add(1); + SEED.store(new_seed, SeqCst); + new_seed >> 1 +} + +/// Sets the seed for the random number generator implemented by LCG. +fn srand_lcg(seed: u64) { + SEED.store(seed - 1, SeqCst); +} + +/// Checking if the CPU core is compatible with hardware random number instructions. +#[cfg(feature = "random-hw")] +fn has_rdrand() -> bool { + #[cfg(target_arch = "x86_64")] + { + let mut ecx: u32; + unsafe { + core::arch::asm!( + "mov eax, 1", + "cpuid", + out("ecx") ecx + ) + } + ecx & (1 << 30) != 0 + } + #[cfg(target_arch = "aarch64")] + { + let mut id_aa64_isar0_el1: u64; + unsafe { + core::arch::asm!( + "mrs {},ID_AA64ISAR0_EL1", + out(reg) id_aa64_isar0_el1 + ) + } + id_aa64_isar0_el1 & (0b1111 << 60) == 0b0001 << 60 + } + #[cfg(target_arch = "riscv64")] + { + false + } +} + +/// Return 64-bit unsigned random interger using cpu instruction +#[cfg(feature = "random-hw")] +fn random_hw() -> u64 { + let mut _random: u64; + + #[cfg(target_arch = "x86_64")] + { + unsafe { + core::arch::asm! { + "rdrand {0:r}", + out(reg) _random + } + } + _random + } + + #[cfg(target_arch = "aarch64")] + { + unsafe { + core::arch::asm! { + "mrs {}, s3_3_c2_c4_0", // s3_3_c2_c4_0 is register `rndr` + out(reg) _random + } + } + _random + } + + #[cfg(target_arch = "riscv64")] + { + panic!("riscv64 has no rdrand instructions") + } +} + +/// Sets the seed for the 32-bit random number generator based on LCG. #[no_mangle] -pub unsafe extern "C" fn srand(seed: c_uint) { - SEED.store(seed.wrapping_sub(1) as u64, SeqCst); +pub unsafe extern "C" fn srand(_seed: c_uint) { + srand_lcg(_seed as u64); } -/// Returns a 32-bit unsigned pseudo random interger. +/// Returns a 32-bit unsigned random integer #[no_mangle] pub unsafe extern "C" fn rand() -> c_int { - let new_seed = SEED.load(SeqCst).wrapping_mul(6364136223846793005) + 1; - SEED.store(new_seed, SeqCst); - (new_seed >> 33) as c_int + #[cfg(feature = "random-hw")] + { + match has_rdrand() { + true => (random_hw() >> 33) as c_int, + false => rand_lcg32() as c_int, + } + } + #[cfg(not(feature = "random-hw"))] + { + rand_lcg32() as c_int + } } -/// Returns a 64-bit unsigned pseudo random number. +/// Returns a 64-bit unsigned random integer #[no_mangle] pub unsafe extern "C" fn random() -> c_long { - let new_seed = SEED.load(SeqCst).wrapping_mul(6364136223846793005) + 1; - SEED.store(new_seed, SeqCst); - new_seed as c_long + #[cfg(feature = "random-hw")] + { + match has_rdrand() { + true => (random_hw() >> 1) as c_long, + false => random_lcg64() as c_long, + } + } + #[cfg(not(feature = "random-hw"))] + { + random_lcg64() as c_long + } }