From 070bdf6f39136ed43c2e168c2688e3e17ef7faf7 Mon Sep 17 00:00:00 2001 From: "zongmin.gu" Date: Thu, 29 Apr 2021 16:46:57 +0800 Subject: [PATCH] Add sendmmsg syscall --- src/libos/src/net/mod.rs | 2 +- src/libos/src/net/socket/mod.rs | 2 +- src/libos/src/net/socket/msg.rs | 7 +++ src/libos/src/net/syscalls.rs | 78 ++++++++++++++++++++++++++++----- src/libos/src/syscall/mod.rs | 6 +-- test/client/main.c | 42 ++++++++++++++++++ test/server/main.c | 25 ++++++++++- 7 files changed, 144 insertions(+), 18 deletions(-) diff --git a/src/libos/src/net/mod.rs b/src/libos/src/net/mod.rs index 553ef7d2..152fb646 100644 --- a/src/libos/src/net/mod.rs +++ b/src/libos/src/net/mod.rs @@ -7,7 +7,7 @@ pub use self::io_multiplexing::{ PollEventFlags, PollFd, THREAD_NOTIFIERS, }; pub use self::socket::{ - msghdr, msghdr_mut, socketpair, unix_socket, AddressFamily, AsUnixSocket, FileFlags, + mmsghdr, msghdr, msghdr_mut, socketpair, unix_socket, AddressFamily, AsUnixSocket, FileFlags, HostSocket, HostSocketType, HowToShut, Iovs, IovsMut, MsgHdr, MsgHdrFlags, MsgHdrMut, RecvFlags, SendFlags, SliceAsLibcIovec, SockAddr, SocketType, UnixAddr, }; diff --git a/src/libos/src/net/socket/mod.rs b/src/libos/src/net/socket/mod.rs index 7fb632e4..38203bcb 100644 --- a/src/libos/src/net/socket/mod.rs +++ b/src/libos/src/net/socket/mod.rs @@ -14,7 +14,7 @@ pub use self::address_family::AddressFamily; pub use self::flags::{FileFlags, MsgHdrFlags, RecvFlags, SendFlags}; pub use self::host::{HostSocket, HostSocketType}; pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec}; -pub use self::msg::{msghdr, msghdr_mut, MsgHdr, MsgHdrMut}; +pub use self::msg::{mmsghdr, msghdr, msghdr_mut, MsgHdr, MsgHdrMut}; pub use self::shutdown::HowToShut; pub use self::socket_address::SockAddr; pub use self::socket_type::SocketType; diff --git a/src/libos/src/net/socket/msg.rs b/src/libos/src/net/socket/msg.rs index 455508b8..522b9102 100644 --- a/src/libos/src/net/socket/msg.rs +++ b/src/libos/src/net/socket/msg.rs @@ -14,6 +14,13 @@ pub struct msghdr { pub msg_flags: c_int, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mmsghdr { + pub msg_hdr: msghdr, + pub msg_len: c_uint, +} + /// C struct for a socket message with mutable pointers #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/libos/src/net/syscalls.rs b/src/libos/src/net/syscalls.rs index f1a12100..2e114955 100644 --- a/src/libos/src/net/syscalls.rs +++ b/src/libos/src/net/syscalls.rs @@ -48,7 +48,7 @@ pub fn do_bind(fd: c_int, addr: *const libc::sockaddr, addr_len: libc::socklen_t trace!("bind to addr: {:?}", unix_addr); unix_socket.bind(&unix_addr)?; } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } Ok(0) @@ -61,7 +61,7 @@ pub fn do_listen(fd: c_int, backlog: c_int) -> Result { } else if let Ok(unix_socket) = file_ref.as_unix_socket() { unix_socket.listen(backlog)?; } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } Ok(0) @@ -99,7 +99,7 @@ pub fn do_connect( unix_socket.connect(&addr)?; } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } Ok(0) @@ -170,7 +170,7 @@ pub fn do_accept4( } Ok(new_fd as isize) } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } } @@ -215,7 +215,7 @@ pub fn do_setsockopt( warn!("setsockopt for unix socket is unimplemented"); Ok(0) } else { - return_errno!(EBADF, "not a socket") + return_errno!(ENOTSOCK, "not a socket") } } @@ -275,7 +275,7 @@ pub fn do_getpeername( } Ok(0) } else { - return_errno!(EBADF, "not a socket") + return_errno!(ENOTSOCK, "not a socket") } } @@ -322,7 +322,7 @@ pub fn do_getsockname( } Ok(0) } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } } @@ -440,7 +440,7 @@ pub fn do_recvfrom( } Ok(data_len as isize) } else { - return_errno!(EBADF, "not a socket"); + return_errno!(ENOTSOCK, "not a socket"); } } @@ -497,9 +497,9 @@ pub fn do_sendmsg(fd: c_int, msg_ptr: *const msghdr, flags_c: c_int) -> Result Re .recvmsg(&mut msg_mut, flags) .map(|bytes_recvd| bytes_recvd as isize) } else if let Ok(socket) = file_ref.as_unix_socket() { - return_errno!(EBADF, "does not support unix socket") + return_errno!(EOPNOTSUPP, "does not support unix socket") } else { - return_errno!(EBADF, "not a socket") + return_errno!(ENOTSOCK, "not a socket") + } +} + +pub fn do_sendmmsg( + fd: c_int, + msgvec_ptr: *mut mmsghdr, + vlen: c_uint, + flags_c: c_int, +) -> Result { + debug!( + "sendmmsg: fd: {}, msg: {:?}, flags: 0x{:x}", + fd, msgvec_ptr, flags_c + ); + + from_user::check_ptr(msgvec_ptr)?; + + let mut msgvec = unsafe { std::slice::from_raw_parts_mut(msgvec_ptr, vlen as usize) }; + let flags = SendFlags::from_bits_truncate(flags_c); + let file_ref = current!().file(fd as FileDesc)?; + + if let Ok(socket) = file_ref.as_host_socket() { + let mut send_count = 0; + for mmsg in (msgvec) { + if !mmsg.msg_hdr.check_member_ptrs().is_ok() { + break; + } + + let msg = unsafe { + if let Ok(msg) = MsgHdr::from_c({ &mmsg.msg_hdr }) { + msg + } else { + break; + } + }; + + if socket + .sendmsg(&msg, flags) + .map(|bytes_sent| { + mmsg.msg_len = bytes_sent as u32; + mmsg.msg_len + }) + .is_ok() + { + send_count += 1; + } else { + break; + } + } + + Ok(send_count as isize) + } else if let Ok(socket) = file_ref.as_unix_socket() { + return_errno!(EOPNOTSUPP, "does not support unix socket") + } else { + return_errno!(ENOTSOCK, "not a socket") } } diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index ca81f297..19f863aa 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -36,8 +36,8 @@ use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t}; use crate::net::{ do_accept, do_accept4, do_bind, do_connect, do_epoll_create, do_epoll_create1, do_epoll_ctl, do_epoll_pwait, do_epoll_wait, do_getpeername, do_getsockname, do_getsockopt, do_listen, - do_poll, do_recvfrom, do_recvmsg, do_select, do_sendmsg, do_sendto, do_setsockopt, do_shutdown, - do_socket, do_socketpair, msghdr, msghdr_mut, + do_poll, do_recvfrom, do_recvmsg, do_select, do_sendmmsg, do_sendmsg, do_sendto, do_setsockopt, + do_shutdown, do_socket, do_socketpair, mmsghdr, msghdr, msghdr_mut, }; use crate::process::{ do_arch_prctl, do_clone, do_exit, do_exit_group, do_futex, do_getegid, do_geteuid, do_getgid, @@ -392,7 +392,7 @@ macro_rules! process_syscall_table_with_callback { (OpenByHandleAt = 304) => handle_unsupported(), (ClockAdjtime = 305) => handle_unsupported(), (Syncfs = 306) => handle_unsupported(), - (Sendmmsg = 307) => handle_unsupported(), + (Sendmmsg = 307) => do_sendmmsg(fd: c_int, msg_ptr: *mut mmsghdr, vlen: c_uint, flags_c: c_int), (Setns = 308) => handle_unsupported(), (Getcpu = 309) => do_getcpu(cpu_ptr: *mut u32, node_ptr: *mut u32), (ProcessVmReadv = 310) => handle_unsupported(), diff --git a/test/client/main.c b/test/client/main.c index 283573c0..3d6a2e92 100644 --- a/test/client/main.c +++ b/test/client/main.c @@ -92,6 +92,43 @@ int client_sendmsg(int server_fd, char *buf) { return ret; } +#ifdef __GLIBC__ + +struct mmsghdr { + struct msghdr msg; + unsigned int len; +}; + +int client_sendmmsg(int server_fd, char *buf) { + int ret = 0; + struct mmsghdr msg_v[2] = {}; + struct iovec iov[1]; + struct msghdr *msg_ptr = &msg_v[0].msg; + + // Set msg0 + msg_ptr->msg_name = NULL; + msg_ptr->msg_namelen = 0; + iov[0].iov_base = buf; + iov[0].iov_len = strlen(buf); + msg_ptr->msg_iov = iov; + msg_ptr->msg_iovlen = 1; + msg_ptr->msg_control = 0; + msg_ptr->msg_controllen = 0; + msg_ptr->msg_flags = 0; + + // Set msg1 + msg_v[1] = msg_v[0]; + msg_ptr = &msg_v[1].msg; + msg_ptr->msg_iov = NULL; + msg_ptr->msg_iovlen = 0; + + ret = sendmmsg(server_fd, msg_v, 2, 0); + if (ret != 2 || msg_v[0].len <= 0 || msg_v[1].len != 0) { + THROW_ERROR("sendmsg failed"); + } +} +#endif + int client_connectionless_sendmsg(char *buf) { int ret = 0; struct msghdr msg; @@ -148,7 +185,12 @@ int main(int argc, const char *argv[]) { neogotiate_msg(server_fd, buf, buf_size); ret = client_sendmsg(server_fd, buf); break; +#ifdef __GLIBC__ case 8803: + neogotiate_msg(server_fd, buf, buf_size); + ret = client_sendmmsg(server_fd, buf); +#endif + case 8804: ret = client_connectionless_sendmsg(DEFAULT_MSG); break; default: diff --git a/test/server/main.c b/test/server/main.c index a97ea6a2..3176b23a 100644 --- a/test/server/main.c +++ b/test/server/main.c @@ -268,13 +268,33 @@ int test_sendmsg_recvmsg() { return ret; } +int test_sendmmsg_recvmsg() { + int ret = 0; + int child_pid = 0; + int client_fd = connect_with_child(8803, &child_pid); + if (client_fd < 0) { + THROW_ERROR("connect failed"); + } + + if (neogotiate_msg(client_fd) < 0) { + THROW_ERROR("neogotiate failed"); + } + + ret = server_recvmsg(client_fd); + if (ret < 0) { return -1; } + + ret = wait_for_child_exit(child_pid); + + return ret; +} + int test_sendmsg_recvmsg_connectionless() { int ret = 0; int child_pid = 0; signal(SIGCHLD, proc_exit); - char *client_argv[] = {"client", "NULL", "8803", NULL}; + char *client_argv[] = {"client", "NULL", "8804", NULL}; ret = posix_spawn(&child_pid, "/bin/client", NULL, NULL, client_argv, NULL); if (ret < 0) { THROW_ERROR("spawn client process error"); @@ -383,6 +403,9 @@ static test_case_t test_cases[] = { TEST_CASE(test_read_write), TEST_CASE(test_send_recv), TEST_CASE(test_sendmsg_recvmsg), +#ifdef __GLIBC__ + TEST_CASE(test_sendmmsg_recvmsg), +#endif TEST_CASE(test_sendmsg_recvmsg_connectionless), TEST_CASE(test_fcntl_setfl_and_getfl), TEST_CASE(test_poll),