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:
sglistsglist_allocsglist_appendsglist_append_biosglist_append_mbufsglist_append_physsglist_append_sglistsglist_append_uiosglist_append_usersglist_append_vmpagessglist_buildsglist_clonesglist_consume_uiosglist_countsglist_count_vmpagessglist_freesglist_holdsglist_initsglist_joinsglist_lengthsglist_resetsglist_slicesglist_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 plusnsegsscatter/gather list elements. themflagsargument can be set to eitherM_NOWAITorM_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 atbufand islenbytes 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 pagesm. the buffer starts at an offset ofpgoffbytes relative to the first page and islenbytes 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 atbufand islenbytes long. themflagsargument can be set to eitherM_NOWAITorM_WAITOK.sglist_cloneβ hands you back a copy of an existing scatter/gather list objectsg. themflagsargument can be set to eitherM_NOWAITorM_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 bysgand gets initialized to manage an array ofmaxsegsscatter/gather list elements pointed to bysegs. use this when you want to initialize a header whose storage did not come fromsglist_alloc.
π«Ά important, bestie: in that case the caller should not callsglist_freeto 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 forsgandsegs. 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 listsg. the range starts atbufand islenbytes long.sglist_append_bioβ appends the physical address ranges described by a single biobpto the scatter/gather listsg.sglist_append_mbufβ appends the physical address ranges described by an entire mbuf chainmto the scatter/gather listsg. whole chain. all of it. πsglist_append_physβ appends a single physical address range to the scatter/gather listsg. the range starts atpaddrand islenbytes long.sglist_append_sglistβ appends the physical address ranges described by the scatter/gather listsourceto the scatter/gather listsg. the ranges start atoffsetwithinsourceand continue forlenbytes.sglist_append_uioβ appends the physical address ranges described by auio(9)object to the scatter/gather listsg.
β οΈ 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 ofsg. also note that this routine does not modifyuio. 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 listsg. that range is relative to the address space of the threadtd, starts atbuf, and islenbytes 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 pagesm. the buffer starts at an offset ofpgoffbytes relative to the first page and islenbytes long.sglist_consume_uioβ this one is a variation ofsglist_append_uio. same as before, it appends the physical address ranges described byuioto the scatter/gather listsg. but unlikesglist_append_uio,sglist_consume_uiomodifies the I/O request to indicate that the appended address ranges have been processed, similar to callinguiomove(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 listsecondontofirst, and then resetssecondto an empty list. returns zero on success or an error on failure.sglist_splitβ splits an existing scatter/gather list into two lists. the firstlengthbytes described by the listoriginalare 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 listoriginal. the sub-range to extract is specified by theoffsetandlengthparameters. 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 listsgso 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 listsg.
β RETURN VALUES β
sglist_alloc,sglist_build, andsglist_clonereturn a new scatter/gather list on success orNULLon failure.- the
sglist_appendfamily of functions, plussglist_consume_uio,sglist_join,sglist_slice, andsglist_split, return zero on success or an error on failure. sglist_countandsglist_count_vmpagesreturn a count of scatter/gather list elements.sglist_lengthreturns 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 listfirstto append the physical address ranges fromsecond. 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*sliceis not empty. it has to be empty, bestie, that's the whole deal. π[ENOMEM]β an attempt to allocate a new scatter/gather list withM_NOWAITset inmflagsfailed. allocation said no. π[EFBIG]β there are not enough available segments in the caller-supplied scatter/gather list in*sliceto 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*headis not empty. again: empty only, no exceptions. π[ENOMEM]β an attempt to allocate a new scatter/gather list withM_NOWAITset inmflagsfailed. π[EFBIG]β there are not enough available segments in the caller-supplied scatter/gather list in*headto 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
- debian bookworm: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 2009 Hudson River Trading LLC
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms