ALQ(9)

πŸ’– ALQ β€” Asynchronous Logging Queues πŸ’–

okay bestie, sit down, hydrate, because we're about to talk about the Asynchronous Logging Queues facility in the ALQ(9) kernel developer's manual, and honestly? it's giving "quietly carrying the whole logging situation on its back." no cap.


✨ NAME ✨

alq, alq_open_flags, alq_open, alq_writen, alq_write, alq_flush, alq_close, alq_getn, alq_get, alq_post_flags, alq_post β€” Asynchronous Logging Queues. that's the whole squad right there 🫢


✨ SYNOPSIS ✨

first things first, you gotta pull in the header:

#include <sys/alq.h>

and then here's the full cast of characters, verbatim, no edits, we do not touch the source code in this house πŸ’…

int
alq_open_flags(struct alq **app, const char *file, struct ucred *cred,
    int cmode, int size, int flags);

int
alq_open(struct alq **app, const char *file, struct ucred *cred,
    int cmode, int size, int count);

int
alq_writen(struct alq *alq, void *data, int len, int flags);

int
alq_write(struct alq *alq, void *data, int flags);

void
alq_flush(struct alq *alq);

void
alq_close(struct alq *alq);

struct ale *
alq_getn(struct alq *alq, int len, int flags);

struct ale *
alq_get(struct alq *alq, int flags);

void
alq_post_flags(struct alq *alq, struct ale *ale, int flags);

void
alq_post(struct alq *alq, struct ale *ale);

✨ DESCRIPTION ✨

so the alq facility is this whole asynchronous fixed-or-variable-length recording mechanism, aka Asynchronous Logging Queues. it can record to any vnode(9), which means bestie it can journal logs to character devices and regular files. versatility? unmatched. 🌈

every single function takes a struct alq argument, and that's an opaque type that keeps all the state info for one Asynchronous Logging Queue. the logging facility itself runs in a separate kernel thread, and that thread services all the log entry requests. main character energy, honestly.

an β€œasynchronous log entry” is defined as struct ale, and it has these members:

struct ale {
        intptr_t        ae_bytesused;   /* # bytes written to ALE. */
        char            *ae_data;       /* Write ptr. */
        int             ae_pad;         /* Unused, compat. */
};

an alq can be created in either fixed or variable length mode:

  • variable length mode β€” accommodates writes of varying length using alq_writen() and alq_getn().
  • fixed length mode β€” accommodates a fixed number of writes using alq_write() and alq_get(), each of fixed size (set at queue creation time).
⚠️ heads up bestie: fixed length mode is deprecated in favour of variable length mode. it's giving "still works but we're moving on."

✨ FUNCTIONS ✨

🌸 alq_open_flags()

creates a brand new variable length asynchronous logging queue.

  • the file argument is the name of the file to open for logging. if the file doesn't exist yet, alq_open() will attempt to create it.
  • the cmode argument gets passed to vn_open() as the requested creation mode, and it's used if the file is going to be created by alq_open(). consumers of this API may wish to pass ALQ_DEFAULT_CMODE, which is a default creation mode suitable for most applications. πŸŽ€
  • the cred argument specifies the credentials to use when opening and performing I/O on the file.
  • the size argument sets the size (in bytes) of the underlying queue.
  • the ALQ_ORDERED flag may be passed in via flags to indicate that the ordering of writer threads waiting for a busy alq to free up resources should be preserved. so polite of it, honestly. 🫢

πŸ¦‹ alq_open() (deprecated, but still here for the girlies)

the deprecated alq_open() function is implemented as a wrapper around alq_open_flags() to provide backwards compatibility to consumers that have not been updated to utilise the newer alq_open_flags() function. it passes all arguments through to alq_open_flags() untouched except for size and count, and sets flags to 0.

  • to create a variable length mode alq: set size to the size (in bytes) of the underlying queue, and set count to 0.
  • to create a fixed length mode alq: set size to the size (in bytes) of each write, and set count to the number of size byte chunks to reserve capacity for.

πŸ’œ alq_writen()

writes len bytes from data to the designated variable length mode queue alq.

if alq_writen() could not write the entry immediately and ALQ_WAITOK is set in flags, the function will be allowed to msleep_spin(9) with the β€œalqwnord” or β€œalqwnres” wait message. 😴

a write will automatically schedule the queue alq to be flushed to disk. you can control this behaviour by passing ALQ_NOACTIVATE via flags to indicate that the write should not schedule alq to be flushed to disk.

πŸ“ alq_write() (deprecated, fixed length girlie)

the deprecated alq_write() function is implemented as a wrapper around alq_writen() to provide backwards compatibility to consumers that have not been updated to utilise variable length mode queues.

it writes size bytes of data (where size was specified at queue creation time) from the data buffer to the alq.

🚨 note that it is an error to call alq_write() on a variable length mode queue. don't do it bestie, it will not slay.

⭐ alq_flush()

used for flushing alq to the log medium that was passed to alq_open().

if alq has data to flush and is not already in the process of being flushed, the function will block doing IO. otherwise, the function will return immediately. bye, no waiting. πŸ‘‘

🌈 alq_close()

closes the asynchronous logging queue alq and flushes all pending write requests to the log medium. it will free all resources that were previously allocated. clean girl energy, we love to see it. ✨

🧚 alq_getn()

returns an asynchronous log entry from alq, initialised to point at a buffer capable of receiving len bytes of data.

⚠️ this function leaves alq in a locked state, until a subsequent alq_post() or alq_post_flags() call is made. yes, it's holding the lock, yes it's on purpose.

if alq_getn() could not obtain len bytes of buffer immediately and ALQ_WAITOK is set in flags, the function will be allowed to msleep_spin(9) with the β€œalqgnord” or β€œalqgnres” wait message.

the caller can choose to write less than len bytes of data to the returned asynchronous log entry by setting the entry's ae_bytesused field to the number of bytes actually written. and bestie, this must be done prior to calling alq_post(). periodt.

πŸ’… alq_get() (deprecated, fixed length)

the deprecated alq_get() function is implemented as a wrapper around alq_getn() to provide backwards compatibility to consumers that have not been updated to utilise variable length mode queues.

the asynchronous log entry returned will be initialised to point at a buffer capable of receiving size bytes of data (where size was specified at queue creation time).

🚨 note that it is an error to call alq_get() on a variable length mode queue.

πŸŽ€ alq_post_flags()

schedules the asynchronous log entry ale (obtained from alq_getn() or alq_get()) for writing to alq.

the ALQ_NOACTIVATE flag may be passed in via flags to indicate that the queue should not be immediately scheduled to be flushed to disk.

this function leaves alq in an unlocked state. releasing the lock, as a treat. 🫢

🌸 alq_post()

implemented as a wrapper around alq_post_flags() to provide backwards compatibility to consumers that have not been updated to utilise the newer alq_post_flags() function. it simply passes all arguments through to alq_post_flags() untouched, and sets flags to 0. simple, iconic, no notes.


✨ IMPLEMENTATION NOTES ✨

the alq_writen() and alq_write() functions both perform a bcopy(3) from the supplied data buffer into the underlying alq buffer. so if you're in a performance critical code path, you may wish to consider using alq_getn() (variable length queues) or alq_get() (fixed length queues) to avoid that extra memory copy. ✨

⚠️ but lowkey watch out: a queue remains locked between calls to alq_getn() or alq_get() and alq_post() or alq_post_flags(). so this method of writing to a queue is unsuitable for situations where the time between calls may be substantial. do not leave the lock hanging bestie, it's rude.

✨ LOCKING ✨

each asynchronous logging queue is protected by a spin mutex.

functions alq_flush() and alq_open() may attempt to acquire an internal sleep mutex, and should consequently not be used in contexts where sleeping is not allowed. πŸ’€


✨ RETURN VALUES ✨

  • alq_open() returns one of the error codes listed in open(2), if it fails to open file, or else it returns 0.
  • alq_writen() and alq_write() return EWOULDBLOCK if ALQ_NOWAIT was set in flags and either the queue is full or the system is shutting down.
  • alq_getn() and alq_get() return NULL if ALQ_NOWAIT was set in flags and either the queue is full or the system is shutting down.
🚨 NOTE: invalid arguments to non-void functions will result in undefined behaviour. i'm not joking bestie, undefined behaviour is not a vibe, it's a jumpscare.

✨ SEE ALSO ✨

syslog(3), kproc(9), ktr(9), msleep_spin(9), vnode(9)

go read those too, they're all connected, iykyk πŸ¦‹


✨ HISTORY ✨

the Asynchronous Logging Queues (ALQ) facility first appeared in FreeBSD 5.0. a legend from day one, fr fr. πŸ‘‘


✨ AUTHORS ✨

the alq facility was written by Jeffrey Roberson <jeff@FreeBSD.org> and extended by Lawrence Stewart <lstewart@freebsd.org>.

this manual page was written by Hiten Pandya <hmp@FreeBSD.org> and revised by Lawrence Stewart <lstewart@freebsd.org>.


and that's the whole page bestie!! πŸŽ€ this version of the ALQ(9) page came to you courtesy of Debian, dated April 26, 2010, and it lives under the ALQ(9) name in the Kernel Developer's Manual. go forth and journal your logs asynchronously, you absolute icon πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from