authorgravatar for joran@ronomon.comJoran Dirk Greef <joran@ronomon.com> 2020-10-04 13:15:39+02:00
committergravatar for joran@ronomon.comJoran Dirk Greef <joran@ronomon.com> 2020-10-04 13:15:39+02:00
log6a53f4be4b349ea8ebef6f7df3ecb333142d70d7
tree93a0b863c5ecca0ba5e6041ff23aa734388a8292
parent3d2de6cfbacb139ee101202e786b9355be92f960

Add openat(), close(), connect(), send(), recv(), as well as tests

Removes non-essential .hardlink_with_next_sqe() and .drain_previous_sqes().

1 files changed, 198 insertions(+), 15 deletions(-)

lib/std/os/linux/io_uring.zig+198-15
......@@ -441,22 +441,75 @@ pub const IO_Uring = struct {
441441 sqe.user_data = user_data;
442442 return sqe;
443443 }
444
445 /// Like `link_with_next_sqe()` but stronger.
446 /// For when you don't want the chain to fail in the event of a completion result error.
447 /// For example, you may know that some commands will fail and may want the chain to continue.
448 /// Hard links are resilient to completion results, but are not resilient to submission errors.
449 pub fn hardlink_with_next_sqe(self: *IO_Uring, sqe: *io_uring_sqe) void {
450 sqe.flags |= linux.IOSQE_IO_HARDLINK;
444
445 /// Queue (but does not submit) an SQE to perform a `connect(2)` on a socket.
446 /// Returns a pointer to the SQE.
447 pub fn connect(
448 self: *IO_Uring,
449 user_data: u64,
450 fd: os.fd_t,
451 addr: *const os.sockaddr,
452 addrlen: os.socklen_t
453 ) !*io_uring_sqe {
454 const sqe = try self.get_sqe();
455 io_uring_prep_connect(sqe, fd, addr, addrlen);
456 sqe.user_data = user_data;
457 return sqe;
451458 }
452
453 /// This creates a full pipeline barrier in the submission queue.
454 /// This SQE will not be started until previous SQEs complete.
455 /// Subsequent SQEs will not be started until this SQE completes.
456 /// In other words, this stalls the entire submission queue.
457 /// You should first consider using link_with_next_sqe() for more granular SQE sequence control.
458 pub fn drain_previous_sqes(self: *IO_Uring, sqe: *io_uring_sqe) void {
459 sqe.flags |= linux.IOSQE_IO_DRAIN;
459
460 /// Queues (but does not submit) an SQE to perform a `recv(2)`.
461 /// Returns a pointer to the SQE.
462 pub fn recv(
463 self: *IO_Uring,
464 user_data: u64,
465 fd: os.fd_t,
466 buffer: []u8,
467 flags: u32
468 ) !*io_uring_sqe {
469 const sqe = try self.get_sqe();
470 io_uring_prep_recv(sqe, fd, buffer, flags);
471 sqe.user_data = user_data;
472 return sqe;
473 }
474
475 /// Queues (but does not submit) an SQE to perform a `send(2)`.
476 /// Returns a pointer to the SQE.
477 pub fn send(
478 self: *IO_Uring,
479 user_data: u64,
480 fd: os.fd_t,
481 buffer: []u8,
482 flags: u32
483 ) !*io_uring_sqe {
484 const sqe = try self.get_sqe();
485 io_uring_prep_send(sqe, fd, buffer, flags);
486 sqe.user_data = user_data;
487 return sqe;
488 }
489
490 /// Queues (but does not submit) an SQE to perform an `openat(2)`.
491 /// Returns a pointer to the SQE.
492 pub fn openat(
493 self: *IO_Uring,
494 user_data: u64,
495 fd: os.fd_t,
496 path: [*:0]const u8,
497 flags: u32,
498 mode: os.mode_t
499 ) !*io_uring_sqe {
500 const sqe = try self.get_sqe();
501 io_uring_prep_openat(sqe, fd, path, flags, mode);
502 sqe.user_data = user_data;
503 return sqe;
504 }
505
506 /// Queues (but does not submit) an SQE to perform a `close(2)`.
507 /// Returns a pointer to the SQE.
508 pub fn close(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*io_uring_sqe {
509 const sqe = try self.get_sqe();
510 io_uring_prep_close(sqe, fd);
511 sqe.user_data = user_data;
512 return sqe;
460513 }
461514
462515 /// Registers an array of file descriptors.
......@@ -1007,3 +1060,133 @@ test "write/read" {
10071060 }, cqe_read);
10081061 testing.expectEqualSlices(u8, buffer_write[0..], buffer_read[0..]);
10091062}
1063
1064test "openat/close" {
1065 if (builtin.os.tag != .linux) return error.SkipZigTest;
1066
1067 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
1068 error.SystemOutdated => return error.SkipZigTest,
1069 error.PermissionDenied => return error.SkipZigTest,
1070 else => return err
1071 };
1072 defer ring.deinit();
1073
1074 const path = "test_io_uring_openat_close";
1075 defer std.fs.cwd().deleteFile(path) catch {};
1076
1077 const flags: u32 = os.O_CLOEXEC | os.O_RDWR | os.O_CREAT;
1078 const mode: os.mode_t = 0o666;
1079 var sqe_openat = try ring.openat(789, linux.AT_FDCWD, path, flags, mode);
1080 testing.expectEqual(io_uring_sqe {
1081 .opcode = .OPENAT,
1082 .flags = 0,
1083 .ioprio = 0,
1084 .fd = linux.AT_FDCWD,
1085 .off = 0,
1086 .addr = @ptrToInt(path),
1087 .len = mode,
1088 .rw_flags = flags,
1089 .user_data = 789,
1090 .buf_index = 0,
1091 .personality = 0,
1092 .splice_fd_in = 0,
1093 .__pad2 = [2]u64{ 0, 0 }
1094 }, sqe_openat.*);
1095 testing.expectEqual(@as(u32, 1), try ring.submit());
1096
1097 var cqe_openat = try ring.copy_cqe();
1098 if (cqe_openat.res == -linux.EINVAL) return error.SkipZigTest;
1099 testing.expectEqual(@as(u64, 789), cqe_openat.user_data);
1100 testing.expect(cqe_openat.res > 0);
1101 testing.expectEqual(@as(u32, 0), cqe_openat.flags);
1102
1103 var sqe_close = try ring.close(1011, cqe_openat.res);
1104 testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode);
1105 testing.expectEqual(cqe_openat.res, sqe_close.fd);
1106 testing.expectEqual(@as(u32, 1), try ring.submit());
1107
1108 var cqe_close = try ring.copy_cqe();
1109 if (cqe_close.res == -linux.EINVAL) return error.SkipZigTest;
1110 testing.expectEqual(linux.io_uring_cqe {
1111 .user_data = 1011,
1112 .res = 0,
1113 .flags = 0,
1114 }, cqe_close);
1115}
1116
1117test "accept/connect/send/recv" {
1118 if (builtin.os.tag != .linux) return error.SkipZigTest;
1119
1120 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
1121 error.SystemOutdated => return error.SkipZigTest,
1122 error.PermissionDenied => return error.SkipZigTest,
1123 else => return err
1124 };
1125 defer ring.deinit();
1126
1127 var address = try net.Address.parseIp4("127.0.0.1", 3131);
1128 const kernel_backlog = 1;
1129 const server = try os.socket(address.any.family, os.SOCK_STREAM | os.SOCK_CLOEXEC, 0);
1130 defer os.close(server);
1131 try os.setsockopt(server, os.SOL_SOCKET, os.SO_REUSEADDR, &mem.toBytes(@as(c_int, 1)));
1132 try os.bind(server, &address.any, address.getOsSockLen());
1133 try os.listen(server, kernel_backlog);
1134
1135 var buffer_send = [_]u8{1,0,1,0,1,0,1,0,1,0};
1136 var buffer_recv = [_]u8{0,1,0,1,0};
1137
1138 var accept_addr: os.sockaddr = undefined;
1139 var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr));
1140 var accept = try ring.accept(0xaaaaaaaa, server, &accept_addr, &accept_addr_len, 0);
1141 testing.expectEqual(@as(u32, 1), try ring.submit());
1142
1143 const client = try os.socket(address.any.family, os.SOCK_STREAM | os.SOCK_CLOEXEC, 0);
1144 defer os.close(client);
1145 var connect = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());
1146 testing.expectEqual(@as(u32, 1), try ring.submit());
1147
1148 var cqe_accept = try ring.copy_cqe();
1149 if (cqe_accept.res == -linux.EINVAL) return error.SkipZigTest;
1150 var cqe_connect = try ring.copy_cqe();
1151 if (cqe_connect.res == -linux.EINVAL) return error.SkipZigTest;
1152
1153 // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first:
1154 if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) {
1155 var a = cqe_accept;
1156 var b = cqe_connect;
1157 cqe_accept = b;
1158 cqe_connect = a;
1159 }
1160
1161 testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data);
1162 testing.expect(cqe_accept.res > 0);
1163 testing.expectEqual(@as(u32, 0), cqe_accept.flags);
1164 testing.expectEqual(linux.io_uring_cqe {
1165 .user_data = 0xcccccccc,
1166 .res = 0,
1167 .flags = 0,
1168 }, cqe_connect);
1169
1170 var send = try ring.send(0xeeeeeeee, client, buffer_send[0..], 0);
1171 send.flags |= linux.IOSQE_IO_LINK;
1172 var recv = try ring.recv(0xffffffff, cqe_accept.res, buffer_recv[0..], 0);
1173 testing.expectEqual(@as(u32, 2), try ring.submit());
1174
1175 var cqe_send = try ring.copy_cqe();
1176 if (cqe_send.res == -linux.EINVAL) return error.SkipZigTest;
1177 testing.expectEqual(linux.io_uring_cqe {
1178 .user_data = 0xeeeeeeee,
1179 .res = buffer_send.len,
1180 .flags = 0,
1181 }, cqe_send);
1182
1183 var cqe_recv = try ring.copy_cqe();
1184 if (cqe_recv.res == -linux.EINVAL) return error.SkipZigTest;
1185 testing.expectEqual(linux.io_uring_cqe {
1186 .user_data = 0xffffffff,
1187 .res = buffer_recv.len,
1188 .flags = 0,
1189 }, cqe_recv);
1190
1191 testing.expectEqualSlices(u8, buffer_send[0..buffer_recv.len], buffer_recv[0..]);
1192}