authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-11-17 16:31:57+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2023-11-17 16:31:57+01:00
log87dd8d56690b4e8187d9a2a9fda9c611199f430f
treeafc3c541cd1c1ff950bb0e893b829e2bfdf6196b
parent256384a2ec2f67fc1b9380be987c63d5702f180c

io_uring: simplify tests by checking kernel version

So far we relied on getting EINVAL in CQE for operations that kernel don't support. The problem with that approach is that there are many other reasons (like wrong params) to get EINVAL. The other problem is when we have an operation that existed before and gets new behavior via different attributes, like accept and accept_direct. Older kernels can fall back to non direct operation although we set attributes for direct operation. Operation completes successfully in both cases but with different results. This commit introduces kernel version check at the start of the test. Making body of the test free of checking for various kernel version differences. Feature availability references: * https://manpages.debian.org/unstable/liburing-dev/io_uring_enter.2.en.html * https://kernel.dk/axboe-kr2022.pdf * https://github.com/Jiboo/zig/blob/5acf7969bc759ce51005924109924d9666f74429/lib/std/os/linux.zig#L3727 * https://github.com/Jiboo/zig/blob/5acf7969bc759ce51005924109924d9666f74429/lib/std/os/linux.zig#L3993

1 files changed, 80 insertions(+), 71 deletions(-)

lib/std/os/linux/io_uring.zig+80-71
...@@ -491,6 +491,7 @@ pub const IO_Uring = struct {...@@ -491,6 +491,7 @@ pub const IO_Uring = struct {
491491
492 /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket.492 /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket.
493 /// Returns a pointer to the SQE.493 /// Returns a pointer to the SQE.
494 /// Available since 5.5
494 pub fn accept(495 pub fn accept(
495 self: *IO_Uring,496 self: *IO_Uring,
496 user_data: u64,497 user_data: u64,
...@@ -511,6 +512,8 @@ pub const IO_Uring = struct {...@@ -511,6 +512,8 @@ pub const IO_Uring = struct {
511 /// which will repeatedly trigger a CQE when a connection request comes in.512 /// which will repeatedly trigger a CQE when a connection request comes in.
512 /// While IORING_CQE_F_MORE flag is set in CQE flags accept will generate513 /// While IORING_CQE_F_MORE flag is set in CQE flags accept will generate
513 /// further CQEs.514 /// further CQEs.
515 ///
516 /// Available since 5.19
514 pub fn accept_multishot(517 pub fn accept_multishot(
515 self: *IO_Uring,518 self: *IO_Uring,
516 user_data: u64,519 user_data: u64,
...@@ -534,6 +537,8 @@ pub const IO_Uring = struct {...@@ -534,6 +537,8 @@ pub const IO_Uring = struct {
534 /// After creation, they can be used by setting IOSQE_FIXED_FILE in the SQE537 /// After creation, they can be used by setting IOSQE_FIXED_FILE in the SQE
535 /// flags member, and setting the SQE fd field to the direct descriptor value538 /// flags member, and setting the SQE fd field to the direct descriptor value
536 /// rather than the regular file descriptor.539 /// rather than the regular file descriptor.
540 ///
541 /// Available since 5.19
537 pub fn accept_direct(542 pub fn accept_direct(
538 self: *IO_Uring,543 self: *IO_Uring,
539 user_data: u64,544 user_data: u64,
...@@ -549,6 +554,7 @@ pub const IO_Uring = struct {...@@ -549,6 +554,7 @@ pub const IO_Uring = struct {
549 }554 }
550555
551 /// Queues an multishot accept using direct (registered) file descriptors.556 /// Queues an multishot accept using direct (registered) file descriptors.
557 /// Available since 5.19
552 pub fn accept_multishot_direct(558 pub fn accept_multishot_direct(
553 self: *IO_Uring,559 self: *IO_Uring,
554 user_data: u64,560 user_data: u64,
...@@ -726,6 +732,7 @@ pub const IO_Uring = struct {...@@ -726,6 +732,7 @@ pub const IO_Uring = struct {
726732
727 /// Queues (but does not submit) an SQE to perform an `openat(2)`.733 /// Queues (but does not submit) an SQE to perform an `openat(2)`.
728 /// Returns a pointer to the SQE.734 /// Returns a pointer to the SQE.
735 /// Available since 5.6.
729 pub fn openat(736 pub fn openat(
730 self: *IO_Uring,737 self: *IO_Uring,
731 user_data: u64,738 user_data: u64,
...@@ -749,6 +756,8 @@ pub const IO_Uring = struct {...@@ -749,6 +756,8 @@ pub const IO_Uring = struct {
749 /// After creation, they can be used by setting IOSQE_FIXED_FILE in the SQE756 /// After creation, they can be used by setting IOSQE_FIXED_FILE in the SQE
750 /// flags member, and setting the SQE fd field to the direct descriptor value757 /// flags member, and setting the SQE fd field to the direct descriptor value
751 /// rather than the regular file descriptor.758 /// rather than the regular file descriptor.
759 ///
760 /// Available since 5.15
752 pub fn openat_direct(761 pub fn openat_direct(
753 self: *IO_Uring,762 self: *IO_Uring,
754 user_data: u64,763 user_data: u64,
...@@ -766,6 +775,7 @@ pub const IO_Uring = struct {...@@ -766,6 +775,7 @@ pub const IO_Uring = struct {
766775
767 /// Queues (but does not submit) an SQE to perform a `close(2)`.776 /// Queues (but does not submit) an SQE to perform a `close(2)`.
768 /// Returns a pointer to the SQE.777 /// Returns a pointer to the SQE.
778 /// Available since 5.6.
769 pub fn close(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*linux.io_uring_sqe {779 pub fn close(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*linux.io_uring_sqe {
770 const sqe = try self.get_sqe();780 const sqe = try self.get_sqe();
771 io_uring_prep_close(sqe, fd);781 io_uring_prep_close(sqe, fd);
...@@ -774,6 +784,7 @@ pub const IO_Uring = struct {...@@ -774,6 +784,7 @@ pub const IO_Uring = struct {
774 }784 }
775785
776 /// Queues close of registered file descriptor.786 /// Queues close of registered file descriptor.
787 /// Available since 5.15
777 pub fn close_direct(self: *IO_Uring, user_data: u64, file_index: u32) !*linux.io_uring_sqe {788 pub fn close_direct(self: *IO_Uring, user_data: u64, file_index: u32) !*linux.io_uring_sqe {
778 const sqe = try self.get_sqe();789 const sqe = try self.get_sqe();
779 io_uring_prep_close_direct(sqe, file_index);790 io_uring_prep_close_direct(sqe, file_index);
...@@ -1232,6 +1243,7 @@ pub const IO_Uring = struct {...@@ -1232,6 +1243,7 @@ pub const IO_Uring = struct {
12321243
1233 /// Prepares a socket creation request.1244 /// Prepares a socket creation request.
1234 /// New socket fd will be returned in completion result.1245 /// New socket fd will be returned in completion result.
1246 /// Available since 5.19
1235 pub fn socket(1247 pub fn socket(
1236 self: *IO_Uring,1248 self: *IO_Uring,
1237 user_data: u64,1249 user_data: u64,
...@@ -1247,6 +1259,7 @@ pub const IO_Uring = struct {...@@ -1247,6 +1259,7 @@ pub const IO_Uring = struct {
1247 }1259 }
12481260
1249 /// Prepares a socket creation request for registered file at index `file_index`.1261 /// Prepares a socket creation request for registered file at index `file_index`.
1262 /// Available since 5.19
1250 pub fn socket_direct(1263 pub fn socket_direct(
1251 self: *IO_Uring,1264 self: *IO_Uring,
1252 user_data: u64,1265 user_data: u64,
...@@ -1264,6 +1277,7 @@ pub const IO_Uring = struct {...@@ -1264,6 +1277,7 @@ pub const IO_Uring = struct {
12641277
1265 /// Prepares a socket creation request for registered file, index chosen by kernel (file index alloc).1278 /// Prepares a socket creation request for registered file, index chosen by kernel (file index alloc).
1266 /// File index will be returned in CQE res field.1279 /// File index will be returned in CQE res field.
1280 /// Available since 5.19
1267 pub fn socket_direct_alloc(1281 pub fn socket_direct_alloc(
1268 self: *IO_Uring,1282 self: *IO_Uring,
1269 user_data: u64,1283 user_data: u64,
...@@ -3826,22 +3840,15 @@ test "accept/connect/send_zc/recv" {...@@ -3826,22 +3840,15 @@ test "accept/connect/send_zc/recv" {
3826}3840}
38273841
3828test "accept_direct" {3842test "accept_direct" {
3829 if (builtin.os.tag != .linux) return error.SkipZigTest;3843 try skipKernelLessThan(.{ .major = 5, .minor = 19, .patch = 0 });
38303844
3831 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {3845 var ring = try IO_Uring.init(1, 0);
3832 error.SystemOutdated => return error.SkipZigTest,
3833 error.PermissionDenied => return error.SkipZigTest,
3834 else => return err,
3835 };
3836 defer ring.deinit();3846 defer ring.deinit();
3837 var address = try net.Address.parseIp4("127.0.0.1", 0);3847 var address = try net.Address.parseIp4("127.0.0.1", 0);
38383848
3839 // register direct file descriptors3849 // register direct file descriptors
3840 var registered_fds = [_]os.fd_t{-1} ** 2;3850 var registered_fds = [_]os.fd_t{-1} ** 2;
3841 ring.register_files(registered_fds[0..]) catch |err| switch (err) {3851 try ring.register_files(registered_fds[0..]);
3842 error.FileDescriptorInvalid => return error.SkipZigTest,
3843 else => return err,
3844 };
38453852
3846 const listener_socket = try createListenerSocket(&address);3853 const listener_socket = try createListenerSocket(&address);
3847 defer os.closeSocket(listener_socket);3854 defer os.closeSocket(listener_socket);
...@@ -3866,10 +3873,8 @@ test "accept_direct" {...@@ -3866,10 +3873,8 @@ test "accept_direct" {
38663873
3867 // accept completion3874 // accept completion
3868 const cqe_accept = try ring.copy_cqe();3875 const cqe_accept = try ring.copy_cqe();
3869 if (cqe_accept.err() == .INVAL) return error.SkipZigTest;
3870 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());3876 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());
3871 const fd_index = cqe_accept.res;3877 const fd_index = cqe_accept.res;
3872 if (fd_index >= registered_fds.len) return error.SkipZigTest; // old kernel fallback to ordinary accept
3873 try testing.expect(fd_index < registered_fds.len);3878 try testing.expect(fd_index < registered_fds.len);
3874 try testing.expect(cqe_accept.user_data == accept_userdata);3879 try testing.expect(cqe_accept.user_data == accept_userdata);
38753880
...@@ -3911,21 +3916,15 @@ test "accept_direct" {...@@ -3911,21 +3916,15 @@ test "accept_direct" {
3911}3916}
39123917
3913test "accept_multishot_direct" {3918test "accept_multishot_direct" {
3914 if (builtin.os.tag != .linux) return error.SkipZigTest;3919 try skipKernelLessThan(.{ .major = 5, .minor = 19, .patch = 0 });
39153920
3916 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {3921 var ring = try IO_Uring.init(1, 0);
3917 error.SystemOutdated => return error.SkipZigTest,
3918 error.PermissionDenied => return error.SkipZigTest,
3919 else => return err,
3920 };
3921 defer ring.deinit();3922 defer ring.deinit();
3923
3922 var address = try net.Address.parseIp4("127.0.0.1", 0);3924 var address = try net.Address.parseIp4("127.0.0.1", 0);
39233925
3924 var registered_fds = [_]os.fd_t{-1} ** 2;3926 var registered_fds = [_]os.fd_t{-1} ** 2;
3925 ring.register_files(registered_fds[0..]) catch |err| switch (err) {3927 try ring.register_files(registered_fds[0..]);
3926 error.FileDescriptorInvalid => return error.SkipZigTest,
3927 else => return err,
3928 };
39293928
3930 const listener_socket = try createListenerSocket(&address);3929 const listener_socket = try createListenerSocket(&address);
3931 defer os.closeSocket(listener_socket);3930 defer os.closeSocket(listener_socket);
...@@ -3933,7 +3932,7 @@ test "accept_multishot_direct" {...@@ -3933,7 +3932,7 @@ test "accept_multishot_direct" {
3933 const accept_userdata: u64 = 0xaaaaaaaa;3932 const accept_userdata: u64 = 0xaaaaaaaa;
39343933
3935 for (0..2) |_| {3934 for (0..2) |_| {
3936 // submit accept3935 // submit multishot accept
3937 // Will chose registered fd and return index of the selected registered file in cqe.3936 // Will chose registered fd and return index of the selected registered file in cqe.
3938 _ = try ring.accept_multishot_direct(accept_userdata, listener_socket, null, null, 0);3937 _ = try ring.accept_multishot_direct(accept_userdata, listener_socket, null, null, 0);
3939 try testing.expectEqual(@as(u32, 1), try ring.submit());3938 try testing.expectEqual(@as(u32, 1), try ring.submit());
...@@ -3946,7 +3945,6 @@ test "accept_multishot_direct" {...@@ -3946,7 +3945,6 @@ test "accept_multishot_direct" {
39463945
3947 // accept completion3946 // accept completion
3948 const cqe_accept = try ring.copy_cqe();3947 const cqe_accept = try ring.copy_cqe();
3949 if (cqe_accept.err() == .INVAL) return error.SkipZigTest;
3950 const fd_index = cqe_accept.res;3948 const fd_index = cqe_accept.res;
3951 try testing.expect(fd_index < registered_fds.len);3949 try testing.expect(fd_index < registered_fds.len);
3952 try testing.expect(cqe_accept.user_data == accept_userdata);3950 try testing.expect(cqe_accept.user_data == accept_userdata);
...@@ -3971,52 +3969,58 @@ test "accept_multishot_direct" {...@@ -3971,52 +3969,58 @@ test "accept_multishot_direct" {
3971 try ring.unregister_files();3969 try ring.unregister_files();
3972}3970}
39733971
3974test "socket/socket_direct/socket_direct_alloc/close_direct" {3972test "socket" {
3975 if (builtin.os.tag != .linux) return error.SkipZigTest;3973 try skipKernelLessThan(.{ .major = 5, .minor = 19, .patch = 0 });
39763974
3977 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {3975 var ring = try IO_Uring.init(2, 0);
3978 error.SystemOutdated => return error.SkipZigTest,
3979 error.PermissionDenied => return error.SkipZigTest,
3980 else => return err,
3981 };
3982 defer ring.deinit();3976 defer ring.deinit();
3983 var address = try net.Address.parseIp4("127.0.0.1", 0);
39843977
3985 // Below are 4 different ways to get socket fd.3978 // prepare, submit socket operation
3986 // Two upfront before register_files, and two after3979 _ = try ring.socket(0, linux.AF.INET, os.SOCK.STREAM, 0, 0);
3987 var registered_fds = [_]os.fd_t{-1} ** 4;3980 try testing.expectEqual(@as(u32, 1), try ring.submit());
3988 // 1. sync syscall socket call3981
3989 registered_fds[0] = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3982 // test completion
3990 // 2. io_uring socket3983 var cqe = try ring.copy_cqe();
3991 const socket_userdata = 0xcccccccc;3984 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3992 _ = try ring.socket(socket_userdata, linux.AF.INET, os.SOCK.STREAM, 0, 0);3985 const fd: os.fd_t = @intCast(cqe.res);
3986 try testing.expect(fd > 2);
3987
3988 os.close(fd);
3989}
3990
3991test "socket_direct/socket_direct_alloc/close_direct" {
3992 try skipKernelLessThan(.{ .major = 5, .minor = 19, .patch = 0 });
3993
3994 var ring = try IO_Uring.init(2, 0);
3995 defer ring.deinit();
3996
3997 var registered_fds = [_]os.fd_t{-1} ** 3;
3998 try ring.register_files(registered_fds[0..]);
3999
4000 // create socket in registered file descriptor at index 0 (last param)
4001 _ = try ring.socket_direct(0, linux.AF.INET, os.SOCK.STREAM, 0, 0, 0);
3993 try testing.expectEqual(@as(u32, 1), try ring.submit());4002 try testing.expectEqual(@as(u32, 1), try ring.submit());
3994 var cqe_socket = try ring.copy_cqe();4003 var cqe_socket = try ring.copy_cqe();
3995 if (cqe_socket.err() == .INVAL) return error.SkipZigTest;
3996 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());4004 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
3997 try testing.expect(cqe_socket.res > 2);4005 try testing.expect(cqe_socket.res == 0);
3998 registered_fds[1] = cqe_socket.res; // set index 1 to created socket
3999
4000 ring.register_files(registered_fds[0..]) catch |err| switch (err) {
4001 error.FileDescriptorInvalid => return error.SkipZigTest,
4002 else => return err,
4003 };
40044006
4005 // 3. io_uring socket_direct, create new socket on index 24007 // create socket in registered file descriptor at index 1 (last param)
4006 _ = try ring.socket_direct(socket_userdata, linux.AF.INET, os.SOCK.STREAM, 0, 0, @intCast(2));4008 _ = try ring.socket_direct(0, linux.AF.INET, os.SOCK.STREAM, 0, 0, 1);
4007 try testing.expectEqual(@as(u32, 1), try ring.submit());4009 try testing.expectEqual(@as(u32, 1), try ring.submit());
4008 cqe_socket = try ring.copy_cqe();4010 cqe_socket = try ring.copy_cqe();
4009 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());4011 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
4010 try testing.expect(cqe_socket.res == 0);4012 try testing.expect(cqe_socket.res == 0); // res is 0 when index is specified
40114013
4012 // 4. io_uring socket_direct_alloc4014 // create socket in kernel chosen file descriptor index (_alloc version)
4013 _ = try ring.socket_direct_alloc(socket_userdata, linux.AF.INET, os.SOCK.STREAM, 0, 0);4015 // completion res has index from registered files
4016 _ = try ring.socket_direct_alloc(0, linux.AF.INET, os.SOCK.STREAM, 0, 0);
4014 try testing.expectEqual(@as(u32, 1), try ring.submit());4017 try testing.expectEqual(@as(u32, 1), try ring.submit());
4015 cqe_socket = try ring.copy_cqe();4018 cqe_socket = try ring.copy_cqe();
4016 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());4019 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
4017 try testing.expect(cqe_socket.res == 3);4020 try testing.expect(cqe_socket.res == 2); // returns registered file index
40184021
4019 // use sockets from registered_fds in connect operation4022 // use sockets from registered_fds in connect operation
4023 var address = try net.Address.parseIp4("127.0.0.1", 0);
4020 const listener_socket = try createListenerSocket(&address);4024 const listener_socket = try createListenerSocket(&address);
4021 defer os.closeSocket(listener_socket);4025 defer os.closeSocket(listener_socket);
4022 const accept_userdata: u64 = 0xaaaaaaaa;4026 const accept_userdata: u64 = 0xaaaaaaaa;
...@@ -4027,14 +4031,12 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {...@@ -4027,14 +4031,12 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {
4027 _ = try ring.accept(accept_userdata, listener_socket, null, null, 0);4031 _ = try ring.accept(accept_userdata, listener_socket, null, null, 0);
4028 // prepare connect with fixed socket4032 // prepare connect with fixed socket
4029 const connect_sqe = try ring.connect(connect_userdata, @intCast(fd_index), &address.any, address.getOsSockLen());4033 const connect_sqe = try ring.connect(connect_userdata, @intCast(fd_index), &address.any, address.getOsSockLen());
4030 connect_sqe.flags |= linux.IOSQE_FIXED_FILE;4034 connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index
4031 // submit both4035 // submit both
4032 try testing.expectEqual(@as(u32, 2), try ring.submit());4036 try testing.expectEqual(@as(u32, 2), try ring.submit());
4033 // get completions4037 // get completions
4034 var cqe_connect = try ring.copy_cqe();4038 var cqe_connect = try ring.copy_cqe();
4035 if (cqe_connect.err() == .INVAL) return error.SkipZigTest;
4036 var cqe_accept = try ring.copy_cqe();4039 var cqe_accept = try ring.copy_cqe();
4037 if (cqe_accept.err() == .INVAL) return error.SkipZigTest;
4038 // ignore order4040 // ignore order
4039 if (cqe_connect.user_data == accept_userdata and cqe_accept.user_data == connect_userdata) {4041 if (cqe_connect.user_data == accept_userdata and cqe_accept.user_data == connect_userdata) {
4040 const a = cqe_accept;4042 const a = cqe_accept;
...@@ -4049,7 +4051,7 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {...@@ -4049,7 +4051,7 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {
4049 try testing.expect(cqe_accept.user_data == accept_userdata);4051 try testing.expect(cqe_accept.user_data == accept_userdata);
4050 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());4052 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());
40514053
4052 // submit and test close completion4054 // submit and test close_direct
4053 _ = try ring.close_direct(close_userdata, @intCast(fd_index));4055 _ = try ring.close_direct(close_userdata, @intCast(fd_index));
4054 try testing.expectEqual(@as(u32, 1), try ring.submit());4056 try testing.expectEqual(@as(u32, 1), try ring.submit());
4055 var cqe_close = try ring.copy_cqe();4057 var cqe_close = try ring.copy_cqe();
...@@ -4061,20 +4063,13 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {...@@ -4061,20 +4063,13 @@ test "socket/socket_direct/socket_direct_alloc/close_direct" {
4061}4063}
40624064
4063test "openat_direct/close_direct" {4065test "openat_direct/close_direct" {
4064 if (builtin.os.tag != .linux) return error.SkipZigTest;4066 try skipKernelLessThan(.{ .major = 5, .minor = 19, .patch = 0 });
40654067
4066 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {4068 var ring = try IO_Uring.init(2, 0);
4067 error.SystemOutdated => return error.SkipZigTest,
4068 error.PermissionDenied => return error.SkipZigTest,
4069 else => return err,
4070 };
4071 defer ring.deinit();4069 defer ring.deinit();
40724070
4073 var registered_fds = [_]os.fd_t{-1} ** 3;4071 var registered_fds = [_]os.fd_t{-1} ** 3;
4074 ring.register_files(registered_fds[0..]) catch |err| switch (err) {4072 try ring.register_files(registered_fds[0..]);
4075 error.FileDescriptorInvalid => return error.SkipZigTest,
4076 else => return err,
4077 };
40784073
4079 var tmp = std.testing.tmpDir(.{});4074 var tmp = std.testing.tmpDir(.{});
4080 defer tmp.cleanup();4075 defer tmp.cleanup();
...@@ -4087,8 +4082,6 @@ test "openat_direct/close_direct" {...@@ -4087,8 +4082,6 @@ test "openat_direct/close_direct" {
4087 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0);4082 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0);
4088 try testing.expectEqual(@as(u32, 1), try ring.submit());4083 try testing.expectEqual(@as(u32, 1), try ring.submit());
4089 var cqe = try ring.copy_cqe();4084 var cqe = try ring.copy_cqe();
4090 if (cqe.err() == .INVAL) return error.SkipZigTest;
4091 if (cqe.res != 0) return error.SkipZigTest; // old kernel fallback to openat without direct
4092 try testing.expectEqual(os.E.SUCCESS, cqe.err());4085 try testing.expectEqual(os.E.SUCCESS, cqe.err());
4093 try testing.expect(cqe.res == 0);4086 try testing.expect(cqe.res == 0);
40944087
...@@ -4103,7 +4096,6 @@ test "openat_direct/close_direct" {...@@ -4103,7 +4096,6 @@ test "openat_direct/close_direct" {
4103 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC);4096 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC);
4104 try testing.expectEqual(@as(u32, 1), try ring.submit());4097 try testing.expectEqual(@as(u32, 1), try ring.submit());
4105 cqe = try ring.copy_cqe();4098 cqe = try ring.copy_cqe();
4106 if (cqe.err() == .INVAL) return error.SkipZigTest; // kernel 5.15 bug
4107 try testing.expectEqual(os.E.SUCCESS, cqe.err());4099 try testing.expectEqual(os.E.SUCCESS, cqe.err());
4108 try testing.expect(cqe.res == 2); // chosen index is in res4100 try testing.expect(cqe.res == 2); // chosen index is in res
41094101
...@@ -4116,3 +4108,20 @@ test "openat_direct/close_direct" {...@@ -4116,3 +4108,20 @@ test "openat_direct/close_direct" {
4116 }4108 }
4117 try ring.unregister_files();4109 try ring.unregister_files();
4118}4110}
4111
4112/// For use in tests. Returns SkipZigTest is kernel version is less than required.
4113fn skipKernelLessThan(required: std.SemanticVersion) !void {
4114 if (builtin.os.tag != .linux) return error.SkipZigTest;
4115
4116 var uts: linux.utsname = undefined;
4117 const res = linux.uname(&uts);
4118 switch (linux.getErrno(res)) {
4119 .SUCCESS => {},
4120 else => |errno| return os.unexpectedErrno(errno),
4121 }
4122
4123 const release = mem.sliceTo(&uts.release, 0);
4124 var current = try std.SemanticVersion.parse(release);
4125 current.pre = null; // don't check pre field
4126 if (required.order(current) == .gt) return error.SkipZigTest;
4127}