Index: sys/amd64/linux/linux.h =================================================================== --- sys/amd64/linux/linux.h +++ sys/amd64/linux/linux.h @@ -407,27 +407,6 @@ #define ifc_buf ifc_ifcu.ifcu_buf #define ifc_req ifc_ifcu.ifcu_req -/* - * poll() - */ -#define LINUX_POLLIN 0x0001 -#define LINUX_POLLPRI 0x0002 -#define LINUX_POLLOUT 0x0004 -#define LINUX_POLLERR 0x0008 -#define LINUX_POLLHUP 0x0010 -#define LINUX_POLLNVAL 0x0020 -#define LINUX_POLLRDNORM 0x0040 -#define LINUX_POLLRDBAND 0x0080 -#define LINUX_POLLWRNORM 0x0100 -#define LINUX_POLLWRBAND 0x0200 -#define LINUX_POLLMSG 0x0400 - -struct l_pollfd { - l_int fd; - l_short events; - l_short revents; -}; - #define LINUX_ARCH_SET_GS 0x1001 #define LINUX_ARCH_SET_FS 0x1002 #define LINUX_ARCH_GET_FS 0x1003 Index: sys/amd64/linux32/linux.h =================================================================== --- sys/amd64/linux32/linux.h +++ sys/amd64/linux32/linux.h @@ -515,27 +515,6 @@ #define ifc_buf ifc_ifcu.ifcu_buf #define ifc_req ifc_ifcu.ifcu_req -/* - * poll() - */ -#define LINUX_POLLIN 0x0001 -#define LINUX_POLLPRI 0x0002 -#define LINUX_POLLOUT 0x0004 -#define LINUX_POLLERR 0x0008 -#define LINUX_POLLHUP 0x0010 -#define LINUX_POLLNVAL 0x0020 -#define LINUX_POLLRDNORM 0x0040 -#define LINUX_POLLRDBAND 0x0080 -#define LINUX_POLLWRNORM 0x0100 -#define LINUX_POLLWRBAND 0x0200 -#define LINUX_POLLMSG 0x0400 - -struct l_pollfd { - l_int fd; - l_short events; - l_short revents; -}; - struct l_user_desc { l_uint entry_number; l_uint base_addr; Index: sys/compat/linux/linux.h =================================================================== --- sys/compat/linux/linux.h +++ sys/compat/linux/linux.h @@ -31,6 +31,23 @@ #include +/* + * poll() + */ +#define LINUX_POLLIN 0x0001 +#define LINUX_POLLPRI 0x0002 +#define LINUX_POLLOUT 0x0004 +#define LINUX_POLLERR 0x0008 +#define LINUX_POLLHUP 0x0010 +#define LINUX_POLLNVAL 0x0020 +#define LINUX_POLLRDNORM 0x0040 +#define LINUX_POLLRDBAND 0x0080 +#define LINUX_POLLWRNORM 0x0100 +#define LINUX_POLLWRBAND 0x0200 +#define LINUX_POLLMSG 0x0400 +#define LINUX_POLLREMOVE 0x1000 +#define LINUX_POLLRDHUP 0x2000 + #define LINUX_IFHWADDRLEN 6 #define LINUX_IFNAMSIZ 16 Index: sys/compat/linux/linux.c =================================================================== --- sys/compat/linux/linux.c +++ sys/compat/linux/linux.c @@ -33,9 +33,13 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include #include #include @@ -50,6 +54,7 @@ #include #include +#include #include struct futex_list futex_list; @@ -627,3 +632,94 @@ return (no_value); return (bsd_ret); } + +void +linux_to_bsd_poll_events(struct thread *td, int fd, short lev, + short *bev) +{ + struct proc *p = td->td_proc; + struct filedesc *fdp; + struct file *fp; + int error; + short bits = 0; + + if (lev & LINUX_POLLIN) + bits |= POLLIN; + if (lev & LINUX_POLLPRI) + bits |= POLLPRI; + if (lev & LINUX_POLLOUT) + bits |= POLLOUT; + if (lev & LINUX_POLLERR) + bits |= POLLERR; + if (lev & LINUX_POLLHUP) + bits |= POLLHUP; + if (lev & LINUX_POLLNVAL) + bits |= POLLNVAL; + if (lev & LINUX_POLLRDNORM) + bits |= POLLRDNORM; + if (lev & LINUX_POLLRDBAND) + bits |= POLLRDBAND; + if (lev & LINUX_POLLWRBAND) + bits |= POLLWRBAND; + if (lev & LINUX_POLLWRNORM) + bits |= POLLWRNORM; + + if (lev & LINUX_POLLRDHUP) { + /* + * It seems that the Linux silencly ignores POLLRDHUP + * on non-socket file descriptors unlike FreeBSD, where + * events bits is more strictly checked (POLLSTANDARD). + */ + fdp = p->p_fd; + error = fget_unlocked(fdp, fd, &cap_no_rights, &fp); + if (error == 0) { + /* + * XXX. On FreeBSD POLLRDHUP applies only to + * stream sockets. + */ + if (fp->f_type == DTYPE_SOCKET) + bits |= POLLRDHUP; + fdrop(fp, td); + } + } + + if (lev & LINUX_POLLMSG) + LINUX_RATELIMIT_MSG_OPT1("unsupported POLLMSG, events(%d)", lev); + if (lev & LINUX_POLLREMOVE) + LINUX_RATELIMIT_MSG_OPT1("unsupported POLLREMOVE, events(%d)", lev); + + *bev = bits; +} + +void +bsd_to_linux_poll_events(short bev, short *lev) +{ + short bits = 0; + + if (bev & POLLIN) + bits |= LINUX_POLLIN; + if (bev & POLLPRI) + bits |= LINUX_POLLPRI; + if (bev & (POLLOUT | POLLWRNORM)) + /* + * POLLWRNORM is equal to POLLOUT on FreeBSD, + * but not on Linux + */ + bits |= LINUX_POLLOUT; + if (bev & POLLERR) + bits |= LINUX_POLLERR; + if (bev & POLLHUP) + bits |= LINUX_POLLHUP; + if (bev & POLLNVAL) + bits |= LINUX_POLLNVAL; + if (bev & POLLRDNORM) + bits |= LINUX_POLLRDNORM; + if (bev & POLLRDBAND) + bits |= LINUX_POLLRDBAND; + if (bev & POLLWRBAND) + bits |= LINUX_POLLWRBAND; + if (bev & POLLRDHUP) + bits |= LINUX_POLLRDHUP; + + *lev = bits; +} Index: sys/compat/linux/linux_common.h =================================================================== --- sys/compat/linux/linux_common.h +++ sys/compat/linux/linux_common.h @@ -41,5 +41,9 @@ struct l_sockaddr **lsa, socklen_t len); int linux_to_bsd_sockaddr(const struct l_sockaddr *lsa, struct sockaddr **sap, socklen_t *len); +void linux_to_bsd_poll_events(struct thread *td, int fd, + short lev, short *bev); +void bsd_to_linux_poll_events(short bev, short *lev); + #endif /* _LINUX_COMMON_H_ */ Index: sys/compat/linux/linux_misc.c =================================================================== --- sys/compat/linux/linux_misc.c +++ sys/compat/linux/linux_misc.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ #include #endif +#include #include #include #include @@ -144,6 +146,10 @@ static int linux_common_ppoll(struct thread *, struct pollfd *, uint32_t, struct timespec *, l_sigset_t *, l_size_t); +static int linux_pollin(struct thread *, struct pollfd *, + struct pollfd *, u_int); +static int linux_pollout(struct thread *, struct pollfd *, + struct pollfd *, u_int); int linux_sysinfo(struct thread *td, struct linux_sysinfo_args *args) @@ -2519,11 +2525,15 @@ struct timespec *tsp, l_sigset_t *sset, l_size_t ssize) { struct timespec ts0, ts1; + struct pollfd stackfds[32]; + struct pollfd *kfds; l_sigset_t l_ss; sigset_t *ssp; sigset_t ss; int error; + if (kern_poll_maxfds(nfds)) + return (EINVAL); if (sset != NULL) { if (ssize != sizeof(l_ss)) return (EINVAL); @@ -2537,7 +2547,17 @@ if (tsp != NULL) nanotime(&ts0); - error = kern_poll(td, fds, nfds, tsp, ssp); + if (nfds > nitems(stackfds)) + kfds = mallocarray(nfds, sizeof(*kfds), M_TEMP, M_WAITOK); + else + kfds = stackfds; + error = linux_pollin(td, kfds, fds, nfds); + if (error != 0) + goto out; + + error = kern_poll_kfds(td, kfds, nfds, tsp, ssp); + if (error == 0) + error = linux_pollout(td, kfds, fds, nfds); if (error == 0 && tsp != NULL) { if (td->td_retval[0]) { @@ -2549,6 +2569,10 @@ } else timespecclear(tsp); } + +out: + if (nfds > nitems(stackfds)) + free(kfds, M_TEMP); return (error); } @@ -2583,6 +2607,48 @@ } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ +static int +linux_pollin(struct thread *td, struct pollfd *fds, struct pollfd *ufds, u_int nfd) +{ + int error; + u_int i; + + error = copyin(ufds, fds, nfd * sizeof(*fds)); + if (error != 0) + return (error); + + for (i = 0; i < nfd; i++) { + if (fds->events != 0) + linux_to_bsd_poll_events(td, fds->fd, + fds->events, &fds->events); + fds++; + } + return (0); +} + +static int +linux_pollout(struct thread *td, struct pollfd *fds, struct pollfd *ufds, u_int nfd) +{ + int error = 0; + u_int i, n = 0; + + for (i = 0; i < nfd; i++) { + if (fds->revents != 0) { + bsd_to_linux_poll_events(fds->revents, + &fds->revents); + n++; + } + error = copyout(&fds->revents, &ufds->revents, + sizeof(ufds->revents)); + if (error) + return (error); + fds++; + ufds++; + } + td->td_retval[0] = n; + return (0); +} + int linux_sched_rr_get_interval(struct thread *td, struct linux_sched_rr_get_interval_args *uap) Index: sys/i386/linux/linux.h =================================================================== --- sys/i386/linux/linux.h +++ sys/i386/linux/linux.h @@ -478,27 +478,6 @@ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ -/* - * poll() - */ -#define LINUX_POLLIN 0x0001 -#define LINUX_POLLPRI 0x0002 -#define LINUX_POLLOUT 0x0004 -#define LINUX_POLLERR 0x0008 -#define LINUX_POLLHUP 0x0010 -#define LINUX_POLLNVAL 0x0020 -#define LINUX_POLLRDNORM 0x0040 -#define LINUX_POLLRDBAND 0x0080 -#define LINUX_POLLWRNORM 0x0100 -#define LINUX_POLLWRBAND 0x0200 -#define LINUX_POLLMSG 0x0400 - -struct l_pollfd { - l_int fd; - l_short events; - l_short revents; -}; - struct l_user_desc { l_uint entry_number; l_uint base_addr;