SGLIST(9)

πŸ’– SGLIST(9) πŸ’– β€” live from the Kernel Developer's Manual, and yes it's a whole personality

okay so bestie, quick vibe check before we dive in: this page lives in the Kernel Developer's Manual, section 9, and it's about sglist plus its absolutely enormous friend group. like, twenty-two functions. they're all icons, they all show up to the group chat, and they all do one collective job. let's get into it. 🫢


✨ NAME ✨

the main characters are sglist and every single one of its besties:

  • sglist
  • sglist_alloc
  • sglist_append
  • sglist_append_bio
  • sglist_append_mbuf
  • sglist_append_phys
  • sglist_append_sglist
  • sglist_append_uio
  • sglist_append_user
  • sglist_append_vmpages
  • sglist_build
  • sglist_clone
  • sglist_consume_uio
  • sglist_count
  • sglist_count_vmpages
  • sglist_free
  • sglist_hold
  • sglist_init
  • sglist_join
  • sglist_length
  • sglist_reset
  • sglist_slice
  • sglist_split

and together they manage a scatter/gather list of physical memory addresses. that's the whole assignment. they ate. πŸ’…


🌸 SYNOPSIS 🌸

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

struct sglist *
sglist_alloc(int nsegs, int mflags);

int
sglist_append(struct sglist *sg, void *buf, size_t len);

int
sglist_append_bio(struct sglist *sg, struct bio *bp);

int
sglist_append_mbuf(struct sglist *sg, struct mbuf *m);

int
sglist_append_phys(struct sglist *sg, vm_paddr_t paddr, size_t len);

int
sglist_append_sglist(struct sglist *sg, struct sglist *source,
    size_t offset, size_t len);

int
sglist_append_uio(struct sglist *sg, struct uio *uio);

int
sglist_append_user(struct sglist *sg, void *buf, size_t len,
    struct thread *td);

int
sglist_append_vmpages(struct sglist *sg, vm_page_t *m, size_t pgoff,
    size_t len);

struct sglist *
sglist_build(void *buf, size_t len, int mflags);

struct sglist *
sglist_clone(struct sglist *sg, int mflags);

int
sglist_consume_uio(struct sglist *sg, struct uio *uio, size_t resid);

int
sglist_count(void *buf, size_t len);

int
sglist_count_vmpages(vm_page_t *m, size_t pgoff, size_t len);

void
sglist_free(struct sglist *sg);

struct sglist *
sglist_hold(struct sglist *sg);

void
sglist_init(struct sglist *sg, int maxsegs, struct sglist_seg *segs);

int
sglist_join(struct sglist *first, struct sglist *second);

size_t
sglist_length(struct sglist *sg);

void
sglist_reset(struct sglist *sg);

int
sglist_slice(struct sglist *original, struct sglist **slice,
    size_t offset, size_t length, int mflags);

int
sglist_split(struct sglist *original, struct sglist **head,
    size_t length, int mflags);

πŸ’« DESCRIPTION πŸ’«

okay so here's the tea β˜•. The sglist API manages physical address ranges. every list holds one or more elements, and every element is just a starting physical address plus a length. that's it. tiny little address-and-length baddies. πŸŽ€

and here's the part where you have to be on your best behaviour: scatter/gather lists are read-only while they are shared. if you wanna alter an existing scatter/gather list and you do not hold the sole reference to it, then you make a new list instead of messing with the existing one. do not be out here editing a list that other people are holding. that's not the move.

every scatter/gather list object also carries a reference count. new lists are created with a single reference. you get new references by calling sglist_hold, and you release them by calling sglist_free.


πŸŽ€ Allocating and Initializing Lists πŸŽ€

every sglist object is made of two things: a header structure, and a variable-length array of scatter/gather list elements. iconic structural duo, honestly.

  • sglist_alloc β€” allocates a brand new list containing a header plus nsegs scatter/gather list elements. the mflags argument can be set to either M_NOWAIT or M_WAITOK.
  • sglist_count β€” returns the number of scatter/gather list elements you'd need to describe the physical address ranges mapped by a single kernel virtual address range. that range starts at buf and is len bytes long.
  • sglist_count_vmpages β€” returns the number of scatter/gather list elements needed to describe the physical address ranges of a buffer backed by an array of virtual memory pages m. the buffer starts at an offset of pgoff bytes relative to the first page and is len bytes long.
  • sglist_build β€” allocates a new scatter/gather list object that describes the physical address ranges mapped by a single kernel virtual address range. that range starts at buf and is len bytes long. the mflags argument can be set to either M_NOWAIT or M_WAITOK.
  • sglist_clone β€” hands you back a copy of an existing scatter/gather list object sg. the mflags argument can be set to either M_NOWAIT or M_WAITOK. this is literally the move when you want a private copy of a scatter/gather list before you go modifying it. πŸ¦‹
  • sglist_init β€” initializes a scatter/gather list header. the header is pointed to by sg and gets initialized to manage an array of maxsegs scatter/gather list elements pointed to by segs. use this when you want to initialize a header whose storage did not come from sglist_alloc.
🫢 important, bestie: in that case the caller should not call sglist_free to release its own reference, and the caller is responsible for making sure all other references to the list are dropped before it releases the storage for sg and segs. clean up after yourself, okay?

🧚 Constructing Scatter/Gather Lists 🧚

the sglist API gives you a whole lil toolkit for building a scatter/gather list that describes one or more objects. specifically, the sglist_append family of routines can append the physical address ranges described by an object onto the end of a scatter/gather list.

all of these routines return 0 on success or an error on failure. and if a request to append an address range to a scatter/gather list fails, the scatter/gather list will remain unchanged. no half-finished messes here, she's tidy.

  • sglist_append β€” appends the physical address ranges described by a single kernel virtual address range to the scatter/gather list sg. the range starts at buf and is len bytes long.
  • sglist_append_bio β€” appends the physical address ranges described by a single bio bp to the scatter/gather list sg.
  • sglist_append_mbuf β€” appends the physical address ranges described by an entire mbuf chain m to the scatter/gather list sg. whole chain. all of it. πŸ’…
  • sglist_append_phys β€” appends a single physical address range to the scatter/gather list sg. the range starts at paddr and is len bytes long.
  • sglist_append_sglist β€” appends the physical address ranges described by the scatter/gather list source to the scatter/gather list sg. the ranges start at offset within source and continue for len bytes.
  • sglist_append_uio β€” appends the physical address ranges described by a uio(9) object to the scatter/gather list sg.
⚠️ two things, and both matter: it is the caller's responsibility to ensure that the pages backing the I/O request are wired for the lifetime of sg. also note that this routine does not modify uio. she looks, she doesn't touch.
  • sglist_append_user β€” appends the physical address ranges described by a single user virtual address range to the scatter/gather list sg. that range is relative to the address space of the thread td, starts at buf, and is len bytes long.
⚠️ again for the people in the back: it is the caller's responsibility to ensure that the pages backing the user buffer are wired for the lifetime of sg.
  • sglist_append_vmpages β€” appends the physical address ranges of a buffer backed by an array of virtual memory pages m. the buffer starts at an offset of pgoff bytes relative to the first page and is len bytes long.
  • sglist_consume_uio β€” this one is a variation of sglist_append_uio. same as before, it appends the physical address ranges described by uio to the scatter/gather list sg. but unlike sglist_append_uio, sglist_consume_uio modifies the I/O request to indicate that the appended address ranges have been processed, similar to calling uiomove(9).

this routine will only append ranges that describe up to resid total bytes in length. if the available segments in the scatter/gather list get exhausted before resid bytes are processed, then the uio structure will be updated to reflect the actual number of bytes processed, and sglist_consume_io will return zero to indicate success. in effect, this function performs partial reads or writes. the caller can compare the uio_resid member of uio before and after calling sglist_consume_uio to figure out the actual number of bytes processed. ✨


🌈 Manipulating Scatter/Gather Lists 🌈

  • sglist_join β€” appends the physical address ranges from the scatter/gather list second onto first, and then resets second to an empty list. returns zero on success or an error on failure.
  • sglist_split β€” splits an existing scatter/gather list into two lists. the first length bytes described by the list original are moved to a new list *head.

if original describes a total address range that is smaller than length bytes, then all of the address ranges will be moved to the new list at *head and original will be an empty list.

the caller may supply an existing scatter/gather list in *head β€” and if so, that list must be empty. otherwise, the caller may set *head to NULL, in which case a new scatter/gather list will be allocated, and in that case mflags may be set to either M_NOWAIT or M_WAITOK.

⚠️ heads up: since the original list gets modified by this call, it must be a private list with no other references. no sharing. she needs her space.

the sglist_split function returns zero on success or an error on failure.

  • sglist_slice β€” generates a new scatter/gather list from a sub-range of an existing scatter/gather list original. the sub-range to extract is specified by the offset and length parameters. the new scatter/gather list is stored in *slice.

same deal as with head for sglist_join: the caller may either provide an empty scatter/gather list, or set *slice to NULL, in which case sglist_slice will allocate a new list subject to mflags.

but unlike sglist_split, sglist_slice does not modify original and does not require it to be a private list. respectful queen. πŸ‘‘

the sglist_split function returns zero on success or an error on failure.


πŸ“ Miscellaneous Routines πŸ“

  • sglist_reset β€” clears the scatter/gather list sg so that it no longer maps any address ranges. this lets you reuse a single scatter/gather list object for multiple requests. sustainable behaviour, we love it. 🌱
  • sglist_length β€” returns the total length of the physical address ranges described by the scatter/gather list sg.

⭐ RETURN VALUES ⭐

  • sglist_alloc, sglist_build, and sglist_clone return a new scatter/gather list on success or NULL on failure.
  • the sglist_append family of functions, plus sglist_consume_uio, sglist_join, sglist_slice, and sglist_split, return zero on success or an error on failure.
  • sglist_count and sglist_count_vmpages return a count of scatter/gather list elements.
  • sglist_length returns a count of address space described by a scatter/gather list in bytes.

and bestie, we're not even done β€” hold that thought, because there's more of this page coming right up. 🧚✨

πŸ’₯ ERRORS πŸ’₯

okay bestie, here's the part where things go sideways, and when they do, this is exactly what you get back. no guessing, no improvising. 🫢

🌸 sglist_append β€” when it fails, it throws:

  • [EINVAL] β€” the scatter/gather list has zero segments. literally empty. nothing there. πŸ’€
  • [EFBIG] β€” there are not enough available segments in the scatter/gather list to append the physical address ranges you specified. too much vibes, not enough room. 😭

πŸŽ€ sglist_consume_uio β€” when it fails, it throws:

  • [EINVAL] β€” the scatter/gather list has zero segments. same energy, still empty. πŸ™ˆ

✨ sglist_join β€” when it fails, it throws:

  • [EFBIG] β€” there are not enough available segments in the scatter/gather list first to append the physical address ranges from second. the first one simply cannot hold what the second one is bringing to the function. πŸ’…

πŸ¦‹ sglist_slice β€” when it fails, it throws:

  • [EINVAL] β€” the original scatter/gather list does not describe enough address space to cover the requested sub-range. you asked for a slice that isn't on the pizza. πŸ“
  • [EINVAL] β€” the caller-supplied scatter/gather list in *slice is not empty. it has to be empty, bestie, that's the whole deal. 😭
  • [ENOMEM] β€” an attempt to allocate a new scatter/gather list with M_NOWAIT set in mflags failed. allocation said no. πŸ’€
  • [EFBIG] β€” there are not enough available segments in the caller-supplied scatter/gather list in *slice to describe the requested physical address ranges.

🌈 sglist_split β€” when it fails, it throws:

  • [EDOOFUS] β€” the original scatter/gather list has more than one reference. yes that's a real errno name and yes it's iconic of the kernel to name it that. πŸ‘‘
  • [EINVAL] β€” the caller-supplied scatter/gather list in *head is not empty. again: empty only, no exceptions. πŸ’…
  • [ENOMEM] β€” an attempt to allocate a new scatter/gather list with M_NOWAIT set in mflags failed. 😭
  • [EFBIG] β€” there are not enough available segments in the caller-supplied scatter/gather list in *head to describe the requested physical address ranges.

πŸ‘€ SEE ALSO πŸ‘€

go read these next, they're the friend group:


πŸ“œ HISTORY πŸ“œ

this API was first introduced in FreeBSD 8.0. an elder. a founding mother. we respect her. πŸ’œ


and that's a wrap on SGLIST(9), bestie πŸŽ€ this page came from the Debian manpage set and it's dated May 16, 2017 β€” so if you're reading this way later, hi, the segments still work the same, slay ✨ go append something πŸ’–

πŸ“¦ where this page comes from