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.weisssc...@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.weisssc...@linutronix.de>