From 41da3165db90a230d6d0e7d000cc816f6a5ec436 Mon Sep 17 00:00:00 2001 From: dchagin Date: Sat, 9 Jan 2016 17:50:13 +0000 Subject: [PATCH] MFC r283488: Implement recvmmsg() and sendmmsg() system calls. git-svn-id: svn://svn.freebsd.org/base/stable/10@293588 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/amd64/linux/linux_dummy.c | 2 - sys/amd64/linux/syscalls.master | 8 +- sys/amd64/linux32/linux32_dummy.c | 2 - sys/amd64/linux32/syscalls.master | 8 +- sys/compat/linux/linux_socket.c | 201 +++++++++++++++++++++++------- sys/compat/linux/linux_socket.h | 9 ++ sys/i386/linux/linux_dummy.c | 2 - sys/i386/linux/syscalls.master | 8 +- 8 files changed, 186 insertions(+), 54 deletions(-) diff --git a/sys/amd64/linux/linux_dummy.c b/sys/amd64/linux/linux_dummy.c index e7653103e..f8910d84c 100644 --- a/sys/amd64/linux/linux_dummy.c +++ b/sys/amd64/linux/linux_dummy.c @@ -108,14 +108,12 @@ DUMMY(preadv); DUMMY(pwritev); DUMMY(rt_tsigqueueinfo); DUMMY(perf_event_open); -DUMMY(recvmmsg); DUMMY(fanotify_init); DUMMY(fanotify_mark); DUMMY(name_to_handle_at); DUMMY(open_by_handle_at); DUMMY(clock_adjtime); DUMMY(syncfs); -DUMMY(sendmmsg); DUMMY(setns); DUMMY(process_vm_readv); DUMMY(process_vm_writev); diff --git a/sys/amd64/linux/syscalls.master b/sys/amd64/linux/syscalls.master index 0a23114a9..aa6338b18 100644 --- a/sys/amd64/linux/syscalls.master +++ b/sys/amd64/linux/syscalls.master @@ -492,7 +492,9 @@ 296 AUE_NULL STD { int linux_pwritev(void); } 297 AUE_NULL STD { int linux_rt_tsigqueueinfo(void); } 298 AUE_NULL STD { int linux_perf_event_open(void); } -299 AUE_NULL STD { int linux_recvmmsg(void); } +299 AUE_NULL STD { int linux_recvmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags, struct l_timespec *timeout); } 300 AUE_NULL STD { int linux_fanotify_init(void); } 301 AUE_NULL STD { int linux_fanotify_mark(void); } 302 AUE_NULL STD { int linux_prlimit64(l_pid_t pid, l_uint resource, \ @@ -501,7 +503,9 @@ 304 AUE_NULL STD { int linux_open_by_handle_at(void); } 305 AUE_NULL STD { int linux_clock_adjtime(void); } 306 AUE_NULL STD { int linux_syncfs(void); } -307 AUE_NULL STD { int linux_sendmmsg(void); } +307 AUE_NULL STD { int linux_sendmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags); } 308 AUE_NULL STD { int linux_setns(void); } 309 AUE_NULL STD { int linux_process_vm_readv(void); } 310 AUE_NULL STD { int linux_process_vm_writev(void); } diff --git a/sys/amd64/linux32/linux32_dummy.c b/sys/amd64/linux32/linux32_dummy.c index 96f3299e2..0022e057b 100644 --- a/sys/amd64/linux32/linux32_dummy.c +++ b/sys/amd64/linux32/linux32_dummy.c @@ -119,7 +119,6 @@ DUMMY(pwritev); DUMMY(rt_tsigqueueinfo); DUMMY(perf_event_open); /* linux 2.6.33: */ -DUMMY(recvmmsg); DUMMY(fanotify_init); DUMMY(fanotify_mark); /* later: */ @@ -127,7 +126,6 @@ DUMMY(name_to_handle_at); DUMMY(open_by_handle_at); DUMMY(clock_adjtime); DUMMY(syncfs); -DUMMY(sendmmsg); DUMMY(setns); DUMMY(process_vm_readv); DUMMY(process_vm_writev); diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master index caf935788..05d2c0ebe 100644 --- a/sys/amd64/linux32/syscalls.master +++ b/sys/amd64/linux32/syscalls.master @@ -559,7 +559,9 @@ 335 AUE_NULL STD { int linux_rt_tsigqueueinfo(void); } 336 AUE_NULL STD { int linux_perf_event_open(void); } ; linux 2.6.33: -337 AUE_NULL STD { int linux_recvmmsg(void); } +337 AUE_NULL STD { int linux_recvmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags, struct l_timespec *timeout); } 338 AUE_NULL STD { int linux_fanotify_init(void); } 339 AUE_NULL STD { int linux_fanotify_mark(void); } ; linux 2.6.36: @@ -572,7 +574,9 @@ 342 AUE_NULL STD { int linux_open_by_handle_at(void); } 343 AUE_NULL STD { int linux_clock_adjtime(void); } 344 AUE_NULL STD { int linux_syncfs(void); } -345 AUE_NULL STD { int linux_sendmmsg(void); } +345 AUE_NULL STD { int linux_sendmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags); } 346 AUE_NULL STD { int linux_setns(void); } 347 AUE_NULL STD { int linux_process_vm_readv(void); } 348 AUE_NULL STD { int linux_process_vm_writev(void); } diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 5d986aba8..5a6c5c258 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -72,9 +72,14 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include #include static int linux_to_bsd_domain(int); +static int linux_sendmsg_common(struct thread *, l_int, struct l_msghdr *, + l_uint); +static int linux_recvmsg_common(struct thread *, l_int, struct l_msghdr *, + l_uint, struct msghdr *); /* * Reads a linux sockaddr and does any necessary translation. @@ -1073,8 +1078,9 @@ linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args) return (error); } -int -linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) +static int +linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr, + l_uint flags) { struct cmsghdr *cmsg; struct cmsgcred cmcred; @@ -1090,8 +1096,8 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) void *data; int error; - error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg)); - if (error) + error = copyin(msghdr, &linux_msg, sizeof(linux_msg)); + if (error != 0) return (error); /* @@ -1105,7 +1111,7 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) linux_msg.msg_control = PTROUT(NULL); error = linux_to_bsd_msghdr(&msg, &linux_msg); - if (error) + if (error != 0) return (error); #ifdef COMPAT_LINUX32 @@ -1114,21 +1120,21 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) #else error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); #endif - if (error) + if (error != 0) return (error); control = NULL; cmsg = NULL; if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) { - error = kern_getsockname(td, args->s, &sa, &datalen); - if (error) + error = kern_getsockname(td, s, &sa, &datalen); + if (error != 0) goto bad; sa_family = sa->sa_family; free(sa, M_SONAME); error = ENOBUFS; - cmsg = malloc(CMSG_HDRSZ, M_LINUX, M_WAITOK | M_ZERO); + cmsg = malloc(CMSG_HDRSZ, M_LINUX, M_WAITOK|M_ZERO); control = m_get(M_WAITOK, MT_CONTROL); if (control == NULL) goto bad; @@ -1136,7 +1142,7 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) do { error = copyin(ptr_cmsg, &linux_cmsg, sizeof(struct l_cmsghdr)); - if (error) + if (error != 0) goto bad; error = EINVAL; @@ -1200,8 +1206,7 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) msg.msg_iov = iov; msg.msg_flags = 0; - error = linux_sendit(td, args->s, &msg, args->flags, control, - UIO_USERSPACE); + error = linux_sendit(td, s, &msg, flags, control, UIO_USERSPACE); bad: free(iov, M_IOV); @@ -1211,11 +1216,49 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) } int -linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) +linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) +{ + + return (linux_sendmsg_common(td, args->s, PTRIN(args->msg), + args->flags)); +} + +int +linux_sendmmsg(struct thread *td, struct linux_sendmmsg_args *args) +{ + struct l_mmsghdr *msg; + l_uint retval; + int error, datagrams; + + if (args->vlen > UIO_MAXIOV) + args->vlen = UIO_MAXIOV; + + msg = PTRIN(args->msg); + datagrams = 0; + while (datagrams < args->vlen) { + error = linux_sendmsg_common(td, args->s, &msg->msg_hdr, + args->flags); + if (error != 0) + break; + + retval = td->td_retval[0]; + error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len)); + if (error != 0) + break; + ++msg; + ++datagrams; + } + if (error == 0) + td->td_retval[0] = datagrams; + return (error); +} + +static int +linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr, + l_uint flags, struct msghdr *msg) { struct cmsghdr *cm; struct cmsgcred *cmcred; - struct msghdr msg; struct l_cmsghdr *linux_cmsg = NULL; struct l_ucred linux_ucred; socklen_t datalen, outlen; @@ -1227,51 +1270,51 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) void *data; int error, i, fd, fds, *fdp; - error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg)); - if (error) + error = copyin(msghdr, &linux_msg, sizeof(linux_msg)); + if (error != 0) return (error); - error = linux_to_bsd_msghdr(&msg, &linux_msg); - if (error) + error = linux_to_bsd_msghdr(msg, &linux_msg); + if (error != 0) return (error); #ifdef COMPAT_LINUX32 - error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen, + error = linux32_copyiniov(PTRIN(msg->msg_iov), msg->msg_iovlen, &iov, EMSGSIZE); #else - error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); + error = copyiniov(msg->msg_iov, msg->msg_iovlen, &iov, EMSGSIZE); #endif - if (error) + if (error != 0) return (error); - if (msg.msg_name) { - error = linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name, - msg.msg_namelen); - if (error) + if (msg->msg_name) { + error = linux_to_bsd_sockaddr((struct sockaddr *)msg->msg_name, + msg->msg_namelen); + if (error != 0) goto bad; } - uiov = msg.msg_iov; - msg.msg_iov = iov; - controlp = (msg.msg_control != NULL) ? &control : NULL; - error = kern_recvit(td, args->s, &msg, UIO_USERSPACE, controlp); - msg.msg_iov = uiov; - if (error) + uiov = msg->msg_iov; + msg->msg_iov = iov; + controlp = (msg->msg_control != NULL) ? &control : NULL; + error = kern_recvit(td, s, msg, UIO_USERSPACE, controlp); + msg->msg_iov = uiov; + if (error != 0) goto bad; - error = bsd_to_linux_msghdr(&msg, &linux_msg); - if (error) + error = bsd_to_linux_msghdr(msg, &linux_msg); + if (error != 0) goto bad; if (linux_msg.msg_name) { error = bsd_to_linux_sockaddr((struct sockaddr *) PTRIN(linux_msg.msg_name)); - if (error) + if (error != 0) goto bad; } if (linux_msg.msg_name && linux_msg.msg_namelen > 2) { error = linux_sa_put(PTRIN(linux_msg.msg_name)); - if (error) + if (error != 0) goto bad; } @@ -1281,10 +1324,10 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) if (control) { linux_cmsg = malloc(L_CMSG_HDRSZ, M_LINUX, M_WAITOK | M_ZERO); - msg.msg_control = mtod(control, struct cmsghdr *); - msg.msg_controllen = control->m_len; + msg->msg_control = mtod(control, struct cmsghdr *); + msg->msg_controllen = control->m_len; - cm = CMSG_FIRSTHDR(&msg); + cm = CMSG_FIRSTHDR(msg); while (cm != NULL) { linux_cmsg->cmsg_type = @@ -1304,7 +1347,7 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) switch (cm->cmsg_type) { case SCM_RIGHTS: - if (args->flags & LINUX_MSG_CMSG_CLOEXEC) { + if (flags & LINUX_MSG_CMSG_CLOEXEC) { fds = datalen / sizeof(int); fdp = data; for (i = 0; i < fds; i++) { @@ -1361,13 +1404,13 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) outbuf += LINUX_CMSG_ALIGN(datalen); outlen += LINUX_CMSG_LEN(datalen); - cm = CMSG_NXTHDR(&msg, cm); + cm = CMSG_NXTHDR(msg, cm); } } out: linux_msg.msg_controllen = outlen; - error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg)); + error = copyout(&linux_msg, msghdr, sizeof(linux_msg)); bad: free(iov, M_IOV); @@ -1377,6 +1420,75 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) return (error); } +int +linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) +{ + struct msghdr bsd_msg; + + return (linux_recvmsg_common(td, args->s, PTRIN(args->msg), + args->flags, &bsd_msg)); +} + +int +linux_recvmmsg(struct thread *td, struct linux_recvmmsg_args *args) +{ + struct l_mmsghdr *msg; + struct msghdr bsd_msg; + struct l_timespec lts; + struct timespec ts, tts; + l_uint retval; + int error, datagrams; + + if (args->timeout) { + error = copyin(args->timeout, <s, sizeof(struct l_timespec)); + if (error != 0) + return (error); + error = linux_to_native_timespec(&ts, <s); + if (error != 0) + return (error); + getnanotime(&tts); + timespecadd(&tts, &ts); + } + + msg = PTRIN(args->msg); + datagrams = 0; + while (datagrams < args->vlen) { + error = linux_recvmsg_common(td, args->s, &msg->msg_hdr, + args->flags & ~LINUX_MSG_WAITFORONE, &bsd_msg); + if (error != 0) + break; + + retval = td->td_retval[0]; + error = copyout(&retval, &msg->msg_len, sizeof(msg->msg_len)); + if (error != 0) + break; + ++msg; + ++datagrams; + + /* + * MSG_WAITFORONE turns on MSG_DONTWAIT after one packet. + */ + if (args->flags & LINUX_MSG_WAITFORONE) + args->flags |= LINUX_MSG_DONTWAIT; + + /* + * See BUGS section of recvmmsg(2). + */ + if (args->timeout) { + getnanotime(&ts); + timespecsub(&ts, &tts); + if (!timespecisset(&ts) || ts.tv_sec > 0) + break; + } + /* Out of band data, return right away. */ + if (bsd_msg.msg_flags & MSG_OOB) + break; + } + if (error == 0) + td->td_retval[0] = datagrams; + return (error); +} + int linux_shutdown(struct thread *td, struct linux_shutdown_args *args) { @@ -1555,7 +1667,8 @@ static const unsigned char lxs_args[] = { LINUX_AL(6) /* recvfrom */, LINUX_AL(2) /* shutdown */, LINUX_AL(5) /* setsockopt */, LINUX_AL(5) /* getsockopt */, LINUX_AL(3) /* sendmsg */, LINUX_AL(3) /* recvmsg */, - LINUX_AL(4) /* accept4 */ + LINUX_AL(4) /* accept4 */, LINUX_AL(5) /* recvmmsg */, + LINUX_AL(4) /* sendmmsg */ }; #define LINUX_AL_SIZE sizeof(lxs_args) / sizeof(lxs_args[0]) - 1 @@ -1611,6 +1724,10 @@ linux_socketcall(struct thread *td, struct linux_socketcall_args *args) return (linux_recvmsg(td, arg)); case LINUX_ACCEPT4: return (linux_accept4(td, arg)); + case LINUX_RECVMMSG: + return (linux_recvmmsg(td, arg)); + case LINUX_SENDMMSG: + return (linux_sendmmsg(td, arg)); } uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what); diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h index 6ee78eecd..9d1399188 100644 --- a/sys/compat/linux/linux_socket.h +++ b/sys/compat/linux/linux_socket.h @@ -48,6 +48,7 @@ #define LINUX_MSG_RST 0x1000 #define LINUX_MSG_ERRQUEUE 0x2000 #define LINUX_MSG_NOSIGNAL 0x4000 +#define LINUX_MSG_WAITFORONE 0x10000 #define LINUX_MSG_CMSG_CLOEXEC 0x40000000 /* Socket-level control message types */ @@ -65,6 +66,12 @@ struct l_msghdr { l_uint msg_flags; }; +struct l_mmsghdr { + struct l_msghdr msg_hdr; + l_uint msg_len; + +}; + struct l_cmsghdr { l_size_t cmsg_len; l_int cmsg_level; @@ -279,6 +286,8 @@ int linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args); #define LINUX_SENDMSG 16 #define LINUX_RECVMSG 17 #define LINUX_ACCEPT4 18 +#define LINUX_RECVMMSG 19 +#define LINUX_SENDMMSG 20 /* Socket options */ #define LINUX_IP_TOS 1 diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c index 74452f699..ed552b366 100644 --- a/sys/i386/linux/linux_dummy.c +++ b/sys/i386/linux/linux_dummy.c @@ -115,7 +115,6 @@ DUMMY(pwritev); DUMMY(rt_tsigqueueinfo); DUMMY(perf_event_open); /* linux 2.6.33: */ -DUMMY(recvmmsg); DUMMY(fanotify_init); DUMMY(fanotify_mark); /* later: */ @@ -123,7 +122,6 @@ DUMMY(name_to_handle_at); DUMMY(open_by_handle_at); DUMMY(clock_adjtime); DUMMY(syncfs); -DUMMY(sendmmsg); DUMMY(setns); DUMMY(process_vm_readv); DUMMY(process_vm_writev); diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master index 5cb57f5d9..4a70b6b92 100644 --- a/sys/i386/linux/syscalls.master +++ b/sys/i386/linux/syscalls.master @@ -567,7 +567,9 @@ 335 AUE_NULL STD { int linux_rt_tsigqueueinfo(void); } 336 AUE_NULL STD { int linux_perf_event_open(void); } ; linux 2.6.33: -337 AUE_NULL STD { int linux_recvmmsg(void); } +337 AUE_NULL STD { int linux_recvmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags, struct l_timespec *timeout); } 338 AUE_NULL STD { int linux_fanotify_init(void); } 339 AUE_NULL STD { int linux_fanotify_mark(void); } ; linux 2.6.36: @@ -580,7 +582,9 @@ 342 AUE_NULL STD { int linux_open_by_handle_at(void); } 343 AUE_NULL STD { int linux_clock_adjtime(void); } 344 AUE_NULL STD { int linux_syncfs(void); } -345 AUE_NULL STD { int linux_sendmmsg(void); } +345 AUE_NULL STD { int linux_sendmmsg(l_int s, \ + struct l_mmsghdr *msg, l_uint vlen, \ + l_uint flags); } 346 AUE_NULL STD { int linux_setns(void); } 347 AUE_NULL STD { int linux_process_vm_readv(void); } 348 AUE_NULL STD { int linux_process_vm_writev(void); } -- 2.45.0