authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2025-03-02 13:11:45+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2025-03-05 13:35:52+01:00
log4df039d235d5f77830fdc30a4c23121f6216364a
tree372b5a4afd8a583edea47b9b3c71c0659b6e876a
parentd98c0893b0a565507af955d4f47e63393ac8e464

io_uring: add setsockopt/getsockopt

ring.cmd_sock is generic socket operation. Two most common uses are setsockopt and getsockopt. This provides same interface as posix versions of this methods. libring has also [sqe_set_flags](https://man7.org/linux/man-pages/man3/io_uring_sqe_set_flags.3.html) method. Adding that in our io_uring_sqe. Adding sqe.link_next method for setting most common flag.

3 files changed, 72 insertions(+), 19 deletions(-)

lib/std/os/linux.zig+1
......@@ -6154,6 +6154,7 @@ pub const io_uring_probe = extern struct {
61546154 resv2: [3]u32,
61556155 ops: [256]io_uring_probe_op,
61566156
6157 /// Is the operation supported on the running kernel.
61576158 pub fn is_supported(self: @This(), op: IORING_OP) bool {
61586159 const i = @intFromEnum(op);
61596160 if (i > @intFromEnum(self.last_op) or i >= self.ops_len)
lib/std/os/linux/IoUring.zig+61-19
......@@ -1399,15 +1399,18 @@ pub fn listen(
13991399 return sqe;
14001400}
14011401
1402fn cmd_sock(
1402/// Prepares an cmd request for a socket.
1403/// See: https://man7.org/linux/man-pages/man3/io_uring_prep_cmd.3.html
1404/// Available since 6.7.
1405pub fn cmd_sock(
14031406 self: *IoUring,
14041407 user_data: u64,
14051408 cmd_op: linux.IO_URING_SOCKET_OP,
14061409 fd: linux.fd_t,
1407 level: u32,
1408 optname: u32,
1409 optval: u64,
1410 optlen: u32,
1410 level: u32, // linux.SOL
1411 optname: u32, // linux.SO
1412 optval: u64, // pointer to the option value
1413 optlen: u32, // size of the option value
14111414) !*linux.io_uring_sqe {
14121415 const sqe = try self.get_sqe();
14131416 sqe.prep_cmd_sock(cmd_op, fd, level, optname, optval, optlen);
......@@ -1415,6 +1418,50 @@ fn cmd_sock(
14151418 return sqe;
14161419}
14171420
1421/// Prepares set socket option for the optname argument, at the protocol
1422/// level specified by the level argument.
1423/// Available since 6.7.n
1424pub fn setsockopt(
1425 self: *IoUring,
1426 user_data: u64,
1427 fd: linux.fd_t,
1428 level: u32, // linux.SOL
1429 optname: u32, // linux.SO
1430 opt: []const u8,
1431) !*linux.io_uring_sqe {
1432 return try self.cmd_sock(
1433 user_data,
1434 .SETSOCKOPT,
1435 fd,
1436 level,
1437 optname,
1438 @intFromPtr(opt.ptr),
1439 @intCast(opt.len),
1440 );
1441}
1442
1443/// Prepares get socket option to retrieve the value for the option specified by
1444/// the option_name argument for the socket specified by the fd argument.
1445/// Available since 6.7.
1446pub fn getsockopt(
1447 self: *IoUring,
1448 user_data: u64,
1449 fd: linux.fd_t,
1450 level: u32, // linux.SOL
1451 optname: u32, // linux.SO
1452 opt: []u8,
1453) !*linux.io_uring_sqe {
1454 return try self.cmd_sock(
1455 user_data,
1456 .GETSOCKOPT,
1457 fd,
1458 level,
1459 optname,
1460 @intFromPtr(opt.ptr),
1461 @intCast(opt.len),
1462 );
1463}
1464
14181465pub const SubmissionQueue = struct {
14191466 head: *u32,
14201467 tail: *u32,
......@@ -4391,13 +4438,10 @@ test "bind/listen/connect" {
43914438 try testing.expect(listen_fd > 2);
43924439
43934440 // Prepare: set socket option * 2, bind, listen
4394 var sock_opt: u32 = 1;
4395 var sqe = try ring.cmd_sock(2, .SETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, @intFromPtr(&sock_opt), @sizeOf(u32));
4396 sqe.flags |= linux.IOSQE_IO_LINK;
4397 sqe = try ring.cmd_sock(3, .SETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, @intFromPtr(&sock_opt), @sizeOf(u32));
4398 sqe.flags |= linux.IOSQE_IO_LINK;
4399 sqe = try ring.bind(4, listen_fd, &addr.any, addr.getOsSockLen(), 0);
4400 sqe.flags |= linux.IOSQE_IO_LINK;
4441 var optval: u32 = 1;
4442 (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next();
4443 (try ring.setsockopt(3, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, mem.asBytes(&optval))).link_next();
4444 (try ring.bind(4, listen_fd, &addr.any, addr.getOsSockLen(), 0)).link_next();
44014445 _ = try ring.listen(5, listen_fd, 1, 0);
44024446 // Submit 4 operations
44034447 try testing.expectEqual(4, try ring.submit());
......@@ -4409,13 +4453,13 @@ test "bind/listen/connect" {
44094453 }
44104454
44114455 // Check that socket option is set
4412 sock_opt = 0xff;
4413 _ = try ring.cmd_sock(5, .GETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, @intFromPtr(&sock_opt), @sizeOf(u32));
4456 optval = 0;
4457 _ = try ring.getsockopt(5, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval));
44144458 try testing.expectEqual(1, try ring.submit());
44154459 cqe = try ring.copy_cqe();
44164460 try testing.expectEqual(5, cqe.user_data);
44174461 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
4418 try testing.expectEqual(1, sock_opt);
4462 try testing.expectEqual(1, optval);
44194463
44204464 // Read system assigned port into addr
44214465 var addr_len: posix.socklen_t = addr.getOsSockLen();
......@@ -4460,8 +4504,7 @@ test "bind/listen/connect" {
44604504
44614505 // Shutdown and close all sockets
44624506 for ([_]posix.socket_t{ connect_fd, accept_fd, listen_fd }) |fd| {
4463 var sqe = try ring.shutdown(9, fd, posix.SHUT.RDWR);
4464 sqe.flags |= linux.IOSQE_IO_LINK;
4507 (try ring.shutdown(9, fd, posix.SHUT.RDWR)).link_next();
44654508 _ = try ring.close(10, fd);
44664509 try testing.expectEqual(2, try ring.submit());
44674510 for (0..2) |i| {
......@@ -4477,8 +4520,7 @@ fn testSendRecv(ring: *IoUring, send_fd: posix.socket_t, recv_fd: posix.socket_t
44774520 var buffer_recv: [buffer_send.len * 2]u8 = undefined;
44784521
44794522 // 2 sends
4480 var sqe = try ring.send(1, send_fd, buffer_send, linux.MSG.WAITALL);
4481 sqe.flags |= linux.IOSQE_IO_LINK;
4523 _ = try ring.send(1, send_fd, buffer_send, linux.MSG.WAITALL);
44824524 _ = try ring.send(2, send_fd, buffer_send, linux.MSG.WAITALL);
44834525 try testing.expectEqual(2, try ring.submit());
44844526 for (0..2) |i| {
lib/std/os/linux/io_uring_sqe.zig+10
......@@ -666,4 +666,14 @@ pub const io_uring_sqe = extern struct {
666666 // addr3 is overloaded, https://github.com/axboe/liburing/blob/e1003e496e66f9b0ae06674869795edf772d5500/src/include/liburing/io_uring.h#L102
667667 sqe.addr3 = optval;
668668 }
669
670 pub fn set_flags(sqe: *linux.io_uring_sqe, flags: u8) void {
671 sqe.flags |= flags;
672 }
673
674 /// This SQE forms a link with the next SQE in the submission ring. Next SQE
675 /// will not be started before this one completes. Forms a chain of SQEs.
676 pub fn link_next(sqe: *linux.io_uring_sqe) void {
677 sqe.flags |= linux.IOSQE_IO_LINK;
678 }
669679};