On Fri, May 22, 2026 at 5:46 PM Pierrick Bouvier < [email protected]> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote: > > Add I/O multiplexing system call shims: select, pselect, and ppoll > > with proper fd_set, timespec/timeval, and signal mask conversion. > > > > Signed-off-by: Stacey Son <[email protected]> > > Signed-off-by: Mikaël Urankar <[email protected]> > > Signed-off-by: Kyle Evans <[email protected]> > > Signed-off-by: Warner Losh <[email protected]> > > Assisted-by: Claude Opus 4.6 (1M context) > > --- > > bsd-user/freebsd/os-time.h | 202 > +++++++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 202 insertions(+) > > > > diff --git a/bsd-user/freebsd/os-time.h b/bsd-user/freebsd/os-time.h > > index 05fa043442..12c5ba02e8 100644 > > --- a/bsd-user/freebsd/os-time.h > > +++ b/bsd-user/freebsd/os-time.h > > @@ -427,5 +427,207 @@ static inline abi_long > do_freebsd_ktimer_gettime(abi_long arg1, abi_long arg2) > > return ret; > > } > > > > +/* select(2) */ > > +static inline abi_long do_freebsd_select(CPUArchState *env, int n, > > + abi_ulong rfd_addr, abi_ulong wfd_addr, abi_ulong efd_addr, > > + abi_ulong target_tv_addr) > > +{ > > + fd_set rfds, wfds, efds; > > + fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; > > + struct timeval tv, *tvp; > > + abi_long ret, error; > > + > > + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); > > + if (ret != 0) { > > + return ret; > > + } > > + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); > > + if (ret != 0) { > > + return ret; > > + } > > + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); > > + if (ret != 0) { > > + return ret; > > + } > > + > > We can factor this with a macro > > > + if (target_tv_addr != 0) { > > + if (t2h_freebsd_timeval(&tv, target_tv_addr)) { > > + return -TARGET_EFAULT; > > + } > > + tvp = &tv; > > + } else { > > + tvp = NULL; > > + } > > + > > + ret = get_errno(safe_select(n, rfds_ptr, wfds_ptr, efds_ptr, tvp)); > > + > > + if (!is_error(ret)) { > > We can simplify depth here by using an early return. > if (is_error(ret)) { > return ret; > } > > > + if (rfd_addr != 0) { > > + error = copy_to_user_fdset(rfd_addr, &rfds, n); > > + if (error != 0) { > > + return error; > > + } > > + } > > + if (wfd_addr != 0) { > > + error = copy_to_user_fdset(wfd_addr, &wfds, n); > > + if (error != 0) { > > + return error; > > + } > > + } > > + if (efd_addr != 0) { > > + error = copy_to_user_fdset(efd_addr, &efds, n); > > + if (error != 0) { > > + return error; > > + } > > + } > > Same, reuse the macro defined above. > > > + if (target_tv_addr != 0) { > > + error = h2t_freebsd_timeval(&tv, target_tv_addr); > > + if (is_error(error)) { > > + return error; > > + } > > + } > > + } > > + return ret; > > +} > > + > > +/* pselect(2) */ > > +static inline abi_long do_freebsd_pselect(CPUArchState *env, int n, > > + abi_ulong rfd_addr, abi_ulong wfd_addr, abi_ulong efd_addr, > > + abi_ulong ts_addr, abi_ulong set_addr) > > +{ > > + CPUState *cpu = env_cpu(env); > > + TaskState *tstate = cpu->opaque; > > + fd_set rfds, wfds, efds; > > + fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; > > + sigset_t *set_ptr; > > + struct timespec ts, *ts_ptr; > > + void *p; > > + abi_long ret, error; > > + > > + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); > > + if (is_error(ret)) { > > + return ret; > > + } > > + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); > > + if (is_error(ret)) { > > + return ret; > > + } > > + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); > > + if (is_error(ret)) { > > + return ret; > > + } > > + > > Same than above. > I like these suggestions, but due to lack of good regression tests, I'm going to defer them since I know the code that's there works. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/66 memorializes the issue. Warner > > + /* Unlike select(), pselect() uses struct timespec instead of > timeval */ > > + if (ts_addr) { > > + if (t2h_freebsd_timespec(&ts, ts_addr)) { > > + return -TARGET_EFAULT; > > + } > > + ts_ptr = &ts; > > + } else { > > + ts_ptr = NULL; > > + } > > + > > + if (set_addr != 0) { > > + p = lock_user(VERIFY_READ, set_addr, sizeof(target_sigset_t), > 1); > > + if (p == NULL) { > > + return -TARGET_EFAULT; > > + } > > + target_to_host_sigset(&tstate->sigsuspend_mask, p); > > + unlock_user(p, set_addr, 0); > > + set_ptr = &tstate->sigsuspend_mask; > > + } else { > > + set_ptr = NULL; > > + } > > + > > + ret = get_errno(safe_pselect(n, rfds_ptr, wfds_ptr, efds_ptr, > ts_ptr, > > + set_ptr)); > > + if (ret != -TARGET_ERESTART) { > > + tstate->in_sigsuspend = true; > > + } > > + if (!is_error(ret)) { > > + if (rfd_addr != 0) { > > + error = copy_to_user_fdset(rfd_addr, &rfds, n); > > + if (is_error(error)) { > > + return error; > > + } > > + } > > + if (wfd_addr != 0) { > > + error = copy_to_user_fdset(wfd_addr, &wfds, n); > > + if (is_error(error)) { > > + return error; > > + } > > + } > > + if (efd_addr != 0) { > > + error = copy_to_user_fdset(efd_addr, &efds, n); > > + if (is_error(error)) { > > + return error; > > + } > > + } > > + } > > + return ret; > > +} > > + > > +/* ppoll(2) */ > > +static inline abi_long do_freebsd_ppoll(CPUArchState *env, abi_long > arg1, > > + abi_long arg2, abi_ulong arg3, abi_ulong arg4) > > +{ > > + CPUState *cpu = env_cpu(env); > > + TaskState *tstate = cpu->opaque; > > + abi_long ret; > > + nfds_t i, nfds = arg2; > > + struct pollfd *pfd; > > + struct target_pollfd *target_pfd; > > + struct timespec ts, *ts_ptr; > > + sigset_t *set_ptr; > > + void *p; > > + > > + target_pfd = lock_user(VERIFY_WRITE, arg1, > > + sizeof(struct target_pollfd) * nfds, 1); > > + if (!target_pfd) { > > + return -TARGET_EFAULT; > > + } > > + pfd = alloca(sizeof(struct pollfd) * nfds); > > + for (i = 0; i < nfds; i++) { > > + pfd[i].fd = tswap32(target_pfd[i].fd); > > + pfd[i].events = tswap16(target_pfd[i].events); > > + } > > + > > + /* Unlike poll(), ppoll() uses struct timespec. */ > > + if (arg3) { > > + if (t2h_freebsd_timespec(&ts, arg3)) { > > + return -TARGET_EFAULT; > > + } > > + ts_ptr = &ts; > > + } else { > > + ts_ptr = NULL; > > + } > > + > > + if (arg4 != 0) { > > + p = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1); > > + if (p == NULL) { > > + return -TARGET_EFAULT; > > + } > > + target_to_host_sigset(&tstate->sigsuspend_mask, p); > > + unlock_user(p, arg4, 0); > > + set_ptr = &tstate->sigsuspend_mask; > > + } else { > > + set_ptr = NULL; > > + } > > + > > + ret = get_errno(ppoll(pfd, nfds, ts_ptr, set_ptr)); > > + if (ret != -TARGET_ERESTART) { > > + tstate->in_sigsuspend = true; > > + } > > + if (!is_error(ret)) { > > + for (i = 0; i < nfds; i++) { > > + target_pfd[i].revents = tswap16(pfd[i].revents); > > + } > > + } > > + unlock_user(target_pfd, arg1, sizeof(struct target_pollfd) * nfds); > > + > > + return ret; > > +} > > + > > +/* kqueue(2) */ > > > > #endif /* FREEBSD_OS_TIME_H */ > > > >
