[PATCH] log: make global data relocatable

Thomas Weißschuh thomas.weissschuh at linutronix.de
Mon Feb 12 10:32:56 CET 2024


When `gd` is relocated during `spl_relocate_stack_gd()` the
doubly-linked circular list in the `log_head` member is broken.

The last element of the list should point back to the initial
`list_head`, but as the initial `list_head` is moved the pointer becomes
stale. As a result the loop in `log_dispatch` would never finish.

Migrate the list to a singly-linked non-circular one which is
easily relocatable.

This should also remove the special handling introduced in
commit e7595aa350ae ("x86: Allow logging to be used in SPL reliably").

Signed-off-by: Thomas Weißschuh <thomas.weissschuh at linutronix.de>
---
 common/log.c                      | 15 +++++++++------
 include/asm-generic/global_data.h |  2 +-
 include/log.h                     |  2 +-
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/common/log.c b/common/log.c
index b2de57fcb3b8..098d40c86d81 100644
--- a/common/log.c
+++ b/common/log.c
@@ -109,7 +109,7 @@ struct log_device *log_device_find_by_name(const char *drv_name)
 {
 	struct log_device *ldev;
 
-	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
+	for (ldev = gd->log_head; ldev; ldev = ldev->sibling_node) {
 		if (!strcmp(drv_name, ldev->drv->name))
 			return ldev;
 	}
@@ -218,7 +218,7 @@ static int log_dispatch(struct log_rec *rec, const char *fmt, va_list args)
 
 	/* Emit message */
 	gd->processing_msg = true;
-	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
+	for (ldev = gd->log_head; ldev; ldev = ldev->sibling_node) {
 		if ((ldev->flags & LOGDF_ENABLE) &&
 		    log_passes_filters(ldev, rec)) {
 			if (!rec->msg) {
@@ -400,7 +400,7 @@ static struct log_device *log_find_device_by_drv(struct log_driver *drv)
 {
 	struct log_device *ldev;
 
-	list_for_each_entry(ldev, &gd->log_head, sibling_node) {
+	for (ldev = gd->log_head; ldev; ldev = ldev->sibling_node) {
 		if (ldev->drv == drv)
 			return ldev;
 	}
@@ -433,13 +433,16 @@ int log_init(void)
 	struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
 	const int count = ll_entry_count(struct log_driver, log_driver);
 	struct log_driver *end = drv + count;
+	struct log_device **log_head;
+
+	gd->log_head = NULL;
+	log_head = (struct log_device **)&gd->log_head;
 
 	/*
 	 * We cannot add runtime data to the driver since it is likely stored
 	 * in rodata. Instead, set up a 'device' corresponding to each driver.
 	 * We only support having a single device for each driver.
 	 */
-	INIT_LIST_HEAD((struct list_head *)&gd->log_head);
 	while (drv < end) {
 		struct log_device *ldev;
 
@@ -451,8 +454,8 @@ int log_init(void)
 		INIT_LIST_HEAD(&ldev->filter_head);
 		ldev->drv = drv;
 		ldev->flags = drv->flags;
-		list_add_tail(&ldev->sibling_node,
-			      (struct list_head *)&gd->log_head);
+		*log_head = ldev;
+		log_head = &ldev->sibling_node;
 		drv++;
 	}
 	gd->flags |= GD_FLG_LOG_READY;
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index fcc3c6e14ca3..a9a407b801c2 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -404,7 +404,7 @@ struct global_data {
 	/**
 	 * @log_head: list of logging devices
 	 */
-	struct list_head log_head;
+	struct log_device *log_head;
 	/**
 	 * @log_fmt: bit mask for logging format
 	 *
diff --git a/include/log.h b/include/log.h
index 6e84f080ef3d..961233684d2a 100644
--- a/include/log.h
+++ b/include/log.h
@@ -455,7 +455,7 @@ struct log_device {
 	unsigned short flags;
 	struct log_driver *drv;
 	struct list_head filter_head;
-	struct list_head sibling_node;
+	struct log_device *sibling_node;
 };
 
 enum {

---
base-commit: e8f2404e093daf6cc3ac2b3233e3c6770d13e371
change-id: 20240208-spl-logging-14a1257c3147

Best regards,
-- 
Thomas Weißschuh <thomas.weissschuh at linutronix.de>



More information about the U-Boot mailing list