[PATCH v2 1/2] lib: sbi_domain: Make HART to domain updates atomic

Huamao Wu huamao.wu at linux.spacemit.com
Wed Oct 28 23:38:28 PDT 2026


A HART's domain assignment is represented in two places: the domain
pointer in the per-HART scratch, and the bit in that domain's
assigned_harts bitmap. They are written by different code depending on
the caller, so a HART can be observed in a state where the two
disagree.

Today the two writes happen outside any common lock:

  - switch_to_next_domain_context() clears the bit in the old domain,
    calls sbi_update_hartindex_to_domain() to rewrite the pointer, and
    sets the bit in the new domain, with a separate lock/unlock around
    each bitmap update.
  - sbi_domain_register() additionally clears the old domain's bit by
    hand and sets the new domain's bit after the call.

Move all of it into sbi_update_hartindex_to_domain() and serialize the
update with a new domain_hart_assign_lock. The function reads the
current domain, clears its bit, writes the pointer, and sets the bit in
the new domain, all under that lock. Readers that take the same lock
therefore see the pointer and the bitmap agree.

sbi_hartindex_to_domain() takes the lock for the load. Both functions
and the new lock are annotated for thread safety analysis, so a caller
that already holds the lock fails to compile rather than deadlocking.

The bitmap itself stays protected by the per-domain assigned_harts_lock,
which is nested inside the global lock and never taken in the opposite
order.

Callers no longer touch the bitmap:
  - switch_to_next_domain_context() becomes a single call.
  - sbi_domain_register() drops the manual clear of the old domain's bit
    and the now redundant set of the new domain's bit.

The global lock is exported so that the HSM HART start path can hold it
across its ownership check in a subsequent change.

Suggested-by: Anup Patel <anup.patel at oss.qualcomm.com>
Signed-off-by: Huamao Wu <huamao.wu at linux.spacemit.com>
---
 include/sbi/sbi_domain.h     | 16 ++++++++++++++--
 lib/sbi/sbi_domain.c         | 37 +++++++++++++++++++++++++++---------
 lib/sbi/sbi_domain_context.c |  8 --------
 3 files changed, 42 insertions(+), 19 deletions(-)

diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h
index 56a21397..dcb613d0 100644
--- a/include/sbi/sbi_domain.h
+++ b/include/sbi/sbi_domain.h
@@ -225,11 +225,23 @@ struct sbi_domain {
 /** The root domain instance */
 extern struct sbi_domain root;
 
+/**
+ * Global lock serializing updates to HART to domain assignments
+ *
+ * A HART to domain assignment is represented by the per-HART scratch
+ * pointer and the per-domain assigned_harts bitmap. Both are updated
+ * under this lock so that readers never observe the two disagreeing.
+ */
+extern spinlock_t domain_hart_assign_lock;
+
 /** Get pointer to sbi_domain from HART index */
-struct sbi_domain *sbi_hartindex_to_domain(u32 hartindex);
+struct sbi_domain *sbi_hartindex_to_domain(u32 hartindex)
+	MUST_NOT_HOLD(&domain_hart_assign_lock);
 
 /** Update HART local pointer to point to specified domain */
-void sbi_update_hartindex_to_domain(u32 hartindex, struct sbi_domain *dom);
+void sbi_update_hartindex_to_domain(u32 hartindex, struct sbi_domain *dom)
+	MUST_NOT_HOLD(&domain_hart_assign_lock)
+	MUST_NOT_HOLD(&dom->assigned_harts_lock);
 
 /** Get pointer to sbi_domain for current HART */
 #define sbi_domain_thishart_ptr() \
diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c
index 42fe5632..88f4af55 100644
--- a/lib/sbi/sbi_domain.c
+++ b/lib/sbi/sbi_domain.c
@@ -34,6 +34,7 @@ struct sbi_domain root = {
 };
 
 static unsigned long domain_hart_ptr_offset;
+DEFINE_SPIN_LOCK(domain_hart_assign_lock);
 
 struct sbi_domain_notify_entry {
 	struct sbi_dlist		node;
@@ -104,25 +105,46 @@ static int sbi_domain_notify_all(struct sbi_domain *dom, enum sbi_domain_notify_
 }
 
 struct sbi_domain *sbi_hartindex_to_domain(u32 hartindex)
+	MUST_NOT_HOLD(&domain_hart_assign_lock)
 {
 	struct sbi_scratch *scratch;
+	struct sbi_domain *dom;
 
 	scratch = sbi_hartindex_to_scratch(hartindex);
 	if (!scratch || !domain_hart_ptr_offset)
 		return NULL;
 
-	return sbi_scratch_read_type(scratch, void *, domain_hart_ptr_offset);
+	spin_lock(&domain_hart_assign_lock);
+	dom = sbi_scratch_read_type(scratch, void *, domain_hart_ptr_offset);
+	spin_unlock(&domain_hart_assign_lock);
+	return dom;
 }
 
 void sbi_update_hartindex_to_domain(u32 hartindex, struct sbi_domain *dom)
+	MUST_NOT_HOLD(&domain_hart_assign_lock)
+	MUST_NOT_HOLD(&dom->assigned_harts_lock)
 {
+	struct sbi_domain *current_dom;
 	struct sbi_scratch *scratch;
 
 	scratch = sbi_hartindex_to_scratch(hartindex);
-	if (!scratch)
+	if (!scratch || !domain_hart_ptr_offset)
 		return;
 
+	spin_lock(&domain_hart_assign_lock);
+	current_dom = sbi_scratch_read_type(scratch, void *, domain_hart_ptr_offset);
+	if (current_dom) {
+		spin_lock(&current_dom->assigned_harts_lock);
+		sbi_hartmask_clear_hartindex(hartindex, &current_dom->assigned_harts);
+		spin_unlock(&current_dom->assigned_harts_lock);
+	}
 	sbi_scratch_write_type(scratch, void *, domain_hart_ptr_offset, dom);
+	if (dom) {
+		spin_lock(&dom->assigned_harts_lock);
+		sbi_hartmask_set_hartindex(hartindex, &dom->assigned_harts);
+		spin_unlock(&dom->assigned_harts_lock);
+	}
+	spin_unlock(&domain_hart_assign_lock);
 }
 
 bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartindex)
@@ -748,7 +770,9 @@ int sbi_domain_register(struct sbi_domain *dom)
 	SPIN_LOCK_INIT(dom->assigned_harts_lock);
 
 	/* Clear assigned HARTs of domain */
+	spin_lock(&dom->assigned_harts_lock);
 	sbi_hartmask_clear_all(&dom->assigned_harts);
+	spin_unlock(&dom->assigned_harts_lock);
 
 	/*
 	 * Assign HART to a domain with the least initialization order
@@ -756,15 +780,10 @@ int sbi_domain_register(struct sbi_domain *dom)
 	 */
 	sbi_hartmask_for_each_hartindex(i, dom->possible_harts) {
 		tdom = sbi_hartindex_to_domain(i);
-		if (tdom) {
-			if (tdom->init_order > dom->init_order)
-				sbi_hartmask_clear_hartindex(i, &tdom->assigned_harts);
-			else
-				continue;
-		}
+		if (tdom && tdom->init_order <= dom->init_order)
+			continue;
 
 		sbi_update_hartindex_to_domain(i, dom);
-		sbi_hartmask_set_hartindex(i, &dom->assigned_harts);
 
 		/*
 		 * If cold boot HART is assigned to this domain then
diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
index 987ac7fa..ee43cd43 100644
--- a/lib/sbi/sbi_domain_context.c
+++ b/lib/sbi/sbi_domain_context.c
@@ -121,16 +121,8 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
 	current_dom = ctx->dom;
 	target_dom = dom_ctx->dom;
 	/* Assign current hart to target domain */
-	spin_lock(&current_dom->assigned_harts_lock);
-	sbi_hartmask_clear_hartindex(hartindex, &current_dom->assigned_harts);
-	spin_unlock(&current_dom->assigned_harts_lock);
-
 	sbi_update_hartindex_to_domain(hartindex, target_dom);
 
-	spin_lock(&target_dom->assigned_harts_lock);
-	sbi_hartmask_set_hartindex(hartindex, &target_dom->assigned_harts);
-	spin_unlock(&target_dom->assigned_harts_lock);
-
 	/* Save current CSR context and restore target domain's CSR context */
 	ctx->sstatus	= csr_swap(CSR_SSTATUS, dom_ctx->sstatus);
 	ctx->sie	= csr_swap(CSR_SIE, dom_ctx->sie);
-- 
2.43.0




More information about the opensbi mailing list