Skip to content

Commit 8aa3b7c

Browse files
authored
feat: add select and pselect6 syscall (#1229)
* feat: add select and pselect6 syscall Signed-off-by: Godones <[email protected]> * fix: fix the select compile error Signed-off-by: Godones <[email protected]> --------- Signed-off-by: Godones <[email protected]>
1 parent a63fac8 commit 8aa3b7c

File tree

8 files changed

+497
-2
lines changed

8 files changed

+497
-2
lines changed

kernel/src/filesystem/poll.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,10 @@ impl Syscall {
164164
}
165165
}
166166

167-
fn do_sys_poll(poll_fds: &mut [PollFd], timeout: Option<Instant>) -> Result<usize, SystemError> {
167+
pub fn do_sys_poll(
168+
poll_fds: &mut [PollFd],
169+
timeout: Option<Instant>,
170+
) -> Result<usize, SystemError> {
168171
let ep_file = EventPoll::create_epoll_file(FileMode::empty())?;
169172

170173
let ep_file = Arc::new(ep_file);
@@ -177,7 +180,7 @@ fn do_sys_poll(poll_fds: &mut [PollFd], timeout: Option<Instant>) -> Result<usiz
177180
}
178181

179182
/// 计算超时的时刻
180-
fn poll_select_set_timeout(timeout_ms: u64) -> Option<Instant> {
183+
pub fn poll_select_set_timeout(timeout_ms: u64) -> Option<Instant> {
181184
Some(Instant::now() + Duration::from_millis(timeout_ms))
182185
}
183186

kernel/src/filesystem/vfs/syscall/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ pub mod sys_mount;
6060
pub mod sys_umount2;
6161

6262
pub mod symlink_utils;
63+
mod sys_pselect6;
64+
mod sys_select;
6365
#[cfg(target_arch = "x86_64")]
6466
mod sys_symlink;
6567
mod sys_symlinkat;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use alloc::vec::Vec;
2+
3+
use system_error::SystemError;
4+
5+
use crate::{
6+
arch::{ipc::signal::SigSet, syscall::nr::SYS_PSELECT6},
7+
filesystem::vfs::syscall::sys_select::common_sys_select,
8+
ipc::signal::set_user_sigmask,
9+
syscall::{
10+
table::{FormattedSyscallParam, Syscall},
11+
user_access::UserBufferReader,
12+
},
13+
};
14+
15+
pub struct SysPselect6;
16+
impl Syscall for SysPselect6 {
17+
fn num_args(&self) -> usize {
18+
6
19+
}
20+
21+
fn handle(
22+
&self,
23+
args: &[usize],
24+
_frame: &mut crate::arch::interrupt::TrapFrame,
25+
) -> Result<usize, SystemError> {
26+
let sigmask_ptr = args[5];
27+
let mut sigmask: Option<SigSet> = None;
28+
if sigmask_ptr != 0 {
29+
let sigmask_reader =
30+
UserBufferReader::new(sigmask_ptr as *const SigSet, size_of::<SigSet>(), true)?;
31+
sigmask.replace(*sigmask_reader.read_one_from_user(0)?);
32+
}
33+
if let Some(mut sigmask) = sigmask {
34+
set_user_sigmask(&mut sigmask);
35+
}
36+
common_sys_select(args[0], args[1], args[2], args[3], args[4])
37+
}
38+
39+
fn entry_format(&self, args: &[usize]) -> Vec<crate::syscall::table::FormattedSyscallParam> {
40+
vec![
41+
FormattedSyscallParam::new("nfds", format!("{}", args[0])),
42+
FormattedSyscallParam::new("readfds", format!("{:#x}", args[1])),
43+
FormattedSyscallParam::new("writefds", format!("{:#x}", args[2])),
44+
FormattedSyscallParam::new("exceptfds", format!("{:#x}", args[3])),
45+
FormattedSyscallParam::new("timeout", format!("{:#x}", args[4])),
46+
FormattedSyscallParam::new("sigmask", format!("{:#x}", args[5])),
47+
]
48+
}
49+
}
50+
51+
syscall_table_macros::declare_syscall!(SYS_PSELECT6, SysPselect6);
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
//! Reference https://github.com/asterinas/asterinas/blob/main/kernel/src/syscall/select.rs
2+
use alloc::vec::Vec;
3+
use system_error::SystemError;
4+
5+
#[cfg(target_arch = "x86_64")]
6+
use crate::arch::syscall::nr::SYS_SELECT;
7+
use crate::{
8+
filesystem::{
9+
epoll::EPollEventType,
10+
poll::{do_sys_poll, poll_select_set_timeout, PollFd},
11+
},
12+
syscall::{
13+
table::{FormattedSyscallParam, Syscall},
14+
user_access::{UserBufferReader, UserBufferWriter},
15+
},
16+
time::{syscall::PosixTimeval, Instant},
17+
};
18+
// Maximum number of file descriptors in a set
19+
const FD_SETSIZE: usize = 1024;
20+
const USIZE_BITS: usize = core::mem::size_of::<usize>() * 8;
21+
/// See https://man7.org/linux/man-pages/man2/select.2.html
22+
pub struct SysSelect;
23+
24+
impl Syscall for SysSelect {
25+
fn num_args(&self) -> usize {
26+
5
27+
}
28+
29+
fn handle(
30+
&self,
31+
args: &[usize],
32+
_frame: &mut crate::arch::interrupt::TrapFrame,
33+
) -> Result<usize, SystemError> {
34+
common_sys_select(args[0], args[1], args[2], args[3], args[4])
35+
}
36+
37+
fn entry_format(&self, args: &[usize]) -> Vec<crate::syscall::table::FormattedSyscallParam> {
38+
vec![
39+
FormattedSyscallParam::new("nfds", format!("{}", args[0])),
40+
FormattedSyscallParam::new("readfds", format!("{:#x}", args[1])),
41+
FormattedSyscallParam::new("writefds", format!("{:#x}", args[2])),
42+
FormattedSyscallParam::new("exceptfds", format!("{:#x}", args[3])),
43+
FormattedSyscallParam::new("timeout", format!("{:#x}", args[4])),
44+
]
45+
}
46+
}
47+
48+
pub fn common_sys_select(
49+
nfds: usize,
50+
readfds_addr: usize,
51+
writefds_addr: usize,
52+
exceptfds_addr: usize,
53+
timeout_ptr: usize,
54+
) -> Result<usize, SystemError> {
55+
// log::debug!(
56+
// "common_sys_select called with nfds = {}, readfds_addr = {:#x}, writefds_addr = {:#x}, exceptfds_addr = {:#x}, timeout_ptr = {:#x}",
57+
// nfds, readfds_addr, writefds_addr, exceptfds_addr, timeout_ptr
58+
// );
59+
let mut timeout: Option<Instant> = None;
60+
if timeout_ptr != 0 {
61+
let tsreader = UserBufferReader::new(
62+
timeout_ptr as *const PosixTimeval,
63+
size_of::<PosixTimeval>(),
64+
true,
65+
)?;
66+
let ts = *tsreader.read_one_from_user::<PosixTimeval>(0)?;
67+
let timeout_ms = ts.tv_sec * 1000 + ts.tv_usec as i64 / 1000;
68+
if timeout_ms >= 0 {
69+
timeout = poll_select_set_timeout(timeout_ms as u64);
70+
}
71+
}
72+
do_sys_select(
73+
nfds as isize,
74+
readfds_addr as *const FdSet,
75+
writefds_addr as *const FdSet,
76+
exceptfds_addr as *const FdSet,
77+
timeout,
78+
)
79+
}
80+
81+
fn do_sys_select(
82+
nfds: isize,
83+
readfds_addr: *const FdSet,
84+
writefds_addr: *const FdSet,
85+
exceptfds_addr: *const FdSet,
86+
timeout: Option<Instant>,
87+
) -> Result<usize, SystemError> {
88+
if nfds < 0 || nfds as usize > FD_SETSIZE {
89+
return Err(SystemError::EINVAL);
90+
}
91+
let get_fdset = |fdset_addr: *const FdSet| -> Result<Option<FdSet>, SystemError> {
92+
let fdset = if fdset_addr.is_null() {
93+
None
94+
} else {
95+
let fdset_buf = UserBufferReader::new(fdset_addr, size_of::<FdSet>(), true)?;
96+
let fdset = *fdset_buf.read_one_from_user::<FdSet>(0)?;
97+
Some(fdset)
98+
};
99+
Ok(fdset)
100+
};
101+
let mut readfds = get_fdset(readfds_addr)?;
102+
let mut writefds = get_fdset(writefds_addr)?;
103+
let mut exceptfds = get_fdset(exceptfds_addr)?;
104+
105+
// log::debug!(
106+
// "nfds = {}, readfds = {:?}, writefds = {:?}, exceptfds = {:?}, timeout = {:?}",
107+
// nfds,
108+
// readfds,
109+
// writefds,
110+
// exceptfds,
111+
// timeout
112+
// );
113+
114+
let num_revents = do_select(
115+
nfds as usize,
116+
readfds.as_mut(),
117+
writefds.as_mut(),
118+
exceptfds.as_mut(),
119+
timeout,
120+
)?;
121+
122+
let set_fdset = |fdset_addr: *const FdSet, fdset: Option<FdSet>| -> Result<(), SystemError> {
123+
if let Some(fdset) = fdset {
124+
let mut fdset_buf =
125+
UserBufferWriter::new(fdset_addr as *mut FdSet, size_of::<FdSet>(), true)?;
126+
fdset_buf.copy_one_to_user(&fdset, 0)?;
127+
}
128+
Ok(())
129+
};
130+
131+
set_fdset(readfds_addr, readfds)?;
132+
set_fdset(writefds_addr, writefds)?;
133+
set_fdset(exceptfds_addr, exceptfds)?;
134+
135+
// log::info!("num_revents = {}", num_revents);
136+
Ok(num_revents)
137+
}
138+
139+
fn do_select(
140+
nfds: usize,
141+
mut readfds: Option<&mut FdSet>,
142+
mut writefds: Option<&mut FdSet>,
143+
mut exceptfds: Option<&mut FdSet>,
144+
timeout: Option<Instant>,
145+
) -> Result<usize, SystemError> {
146+
let mut poll_fds = {
147+
let mut poll_fds = Vec::with_capacity(nfds);
148+
for fd in 0..nfds {
149+
let events = {
150+
let readable = readfds.as_ref().is_some_and(|fds| fds.is_set(fd));
151+
let writable = writefds.as_ref().is_some_and(|fds| fds.is_set(fd));
152+
let except = exceptfds.as_ref().is_some_and(|fds| fds.is_set(fd));
153+
convert_rwe_to_events(readable, writable, except)
154+
};
155+
156+
if events.is_empty() {
157+
continue;
158+
}
159+
160+
let poll_fd = PollFd {
161+
fd: fd as i32,
162+
events: events.bits() as _,
163+
revents: 0,
164+
};
165+
poll_fds.push(poll_fd);
166+
}
167+
poll_fds
168+
};
169+
if let Some(fds) = readfds.as_mut() {
170+
fds.clear();
171+
}
172+
if let Some(fds) = writefds.as_mut() {
173+
fds.clear();
174+
}
175+
if let Some(fds) = exceptfds.as_mut() {
176+
fds.clear();
177+
}
178+
179+
// call the underlying poll syscall
180+
let num_revents = do_sys_poll(&mut poll_fds, timeout)?;
181+
if num_revents == 0 {
182+
return Ok(0);
183+
}
184+
185+
let mut total_revents = 0;
186+
for poll_fd in &poll_fds {
187+
let fd = poll_fd.fd as usize;
188+
let revents = poll_fd.revents;
189+
let revents = EPollEventType::from_bits_truncate(revents as u32);
190+
let (readable, writable, except) = convert_events_to_rwe(revents)?;
191+
if let Some(ref mut fds) = readfds
192+
&& readable
193+
{
194+
fds.set(fd)?;
195+
total_revents += 1;
196+
}
197+
if let Some(ref mut fds) = writefds
198+
&& writable
199+
{
200+
fds.set(fd)?;
201+
total_revents += 1;
202+
}
203+
if let Some(ref mut fds) = exceptfds
204+
&& except
205+
{
206+
fds.set(fd)?;
207+
total_revents += 1;
208+
}
209+
}
210+
Ok(total_revents)
211+
}
212+
213+
/// Converts `select` RWE input to `poll` I/O event input
214+
/// according to Linux's behavior.
215+
fn convert_rwe_to_events(readable: bool, writable: bool, except: bool) -> EPollEventType {
216+
let mut events = EPollEventType::empty();
217+
if readable {
218+
events |= EPollEventType::EPOLLIN;
219+
}
220+
if writable {
221+
events |= EPollEventType::EPOLLOUT;
222+
}
223+
if except {
224+
events |= EPollEventType::EPOLLPRI;
225+
}
226+
events
227+
}
228+
229+
/// Converts `poll` I/O event results to `select` RWE results
230+
/// according to Linux's behavior.
231+
fn convert_events_to_rwe(events: EPollEventType) -> Result<(bool, bool, bool), SystemError> {
232+
if events.contains(EPollEventType::EPOLLNVAL) {
233+
return Err(SystemError::EBADF);
234+
}
235+
236+
let readable = events
237+
.intersects(EPollEventType::EPOLLIN | EPollEventType::EPOLLHUP | EPollEventType::EPOLLERR);
238+
let writable = events.intersects(EPollEventType::EPOLLOUT | EPollEventType::EPOLLERR);
239+
let except = events.contains(EPollEventType::EPOLLPRI);
240+
Ok((readable, writable, except))
241+
}
242+
243+
#[derive(Debug, Clone, Copy)]
244+
#[repr(C)]
245+
struct FdSet {
246+
fds_bits: [usize; FD_SETSIZE / USIZE_BITS],
247+
}
248+
249+
impl FdSet {
250+
/// Equivalent to FD_SET.
251+
pub fn set(&mut self, fd: usize) -> Result<(), SystemError> {
252+
if fd >= FD_SETSIZE {
253+
return Err(SystemError::EINVAL);
254+
}
255+
self.fds_bits[fd / USIZE_BITS] |= 1 << (fd % USIZE_BITS);
256+
Ok(())
257+
}
258+
259+
/// Equivalent to FD_CLR.
260+
#[expect(unused)]
261+
pub fn unset(&mut self, fd: usize) -> Result<(), SystemError> {
262+
if fd >= FD_SETSIZE {
263+
return Err(SystemError::EINVAL);
264+
}
265+
self.fds_bits[fd / USIZE_BITS] &= !(1 << (fd % USIZE_BITS));
266+
Ok(())
267+
}
268+
269+
/// Equivalent to FD_ISSET.
270+
pub fn is_set(&self, fd: usize) -> bool {
271+
if fd >= FD_SETSIZE {
272+
return false;
273+
}
274+
(self.fds_bits[fd / USIZE_BITS] & (1 << (fd % USIZE_BITS))) != 0
275+
}
276+
277+
/// Equivalent to FD_ZERO.
278+
pub fn clear(&mut self) {
279+
for slot in self.fds_bits.iter_mut() {
280+
*slot = 0;
281+
}
282+
}
283+
}
284+
#[cfg(target_arch = "x86_64")]
285+
syscall_table_macros::declare_syscall!(SYS_SELECT, SysSelect);

kernel/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#![feature(vec_into_raw_parts)]
2424
#![feature(linkage)]
2525
#![feature(panic_can_unwind)]
26+
#![feature(let_chains)]
2627
#![allow(
2728
static_mut_refs,
2829
non_local_definitions,

user/apps/test_select/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ifeq ($(ARCH), x86_64)
2+
CROSS_COMPILE=x86_64-linux-musl-
3+
else ifeq ($(ARCH), riscv64)
4+
CROSS_COMPILE=riscv64-linux-musl-
5+
endif
6+
7+
CC=$(CROSS_COMPILE)gcc
8+
9+
10+
all:
11+
$(CC) -static -o test_select main.c
12+
13+
.PHONY: install clean
14+
install: all
15+
mv test_select $(DADK_CURRENT_BUILD_DIR)/test_select
16+
17+
clean:
18+
rm test_select *.o
19+
20+
fmt:

0 commit comments

Comments
 (0)