Commit 3605dd307f

Hiroaki Nakamura <hnakamur@gmail.com>
2022-03-03 21:13:54
os/linux/io_uring: add recvmsg and sendmsg (#10212)
* os/linux/io_uring: add recvmsg and sendmsg * Use std.os.iovec and std.os.iovec_const * Remove msg_ prefix in msghdr and msghdr_const in arm64 etc * Strip msg_ prefix in msghdr and msghdr_const for linux arm-eabi * Copy msghdr and msghdr_const from i386 to mips * Add sockaddr to lib/std/os/linux/mips.zig * Copy msghdr and msghdr_const from x86_64 to riscv64
1 parent e91c16e
lib/std/os/linux/arm-eabi.zig
@@ -648,23 +648,23 @@ pub const Flock = extern struct {
 };
 
 pub const msghdr = extern struct {
-    msg_name: ?*sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec,
-    msg_iovlen: i32,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
-    msg_flags: i32,
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: i32,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
 };
 
 pub const msghdr_const = extern struct {
-    msg_name: ?*const sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec_const,
-    msg_iovlen: i32,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
-    msg_flags: i32,
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: i32,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
 };
 
 pub const blksize_t = i32;
lib/std/os/linux/arm64.zig
@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
 const linux = std.os.linux;
 const socklen_t = linux.socklen_t;
 const sockaddr = linux.sockaddr;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const uid_t = linux.uid_t;
 const gid_t = linux.gid_t;
 const pid_t = linux.pid_t;
@@ -499,27 +499,27 @@ pub const Flock = extern struct {
 };
 
 pub const msghdr = extern struct {
-    msg_name: ?*sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec,
-    msg_iovlen: i32,
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: i32,
     __pad1: i32 = 0,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
+    control: ?*anyopaque,
+    controllen: socklen_t,
     __pad2: socklen_t = 0,
-    msg_flags: i32,
+    flags: i32,
 };
 
 pub const msghdr_const = extern struct {
-    msg_name: ?*const sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec_const,
-    msg_iovlen: i32,
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: i32,
     __pad1: i32 = 0,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
+    control: ?*anyopaque,
+    controllen: socklen_t,
     __pad2: socklen_t = 0,
-    msg_flags: i32,
+    flags: i32,
 };
 
 pub const blksize_t = i32;
lib/std/os/linux/i386.zig
@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
 const maxInt = std.math.maxInt;
 const linux = std.os.linux;
 const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const uid_t = linux.uid_t;
 const gid_t = linux.gid_t;
 const pid_t = linux.pid_t;
lib/std/os/linux/io_uring.zig
@@ -537,6 +537,36 @@ pub const IO_Uring = struct {
         return sqe;
     }
 
+    /// Queues (but does not submit) an SQE to perform a `recvmsg(2)`.
+    /// Returns a pointer to the SQE.
+    pub fn recvmsg(
+        self: *IO_Uring,
+        user_data: u64,
+        fd: os.fd_t,
+        msg: *os.msghdr,
+        flags: u32,
+    ) !*io_uring_sqe {
+        const sqe = try self.get_sqe();
+        io_uring_prep_recvmsg(sqe, fd, msg, flags);
+        sqe.user_data = user_data;
+        return sqe;
+    }
+
+    /// Queues (but does not submit) an SQE to perform a `sendmsg(2)`.
+    /// Returns a pointer to the SQE.
+    pub fn sendmsg(
+        self: *IO_Uring,
+        user_data: u64,
+        fd: os.fd_t,
+        msg: *const os.msghdr_const,
+        flags: u32,
+    ) !*io_uring_sqe {
+        const sqe = try self.get_sqe();
+        io_uring_prep_sendmsg(sqe, fd, msg, flags);
+        sqe.user_data = user_data;
+        return sqe;
+    }
+
     /// Queues (but does not submit) an SQE to perform an `openat(2)`.
     /// Returns a pointer to the SQE.
     pub fn openat(
@@ -1237,6 +1267,26 @@ pub fn io_uring_prep_send(sqe: *io_uring_sqe, fd: os.fd_t, buffer: []const u8, f
     sqe.rw_flags = flags;
 }
 
+pub fn io_uring_prep_recvmsg(
+    sqe: *io_uring_sqe,
+    fd: os.fd_t,
+    msg: *os.msghdr,
+    flags: u32,
+) void {
+    linux.io_uring_prep_rw(.RECVMSG, sqe, fd, @ptrToInt(msg), 1, 0);
+    sqe.rw_flags = flags;
+}
+
+pub fn io_uring_prep_sendmsg(
+    sqe: *io_uring_sqe,
+    fd: os.fd_t,
+    msg: *const os.msghdr_const,
+    flags: u32,
+) void {
+    linux.io_uring_prep_rw(.SENDMSG, sqe, fd, @ptrToInt(msg), 1, 0);
+    sqe.rw_flags = flags;
+}
+
 pub fn io_uring_prep_openat(
     sqe: *io_uring_sqe,
     fd: os.fd_t,
@@ -1908,6 +1958,88 @@ test "accept/connect/send/recv" {
     try testing.expectEqualSlices(u8, buffer_send[0..buffer_recv.len], buffer_recv[0..]);
 }
 
+test "sendmsg/recvmsg" {
+    if (builtin.os.tag != .linux) return error.SkipZigTest;
+
+    var ring = IO_Uring.init(2, 0) catch |err| switch (err) {
+        error.SystemOutdated => return error.SkipZigTest,
+        error.PermissionDenied => return error.SkipZigTest,
+        else => return err,
+    };
+    defer ring.deinit();
+
+    const address_server = try net.Address.parseIp4("127.0.0.1", 3131);
+
+    const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
+    defer os.close(server);
+    try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
+    try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
+    try os.bind(server, &address_server.any, address_server.getOsSockLen());
+
+    const client = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
+    defer os.close(client);
+
+    const buffer_send = [_]u8{42} ** 128;
+    var iovecs_send = [_]os.iovec_const{
+        os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len },
+    };
+    const msg_send = os.msghdr_const{
+        .name = &address_server.any,
+        .namelen = address_server.getOsSockLen(),
+        .iov = &iovecs_send,
+        .iovlen = 1,
+        .control = null,
+        .controllen = 0,
+        .flags = 0,
+    };
+    const sqe_sendmsg = try ring.sendmsg(0x11111111, client, &msg_send, 0);
+    sqe_sendmsg.flags |= linux.IOSQE_IO_LINK;
+    try testing.expectEqual(linux.IORING_OP.SENDMSG, sqe_sendmsg.opcode);
+    try testing.expectEqual(client, sqe_sendmsg.fd);
+
+    var buffer_recv = [_]u8{0} ** 128;
+    var iovecs_recv = [_]os.iovec{
+        os.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len },
+    };
+    var addr = [_]u8{0} ** 4;
+    var address_recv = net.Address.initIp4(addr, 0);
+    var msg_recv: os.msghdr = os.msghdr{
+        .name = &address_recv.any,
+        .namelen = address_recv.getOsSockLen(),
+        .iov = &iovecs_recv,
+        .iovlen = 1,
+        .control = null,
+        .controllen = 0,
+        .flags = 0,
+    };
+    const sqe_recvmsg = try ring.recvmsg(0x22222222, server, &msg_recv, 0);
+    try testing.expectEqual(linux.IORING_OP.RECVMSG, sqe_recvmsg.opcode);
+    try testing.expectEqual(server, sqe_recvmsg.fd);
+
+    try testing.expectEqual(@as(u32, 2), ring.sq_ready());
+    try testing.expectEqual(@as(u32, 2), try ring.submit_and_wait(2));
+    try testing.expectEqual(@as(u32, 0), ring.sq_ready());
+    try testing.expectEqual(@as(u32, 2), ring.cq_ready());
+
+    const cqe_sendmsg = try ring.copy_cqe();
+    if (cqe_sendmsg.res == -@as(i32, @enumToInt(linux.E.INVAL))) return error.SkipZigTest;
+    try testing.expectEqual(linux.io_uring_cqe{
+        .user_data = 0x11111111,
+        .res = buffer_send.len,
+        .flags = 0,
+    }, cqe_sendmsg);
+
+    const cqe_recvmsg = try ring.copy_cqe();
+    if (cqe_recvmsg.res == -@as(i32, @enumToInt(linux.E.INVAL))) return error.SkipZigTest;
+    try testing.expectEqual(linux.io_uring_cqe{
+        .user_data = 0x22222222,
+        .res = buffer_recv.len,
+        .flags = 0,
+    }, cqe_recvmsg);
+
+    try testing.expectEqualSlices(u8, buffer_send[0..buffer_recv.len], buffer_recv[0..]);
+}
+
 test "timeout (after a relative time)" {
     if (builtin.os.tag != .linux) return error.SkipZigTest;
 
lib/std/os/linux/mips.zig
@@ -2,11 +2,12 @@ const std = @import("../../std.zig");
 const maxInt = std.math.maxInt;
 const linux = std.os.linux;
 const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const uid_t = linux.uid_t;
 const gid_t = linux.gid_t;
 const pid_t = linux.pid_t;
+const sockaddr = linux.sockaddr;
 const timespec = linux.timespec;
 
 pub fn syscall0(number: SYS) usize {
@@ -716,6 +717,26 @@ pub const Flock = extern struct {
     __unused: [4]u8,
 };
 
+pub const msghdr = extern struct {
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: i32,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: i32,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
+};
+
 pub const blksize_t = i32;
 pub const nlink_t = u32;
 pub const time_t = i32;
lib/std/os/linux/powerpc.zig
@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
 const maxInt = std.math.maxInt;
 const linux = std.os.linux;
 const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const uid_t = linux.uid_t;
 const gid_t = linux.gid_t;
 const pid_t = linux.pid_t;
@@ -651,23 +651,23 @@ pub const Flock = extern struct {
 };
 
 pub const msghdr = extern struct {
-    msg_name: ?*sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec,
-    msg_iovlen: usize,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
-    msg_flags: i32,
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: usize,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
 };
 
 pub const msghdr_const = extern struct {
-    msg_name: ?*const sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec_const,
-    msg_iovlen: usize,
-    msg_control: ?*anyopaque,
-    msg_controllen: socklen_t,
-    msg_flags: i32,
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: usize,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    flags: i32,
 };
 
 pub const blksize_t = i32;
lib/std/os/linux/powerpc64.zig
@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
 const maxInt = std.math.maxInt;
 const linux = std.os.linux;
 const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const uid_t = linux.uid_t;
 const gid_t = linux.gid_t;
 const pid_t = linux.pid_t;
@@ -626,23 +626,23 @@ pub const Flock = extern struct {
 };
 
 pub const msghdr = extern struct {
-    msg_name: ?*sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec,
-    msg_iovlen: usize,
-    msg_control: ?*anyopaque,
-    msg_controllen: usize,
-    msg_flags: i32,
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: usize,
+    control: ?*anyopaque,
+    controllen: usize,
+    flags: i32,
 };
 
 pub const msghdr_const = extern struct {
-    msg_name: ?*const sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec_const,
-    msg_iovlen: usize,
-    msg_control: ?*anyopaque,
-    msg_controllen: usize,
-    msg_flags: i32,
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: usize,
+    control: ?*anyopaque,
+    controllen: usize,
+    flags: i32,
 };
 
 pub const blksize_t = i64;
lib/std/os/linux/riscv64.zig
@@ -1,7 +1,12 @@
 const std = @import("../../std.zig");
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
+const linux = std.os.linux;
 const uid_t = std.os.linux.uid_t;
 const gid_t = std.os.linux.gid_t;
 const pid_t = std.os.linux.pid_t;
+const sockaddr = linux.sockaddr;
+const socklen_t = linux.socklen_t;
 const timespec = std.os.linux.timespec;
 
 pub fn syscall0(number: SYS) usize {
@@ -488,6 +493,30 @@ pub const Flock = extern struct {
     __unused: [4]u8,
 };
 
+pub const msghdr = extern struct {
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: i32,
+    __pad1: i32 = 0,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    __pad2: socklen_t = 0,
+    flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: i32,
+    __pad1: i32 = 0,
+    control: ?*anyopaque,
+    controllen: socklen_t,
+    __pad2: socklen_t = 0,
+    flags: i32,
+};
+
 // The `stat` definition used by the Linux kernel.
 pub const Stat = extern struct {
     dev: dev_t,
lib/std/os/linux/sparc64.zig
@@ -9,8 +9,8 @@ const sigset_t = linux.sigset_t;
 const linux = std.os.linux;
 const sockaddr = linux.sockaddr;
 const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-const iovec_const = linux.iovec_const;
+const iovec = std.os.iovec;
+const iovec_const = std.os.iovec_const;
 const timespec = linux.timespec;
 
 pub fn syscall_pipe(fd: *[2]i32) usize {
@@ -656,23 +656,23 @@ pub const Flock = extern struct {
 };
 
 pub const msghdr = extern struct {
-    msg_name: ?*sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec,
-    msg_iovlen: u64,
-    msg_control: ?*anyopaque,
-    msg_controllen: u64,
-    msg_flags: i32,
+    name: ?*sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec,
+    iovlen: u64,
+    control: ?*anyopaque,
+    controllen: u64,
+    flags: i32,
 };
 
 pub const msghdr_const = extern struct {
-    msg_name: ?*const sockaddr,
-    msg_namelen: socklen_t,
-    msg_iov: [*]iovec_const,
-    msg_iovlen: u64,
-    msg_control: ?*anyopaque,
-    msg_controllen: u64,
-    msg_flags: i32,
+    name: ?*const sockaddr,
+    namelen: socklen_t,
+    iov: [*]iovec_const,
+    iovlen: u64,
+    control: ?*anyopaque,
+    controllen: u64,
+    flags: i32,
 };
 
 pub const off_t = i64;