| ... | @@ -124,13 +124,13 @@ pub const IO_Uring = struct { | ... | @@ -124,13 +124,13 @@ pub const IO_Uring = struct { |
| 124 | self.fd = -1; | 124 | self.fd = -1; |
| 125 | } | 125 | } |
| 126 | | 126 | |
| 127 | /// Returns a vacant SQE, or an error if the submission queue is full. | 127 | /// Returns a pointer to a zeroed SQE, or an error if the submission queue is full. |
| 128 | /// We follow the implementation (and atomics) of liburing's `io_uring_get_sqe()` exactly. | 128 | /// We follow the implementation (and atomics) of liburing's `io_uring_get_sqe()` exactly. |
| 129 | /// However, instead of a null we return an error to force safe handling. | 129 | /// However, instead of a null we return an error to force safe handling. |
| 130 | /// Any situation where the submission queue is full tends more towards a control flow error, | 130 | /// Any situation where the submission queue is full tends more towards a control flow error, |
| 131 | /// and the null return in liburing is more a C idiom than anything else, for lack of a better | 131 | /// and the null return in liburing is more a C idiom than anything else, for lack of a better |
| 132 | /// alternative. In Zig, we have first-class error handling... so let's use it. | 132 | /// alternative. In Zig, we have first-class error handling... so let's use it. |
| 133 | /// Matches the implementation of io_uring_get_sqe() in liburing. | 133 | /// Matches the implementation of io_uring_get_sqe() in liburing, except zeroes for safety. |
| 134 | pub fn get_sqe(self: *IO_Uring) !*io_uring_sqe { | 134 | pub fn get_sqe(self: *IO_Uring) !*io_uring_sqe { |
| 135 | const head = @atomicLoad(u32, self.sq.head, .Acquire); | 135 | const head = @atomicLoad(u32, self.sq.head, .Acquire); |
| 136 | // Remember that these head and tail offsets wrap around every four billion operations. | 136 | // Remember that these head and tail offsets wrap around every four billion operations. |
| ... | @@ -139,6 +139,8 @@ pub const IO_Uring = struct { | ... | @@ -139,6 +139,8 @@ pub const IO_Uring = struct { |
| 139 | if (next -% head > self.sq.sqes.len) return error.SubmissionQueueFull; | 139 | if (next -% head > self.sq.sqes.len) return error.SubmissionQueueFull; |
| 140 | var sqe = &self.sq.sqes[self.sq.sqe_tail & self.sq.mask.*]; | 140 | var sqe = &self.sq.sqes[self.sq.sqe_tail & self.sq.mask.*]; |
| 141 | self.sq.sqe_tail = next; | 141 | self.sq.sqe_tail = next; |
| | 142 | // We zero the SQE slot here in a single place, rather than in many `queue_` methods. |
| | 143 | @memset(@ptrCast([*]u8, sqe), 0, @sizeOf(io_uring_sqe)); |
| 142 | return sqe; | 144 | return sqe; |
| 143 | } | 145 | } |
| 144 | | 146 | |
| ... | @@ -312,14 +314,12 @@ pub const IO_Uring = struct { | ... | @@ -312,14 +314,12 @@ pub const IO_Uring = struct { |
| 312 | // sqe->addr2 holds a pointer to socklen_t, and finally sqe->accept_flags holds the flags | 314 | // sqe->addr2 holds a pointer to socklen_t, and finally sqe->accept_flags holds the flags |
| 313 | // for accept(4)." - https://lwn.net/ml/linux-block/20191025173037.13486-1-axboe@kernel.dk/ | 315 | // for accept(4)." - https://lwn.net/ml/linux-block/20191025173037.13486-1-axboe@kernel.dk/ |
| 314 | const sqe = try self.get_sqe(); | 316 | const sqe = try self.get_sqe(); |
| 315 | sqe.* = .{ | 317 | sqe.opcode = .ACCEPT; |
| 316 | .opcode = .ACCEPT, | 318 | sqe.fd = fd; |
| 317 | .fd = fd, | 319 | sqe.off = @ptrToInt(addrlen); // `addr2` is a newer union member that maps to `off`. |
| 318 | .off = @ptrToInt(addrlen), // `addr2` is a newer union member that maps to `off`. | 320 | sqe.addr = @ptrToInt(addr); |
| 319 | .addr = @ptrToInt(addr), | 321 | sqe.user_data = user_data; |
| 320 | .user_data = user_data, | 322 | sqe.opflags = accept_flags; |
| 321 | .opflags = accept_flags | | |
| 322 | }; | | |
| 323 | return sqe; | 323 | return sqe; |
| 324 | } | 324 | } |
| 325 | | 325 | |
| ... | @@ -334,11 +334,9 @@ pub const IO_Uring = struct { | ... | @@ -334,11 +334,9 @@ pub const IO_Uring = struct { |
| 334 | /// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync. | 334 | /// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync. |
| 335 | pub fn queue_fsync(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*io_uring_sqe { | 335 | pub fn queue_fsync(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*io_uring_sqe { |
| 336 | const sqe = try self.get_sqe(); | 336 | const sqe = try self.get_sqe(); |
| 337 | sqe.* = .{ | 337 | sqe.opcode = .FSYNC; |
| 338 | .opcode = .FSYNC, | 338 | sqe.fd = fd; |
| 339 | .fd = fd, | 339 | sqe.user_data = user_data; |
| 340 | .user_data = user_data | | |
| 341 | }; | | |
| 342 | return sqe; | 340 | return sqe; |
| 343 | } | 341 | } |
| 344 | | 342 | |
| ... | @@ -349,10 +347,8 @@ pub const IO_Uring = struct { | ... | @@ -349,10 +347,8 @@ pub const IO_Uring = struct { |
| 349 | /// know when the ring is idle before acting on a kill signal. | 347 | /// know when the ring is idle before acting on a kill signal. |
| 350 | pub fn queue_nop(self: *IO_Uring, user_data: u64) !*io_uring_sqe { | 348 | pub fn queue_nop(self: *IO_Uring, user_data: u64) !*io_uring_sqe { |
| 351 | const sqe = try self.get_sqe(); | 349 | const sqe = try self.get_sqe(); |
| 352 | sqe.* = .{ | 350 | sqe.opcode = .NOP; |
| 353 | .opcode = .NOP, | 351 | sqe.user_data = user_data; |
| 354 | .user_data = user_data | | |
| 355 | }; | | |
| 356 | return sqe; | 352 | return sqe; |
| 357 | } | 353 | } |
| 358 | | 354 | |
| ... | @@ -366,14 +362,12 @@ pub const IO_Uring = struct { | ... | @@ -366,14 +362,12 @@ pub const IO_Uring = struct { |
| 366 | offset: u64 | 362 | offset: u64 |
| 367 | ) !*io_uring_sqe { | 363 | ) !*io_uring_sqe { |
| 368 | const sqe = try self.get_sqe(); | 364 | const sqe = try self.get_sqe(); |
| 369 | sqe.* = .{ | 365 | sqe.opcode = .READ; |
| 370 | .opcode = .READ, | 366 | sqe.fd = fd; |
| 371 | .fd = fd, | 367 | sqe.off = offset; |
| 372 | .off = offset, | 368 | sqe.addr = @ptrToInt(buffer.ptr); |
| 373 | .addr = @ptrToInt(buffer.ptr), | 369 | sqe.len = @truncate(u32, buffer.len); |
| 374 | .len = @truncate(u32, buffer.len), | 370 | sqe.user_data = user_data; |
| 375 | .user_data = user_data | | |
| 376 | }; | | |
| 377 | return sqe; | 371 | return sqe; |
| 378 | } | 372 | } |
| 379 | | 373 | |
| ... | @@ -387,14 +381,12 @@ pub const IO_Uring = struct { | ... | @@ -387,14 +381,12 @@ pub const IO_Uring = struct { |
| 387 | offset: u64 | 381 | offset: u64 |
| 388 | ) !*io_uring_sqe { | 382 | ) !*io_uring_sqe { |
| 389 | const sqe = try self.get_sqe(); | 383 | const sqe = try self.get_sqe(); |
| 390 | sqe.* = .{ | 384 | sqe.opcode = .WRITE; |
| 391 | .opcode = .WRITE, | 385 | sqe.fd = fd; |
| 392 | .fd = fd, | 386 | sqe.off = offset; |
| 393 | .off = offset, | 387 | sqe.addr = @ptrToInt(buffer.ptr); |
| 394 | .addr = @ptrToInt(buffer.ptr), | 388 | sqe.len = @truncate(u32, buffer.len); |
| 395 | .len = @truncate(u32, buffer.len), | 389 | sqe.user_data = user_data; |
| 396 | .user_data = user_data | | |
| 397 | }; | | |
| 398 | return sqe; | 390 | return sqe; |
| 399 | } | 391 | } |
| 400 | | 392 | |
| ... | @@ -410,14 +402,12 @@ pub const IO_Uring = struct { | ... | @@ -410,14 +402,12 @@ pub const IO_Uring = struct { |
| 410 | offset: u64 | 402 | offset: u64 |
| 411 | ) !*io_uring_sqe { | 403 | ) !*io_uring_sqe { |
| 412 | const sqe = try self.get_sqe(); | 404 | const sqe = try self.get_sqe(); |
| 413 | sqe.* = .{ | 405 | sqe.opcode = .READV; |
| 414 | .opcode = .READV, | 406 | sqe.fd = fd; |
| 415 | .fd = fd, | 407 | sqe.off = offset; |
| 416 | .off = offset, | 408 | sqe.addr = @ptrToInt(iovecs.ptr); |
| 417 | .addr = @ptrToInt(iovecs.ptr), | 409 | sqe.len = @truncate(u32, iovecs.len); |
| 418 | .len = @truncate(u32, iovecs.len), | 410 | sqe.user_data = user_data; |
| 419 | .user_data = user_data | | |
| 420 | }; | | |
| 421 | return sqe; | 411 | return sqe; |
| 422 | } | 412 | } |
| 423 | | 413 | |
| ... | @@ -433,14 +423,12 @@ pub const IO_Uring = struct { | ... | @@ -433,14 +423,12 @@ pub const IO_Uring = struct { |
| 433 | offset: u64 | 423 | offset: u64 |
| 434 | ) !*io_uring_sqe { | 424 | ) !*io_uring_sqe { |
| 435 | const sqe = try self.get_sqe(); | 425 | const sqe = try self.get_sqe(); |
| 436 | sqe.* = .{ | 426 | sqe.opcode = .WRITEV; |
| 437 | .opcode = .WRITEV, | 427 | sqe.fd = fd; |
| 438 | .fd = fd, | 428 | sqe.off = offset; |
| 439 | .off = offset, | 429 | sqe.addr = @ptrToInt(iovecs.ptr); |
| 440 | .addr = @ptrToInt(iovecs.ptr), | 430 | sqe.len = @truncate(u32, iovecs.len); |
| 441 | .len = @truncate(u32, iovecs.len), | 431 | sqe.user_data = user_data; |
| 442 | .user_data = user_data | | |
| 443 | }; | | |
| 444 | return sqe; | 432 | return sqe; |
| 445 | } | 433 | } |
| 446 | | 434 | |