SMR_LIST_INIT(9)
π SMR_LIST_INIT(9)
okay so bestie, this one is straight out of the Kernel Developer's Manual and it's a whole family of macros β not just the one in the title. The SMR_* list macros. Singly-linked lists, regular lists, and tail queues, all built to live inside the safe memory reclamation mechanism. It's giving concurrency, it's giving lock-free reader energy. Let's get into it π«Ά
β¨ NAME β¨
Literally all of these live on this page, no cap:
- singly-linked list girlies:
SMR_SLIST_ENTRY,SMR_SLIST_HEAD,SMR_SLIST_HEAD_INITIALIZER,SMR_SLIST_INIT,SMR_SLIST_FIRST,SMR_SLIST_NEXT,SMR_SLIST_FOREACH,SMR_SLIST_FIRST_LOCKED,SMR_SLIST_NEXT_LOCKED,SMR_SLIST_EMPTY_LOCKED,SMR_SLIST_FOREACH_LOCKED,SMR_SLIST_FOREACH_SAFE_LOCKED,SMR_SLIST_INSERT_HEAD_LOCKED,SMR_SLIST_INSERT_AFTER_LOCKED,SMR_SLIST_REMOVE_HEAD_LOCKED,SMR_SLIST_REMOVE_AFTER_LOCKED,SMR_SLIST_REMOVE_LOCKED - doubly-linked list girlies:
SMR_LIST_ENTRY,SMR_LIST_HEAD,SMR_LIST_HEAD_INITIALIZER,SMR_LIST_INIT,SMR_LIST_FIRST,SMR_LIST_NEXT,SMR_LIST_FOREACH,SMR_LIST_FIRST_LOCKED,SMR_LIST_NEXT_LOCKED,SMR_LIST_EMPTY_LOCKED,SMR_LIST_FOREACH_LOCKED,SMR_LIST_FOREACH_SAFE_LOCKED,SMR_LIST_INSERT_HEAD_LOCKED,SMR_LIST_INSERT_AFTER_LOCKED,SMR_LIST_INSERT_BEFORE_LOCKED,SMR_LIST_REMOVE_LOCKED - tail queue girlies:
SMR_TAILQ_ENTRY,SMR_TAILQ_HEAD,SMR_TAILQ_HEAD_INITIALIZER,SMR_TAILQ_INIT,SMR_TAILQ_FIRST,SMR_TAILQ_NEXT,SMR_TAILQ_FOREACH,SMR_TAILQ_FIRST_LOCKED,SMR_TAILQ_NEXT_LOCKED,SMR_TAILQ_LAST_LOCKED,SMR_TAILQ_EMPTY_LOCKED,SMR_TAILQ_FOREACH_LOCKED,SMR_TAILQ_FOREACH_SAFE_LOCKED,SMR_TAILQ_INSERT_HEAD_LOCKED,SMR_TAILQ_INSERT_TAIL_LOCKED,SMR_TAILQ_INSERT_AFTER_LOCKED,SMR_TAILQ_INSERT_BEFORE_LOCKED,SMR_TAILQ_REMOVE_LOCKED
β also known, collectively, as the SMR list macros. Iconic behavior π
πΈ SYNOPSIS πΈ
#include <sys/smr.h>
SMR_SLIST_ENTRY(TYPE);
void
SMR_SLIST_INIT(SMR_SLIST_HEAD *head);
TYPE *
SMR_SLIST_FIRST(SMR_SLIST_HEAD *head);
TYPE *
SMR_SLIST_NEXT(TYPE *elm, FIELDNAME);
SMR_SLIST_FOREACH(VARNAME, SMR_SLIST_HEAD *head, FIELDNAME);
TYPE *
SMR_SLIST_FIRST_LOCKED(SMR_SLIST_HEAD *head);
TYPE *
SMR_SLIST_NEXT_LOCKED(TYPE *elm, FIELDNAME);
int
SMR_SLIST_EMPTY_LOCKED(SMR_SLIST_HEAD *head);
SMR_SLIST_FOREACH_LOCKED(VARNAME, SMR_SLIST_HEAD *head, FIELDNAME);
SMR_SLIST_FOREACH_SAFE_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME,
TEMP_VARNAME);
void
SMR_SLIST_INSERT_HEAD_LOCKED(SMR_SLIST_HEAD *head, struct TYPE *elm,
FIELDNAME);
void
SMR_SLIST_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
FIELDNAME);
void
SMR_SLIST_REMOVE_HEAD_LOCKED(SMR_SLIST_HEAD *head, FIELDNAME);
void
SMR_SLIST_REMOVE_AFTER_LOCKED(struct TYPE *elm, FIELDNAME);
void
SMR_SLIST_REMOVE_LOCKED(SMR_SLIST_HEAD *head, struct TYPE *elm, TYPE,
FIELDNAME);
SMR_LIST_ENTRY(TYPE);
void
SMR_LIST_INIT(SMR_LIST_HEAD *head);
TYPE *
SMR_LIST_FIRST(SMR_LIST_HEAD *head);
TYPE *
SMR_LIST_NEXT(TYPE *elm, FIELDNAME);
TYPE *
SMR_LIST_FIRST_LOCKED(SMR_LIST_HEAD *head);
TYPE *
SMR_LIST_NEXT_LOCKED(TYPE *elm, FIELDNAME);
int
SMR_LIST_EMPTY_LOCKED(SMR_LIST_HEAD *head);
SMR_LIST_FOREACH(VARNAME, SMR_LIST_HEAD *head, FIELDNAME);
SMR_LIST_FOREACH_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME);
SMR_LIST_FOREACH_SAFE_LOCKED(VARNAME, SMR_LIST_HEAD *head, FIELDNAME,
TEMP_VARNAME);
void
SMR_LIST_INSERT_HEAD_LOCKED(SMR_LIST_HEAD *head, struct TYPE *elm,
FIELDNAME);
void
SMR_LIST_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
FIELDNAME);
void
SMR_LIST_INSERT_BEFORE_LOCKED(struct TYPE *listelm, struct TYPE *elm,
FIELDNAME);
void
SMR_LIST_REMOVE_LOCKED(struct TYPE *elm, FIELDNAME);
SMR_TAILQ_ENTRY(TYPE);
void
SMR_TAILQ_INIT(SMR_TAILQ_HEAD *head);
TYPE *
SMR_TAILQ_FIRST(SMR_TAILQ_HEAD *head);
TYPE *
SMR_TAILQ_NEXT(TYPE *elm, FIELDNAME);
TYPE *
SMR_TAILQ_FIRST_LOCKED(SMR_TAILQ_HEAD *head);
TYPE *
SMR_TAILQ_NEXT_LOCKED(TYPE *elm, FIELDNAME);
TYPE *
SMR_TAILQ_LAST_LOCKED(SMR_TAILQ_HEAD *head);
SMR_TAILQ_FOREACH(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME);
SMR_TAILQ_FOREACH_LOCKED(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME);
SMR_TAILQ_FOREACH_SAFE_LOCKED(VARNAME, SMR_TAILQ_HEAD *head, FIELDNAME,
TEMP_VARNAME);
void
SMR_TAILQ_INSERT_HEAD_LOCKED(SMR_TAILQ_HEAD *head, struct TYPE *elm,
FIELDNAME);
void
SMR_TAILQ_INSERT_TAIL_LOCKED(SMR_TAILQ_HEAD *head, struct TYPE *elm,
FIELDNAME);
void
SMR_TAILQ_INSERT_AFTER_LOCKED(struct TYPE *listelm, struct TYPE *elm,
FIELDNAME);
void
SMR_TAILQ_INSERT_BEFORE_LOCKED(struct TYPE *listelm, struct TYPE *elm,
FIELDNAME);
void
SMR_TAILQ_REMOVE_LOCKED(struct TYPE *elm, FIELDNAME);
π¦ DESCRIPTION π¦
So the SMR list macros define and operate on singly-linked lists, lists and tail queues β and the whole point is that they're usable with the safe memory reclamation mechanism. A data structure built with these macros can be accessed concurrently by multiple readers and a single writer. Multiple readers, one writer. That's the arrangement. π
Readers have to access the data structure inside an SMR read-side critical section. You enter that critical section with smr_read_enter(9) and you leave it with smr_read_leave(9). That's it, that's the vibe check.
Writers must ensure exclusive write access. Do that with a lock, like mutex(9) or rwlock(9). Note the mutual exclusion of writers does not need to apply to readers β that's the whole magic trick β¨
β οΈ Okay listen, this part matters: when an element has been removed from the data structure, the element must not be deleted or re-inserted before all reader references to it have disappeared. The writer has to use eithersmr_barrier(9)orsmr_call(9)to make sure the element can no longer be accessed by readers. No skipping this step, I'm serious π
π Singly-Linked Lists
The SMR_SLIST_ENTRY() macro declares a structure that connects the elements in the list. That's the little link-y bit π
SMR_SLIST_INIT()initializes the list head to an empty state.SMR_SLIST_FIRST()andSMR_SLIST_FIRST_LOCKED()return the first element on the list head, orNULLif the list is empty.SMR_SLIST_NEXT()andSMR_SLIST_NEXT_LOCKED()return the successor of the element elm, orNULLif there are no more elements on the list.SMR_SLIST_EMPTY_LOCKED()returns true if the list head is empty.SMR_SLIST_FOREACH()andSMR_SLIST_FOREACH_LOCKED()traverse the list head in forward direction.SMR_SLIST_FOREACH_SAFE_LOCKED()traverses the list head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the list and defer its freeing usingsmr_call(9).SMR_SLIST_INSERT_HEAD_LOCKED()inserts the new element elm at the head of the list.SMR_SLIST_INSERT_AFTER_LOCKED()inserts the new element elm after the element listelm.SMR_SLIST_REMOVE_HEAD_LOCKED()removes the first element of the list head.SMR_SLIST_REMOVE_AFTER_LOCKED()removes the list element immediately following elm.SMR_SLIST_REMOVE_LOCKED()removes the list element elm from the list head.
π§ Linked Lists
The SMR_LIST_ENTRY() macro declares a structure that connects the elements in the list. Same deal, doubly-linked edition π
SMR_LIST_INIT()initializes the list head to an empty state.SMR_LIST_FIRST()andSMR_LIST_FIRST_LOCKED()return the first element on the list head, orNULLif the list is empty.SMR_LIST_NEXT()andSMR_LIST_NEXT_LOCKED()return the successor of the element elm, orNULLif there are no more elements on the list.SMR_LIST_EMPTY_LOCKED()returns true if the list head is empty.SMR_LIST_FOREACH()andSMR_LIST_FOREACH_LOCKED()traverse the list head in forward direction.SMR_LIST_FOREACH_SAFE_LOCKED()traverses the list head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the list and defer its freeing usingsmr_call(9).SMR_LIST_INSERT_HEAD_LOCKED()inserts the new element elm at the head of the list.SMR_LIST_INSERT_AFTER_LOCKED()inserts the new element elm after the element listelm.SMR_LIST_INSERT_BEFORE_LOCKED()inserts the new element elm before the element listelm.SMR_LIST_REMOVE_LOCKED()removes the element elm from the list head.
π Tail Queues
The SMR_TAILQ_ENTRY() macro declares a structure that connects the elements in the tail queue. Bestie's favourite, honestly β you get a tail AND a head π
SMR_TAILQ_INIT()initializes the tail queue head to an empty state.SMR_TAILQ_FIRST()andSMR_TAILQ_FIRST_LOCKED()return the first element in the queue head, orNULLif the queue is empty.SMR_TAILQ_NEXT()andSMR_TAILQ_NEXT_LOCKED()return the successor of the element elm, orNULLif there are no more elements in the queue.SMR_TAILQ_EMPTY_LOCKED()returns true if the queue head is empty.SMR_TAILQ_FOREACH()andSMR_TAILQ_FOREACH_LOCKED()traverse the queue head in forward direction.SMR_TAILQ_FOREACH_SAFE_LOCKED()traverses the queue head in forward direction. It is permitted to remove the element referenced by variable VARNAME from the queue and defer its freeing usingsmr_call(9).SMR_TAILQ_INSERT_HEAD_LOCKED()inserts the new element elm at the head of the queue.SMR_TAILQ_INSERT_TAIL_LOCKED()inserts the new element elm at the tail of the queue.SMR_TAILQ_INSERT_AFTER_LOCKED()inserts the new element elm after the element listelm.SMR_TAILQ_INSERT_BEFORE_LOCKED()inserts the new element elm before the element listelm.SMR_TAILQ_REMOVE_LOCKED()removes the element elm from the queue head.
π CONTEXT π
All SMR list macros can be used during autoconf, from process context, or from interrupt context. All of them. Every single one. Flexible queens π
Can be used from an SMR read-side critical section:
SMR_SLIST_FIRST,SMR_SLIST_NEXT,SMR_SLIST_FOREACHSMR_LIST_FIRST,SMR_LIST_NEXT,SMR_LIST_FOREACHSMR_TAILQ_FIRST,SMR_TAILQ_NEXT,SMR_TAILQ_FOREACH
Can be used from writer context:
SMR_SLIST_INIT,SMR_SLIST_FIRST_LOCKED,SMR_SLIST_NEXT_LOCKED,SMR_SLIST_EMPTY_LOCKED,SMR_SLIST_FOREACH_LOCKED,SMR_SLIST_FOREACH_SAFE_LOCKED,SMR_SLIST_INSERT_HEAD_LOCKED,SMR_SLIST_INSERT_AFTER_LOCKED,SMR_SLIST_REMOVE_HEAD_LOCKED,SMR_SLIST_REMOVE_AFTER_LOCKED,SMR_SLIST_REMOVE_LOCKEDSMR_LIST_INIT,SMR_LIST_FIRST_LOCKED,SMR_LIST_NEXT_LOCKED,SMR_LIST_EMPTY_LOCKED,SMR_LIST_FOREACH_LOCKED,SMR_LIST_FOREACH_SAFE_LOCKED,SMR_LIST_INSERT_HEAD_LOCKED,SMR_LIST_INSERT_AFTER_LOCKED,SMR_LIST_INSERT_BEFORE_LOCKED,SMR_LIST_REMOVE_LOCKEDSMR_TAILQ_INIT,SMR_TAILQ_FIRST_LOCKED,SMR_TAILQ_NEXT_LOCKED,SMR_TAILQ_EMPTY_LOCKED,SMR_TAILQ_FOREACH_LOCKED,SMR_TAILQ_FOREACH_SAFE_LOCKED,SMR_TAILQ_INSERT_HEAD_LOCKED,SMR_TAILQ_INSERT_TAIL_LOCKED,SMR_TAILQ_INSERT_AFTER_LOCKED,SMR_TAILQ_INSERT_BEFORE_LOCKED,SMR_TAILQ_REMOVE_LOCKED
π SEE ALSO π
go read those too, iykyk π«Ά
π HISTORY π
The SMR list macros first appeared in OpenBSD 6.5. Periodt. A whole era π
and that's the whole page bestie β written by the Debian crew, dated January 16, 2022, and served to you with love. mwah π
π¦ where this page comes from
- nixpkgs unstable: cvs-export
- Copyright (c) 2019 Visa Hankala
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms