UIO(9)

πŸ’– UIO(9) β€” the kernel dev manual, bestie edition πŸ’–

okay so welcome to UIO(9), the page all about uio, uiomove, uiomove_frombuf, and uiomove_nofault β€” aka the device driver I/O routines. this is the kernel developer's manual and honestly? it's giving backstage pass to how drivers actually move your bytes around. πŸŽ€


✨ NAME ✨

uio, uiomove, uiomove_frombuf, uiomove_nofault β€” device driver I/O routines. periodt. πŸ’…


🌸 SYNOPSIS 🌸

first up, the includes you're gonna need, no skipping:

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

and here's the icon itself, the struct uio (copy it exactly, bestie, every field matters):

struct uio {
        struct  iovec *uio_iov;         /* scatter/gather list */
        int     uio_iovcnt;             /* length of scatter/gather list */
        off_t   uio_offset;             /* offset in target object */
        ssize_t uio_resid;              /* remaining bytes to copy */
        enum    uio_seg uio_segflg;     /* address space */
        enum    uio_rw uio_rw;          /* operation */
        struct  thread *uio_td;         /* owner */
};

and the three functions, all returning int:

int
uiomove(void *buf, int howmuch, struct uio *uiop);

int
uiomove_frombuf(void *buf, int howmuch, struct uio *uiop);

int
uiomove_nofault(void *buf, int howmuch, struct uio *uiop);

πŸ¦‹ DESCRIPTION πŸ¦‹

so uiomove(), uiomove_frombuf(), and uiomove_nofault() are the girlies that transfer data between buffers and I/O vectors β€” and lowkey the whole plot twist is that those vectors might cross the user/kernel space boundary. πŸ’€

here's the tea: whenever a read(2), write(2), readv(2), or writev(2) system call gets handed off to a character-device driver, the driver's d_read or d_write entry gets called with a pointer to a struct uio. that struct is the transfer request, encoded. your driver should then use uiomove() or uiomove_nofault() to actually get at the data inside. no cap. πŸ”₯

now let's do a vibe check on each field:

  • uio_iov β€” the array of I/O vectors getting processed. for scatter/gather I/O this is gonna be more than one vector, bestie.
  • uio_iovcnt β€” how many I/O vectors are present.
  • uio_offset β€” the offset into the device.
  • uio_resid β€” how many bytes are left to process. gets updated after the transfer. ✨
  • uio_segflg β€” one of these flags, choose wisely:
    • UIO_USERSPACE β€” the I/O vector points into a process's address space.
    • UIO_SYSSPACE β€” the I/O vector points into the kernel address space.
    • UIO_NOCOPY β€” do not copy, it's already in the object.
  • uio_rw β€” the direction of the desired transfer, either UIO_READ or UIO_WRITE.
  • uio_td β€” pointer to a struct thread for the associated thread. used when uio_segflg says the transfer is happening to/from a process's address space.

🧚 uiomove_nofault() β€” the picky one

this function requires that the buffer and I/O vectors be accessible without incurring a page fault. like, the source and destination addresses must be physically mapped for read and write access respectively, and neither the source nor the destination address may be pageable. so basically uiomove_nofault() can be called from contexts where acquiring virtual memory system locks or sleeping is prohibited. it's the "we literally cannot afford to sleep rn" girlie. 😭

πŸ“ uiomove_frombuf() β€” the convenience queen

uiomove_frombuf() is a convenience wrapper around uiomove() for drivers that serve data wholly contained within an existing buffer in memory. she validates the uio_offset and uio_resid values against the size of that existing buffer, and handles short transfers when the request only partially overlaps the buffer. and when uio_offset is greater than or equal to the buffer size? result is success with no bytes transferred β€” which effectively signals EOF. iconic behaviour, honestly. πŸ‘‘


⭐ RETURN VALUES ⭐

on success, uiomove(), uiomove_frombuf(), and uiomove_nofault() all return 0. on error they return an appropriate error code. that's it, that's the whole thing. 🫢


🌈 EXAMPLES 🌈

okay so the idea here is that the driver keeps a private buffer for its data and processes the request in chunks of max the size of that buffer. but hear me out β€” the buffer handling below is very simplified and will not work (the buffer pointer isn't being advanced in case of a partial read). it's just here to demonstrate the uio handling. do not ship it. πŸ™ˆ

/* MIN() can be found there: */
#include <sys/param.h>

#define BUFSIZE 512
static char buffer[BUFSIZE];

static int data_available;      /* amount of data that can be read */

static int
fooread(struct cdev *dev, struct uio *uio, int flag)
{
        int rv, amnt;

        rv = 0;
        while (uio->uio_resid > 0) {
                if (data_available > 0) {
                        amnt = MIN(uio->uio_resid, data_available);
                        rv = uiomove(buffer, amnt, uio);
                        if (rv != 0)
                                break;
                        data_available -= amnt;
                } else
                        tsleep(...);    /* wait for a better time */
        }
        if (rv != 0) {
                /* do error cleanup here */
        }
        return (rv);
}

πŸ’€ ERRORS πŸ’€

uiomove() and uiomove_nofault() will fail and return this error code if:

and in addition, uiomove_nofault() will fail and return this error code if:

  • [EFAULT] β€” a page fault occurs.
fr fr, read those twice. uiomove_nofault() has two ways to hand you an EFAULT and one of them is just "a page fault happened". πŸ’…

πŸ”₯ SEE ALSO πŸ”₯

read(2), readv(2), write(2), writev(2), copyin(9), copyout(9), sleep(9)


πŸ‘‘ HISTORY πŸ‘‘

the uio mechanism appeared in some early version of Unix. ancient. legendary. the blueprint. 🌟


πŸ’œ AUTHORS πŸ’œ

this manual page was written by Jârg Wunsch. thank you for your service, king. 🫢


okay that's the whole page bestie, you're officially uio-pilled πŸ’–βœ¨ this was Debian's UIO(9), dated March 11, 2017 β€” go forth and move those bytes responsibly. mwah. πŸŽ€πŸ¦‹πŸ’…

πŸ“¦ where this page comes from