io_uring: implement register_files_update

This commit is contained in:
Vincent Rischmann
2021-11-14 21:56:27 +01:00
committed by Andrew Kelley
parent d94b032e92
commit 3d528161c8

View File

@@ -724,6 +724,37 @@ pub const IO_Uring = struct {
try handle_registration_result(res);
}
/// Updates registered file descriptors.
///
/// Updates are applied starting at the provided offset in the original file descriptors slice.
/// There are three kind of updates:
/// * turning a sparse entry (where the fd is -1) into a real one
/// * removing an existing entry (set the fd to -1)
/// * replacing an existing entry with a new fd
/// Adding new file descriptors must be done with `register_files`.
pub fn register_files_update(self: *IO_Uring, offset: u32, fds: []const os.fd_t) !void {
assert(self.fd >= 0);
const FilesUpdate = struct {
offset: u32,
resv: u32,
fds: u64 align(8),
};
var update = FilesUpdate{
.offset = offset,
.resv = @as(u32, 0),
.fds = @as(u64, @ptrToInt(fds.ptr)),
};
const res = linux.io_uring_register(
self.fd,
.REGISTER_FILES_UPDATE,
@ptrCast(*const c_void, &update),
@intCast(u32, fds.len),
);
try handle_registration_result(res);
}
/// Registers the file descriptor for an eventfd that will be notified of completion events on
/// an io_uring instance.
/// Only a single a eventfd can be registered at any given point in time.
@@ -1949,3 +1980,90 @@ test "accept/connect/recv/cancel" {
.flags = 0,
}, cqe_cancel);
}
test "register_files_update" {
if (builtin.os.tag != .linux) return error.SkipZigTest;
var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
else => return err,
};
defer ring.deinit();
const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0);
defer os.close(fd);
var registered_fds = [_]os.fd_t{0} ** 2;
const fd_index = 0;
const fd_index2 = 1;
registered_fds[fd_index] = fd;
registered_fds[fd_index2] = -1;
ring.register_files(registered_fds[0..]) catch |err| switch (err) {
// Happens when the kernel doesn't support sparse entry (-1) in the file descriptors array.
error.FileDescriptorInvalid => return error.SkipZigTest,
else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
};
// Test IORING_REGISTER_FILES_UPDATE
// Only available since Linux 5.5
const fd2 = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0);
defer os.close(fd2);
registered_fds[fd_index] = fd2;
registered_fds[fd_index2] = -1;
try ring.register_files_update(0, registered_fds[0..]);
var buffer = [_]u8{42} ** 128;
{
const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
sqe.flags |= linux.IOSQE_FIXED_FILE;
try testing.expectEqual(@as(u32, 1), try ring.submit());
try testing.expectEqual(linux.io_uring_cqe{
.user_data = 0xcccccccc,
.res = buffer.len,
.flags = 0,
}, try ring.copy_cqe());
try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer.len), buffer[0..]);
}
// Test with a non-zero offset
registered_fds[fd_index] = -1;
registered_fds[fd_index2] = -1;
try ring.register_files_update(1, registered_fds[1..]);
{
// Next read should still work since fd_index in the registered file descriptors hasn't been updated yet.
const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
sqe.flags |= linux.IOSQE_FIXED_FILE;
try testing.expectEqual(@as(u32, 1), try ring.submit());
try testing.expectEqual(linux.io_uring_cqe{
.user_data = 0xcccccccc,
.res = buffer.len,
.flags = 0,
}, try ring.copy_cqe());
try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer.len), buffer[0..]);
}
try ring.register_files_update(0, registered_fds[0..]);
{
// Now this should fail since both fds are sparse (-1)
const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
sqe.flags |= linux.IOSQE_FIXED_FILE;
try testing.expectEqual(@as(u32, 1), try ring.submit());
const cqe = try ring.copy_cqe();
try testing.expectEqual(os.linux.E.BADF, cqe.err());
}
try ring.unregister_files();
}