From 2a4f2d0e567f548603c0ab5891b67004de016b89 Mon Sep 17 00:00:00 2001 From: christos Date: Fri, 3 Feb 2017 16:57:39 +0000 Subject: [PATCH] add sendmmsg and recvmmsg --- sys/compat/linux/common/linux_socket.c | 200 ++++++++++++++++++++- sys/compat/linux/common/linux_socket.h | 7 +- sys/compat/linux/common/linux_socketcall.c | 10 +- sys/compat/linux/common/linux_socketcall.h | 21 ++- 4 files changed, 227 insertions(+), 11 deletions(-) diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index 3a23afd6d72b..4d01796de2c1 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socket.c,v 1.136 2017/02/03 13:08:08 christos Exp $ */ +/* $NetBSD: linux_socket.c,v 1.137 2017/02/03 16:57:39 christos Exp $ */ /*- * Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.136 2017/02/03 13:08:08 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.137 2017/02/03 16:57:39 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_inet.h" @@ -82,6 +82,7 @@ __KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.136 2017/02/03 13:08:08 christos #include #include #include +#include #include #include #if !defined(__alpha__) && !defined(__amd64__) @@ -124,8 +125,8 @@ static int linux_get_sa(struct lwp *, int, struct sockaddr_big *, static int linux_sa_put(struct osockaddr *osa); static int linux_to_bsd_msg_flags(int); static int bsd_to_linux_msg_flags(int); -static void linux_to_bsd_msghdr(struct linux_msghdr *, struct msghdr *); -static void bsd_to_linux_msghdr(struct msghdr *, struct linux_msghdr *); +static void linux_to_bsd_msghdr(const struct linux_msghdr *, struct msghdr *); +static void bsd_to_linux_msghdr(const struct msghdr *, struct linux_msghdr *); static const int linux_to_bsd_domain_[LINUX_AF_MAX] = { AF_UNSPEC, @@ -433,7 +434,7 @@ linux_sys_sendto(struct lwp *l, const struct linux_sys_sendto_args *uap, registe } static void -linux_to_bsd_msghdr(struct linux_msghdr *lmsg, struct msghdr *bmsg) +linux_to_bsd_msghdr(const struct linux_msghdr *lmsg, struct msghdr *bmsg) { bmsg->msg_name = lmsg->msg_name; bmsg->msg_namelen = lmsg->msg_namelen; @@ -445,7 +446,7 @@ linux_to_bsd_msghdr(struct linux_msghdr *lmsg, struct msghdr *bmsg) } static void -bsd_to_linux_msghdr(struct msghdr *bmsg, struct linux_msghdr *lmsg) +bsd_to_linux_msghdr(const struct msghdr *bmsg, struct linux_msghdr *lmsg) { lmsg->msg_name = bmsg->msg_name; lmsg->msg_namelen = bmsg->msg_namelen; @@ -1742,3 +1743,190 @@ linux_sys_accept4(struct lwp *l, const struct linux_sys_accept4_args *uap, regis return 0; } + +int +linux_sys_sendmmsg(struct lwp *l, const struct linux_sys_sendmmsg_args *uap, + register_t *retval) +{ + /* { + syscallarg(int) s; + syscallarg(struct linux_mmsghdr *) msgvec; + syscallarg(unsigned int) vlen; + syscallarg(unsigned int) flags; + } */ + struct linux_mmsghdr lmsg; + struct mmsghdr bmsg; + struct socket *so; + file_t *fp; + struct msghdr *msg = &bmsg.msg_hdr; + int error, s; + unsigned int vlen, flags, dg; + + if ((flags = linux_to_bsd_msg_flags(SCARG(uap, flags))) == -1) + return EINVAL; + + flags = (flags & MSG_USERFLAGS) | MSG_IOVUSRSPACE; + + s = SCARG(uap, s); + if ((error = fd_getsock1(s, &so, &fp)) != 0) + return error; + + vlen = SCARG(uap, vlen); + if (vlen > 1024) + vlen = 1024; + + for (dg = 0; dg < vlen;) { + error = copyin(SCARG(uap, msgvec) + dg, &lmsg, sizeof(lmsg)); + if (error) + break; + linux_to_bsd_msghdr(&lmsg.msg_hdr, &bmsg.msg_hdr); + + msg->msg_flags = flags; + + error = do_sys_sendmsg_so(l, s, so, fp, msg, flags, + &msg, sizeof(msg), retval); + if (error) + break; + + ktrkuser("msghdr", msg, sizeof *msg); + lmsg.msg_len = *retval; + error = copyout(&lmsg, SCARG(uap, msgvec) + dg, sizeof(lmsg)); + if (error) + break; + dg++; + + } + + *retval = dg; + if (error) + so->so_error = error; + + fd_putfile(s); + + /* + * If we succeeded at least once, return 0, hopefully so->so_error + * will catch it next time. + */ + if (dg) + return 0; + return error; +} + +int +linux_sys_recvmmsg(struct lwp *l, const struct linux_sys_recvmmsg_args *uap, + register_t *retval) +{ + /* { + syscallarg(int) s; + syscallarg(struct linux_mmsghdr *) msgvec; + syscallarg(unsigned int) vlen; + syscallarg(unsigned int) flags; + syscallarg(struct linux_timespec *) timeout; + } */ + struct linux_mmsghdr lmsg; + struct mmsghdr bmsg; + struct socket *so; + struct msghdr *msg = &bmsg.msg_hdr; + int error, s; + struct mbuf *from, *control; + struct timespec ts, now; + struct linux_timespec lts; + unsigned int vlen, flags, dg; + + if (SCARG(uap, timeout)) { + error = copyin(SCARG(uap, timeout), <s, sizeof(lts)); + return error; + ts.tv_sec = lts.tv_sec; + ts.tv_nsec = lts.tv_nsec; + getnanotime(&now); + timespecadd(&now, &ts, &ts); + } + + s = SCARG(uap, s); + if ((error = fd_getsock(s, &so)) != 0) + return error; + + vlen = SCARG(uap, vlen); + if (vlen > 1024) + vlen = 1024; + + from = NULL; + flags = (SCARG(uap, flags) & MSG_USERFLAGS) | MSG_IOVUSRSPACE; + + for (dg = 0; dg < vlen;) { + error = copyin(SCARG(uap, msgvec) + dg, &lmsg, sizeof(lmsg)); + if (error) + break; + linux_to_bsd_msghdr(&lmsg.msg_hdr, &bmsg.msg_hdr); + msg->msg_flags = flags & ~MSG_WAITFORONE; + + if (from != NULL) { + m_free(from); + from = NULL; + } + + error = do_sys_recvmsg_so(l, s, so, msg, NULL, 0, &from, + msg->msg_control != NULL ? &control : NULL, retval); + if (error) { + if (error == EAGAIN && dg > 0) + error = 0; + break; + } + + if (msg->msg_control != NULL) + error = linux_copyout_msg_control(l, msg, control); + if (error) + break; + + if (from != NULL) { + mtod(from, struct osockaddr *)->sa_family = + bsd_to_linux_domain(mtod(from, + struct sockaddr *)->sa_family); + error = copyout_sockname(msg->msg_name, + &msg->msg_namelen, 0, from); + if (error) + break; + } + + + lmsg.msg_len = *retval; + ktrkuser("msghdr", msg, sizeof(*msg)); + bsd_to_linux_msghdr(msg, &lmsg.msg_hdr); + error = copyout(&lmsg, SCARG(uap, msgvec) + dg, sizeof(lmsg)); + if (error) + break; + + dg++; + if (msg->msg_flags & MSG_OOB) + break; + + if (SCARG(uap, timeout)) { + getnanotime(&now); + timespecsub(&now, &ts, &now); + if (now.tv_sec > 0) + break; + } + + if (flags & MSG_WAITFORONE) + flags |= MSG_DONTWAIT; + + } + + if (from != NULL) + m_free(from); + + *retval = dg; + if (error) + so->so_error = error; + + fd_putfile(s); + + /* + * If we succeeded at least once, return 0, hopefully so->so_error + * will catch it next time. + */ + if (dg) + return 0; + + return error; +} diff --git a/sys/compat/linux/common/linux_socket.h b/sys/compat/linux/common/linux_socket.h index 2395bbed05fa..3b14fd15ef54 100644 --- a/sys/compat/linux/common/linux_socket.h +++ b/sys/compat/linux/common/linux_socket.h @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socket.h,v 1.22 2014/01/27 19:19:15 njoly Exp $ */ +/* $NetBSD: linux_socket.h,v 1.23 2017/02/03 16:57:39 christos Exp $ */ /*- * Copyright (c) 1995, 1998 The NetBSD Foundation, Inc. @@ -140,6 +140,11 @@ struct linux_msghdr { unsigned int msg_flags; }; +struct linux_mmsghdr { + struct linux_msghdr msg_hdr; + unsigned int msg_len; +}; + /* * Message flags (for sendmsg/recvmsg) */ diff --git a/sys/compat/linux/common/linux_socketcall.c b/sys/compat/linux/common/linux_socketcall.c index c31d66144a76..d983420628f0 100644 --- a/sys/compat/linux/common/linux_socketcall.c +++ b/sys/compat/linux/common/linux_socketcall.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socketcall.c,v 1.46 2017/02/03 13:08:08 christos Exp $ */ +/* $NetBSD: linux_socketcall.c,v 1.47 2017/02/03 16:57:39 christos Exp $ */ /*- * Copyright (c) 1995, 1998 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: linux_socketcall.c,v 1.46 2017/02/03 13:08:08 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: linux_socketcall.c,v 1.47 2017/02/03 16:57:39 christos Exp $"); #include #include @@ -219,6 +219,12 @@ linux_sys_socketcall(struct lwp *l, const struct linux_sys_socketcall_args *uap, case LINUX_SYS_ACCEPT4: error = linux_sys_accept4(l, (void *)&lda, retval); break; + case LINUX_SYS_RECVMMSG: + error = linux_sys_recvmmsg(l, (void *)&lda, retval); + break; + case LINUX_SYS_SENDMMSG: + error = linux_sys_sendmmsg(l, (void *)&lda, retval); + break; default: error = ENOSYS; break; diff --git a/sys/compat/linux/common/linux_socketcall.h b/sys/compat/linux/common/linux_socketcall.h index a722be206b09..d61baab41dec 100644 --- a/sys/compat/linux/common/linux_socketcall.h +++ b/sys/compat/linux/common/linux_socketcall.h @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socketcall.h,v 1.18 2017/02/03 13:08:08 christos Exp $ */ +/* $NetBSD: linux_socketcall.h,v 1.19 2017/02/03 16:57:39 christos Exp $ */ /*- * Copyright (c) 1995, 1998 The NetBSD Foundation, Inc. @@ -95,7 +95,7 @@ #define LINUX_SYS_RECVMMSG 19 #define LINUX_SYS_SENDMMSG 20 -#define LINUX_MAX_SOCKETCALL 18 /* no send/recv mmsg yet */ +#define LINUX_MAX_SOCKETCALL 20 /* @@ -231,6 +231,21 @@ struct linux_sys_accept4_args { syscallarg(int) flags; }; +struct linux_sys_recvmmsg_args { + syscallarg(int) s; + syscallarg(struct linux_mmsghdr *) msgvec; + syscallarg(unsigned int) vlen; + syscallarg(unsigned int) flags; + syscallarg(struct linux_timespec *) timeout; +}; + +struct linux_sys_sendmmsg_args { + syscallarg(int) s; + syscallarg(struct linux_mmsghdr *) msgvec; + syscallarg(unsigned int) vlen; + syscallarg(unsigned int) flags; +}; + # ifdef _KERNEL __BEGIN_DECLS #define SYS_DEF(foo) int foo(struct lwp *, const struct foo##_args *, register_t *); @@ -250,6 +265,8 @@ SYS_DEF(linux_sys_recv) SYS_DEF(linux_sys_send) SYS_DEF(linux_sys_accept) SYS_DEF(linux_sys_accept4) +SYS_DEF(linux_sys_recvmmsg) +SYS_DEF(linux_sys_sendmmsg) #undef SYS_DEF __END_DECLS # endif /* !_KERNEL */