Add syscall sendmmsg

This commit is contained in:
jiangjianfeng 2025-12-04 11:49:49 +00:00 committed by Tate, Hongliang Tian
parent 899e32c452
commit a0042f7d28
12 changed files with 149 additions and 34 deletions

View File

@ -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 |

View File

@ -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:

View File

@ -3,6 +3,17 @@ struct sockaddr = {
..
};
struct msg_hdr = {
msg_name = <sockaddr>,
msg_control = NULL,
..
};
struct mmsg_hdr = {
hdr = <msg_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 = <sockaddr>,
msg_control = NULL,
..
},
msg = <msg_hdr>,
flags = 0
);
// Send multiple messages on a socket
sendmmsg(
sockfd,
mmsgs = [ <mmsg_hdr> ],
mmsg_count,
flags = 0
);

View File

@ -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]);

View File

@ -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]);

View File

@ -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]);

View File

@ -132,6 +132,7 @@ mod semctl;
mod semget;
mod semop;
mod sendfile;
mod sendmmsg;
mod sendmsg;
mod sendto;
mod set_ioprio;

View File

@ -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)?;

View File

@ -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<SyscallReturn> {
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::<CMmsgHdr>() * i;
let mut mmsghdr = user_space.read_val::<CMmsgHdr>(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(())
}

View File

@ -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<usize> {
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 _))
})
}

View File

@ -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<VmReaderArray<'a>> {
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<VmWriterArray<'a>> {
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)
}
}

View File

@ -1324,7 +1324,7 @@ sendfile08_64
# sendmsg02
# sendmsg03
sendmmsg01
# sendmmsg01
sendmmsg02
# sendto01