hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 894f7029fe39643c5cba46c81838739762c4b1e3
parent 5fb8ec29c21a8f68e9da23b1a972dadb3c98cfd2
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 20 Oct 2021 09:22:31 +0200

linux::io_uring: implement provide_buffers support

Note that this has not been tested. A follow-up patch will integrate
this with iobus and it will be tested there.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Miobus/io_uring/bus.ha | 2+-
Miobus/io_uring/types.ha | 2++
Mlinux/io_uring/cqe.ha | 9+++++++++
Mlinux/io_uring/sqe.ha | 37+++++++++++++++++++++++++++++++++++++
Mlinux/io_uring/uring.ha | 15++++++++++++---
5 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/iobus/io_uring/bus.ha b/iobus/io_uring/bus.ha @@ -5,7 +5,7 @@ use linux::io_uring; def DEFAULT_RING_SIZE: u32 = 512; // Creates a new io_uring I/O bus. -export fn new() (*bus | errors::error) = { +export fn new() (*bus | error) = { let params = io_uring::params { ... }; match (io_uring::setup(DEFAULT_RING_SIZE, &params)) { case err: io_uring::error => diff --git a/iobus/io_uring/types.ha b/iobus/io_uring/types.ha @@ -3,6 +3,8 @@ use io; use linux::io_uring; use rt; +// TODO: Add our own nobuffers error + // All errors which may be raised by iobus. export type error = io_uring::error; diff --git a/linux/io_uring/cqe.ha b/linux/io_uring/cqe.ha @@ -37,6 +37,15 @@ export fn result(cqe: *cqe) (int | error) = export fn get_user(cqe: *cqe) nullable *void = cqe.user_data: uintptr: nullable *void; +// Returns the buffer ID used for this [[cqe]] in combination with +// [[set_buffer_select]]. Aborts the program if this CQE was not configured to +// use a buffer pool. +export fn get_buffer_id(cqe: *cqe) u16 = { + assert(cqe.flags & cqe_flags::F_BUFFER > 0, + "get_buffer_id called for CQE without buffer"); + return (cqe.flags: u32 >> CQE_BUFFER_SHIFT): u16; +}; + fn peek_cqe(ring: *io_uring) (nullable *cqe, uint) = { let head = *ring.cq.khead; let tail = *ring.cq.ktail; diff --git a/linux/io_uring/sqe.ha b/linux/io_uring/sqe.ha @@ -33,6 +33,14 @@ export fn set_user(sqe: *sqe, user_data: *void) void = { sqe.user_data = user_data: uintptr: u64; }; +// Sets the BUFFER_SELECT flag and sets the desired buffer group. See +// [[provide_buffers]] for configuring buffer groups, and [[get_buffer_id]] to +// retrieve the buffer used from the corresponding [[cqe]]. +export fn set_buffer_select(sqe: *sqe, group: u16) void = { + sqe.flags |= flags::BUFFER_SELECT; + sqe.buf_group = group; +}; + // Prepares a no-op "operation" for an [[sqe]]. export fn nop(sqe: *sqe, flags: flags...) void = { prep(sqe, op::NOP, flags...); @@ -303,3 +311,32 @@ export fn openat( export fn close(sqe: *sqe, fd: int, flags: flags...) void = { preprw(sqe, op::CLOSE, fd, null, 0, 0, flags...); }; + +// Prepares an [[sqe]] operation which provides a buffer pool to the kernel. +// len(pool) must be equal to nbuf * bufsz. See [[set_buffer_select]] to use +// the buffer pool for a subsequent request. +export fn provide_buffers( + sqe: *sqe, + group: u16, + pool: []u8, + nbuf: size, + bufsz: size, + bufid: u16, + flags: flags... +) void = { + assert(len(pool) == nbuf * bufsz); + preprw(sqe, op::PROVIDE_BUFFERS, nbuf: int, pool: *[*]u8, + bufsz: uint, bufid: uint, flags...); + sqe.buf_group = group; +}; + +// Removes buffers previously registered with [[provide_buffers]]. +export fn remove_buffers( + sqe: *sqe, + nbuf: size, + group: u16, + flags: flags... +) void = { + preprw(sqe, op::REMOVE_BUFFERS, nbuf: int, null, 0, 0, flags...); + sqe.buf_group = group; +}; diff --git a/linux/io_uring/uring.ha b/linux/io_uring/uring.ha @@ -1,17 +1,26 @@ use errors; +// Returned when buffer pool use was configured for an [[sqe]], but there are no +// buffers available. +export type nobuffers = !void; + // All errors which may be returned by this module. -export type error = !errors::error; +export type error = !(errors::error | nobuffers); // Converts an [[error]] into a human-readable string. export fn strerror(err: error) const str = { - return errors::strerror(err); + match (err) { + case nobuffers => + return "Buffer pool exhausted"; + case err: errors::error => + return errors::strerror(err); + }; }; // The maximum value for the first parameter of [[setup]]. export def MAX_ENTRIES: uint = 4096; -def CQE_BUFFER_SHIFT: uint = 16; +def CQE_BUFFER_SHIFT: u32 = 16; def OFF_SQ_RING: u64 = 0; def OFF_CQ_RING: u64 = 0x8000000; def OFF_SQES: u64 = 0x10000000;