git: 34740937f7a4 - main - queue: New debug macros for STAILQ

From: Olivier Certner <olce_at_FreeBSD.org>
Date: Mon, 16 Dec 2024 14:45:06 UTC
The branch main has been updated by olce:

URL: https://cgit.FreeBSD.org/src/commit/?id=34740937f7a46c7475bb57e804701ba8830bf6ed

commit 34740937f7a46c7475bb57e804701ba8830bf6ed
Author:     Olivier Certner <olce@FreeBSD.org>
AuthorDate: 2024-07-08 16:15:49 +0000
Commit:     Olivier Certner <olce@FreeBSD.org>
CommitDate: 2024-12-16 14:42:26 +0000

    queue: New debug macros for STAILQ
    
    The new STAILQ_ASSERT_EMPTY() macro allows callers to assert that some
    STAILQ is empty.  It leverages the new QMD_STAILQ_CHECK_EMPTY() internal
    macro.
    
    QMD_STAILQ_CHECK_EMPTY() is a check for empty STAILQ, where heads's
    'stqh_last' field must point to the 'stqh_first' one.  Use it in
    STAILQ_ASSERT_EMPTY().
    
    QMD_STAILQ_CHECK_TAIL() checks that the tail pointed by 'head' does not
    have a next element.  It is similar to the already existing
    QMD_TAILQ_CHECK_TAIL(), but without the superfluous 'field' argument and
    clearer documentation.  Use it in STAILQ_INSERT_TAIL().
    
    Approved by:    markj (mentor)
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D46889
---
 sys/sys/queue.h | 41 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/sys/sys/queue.h b/sys/sys/queue.h
index 0479d780fd85..8a1c6cd7afbb 100644
--- a/sys/sys/queue.h
+++ b/sys/sys/queue.h
@@ -339,6 +339,40 @@ struct {								\
 /*
  * Singly-linked Tail queue functions.
  */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+/*
+ * QMD_STAILQ_CHECK_EMPTY(STAILQ_HEAD *head)
+ *
+ * Validates that the stailq head's pointer to the last element's next pointer
+ * actually points to the head's first element pointer field.
+ */
+#define	QMD_STAILQ_CHECK_EMPTY(head) do {				\
+	if ((head)->stqh_last != &(head)->stqh_first)			\
+		panic("Empty stailq %p->stqh_last is %p, not head's "	\
+		    "first field address", (head), (head)->stqh_last);	\
+} while (0)
+
+#define	STAILQ_ASSERT_EMPTY(head) do {					\
+	if (!STAILQ_EMPTY((head)))					\
+		panic("stailq %p is not empty", (head));		\
+}
+
+/*
+ * QMD_STAILQ_CHECK_TAIL(STAILQ_HEAD *head)
+ *
+ * Validates that the stailq's last element's next pointer is NULL.
+ */
+#define	QMD_STAILQ_CHECK_TAIL(head) do {				\
+	if (*(head)->stqh_last != NULL)					\
+		panic("Stailq %p last element's next pointer is %p, "	\
+		    "not NULL", (head), *(head)->stqh_last);		\
+} while (0)
+#else
+#define	QMD_STAILQ_CHECK_EMPTY(head)
+#define STAILQ_ASSERT_EMPTY(head)
+#define	QMD_STAILQ_CHECK_TAIL(head)
+#endif /* (_KERNEL && INVARIANTS) */
+
 #define	STAILQ_CONCAT(head1, head2) do {				\
 	if (!STAILQ_EMPTY((head2))) {					\
 		*(head1)->stqh_last = (head2)->stqh_first;		\
@@ -347,7 +381,11 @@ struct {								\
 	}								\
 } while (0)
 
-#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+#define	STAILQ_EMPTY(head)	({					\
+	if (STAILQ_FIRST(head) == NULL)					\
+		QMD_STAILQ_CHECK_EMPTY(head);				\
+	STAILQ_FIRST(head) == NULL;					\
+})
 
 #define	STAILQ_FIRST(head)	((head)->stqh_first)
 
@@ -389,6 +427,7 @@ struct {								\
 } while (0)
 
 #define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	QMD_STAILQ_CHECK_TAIL(head);					\
 	STAILQ_NEXT((elm), field) = NULL;				\
 	*(head)->stqh_last = (elm);					\
 	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\