Re: [PATCH v2 19/37] bsd-user: Add select, pselect, and ppoll to os-time.h

Warner Losh <[email protected]>
Newsgroups org.nongnu.qemu-devel
Message-ID <CANCZdfooM-hzozBKdFPaudPMCkJYBA7rvkaOEigyvskAQjeXkg@mail.gmail.com>
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 */
> >
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.