From 04f992e5cfb99d2fd6f26abbeaa6d6db7260e204 Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Thu, 1 Oct 2026 17:06:46 -0400 Subject: [PATCH] lib: sbi: Order the publication of IPI device nodes sbi_ipi_add_device() links the new node into ipi_dev_node_list with plain stores while warm harts already walk the list with plain loads in sbi_ipi_raw_clear(true). A reader can see the node pointer before the node's fields and the device's per-hart data are visible. Publish the node after smp_wmb() and pair it with smp_rmb() after each node load in the all-devices walk. Signed-off-by: Nickolai Zeldovich Co-Authored-By: Claude Fable 5.1 --- lib/sbi/sbi_ipi.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c index b04a5877bd1..e933ccd780b 100644 --- a/lib/sbi/sbi_ipi.c +++ b/lib/sbi/sbi_ipi.c @@ -314,6 +314,14 @@ void sbi_ipi_raw_clear(bool all_devices) if (all_devices) { sbi_list_for_each_entry(entry, &ipi_dev_node_list, head) { + /* + * Pair with the smp_wmb() in sbi_ipi_add_device(): + * a warm hart may reach here while the cold hart is + * still adding devices. Order the load of the node + * pointer before the loads of the node's contents + * and of the per-hart data that ipi_clear() reads. + */ + smp_rmb(); if (entry->dev->ipi_clear) entry->dev->ipi_clear(); } @@ -349,9 +357,22 @@ void sbi_ipi_add_device(const struct sbi_ipi_device *dev) entry = sbi_zalloc(sizeof(*entry)); if (!entry) return; - SBI_INIT_LIST_HEAD(&entry->head); entry->dev = dev; - sbi_list_add_tail(&entry->head, &ipi_dev_node_list); + entry->head.prev = ipi_dev_node_list.prev; + entry->head.next = &ipi_dev_node_list; + + /* + * Warm harts walk ipi_dev_node_list in sbi_ipi_raw_clear(true) + * from wait_for_coldboot() onwards, concurrently with the cold + * hart's cold-boot initialization. Make the node's contents + * (and the device's per-hart data set up by its cold_init) + * visible before the node becomes reachable from the list head. + * The reader pairs with this through the smp_rmb() in + * sbi_ipi_raw_clear(). + */ + smp_wmb(); + ipi_dev_node_list.prev->next = &entry->head; + ipi_dev_node_list.prev = &entry->head; if (!ipi_dev || ipi_dev->rating < dev->rating) ipi_dev = dev;