io_uring_prep_writev2(3)

πŸ’– io_uring_prep_writev2 β€” bestie's guide to vectored writes (with flags!!) πŸ’–

okay so this is a page from the liburing Manual, and it's literally about one function: io_uring_prep_writev2(3). she's the flags-having cousin of the regular vectored write. let's get into it 🫢


✨ NAME ✨

io_uring_prep_writev2 β€” get a vectored I/O write request all prepped and cute, but this time you can pass flags


🌸 SYNOPSIS 🌸

#include <sys/uio.h>
#include <liburing.h>

void io_uring_prep_writev2(struct io_uring_sqe *sqe,
                           int fd,
                           const struct iovec *iovecs,
                           unsigned nr_vecs,
                           __u64 offset,
                           int flags);

no return value bestie, it just does the thing and sets up your sqe πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

so io_uring_prep_writev2(3) preps a vectored IO write request. the submission queue entry sqe gets set up to use the file descriptor fd and start writing nr_vecs from the iovecs array, at the offset you specify. and the whole vibe of the operation can be controlled with the flags parameter πŸ‘‘

the supported values for flags are:

  • RWF_HIPRI β€” high priority request, poll if possible. she's important, ok?? ⭐
  • RWF_DSYNC β€” per-IO O_DSYNC
  • RWF_SYNC β€” per-IO O_SYNC
  • RWF_NOWAIT β€” per-IO, returns -EAGAIN if the operation would block πŸ™ˆ
  • RWF_APPEND β€” per-IO O_APPEND

now for the offset tea β˜• on files that support seeking, if offset is set to -1, the write operation starts at the file offset, and the file offset gets incremented by however many bytes were written. check out write(2) for more deets. but β€” and this is important β€” for an async API, reading and updating the current file offset can lowkey result in unpredictable behavior, unless access to the file is serialized. so it's not encouraged to use this feature if you can just provide the IO offset you want from your application or library. fr fr.

on files that are not capable of seeking, offset must be 0 or -1. no in-between, no vibes, just those two.

after the write has been prepared, it can be submitted with one of the submit functions. and then we go πŸš€


πŸŽ€ RETURN VALUE πŸŽ€

None. nothing. nada. she gives you nothing back, the results show up in the CQE, keep reading bestie ⬇️


πŸ’œ ERRORS πŸ’œ

the res field of the CQE will contain the result of the operation. see the related man page for details on possible values. and here's the part that trips people up: where synchronous system calls return -1 on failure and set errno to the actual error value, io_uring never uses errno. instead it returns the negated errno directly in the CQE res field. so no errno sleuthing, just read res πŸ’€


🌈 NOTES 🌈

unless your application explicitly needs to pass in more than one iovec, it's more efficient to use io_uring_prep_write(3) instead of this function β€” because no state has to be maintained for a non-vectored IO request. simpler is sometimes just better, iykyk.

and as with any request that passes in data in a struct, that data must remain valid until the request has been successfully submitted. it does not need to remain valid until completion. once a request has been submitted, the in-kernel state is stable πŸ”’

very early kernels (5.4 and earlier) required state to be stable until the completion occurred. so if you're out here supporting ancient kernels, that's a you problem, but a fixable one 😭

applications can test for this behavior by inspecting the IORING_FEAT_SUBMIT_STABLE flag passed back from io_uring_queue_init_params(3). check the flag, know your kernel, live your truth ✨


πŸ‘‘ SEE ALSO πŸ‘‘


okay that's the whole page bestie, you ate that πŸ“

this little love letter was brought to you by liburing-2.2, dated November 15, 2021 β€” and it's literally still iconic. go prep some writes, mwah πŸ’…πŸ’–

πŸ“¦ where this page comes from