git: 14d4c1845d1b - stable/14 - queue: Add atomic variants for *_EMPTY

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Sat, 08 Mar 2025 08:13:53 UTC
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=14d4c1845d1b03e29aa29eaccba1e9677aa13eb1

commit 14d4c1845d1b03e29aa29eaccba1e9677aa13eb1
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-02-14 15:45:11 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-03-07 22:51:48 +0000

    queue: Add atomic variants for *_EMPTY
    
    In some places, these macros are used without a lock, under the
    assumption that they are naturally atomic.  After commit
    34740937f7a4 ("queue: New debug macros for STAILQ"), this assumption is
    false.
    
    Provide *_EMPTY_ATOMIC for such cases.  This lets us include extra debug
    checks for the non-atomic case, and gives us a way to explicitly
    annotate unlocked checks, which generally deserve extra scrutiny and
    might otherwise raise reports from KCSAN.
    
    Reviewed by:    kib, olce (previous version)
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D48899
    
    (cherry picked from commit d2870b8666f2438af400269c0f6a1a48031bb71e)
---
 share/man/man3/queue.3 | 26 +++++++++++++++++++++++++-
 sys/sys/queue.h        | 12 ++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/share/man/man3/queue.3 b/share/man/man3/queue.3
index ea395d7ffe7f..9fc57a59cf11 100644
--- a/share/man/man3/queue.3
+++ b/share/man/man3/queue.3
@@ -27,7 +27,7 @@
 .\"
 .\"	@(#)queue.3	8.2 (Berkeley) 1/24/94
 .\"
-.Dd April 8, 2024
+.Dd February 10, 2025
 .Dt QUEUE 3
 .Os
 .Sh NAME
@@ -35,6 +35,7 @@
 .Nm SLIST_CLASS_HEAD ,
 .Nm SLIST_CONCAT ,
 .Nm SLIST_EMPTY ,
+.Nm SLIST_EMPTY_ATOMIC ,
 .Nm SLIST_ENTRY ,
 .Nm SLIST_FIRST ,
 .Nm SLIST_FOREACH ,
@@ -55,6 +56,7 @@
 .Nm STAILQ_CLASS_HEAD ,
 .Nm STAILQ_CONCAT ,
 .Nm STAILQ_EMPTY ,
+.Nm STAILQ_EMPTY_ATOMIC ,
 .Nm STAILQ_ENTRY ,
 .Nm STAILQ_FIRST ,
 .Nm STAILQ_FOREACH ,
@@ -77,6 +79,7 @@
 .Nm LIST_CLASS_HEAD ,
 .Nm LIST_CONCAT ,
 .Nm LIST_EMPTY ,
+.Nm LIST_EMPTY_ATOMIC ,
 .Nm LIST_ENTRY ,
 .Nm LIST_FIRST ,
 .Nm LIST_FOREACH ,
@@ -98,6 +101,7 @@
 .Nm TAILQ_CLASS_HEAD ,
 .Nm TAILQ_CONCAT ,
 .Nm TAILQ_EMPTY ,
+.Nm TAILQ_EMPTY_ATOMIC ,
 .Nm TAILQ_ENTRY ,
 .Nm TAILQ_FIRST ,
 .Nm TAILQ_FOREACH ,
@@ -130,6 +134,7 @@ lists and tail queues
 .Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
 .Fn SLIST_CONCAT "SLIST_HEAD *head1" "SLIST_HEAD *head2" "TYPE" "SLIST_ENTRY NAME"
 .Fn SLIST_EMPTY "SLIST_HEAD *head"
+.Fn SLIST_EMPTY_ATOMIC "SLIST_HEAD *head"
 .Fn SLIST_ENTRY "TYPE"
 .Fn SLIST_FIRST "SLIST_HEAD *head"
 .Fn SLIST_FOREACH "TYPE *var" "SLIST_HEAD *head" "SLIST_ENTRY NAME"
@@ -151,6 +156,7 @@ lists and tail queues
 .Fn STAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE"
 .Fn STAILQ_CONCAT "STAILQ_HEAD *head1" "STAILQ_HEAD *head2"
 .Fn STAILQ_EMPTY "STAILQ_HEAD *head"
+.Fn STAILQ_EMPTY_ATOMIC "STAILQ_HEAD *head"
 .Fn STAILQ_ENTRY "TYPE"
 .Fn STAILQ_FIRST "STAILQ_HEAD *head"
 .Fn STAILQ_FOREACH "TYPE *var" "STAILQ_HEAD *head" "STAILQ_ENTRY NAME"
@@ -174,6 +180,7 @@ lists and tail queues
 .Fn LIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
 .Fn LIST_CONCAT "LIST_HEAD *head1" "LIST_HEAD *head2" "TYPE" "LIST_ENTRY NAME"
 .Fn LIST_EMPTY "LIST_HEAD *head"
+.Fn LIST_EMPTY_ATOMIC "LIST_HEAD *head"
 .Fn LIST_ENTRY "TYPE"
 .Fn LIST_FIRST "LIST_HEAD *head"
 .Fn LIST_FOREACH "TYPE *var" "LIST_HEAD *head" "LIST_ENTRY NAME"
@@ -196,6 +203,7 @@ lists and tail queues
 .Fn TAILQ_CLASS_HEAD "HEADNAME" "CLASSTYPE"
 .Fn TAILQ_CONCAT "TAILQ_HEAD *head1" "TAILQ_HEAD *head2" "TAILQ_ENTRY NAME"
 .Fn TAILQ_EMPTY "TAILQ_HEAD *head"
+.Fn TAILQ_EMPTY_ATOMIC "TAILQ_HEAD *head"
 .Fn TAILQ_ENTRY "TYPE"
 .Fn TAILQ_FIRST "TAILQ_HEAD *head"
 .Fn TAILQ_FOREACH "TYPE *var" "TAILQ_HEAD *head" "TAILQ_ENTRY NAME"
@@ -427,6 +435,10 @@ high-usage code paths or to operate on long lists.
 The macro
 .Nm SLIST_EMPTY
 evaluates to true if there are no elements in the list.
+The
+.Nm SLIST_EMPTY_ATOMIC
+variant has the same behavior, but can be safely used in contexts where it is
+possible that a different thread is concurrently updating the list.
 .Pp
 The macro
 .Nm SLIST_ENTRY
@@ -635,6 +647,10 @@ removing all entries from the former.
 The macro
 .Nm STAILQ_EMPTY
 evaluates to true if there are no items on the tail queue.
+The
+.Nm STAILQ_EMPTY_ATOMIC
+variant has the same behavior, but can be safely used in contexts where it is
+possible that a different thread is concurrently updating the queue.
 .Pp
 The macro
 .Nm STAILQ_ENTRY
@@ -868,6 +884,10 @@ high-usage code paths or to operate on long lists.
 The macro
 .Nm LIST_EMPTY
 evaluates to true if there are no elements in the list.
+The
+.Nm LIST_EMPTY_ATOMIC
+variant has the same behavior, but can be safely used in contexts where it is
+possible that a different thread is concurrently updating the list.
 .Pp
 The macro
 .Nm LIST_ENTRY
@@ -1086,6 +1106,10 @@ removing all entries from the former.
 The macro
 .Nm TAILQ_EMPTY
 evaluates to true if there are no items on the tail queue.
+The
+.Nm TAILQ_EMPTY_ATOMIC
+variant has the same behavior, but can be safely used in contexts where it is
+possible that a different thread is concurrently updating the queue.
 .Pp
 The macro
 .Nm TAILQ_ENTRY
diff --git a/sys/sys/queue.h b/sys/sys/queue.h
index 38c319704cc4..dd3956d7c111 100644
--- a/sys/sys/queue.h
+++ b/sys/sys/queue.h
@@ -228,6 +228,9 @@ struct {								\
 
 #define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
 
+#define	SLIST_EMPTY_ATOMIC(head)					\
+	(atomic_load_ptr(&(head)->slh_first) == NULL)
+
 #define	SLIST_FIRST(head)	((head)->slh_first)
 
 #define	SLIST_FOREACH(var, head, field)					\
@@ -389,6 +392,9 @@ struct {								\
 	STAILQ_FIRST(head) == NULL;					\
 })
 
+#define	STAILQ_EMPTY_ATOMIC(head)					\
+	(atomic_load_ptr(&(head)->stqh_first) == NULL)
+
 #define	STAILQ_FIRST(head)	((head)->stqh_first)
 
 #define	STAILQ_FOREACH(var, head, field)				\
@@ -577,6 +583,9 @@ struct {								\
 
 #define	LIST_EMPTY(head)	((head)->lh_first == NULL)
 
+#define	LIST_EMPTY_ATOMIC(head)						\
+	(atomic_load_ptr(&(head)->lh_first) == NULL)
+
 #define	LIST_FIRST(head)	((head)->lh_first)
 
 #define	LIST_FOREACH(var, head, field)					\
@@ -781,6 +790,9 @@ struct {								\
 
 #define	TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
 
+#define	TAILQ_EMPTY_ATOMIC(head)					\
+	(atomic_load_ptr(&(head)->tqh_first) == NULL)
+
 #define	TAILQ_FIRST(head)	((head)->tqh_first)
 
 #define	TAILQ_FOREACH(var, head, field)					\