authorgravatar for joran@ronomon.comJoran Dirk Greef <joran@ronomon.com> 2020-09-20 14:21:44+02:00
committergravatar for joran@ronomon.comJoran Dirk Greef <joran@ronomon.com> 2020-09-20 14:21:44+02:00
logabebacda322074c040778aaca5347c8cd714362e
treedcb88501fd50f8c3d7c3862980815da6daf2fb8d
parent4bc1b7a7ac99d57619b4f9a84e159310820e83ff

Handle all possible syscall errors and bring errors in line with os.zig


1 files changed, 82 insertions(+), 25 deletions(-)

lib/std/os/linux/io_uring.zig+82-25
...@@ -60,7 +60,22 @@ pub const IO_Uring = struct {...@@ -60,7 +60,22 @@ pub const IO_Uring = struct {
60 assert(p.resv[2] == 0);60 assert(p.resv[2] == 0);
6161
62 const res = linux.io_uring_setup(entries, p);62 const res = linux.io_uring_setup(entries, p);
63 try check_errno(res);63 switch (linux.getErrno(res)) {
64 0 => {},
65 linux.EFAULT => return error.ParamsOutsideAccessibleAddressSpace,
66 // The resv array contains non-zero data, p.flags contains an unsupported flag,
67 // entries out of bounds, IORING_SETUP_SQ_AFF was specified without IORING_SETUP_SQPOLL,
68 // or IORING_SETUP_CQSIZE was specified but io_uring_params.cq_entries was invalid:
69 linux.EINVAL => return error.ArgumentsInvalid,
70 linux.EMFILE => return error.ProcessFdQuotaExceeded,
71 linux.ENFILE => return error.SystemFdQuotaExceeded,
72 linux.ENOMEM => return error.SystemResources,
73 // IORING_SETUP_SQPOLL was specified but effective user ID lacks sufficient privileges,
74 // or a container seccomp policy prohibits io_uring syscalls:
75 linux.EPERM => return error.PermissionDenied,
76 linux.ENOSYS => return error.SystemOutdated,
77 else => |errno| return os.unexpectedErrno(errno)
78 }
64 const fd = @intCast(i32, res);79 const fd = @intCast(i32, res);
65 assert(fd >= 0);80 assert(fd >= 0);
66 errdefer os.close(fd);81 errdefer os.close(fd);
...@@ -75,7 +90,7 @@ pub const IO_Uring = struct {...@@ -75,7 +90,7 @@ pub const IO_Uring = struct {
75 // We do not support the double mmap() done before 5.4, because we want to keep the90 // We do not support the double mmap() done before 5.4, because we want to keep the
76 // init/deinit mmap paths simple and because io_uring has had many bug fixes even since 5.4.91 // init/deinit mmap paths simple and because io_uring has had many bug fixes even since 5.4.
77 if ((p.features & linux.IORING_FEAT_SINGLE_MMAP) == 0) {92 if ((p.features & linux.IORING_FEAT_SINGLE_MMAP) == 0) {
78 return error.UnsupportedKernel;93 return error.SystemOutdated;
79 }94 }
8095
81 // Check that the kernel has actually set params and that "impossible is nothing".96 // Check that the kernel has actually set params and that "impossible is nothing".
...@@ -172,7 +187,31 @@ pub const IO_Uring = struct {...@@ -172,7 +187,31 @@ pub const IO_Uring = struct {
172 fn enter(self: *IO_Uring, to_submit: u32, min_complete: u32, flags: u32) !u32 {187 fn enter(self: *IO_Uring, to_submit: u32, min_complete: u32, flags: u32) !u32 {
173 assert(self.fd >= 0);188 assert(self.fd >= 0);
174 const res = linux.io_uring_enter(self.fd, to_submit, min_complete, flags, null);189 const res = linux.io_uring_enter(self.fd, to_submit, min_complete, flags, null);
175 try check_errno(res);190 switch (linux.getErrno(res)) {
191 0 => {},
192 // The kernel was unable to allocate memory or ran out of resources for the request.
193 // The application should wait for some completions and try again:
194 linux.EAGAIN => return error.SystemResources,
195 // The application attempted to overcommit the number of requests it can have pending.
196 // The application should wait for some completions and try again:
197 linux.EBUSY => return error.CompletionQueueOvercommitted,
198 // The SQE `fd` is invalid, or IOSQE_FIXED_FILE was set but no files were registered:
199 linux.EBADF => return error.FileDescriptorInvalid,
200 // The buffer is outside the process' accessible address space, or IORING_OP_READ_FIXED
201 // or IORING_OP_WRITE_FIXED was specified but no buffers were registered, or the range
202 // described by `addr` and `len` is not within the buffer registered at `buf_index`:
203 linux.EFAULT => return error.BufferInvalid,
204 // The SQE is invalid, or valid but the ring was setup with IORING_SETUP_IOPOLL:
205 linux.EINVAL => return error.SubmissionQueueEntryInvalid,
206 linux.ENXIO => return error.RingShuttingDown,
207 // The kernel believes our `self.fd` does not refer to an io_uring instance,
208 // or the opcode is valid but not supported by this kernel (more likely):
209 linux.EOPNOTSUPP => return error.OpcodeNotSupported,
210 // The operation was interrupted by a delivery of a signal before it could complete.
211 // This can happen while waiting for events with IORING_ENTER_GETEVENTS:
212 linux.EINTR => return error.SignalInterrupt,
213 else => |errno| return os.unexpectedErrno(errno)
214 }
176 return @truncate(u32, res);215 return @truncate(u32, res);
177 }216 }
178217
...@@ -479,7 +518,25 @@ pub const IO_Uring = struct {...@@ -479,7 +518,25 @@ pub const IO_Uring = struct {
479 @ptrCast(*const c_void, fds.ptr),518 @ptrCast(*const c_void, fds.ptr),
480 @truncate(u32, fds.len)519 @truncate(u32, fds.len)
481 );520 );
482 try check_errno(res);521 switch (linux.getErrno(res)) {
522 0 => {},
523 // One or more fds in the array are invalid, or the kernel does not support sparse sets:
524 linux.EBADF => return error.FileDescriptorInvalid,
525 linux.EBUSY => return error.FilesAlreadyRegistered,
526 linux.EINVAL => return error.FilesEmpty,
527 // Adding `nr_args` file references would exceed the maximum allowed number of files the
528 // user is allowed to have according to the per-user RLIMIT_NOFILE resource limit and
529 // the CAP_SYS_RESOURCE capability is not set, or `nr_args` exceeds the maximum allowed
530 // for a fixed file set (older kernels have a limit of 1024 files vs 64K files):
531 linux.EMFILE => return error.UserFdQuotaExceeded,
532 // Insufficient kernel resources, or the caller had a non-zero RLIMIT_MEMLOCK soft
533 // resource limit but tried to lock more memory than the limit permitted (not enforced
534 // when the process is privileged with CAP_IPC_LOCK):
535 linux.ENOMEM => return error.SystemResources,
536 // Attempt to register files on a ring already registering files or being torn down:
537 linux.ENXIO => return error.RingShuttingDownOrAlreadyRegisteringFiles,
538 else => |errno| return os.unexpectedErrno(errno)
539 }
483 }540 }
484541
485 /// Changes the semantics of the SQE's `fd` to refer to a pre-registered file descriptor.542 /// Changes the semantics of the SQE's `fd` to refer to a pre-registered file descriptor.
...@@ -491,7 +548,11 @@ pub const IO_Uring = struct {...@@ -491,7 +548,11 @@ pub const IO_Uring = struct {
491 pub fn unregister_files(self: *IO_Uring) !void {548 pub fn unregister_files(self: *IO_Uring) !void {
492 assert(self.fd >= 0);549 assert(self.fd >= 0);
493 const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0);550 const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0);
494 try check_errno(res);551 switch (linux.getErrno(res)) {
552 0 => {},
553 linux.ENXIO => return error.FilesNotRegistered,
554 else => |errno| return os.unexpectedErrno(errno)
555 }
495 }556 }
496};557};
497558
...@@ -607,20 +668,13 @@ pub const CompletionQueue = struct {...@@ -607,20 +668,13 @@ pub const CompletionQueue = struct {
607 }668 }
608};669};
609670
610inline fn check_errno(res: usize) !void {
611 switch (linux.getErrno(res)) {
612 0 => return,
613 linux.ENOSYS => return error.UnsupportedKernel,
614 else => |errno| return os.unexpectedErrno(errno)
615 }
616}
617
618test "queue_nop" {671test "queue_nop" {
619 if (builtin.os.tag != .linux) return error.SkipZigTest;672 if (builtin.os.tag != .linux) return error.SkipZigTest;
620673
621 var ring = IO_Uring.init(1, 0) catch |err| {674 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
622 if (err == error.UnsupportedKernel) return error.SkipZigTest;675 error.SystemOutdated => return error.SkipZigTest,
623 return err;676 error.PermissionDenied => return error.SkipZigTest,
677 else => return err
624 };678 };
625 defer {679 defer {
626 ring.deinit();680 ring.deinit();
...@@ -684,9 +738,10 @@ test "queue_nop" {...@@ -684,9 +738,10 @@ test "queue_nop" {
684test "queue_readv" {738test "queue_readv" {
685 if (builtin.os.tag != .linux) return error.SkipZigTest;739 if (builtin.os.tag != .linux) return error.SkipZigTest;
686740
687 var ring = IO_Uring.init(1, 0) catch |err| {741 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
688 if (err == error.UnsupportedKernel) return error.SkipZigTest;742 error.SystemOutdated => return error.SkipZigTest,
689 return err;743 error.PermissionDenied => return error.SkipZigTest,
744 else => return err
690 };745 };
691 defer ring.deinit();746 defer ring.deinit();
692747
...@@ -725,9 +780,10 @@ test "queue_readv" {...@@ -725,9 +780,10 @@ test "queue_readv" {
725test "queue_writev/queue_fsync" {780test "queue_writev/queue_fsync" {
726 if (builtin.os.tag != .linux) return error.SkipZigTest;781 if (builtin.os.tag != .linux) return error.SkipZigTest;
727782
728 var ring = IO_Uring.init(2, 0) catch |err| {783 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {
729 if (err == error.UnsupportedKernel) return error.SkipZigTest;784 error.SystemOutdated => return error.SkipZigTest,
730 return err;785 error.PermissionDenied => return error.SkipZigTest,
786 else => return err
731 };787 };
732 defer ring.deinit();788 defer ring.deinit();
733 789
...@@ -769,9 +825,10 @@ test "queue_writev/queue_fsync" {...@@ -769,9 +825,10 @@ test "queue_writev/queue_fsync" {
769test "queue_write/queue_read" {825test "queue_write/queue_read" {
770 if (builtin.os.tag != .linux) return error.SkipZigTest;826 if (builtin.os.tag != .linux) return error.SkipZigTest;
771827
772 var ring = IO_Uring.init(2, 0) catch |err| {828 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {
773 if (err == error.UnsupportedKernel) return error.SkipZigTest;829 error.SystemOutdated => return error.SkipZigTest,
774 return err;830 error.PermissionDenied => return error.SkipZigTest,
831 else => return err
775 };832 };
776 defer ring.deinit();833 defer ring.deinit();
777 834