io_submit(2)

โœจ io_submit(2) โœจ โ€” the Linux syscall manual, but make it make sense ๐Ÿ’–

okay bestie, sit down, hydrate, and let's talk about io_submit(2) โ€” that's the syscall page from the Linux man-pages, and yes it's giving "kernel internals" but honestly? it's iconic. ๐Ÿฆ‹


๐ŸŒธ NAME ๐ŸŒธ

io_submit โ€” submit asynchronous I/O blocks for processing. that's it, that's the whole vibe. you hand the kernel a pile of I/O requests and it goes "bet, I'll get to those." ๐Ÿ’…

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc). the basics, no cap.

๐ŸŽ€ SYNOPSIS ๐ŸŽ€

#include <linux/aio_abi.h>
#include <sys/syscall.h>    /* Definition of SYS_* constants */
#include <unistd.h>

int syscall(SYS_io_submit, aio_context_t ctx_id,
            long n, struct iocb *iocbpp[n]);

yes it's the raw syscall(2) route. we'll talk about the glow-up version in VERSIONS, don't worry. ๐Ÿ™ˆ

๐Ÿ’– DESCRIPTION ๐Ÿ’–

not me about to say the most important thing first: you probably want to use the io_setup(3) wrapper function provided by libaio instead. see VERSIONS for the tea. ๐Ÿ“

the actual tea โ˜•

io_submit() queues up n I/O request blocks for processing in the AIO context ctx_id. the iocbpp argument should be an array of n AIO control blocks, and those bad boys get submitted to context ctx_id. it's giving "batch submit, no small talk." โœจ

the iocb structure ๐Ÿ‘‘

the iocb (I/O control block) structure, defined over in linux/aio_abi.h, is literally the blueprint โ€” it defines the parameters that control the I/O operation. main character energy fr fr.

#include <linux/aio_abi.h>

struct iocb {
    __u64   aio_data;
    __u32   PADDED(aio_key, aio_rw_flags);
    __u16   aio_lio_opcode;
    __s16   aio_reqprio;
    __u32   aio_fildes;
    __u64   aio_buf;
    __u64   aio_nbytes;
    __s64   aio_offset;
    __u64   aio_reserved2;
    __u32   aio_flags;
    __u32   aio_resfd;
};

the fields, one by one ๐Ÿงš

  • aio_data โ€” this data gets copied into the data field of the io_event structure when the I/O completes (peep io_getevents(2)). it's your little note-to-self that comes back to you. ๐Ÿซถ
  • aio_key โ€” internal kernel field. do NOT modify this field after an io_submit() call. hands off, bestie, that one's not yours. ๐Ÿšซ
  • aio_rw_flags โ€” this defines the R/W flags passed with the structure. the valid values are:
    • RWF_APPEND (since Linux 4.16) โ€” append data to the end of the file. check the description of the flag with the same name in pwritev2(2), plus the O_APPEND description in open(2). the aio_offset field is ignored, and the file offset is not changed. no cap.
    • RWF_DSYNC (since Linux 4.13) โ€” write operation completes according to the requirement of synchronized I/O data integrity. see the same-name flag in pwritev2(2) and the O_DSYNC bit in open(2).
    • RWF_HIPRI (since Linux 4.13) โ€” high priority request, poll if possible. VIP treatment, literally. ๐Ÿ‘‘
    • RWF_NOWAIT (since Linux 4.14) โ€” don't wait if the I/O will block for operations like file block allocations, dirty page flush, mutex locks, or a congested block device inside the kernel. if any of those conditions are met, the control block is returned immediately with a return value of -EAGAIN in the res field of the io_event structure (see io_getevents(2)). it's the "I'm not waiting in line" flag. ๐Ÿ’…
    • RWF_SYNC (since Linux 4.13) โ€” write operation completes according to the requirement of synchronized I/O file integrity. see the same-name flag in pwritev2(2) and the O_SYNC description in open(2).
    • RWF_NOAPPEND (since Linux 6.9) โ€” do not honor the O_APPEND open(2) flag. see the RWF_NOAPPEND description in pwritev2(2).
    • RWF_ATOMIC (since Linux 6.11) โ€” write a block of data such that a write will never be torn from power fail or similar. see the RWF_ATOMIC description in pwritev2(2). for usage with IOCB_CMD_PWRITEV, the upper vector limit is stx_atomic_write_segments_max โ€” check the STATX_WRITE_ATOMIC and stx_atomic_write_segments_max descriptions in statx(2). atomicity is the moment. ๐Ÿ”ฅ
  • aio_lio_opcode โ€” this defines the type of I/O to be performed by the iocb structure. the valid values come from the enum defined in linux/aio_abi.h:
enum {
    IOCB_CMD_PREAD = 0,
    IOCB_CMD_PWRITE = 1,
    IOCB_CMD_FSYNC = 2,
    IOCB_CMD_FDSYNC = 3,
    IOCB_CMD_POLL = 5,
    IOCB_CMD_NOOP = 6,
    IOCB_CMD_PREADV = 7,
    IOCB_CMD_PWRITEV = 8,
};
  • aio_reqprio โ€” this defines the requests priority.
  • aio_fildes โ€” the file descriptor on which the I/O operation is to be performed.
  • aio_buf โ€” this is the buffer used to transfer data for a read or write operation.
  • aio_nbytes โ€” this is the size of the buffer pointed to by aio_buf.
  • aio_offset โ€” this is the file offset at which the I/O operation is to be performed.
  • aio_flags โ€” this is the set of flags associated with the iocb structure. the valid values are:
    • IOCB_FLAG_RESFD โ€” asynchronous I/O control must signal the file descriptor mentioned in aio_resfd upon completion. it's a little "ding! done ๐Ÿ’Œ" notification.
    • IOCB_FLAG_IOPRIO (since Linux 4.18) โ€” interpret the aio_reqprio field as an IOPRIO_VALUE as defined by linux/ioprio.h.
  • aio_resfd โ€” the file descriptor to signal in the event of asynchronous I/O completion.

โญ RETURN VALUE โญ

On success, io_submit() returns the number of iocbs submitted โ€” and heads up, that number may be less than n, or 0 if n is zero. so yeah, partial submissions are a real thing, it's not lying to you. ๐Ÿ˜ญ

On error, -1 is returned, and errno is set to indicate the error.

๐Ÿ’€ ERRORS ๐Ÿ’€

  • EAGAIN โ€” insufficient resources are available to queue any iocbs. the queue said "we're full bestie." ๐Ÿซ 
  • EBADF โ€” the file descriptor specified in the first iocb is invalid.
  • EFAULT โ€” one of the data structures points to invalid data.
  • EINVAL โ€” the AIO context specified by ctx_id is invalid.
  • EINVAL โ€” n is less than 0.
  • EINVAL โ€” the iocb at *iocbpp[0] is not properly initialized.
  • EINVAL โ€” the operation specified is invalid for the file descriptor in the iocb.
  • EINVAL โ€” the value in the aio_reqprio field is invalid.
  • ENOSYS โ€” io_submit() is not implemented on this architecture. (this arch said "not my problem.") ๐Ÿ™ˆ
  • EPERM โ€” the aio_reqprio field is set with the class IOPRIO_CLASS_RT, but the submitting context does not have the CAP_SYS_ADMIN capability. permission check said no. ๐Ÿšซ

๐ŸŒˆ VERSIONS ๐ŸŒˆ

libaio provides a wrapper function with the same name, but a different prototype and return value. you probably want to use that wrapper. (this is me, gently, taking your hand and steering you toward the wrapper.) ๐Ÿซถ

๐Ÿ“œ STANDARDS ๐Ÿ“œ

Linux.

๐Ÿฆ‹ HISTORY ๐Ÿฆ‹

Linux 2.5. vintage, honestly.

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

io_cancel(2), io_destroy(2), io_getevents(2), io_setup(2), io_submit(3), aio(7)


and that's the page, bestie!! ๐ŸŽ€ this little writeup came from Linux man-pages 6.20, dated 2026-04-11 โ€” so if the kernel did a glow-up since then, that's between you and the source tree. go submit some I/O, you're doing amazing, periodt. ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: GPL-1.0-or-later
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms