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 {...@@ -6154,6 +6154,7 @@ pub const io_uring_probe = extern struct {
6154 resv2: [3]u32,6154 resv2: [3]u32,
6155 ops: [256]io_uring_probe_op,6155 ops: [256]io_uring_probe_op,
61566156
6157 /// Is the operation supported on the running kernel.
6157 pub fn is_supported(self: @This(), op: IORING_OP) bool {6158 pub fn is_supported(self: @This(), op: IORING_OP) bool {
6158 const i = @intFromEnum(op);6159 const i = @intFromEnum(op);
6159 if (i > @intFromEnum(self.last_op) or i >= self.ops_len)6160 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(...@@ -1399,15 +1399,18 @@ pub fn listen(
1399 return sqe;1399 return sqe;
1400}1400}
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(
1403 self: *IoUring,1406 self: *IoUring,
1404 user_data: u64,1407 user_data: u64,
1405 cmd_op: linux.IO_URING_SOCKET_OP,1408 cmd_op: linux.IO_URING_SOCKET_OP,
1406 fd: linux.fd_t,1409 fd: linux.fd_t,
1407 level: u32,1410 level: u32, // linux.SOL
1408 optname: u32,1411 optname: u32, // linux.SO
1409 optval: u64,1412 optval: u64, // pointer to the option value
1410 optlen: u32,1413 optlen: u32, // size of the option value
1411) !*linux.io_uring_sqe {1414) !*linux.io_uring_sqe {
1412 const sqe = try self.get_sqe();1415 const sqe = try self.get_sqe();
1413 sqe.prep_cmd_sock(cmd_op, fd, level, optname, optval, optlen);1416 sqe.prep_cmd_sock(cmd_op, fd, level, optname, optval, optlen);
...@@ -1415,6 +1418,50 @@ fn cmd_sock(...@@ -1415,6 +1418,50 @@ fn cmd_sock(
1415 return sqe;1418 return sqe;
1416}1419}
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
1418pub const SubmissionQueue = struct {1465pub const SubmissionQueue = struct {
1419 head: *u32,1466 head: *u32,
1420 tail: *u32,1467 tail: *u32,
...@@ -4391,13 +4438,10 @@ test "bind/listen/connect" {...@@ -4391,13 +4438,10 @@ test "bind/listen/connect" {
4391 try testing.expect(listen_fd > 2);4438 try testing.expect(listen_fd > 2);
43924439
4393 // Prepare: set socket option * 2, bind, listen4440 // Prepare: set socket option * 2, bind, listen
4394 var sock_opt: u32 = 1;4441 var optval: u32 = 1;
4395 var sqe = try ring.cmd_sock(2, .SETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, @intFromPtr(&sock_opt), @sizeOf(u32));4442 (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next();
4396 sqe.flags |= linux.IOSQE_IO_LINK;4443 (try ring.setsockopt(3, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, mem.asBytes(&optval))).link_next();
4397 sqe = try ring.cmd_sock(3, .SETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, @intFromPtr(&sock_opt), @sizeOf(u32));4444 (try ring.bind(4, listen_fd, &addr.any, addr.getOsSockLen(), 0)).link_next();
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;
4401 _ = try ring.listen(5, listen_fd, 1, 0);4445 _ = try ring.listen(5, listen_fd, 1, 0);
4402 // Submit 4 operations4446 // Submit 4 operations
4403 try testing.expectEqual(4, try ring.submit());4447 try testing.expectEqual(4, try ring.submit());
...@@ -4409,13 +4453,13 @@ test "bind/listen/connect" {...@@ -4409,13 +4453,13 @@ test "bind/listen/connect" {
4409 }4453 }
44104454
4411 // Check that socket option is set4455 // Check that socket option is set
4412 sock_opt = 0xff;4456 optval = 0;
4413 _ = try ring.cmd_sock(5, .GETSOCKOPT, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, @intFromPtr(&sock_opt), @sizeOf(u32));4457 _ = try ring.getsockopt(5, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval));
4414 try testing.expectEqual(1, try ring.submit());4458 try testing.expectEqual(1, try ring.submit());
4415 cqe = try ring.copy_cqe();4459 cqe = try ring.copy_cqe();
4416 try testing.expectEqual(5, cqe.user_data);4460 try testing.expectEqual(5, cqe.user_data);
4417 try testing.expectEqual(posix.E.SUCCESS, cqe.err());4461 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
4418 try testing.expectEqual(1, sock_opt);4462 try testing.expectEqual(1, optval);
44194463
4420 // Read system assigned port into addr4464 // Read system assigned port into addr
4421 var addr_len: posix.socklen_t = addr.getOsSockLen();4465 var addr_len: posix.socklen_t = addr.getOsSockLen();
...@@ -4460,8 +4504,7 @@ test "bind/listen/connect" {...@@ -4460,8 +4504,7 @@ test "bind/listen/connect" {
44604504
4461 // Shutdown and close all sockets4505 // Shutdown and close all sockets
4462 for ([_]posix.socket_t{ connect_fd, accept_fd, listen_fd }) |fd| {4506 for ([_]posix.socket_t{ connect_fd, accept_fd, listen_fd }) |fd| {
4463 var sqe = try ring.shutdown(9, fd, posix.SHUT.RDWR);4507 (try ring.shutdown(9, fd, posix.SHUT.RDWR)).link_next();
4464 sqe.flags |= linux.IOSQE_IO_LINK;
4465 _ = try ring.close(10, fd);4508 _ = try ring.close(10, fd);
4466 try testing.expectEqual(2, try ring.submit());4509 try testing.expectEqual(2, try ring.submit());
4467 for (0..2) |i| {4510 for (0..2) |i| {
...@@ -4477,8 +4520,7 @@ fn testSendRecv(ring: *IoUring, send_fd: posix.socket_t, recv_fd: posix.socket_t...@@ -4477,8 +4520,7 @@ fn testSendRecv(ring: *IoUring, send_fd: posix.socket_t, recv_fd: posix.socket_t
4477 var buffer_recv: [buffer_send.len * 2]u8 = undefined;4520 var buffer_recv: [buffer_send.len * 2]u8 = undefined;
44784521
4479 // 2 sends4522 // 2 sends
4480 var sqe = try ring.send(1, send_fd, buffer_send, linux.MSG.WAITALL);4523 _ = try ring.send(1, send_fd, buffer_send, linux.MSG.WAITALL);
4481 sqe.flags |= linux.IOSQE_IO_LINK;
4482 _ = try ring.send(2, send_fd, buffer_send, linux.MSG.WAITALL);4524 _ = try ring.send(2, send_fd, buffer_send, linux.MSG.WAITALL);
4483 try testing.expectEqual(2, try ring.submit());4525 try testing.expectEqual(2, try ring.submit());
4484 for (0..2) |i| {4526 for (0..2) |i| {
lib/std/os/linux/io_uring_sqe.zig+10
...@@ -666,4 +666,14 @@ pub const io_uring_sqe = extern struct {...@@ -666,4 +666,14 @@ pub const io_uring_sqe = extern struct {
666 // addr3 is overloaded, https://github.com/axboe/liburing/blob/e1003e496e66f9b0ae06674869795edf772d5500/src/include/liburing/io_uring.h#L102666 // addr3 is overloaded, https://github.com/axboe/liburing/blob/e1003e496e66f9b0ae06674869795edf772d5500/src/include/liburing/io_uring.h#L102
667 sqe.addr3 = optval;667 sqe.addr3 = optval;
668 }668 }
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 }
669};679};