diff --git a/book/src/kernel/linux-compatibility/README.md b/book/src/kernel/linux-compatibility/README.md index e58b68d5b..0a65cdff4 100644 --- a/book/src/kernel/linux-compatibility/README.md +++ b/book/src/kernel/linux-compatibility/README.md @@ -15,7 +15,7 @@ support the loading of Linux kernel modules. ## System Calls At the time of writing, -Asterinas supports over 225 Linux system calls for the x86-64 architecture, +Asterinas supports over 226 Linux system calls for the x86-64 architecture, which are summarized in the table below. | Numbers | Names | Supported | Flag Coverage | @@ -327,7 +327,7 @@ which are summarized in the table below. | 304 | open_by_handle_at | ❌ | N/A | | 305 | clock_adjtime | ❌ | N/A | | 306 | syncfs | ❌ | N/A | -| 307 | sendmmsg | ❌ | N/A | +| 307 | sendmmsg | ✅ | [⚠️](syscall-flag-coverage/networking-and-sockets/#sendto-and-sendmsg) | | 308 | setns | ✅ | ❓ | | 309 | getcpu | ✅ | 💯 | | 310 | process_vm_readv | ❌ | N/A | diff --git a/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/README.md b/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/README.md index 60b4520bf..924ded93d 100644 --- a/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/README.md +++ b/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/README.md @@ -59,7 +59,7 @@ see [the man page](https://man7.org/linux/man-pages/man2/connect.2.html). ## Socket Communication -### `sendto` and `sendmsg` +### `sendto`, `sendmsg` and `sendmmsg` Supported functionality in SCML: diff --git a/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/sendto_and_sendmsg.scml b/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/sendto_and_sendmsg.scml index 27b001029..e5883d148 100644 --- a/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/sendto_and_sendmsg.scml +++ b/book/src/kernel/linux-compatibility/syscall-flag-coverage/networking-and-sockets/sendto_and_sendmsg.scml @@ -3,6 +3,17 @@ struct sockaddr = { .. }; +struct msg_hdr = { + msg_name = , + msg_control = NULL, + .. +}; + +struct mmsg_hdr = { + hdr = , + msg_len +}; + // Send message on a socket sendto( sockfd, buf, len, @@ -14,10 +25,15 @@ sendto( // Send message using scatter-gather buffers and ancillary data sendmsg( sockfd, - msg = { - msg_name = , - msg_control = NULL, - .. - }, + msg = , + flags = 0 +); + + +// Send multiple messages on a socket +sendmmsg( + sockfd, + mmsgs = [ ], + mmsg_count, flags = 0 ); diff --git a/kernel/src/syscall/arch/loongarch.rs b/kernel/src/syscall/arch/loongarch.rs index dd29cead2..250cc343d 100644 --- a/kernel/src/syscall/arch/loongarch.rs +++ b/kernel/src/syscall/arch/loongarch.rs @@ -109,6 +109,7 @@ use super::{ semget::sys_semget, semop::{sys_semop, sys_semtimedop}, sendfile::sys_sendfile, + sendmmsg::sys_sendmmsg, sendmsg::sys_sendmsg, sendto::sys_sendto, set_ioprio::sys_ioprio_set, @@ -339,6 +340,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_WAIT4 = 260 => sys_wait4(args[..4]); SYS_PRLIMIT64 = 261 => sys_prlimit64(args[..4]); SYS_SETNS = 268 => sys_setns(args[..2]); + SYS_SENDMMSG = 269 => sys_sendmmsg(args[..4]); SYS_SCHED_SETATTR = 274 => sys_sched_setattr(args[..3]); SYS_SCHED_GETATTR = 275 => sys_sched_getattr(args[..4]); SYS_RENAMEAT2 = 276 => sys_renameat2(args[..5]); diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs index 027425f02..f68722ae8 100644 --- a/kernel/src/syscall/arch/riscv.rs +++ b/kernel/src/syscall/arch/riscv.rs @@ -109,6 +109,7 @@ use super::{ semget::sys_semget, semop::{sys_semop, sys_semtimedop}, sendfile::sys_sendfile, + sendmmsg::sys_sendmmsg, sendmsg::sys_sendmsg, sendto::sys_sendto, set_ioprio::sys_ioprio_set, @@ -341,6 +342,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_WAIT4 = 260 => sys_wait4(args[..4]); SYS_PRLIMIT64 = 261 => sys_prlimit64(args[..4]); SYS_SETNS = 268 => sys_setns(args[..2]); + SYS_SENDMMSG = 269 => sys_sendmmsg(args[..4]); SYS_SCHED_SETATTR = 274 => sys_sched_setattr(args[..3]); SYS_SCHED_GETATTR = 275 => sys_sched_getattr(args[..4]); SYS_RENAMEAT2 = 276 => sys_renameat2(args[..5]); diff --git a/kernel/src/syscall/arch/x86.rs b/kernel/src/syscall/arch/x86.rs index a5c29b636..3c1e4c432 100644 --- a/kernel/src/syscall/arch/x86.rs +++ b/kernel/src/syscall/arch/x86.rs @@ -120,6 +120,7 @@ use super::{ semget::sys_semget, semop::{sys_semop, sys_semtimedop}, sendfile::sys_sendfile, + sendmmsg::sys_sendmmsg, sendmsg::sys_sendmsg, sendto::sys_sendto, set_ioprio::sys_ioprio_set, @@ -388,6 +389,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_PREADV = 295 => sys_preadv(args[..5]); SYS_PWRITEV = 296 => sys_pwritev(args[..5]); SYS_PRLIMIT64 = 302 => sys_prlimit64(args[..4]); + SYS_SENDMMSG = 307 => sys_sendmmsg(args[..4]); SYS_SETNS = 308 => sys_setns(args[..2]); SYS_GETCPU = 309 => sys_getcpu(args[..3]); SYS_SCHED_SETATTR = 314 => sys_sched_setattr(args[..3]); diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index b12d15f7a..307c131d1 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -132,6 +132,7 @@ mod semctl; mod semget; mod semop; mod sendfile; +mod sendmmsg; mod sendmsg; mod sendto; mod set_ioprio; diff --git a/kernel/src/syscall/recvmsg.rs b/kernel/src/syscall/recvmsg.rs index db30f8bcd..299ed7282 100644 --- a/kernel/src/syscall/recvmsg.rs +++ b/kernel/src/syscall/recvmsg.rs @@ -47,7 +47,7 @@ pub fn sys_recvmsg( let control_messages = message_header.control_messages(); c_user_msghdr.msg_controllen = - c_user_msghdr.write_control_messages_to_user(control_messages, &user_space)?; + c_user_msghdr.write_control_messages_to_user(control_messages, &user_space)? as _; user_space.write_val(user_msghdr_ptr, &c_user_msghdr)?; diff --git a/kernel/src/syscall/sendmmsg.rs b/kernel/src/syscall/sendmmsg.rs new file mode 100644 index 000000000..103ceaf51 --- /dev/null +++ b/kernel/src/syscall/sendmmsg.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::{ + fs::file_table::FileDesc, + net::socket::{util::SendRecvFlags, Socket}, + prelude::*, + syscall::{sendmsg::send_one_message, SyscallReturn}, + util::net::CUserMsgHdr, +}; + +pub fn sys_sendmmsg( + sockfd: FileDesc, + mmsghdrs_addr: Vaddr, + count: usize, + flags: i32, + ctx: &Context, +) -> Result { + let flags = SendRecvFlags::from_bits(flags) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid send recv flags"))?; + + debug!( + "sockfd = {}, mmsghdrs = {:#x}, count = {}, flags = {:?}", + sockfd, mmsghdrs_addr, count, flags + ); + + if !flags.is_empty() { + warn!("sendmmsg flags {:?} are not supported", flags); + } + + let file = { + // Reading control messages may access the file table, + // so we have to clone the file and drop the file table reference here. + let file_table = ctx.thread_local.borrow_file_table(); + let file_table_locked = file_table.unwrap().read(); + file_table_locked.get_file(sockfd)?.clone() + }; + let socket = file.as_socket_or_err()?; + + let mut sent_msgs = 0; + match send_mmsg_hdrs(socket, mmsghdrs_addr, count, flags, &mut sent_msgs, ctx) { + // Only return error if no packets are sent successfully. + Err(e) if sent_msgs == 0 => Err(e), + _ => Ok(SyscallReturn::Return(sent_msgs as _)), + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +struct CMmsgHdr { + msg_hdr: CUserMsgHdr, + msg_len: u32, +} + +fn send_mmsg_hdrs( + socket: &dyn Socket, + mmsghdrs_addr: Vaddr, + count: usize, + flags: SendRecvFlags, + sent_msgs: &mut usize, + ctx: &Context, +) -> Result<()> { + let user_space = ctx.user_space(); + + for i in 0..count { + let addr = mmsghdrs_addr + core::mem::size_of::() * i; + let mut mmsghdr = user_space.read_val::(addr)?; + + let sent_bytes = send_one_message(socket, &mmsghdr.msg_hdr, &user_space, flags)?; + + mmsghdr.msg_len = sent_bytes as u32; + user_space.write_val(addr, &mmsghdr)?; + + *sent_msgs += 1; + } + + Ok(()) +} diff --git a/kernel/src/syscall/sendmsg.rs b/kernel/src/syscall/sendmsg.rs index f571153b8..28c2c0a5e 100644 --- a/kernel/src/syscall/sendmsg.rs +++ b/kernel/src/syscall/sendmsg.rs @@ -2,8 +2,11 @@ use super::SyscallReturn; use crate::{ - fs::file_table::{get_file_fast, FileDesc}, - net::socket::util::{MessageHeader, SendRecvFlags}, + fs::file_table::FileDesc, + net::socket::{ + util::{MessageHeader, SendRecvFlags}, + Socket, + }, prelude::*, util::net::CUserMsgHdr, }; @@ -23,26 +26,38 @@ pub fn sys_sendmsg( sockfd, c_user_msghdr, flags ); - let message_header = { - let addr = c_user_msghdr.read_socket_addr_from_user()?; - // Reading control messages may access the file table, so it should be called before - // `borrow_file_table_mut`. - let control_messages = c_user_msghdr.read_control_messages_from_user(&user_space)?; - MessageHeader::new(addr, control_messages) + let file = { + // Reading control messages may access the file table, + // so we have to clone the file and drop the file table reference here. + let file_table = ctx.thread_local.borrow_file_table(); + let file_table_locked = file_table.unwrap().read(); + file_table_locked.get_file(sockfd)?.clone() }; - let mut io_vec_reader = c_user_msghdr.copy_reader_array_from_user(&user_space)?; - - let mut file_table = ctx.thread_local.borrow_file_table_mut(); - let file = get_file_fast!(&mut file_table, sockfd); let socket = file.as_socket_or_err()?; - let total_bytes = socket + let total_bytes = send_one_message(socket, &c_user_msghdr, &user_space, flags)?; + + Ok(SyscallReturn::Return(total_bytes as _)) +} + +pub(super) fn send_one_message( + socket: &dyn Socket, + c_user_msghdr: &CUserMsgHdr, + user_space: &CurrentUserSpace, + flags: SendRecvFlags, +) -> Result { + let message_header = { + let addr = c_user_msghdr.read_socket_addr_from_user()?; + let control_messages = c_user_msghdr.read_control_messages_from_user(user_space)?; + MessageHeader::new(addr, control_messages) + }; + let mut io_vec_reader = c_user_msghdr.copy_reader_array_from_user(user_space)?; + + socket .sendmsg(&mut io_vec_reader, message_header, flags) .map_err(|err| match err.error() { // FIXME: `sendmsg` should not be restarted if a timeout has been set on the socket using `setsockopt`. Errno::EINTR => Error::new(Errno::ERESTARTSYS), _ => err, - })?; - - Ok(SyscallReturn::Return(total_bytes as _)) + }) } diff --git a/kernel/src/util/net/socket.rs b/kernel/src/util/net/socket.rs index db0ff5de1..4ade491bb 100644 --- a/kernel/src/util/net/socket.rs +++ b/kernel/src/util/net/socket.rs @@ -87,11 +87,11 @@ pub struct CUserMsgHdr { /// Scatter/Gather iov array pub msg_iov: Vaddr, /// The # of elements in msg_iov - pub msg_iovlen: u32, + pub msg_iovlen: usize, /// Ancillary data pub msg_control: Vaddr, /// Ancillary data buffer length - pub msg_controllen: u32, + pub msg_controllen: usize, /// Flags on received message pub msg_flags: u32, } @@ -129,7 +129,7 @@ impl CUserMsgHdr { return Ok(Vec::new()); } - let mut reader = user_space.reader(self.msg_control, self.msg_controllen as usize)?; + let mut reader = user_space.reader(self.msg_control, self.msg_controllen)?; let control_messages = ControlMessage::read_all_from(&mut reader)?; Ok(control_messages) } @@ -148,7 +148,7 @@ impl CUserMsgHdr { return Ok(0); } - let mut writer = user_space.writer(self.msg_control, self.msg_controllen as usize)?; + let mut writer = user_space.writer(self.msg_control, self.msg_controllen)?; let write_len = ControlMessage::write_all_to(control_messages, &mut writer) as u32; Ok(write_len) } @@ -157,21 +157,21 @@ impl CUserMsgHdr { &self, user_space: &'a CurrentUserSpace<'a>, ) -> Result> { - if self.msg_iovlen as usize > MAX_IO_VECTOR_LENGTH { + if self.msg_iovlen > MAX_IO_VECTOR_LENGTH { return_errno_with_message!(Errno::EMSGSIZE, "the I/O vector contains too many buffers"); } - VmReaderArray::from_user_io_vecs(user_space, self.msg_iov, self.msg_iovlen as usize) + VmReaderArray::from_user_io_vecs(user_space, self.msg_iov, self.msg_iovlen) } pub fn copy_writer_array_from_user<'a>( &self, user_space: &'a CurrentUserSpace<'a>, ) -> Result> { - if self.msg_iovlen as usize > MAX_IO_VECTOR_LENGTH { + if self.msg_iovlen > MAX_IO_VECTOR_LENGTH { return_errno_with_message!(Errno::EMSGSIZE, "the I/O vector contains too many buffers"); } - VmWriterArray::from_user_io_vecs(user_space, self.msg_iov, self.msg_iovlen as usize) + VmWriterArray::from_user_io_vecs(user_space, self.msg_iov, self.msg_iovlen) } } diff --git a/test/src/syscall/ltp/testcases/all.txt b/test/src/syscall/ltp/testcases/all.txt index a679cb5e7..97f4b99c1 100644 --- a/test/src/syscall/ltp/testcases/all.txt +++ b/test/src/syscall/ltp/testcases/all.txt @@ -1324,7 +1324,7 @@ sendfile08_64 # sendmsg02 # sendmsg03 -sendmmsg01 +# sendmmsg01 sendmmsg02 # sendto01