All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] reset: use a shared SRCU domain for reset controls
@ 2026-04-17 15:48 Steven Price
  2026-04-20  8:18 ` Steven Price
  2026-04-23 10:27 ` Philipp Zabel
  0 siblings, 2 replies; 8+ messages in thread
From: Steven Price @ 2026-04-17 15:48 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski, Steven Price

Commit 78ebbff6d1a0 ("reset: handle removing supplier before consumers")
added a dynamically initialized srcu_struct to every reset_control and
cleaned it up again when the handle was dropped.

That breaks early boot users which acquire and release reset handles
before workqueues are online. On rk3288 this shows up during
rockchip_smp_prepare_cpus(), where pmu_set_power_domain() gets a reset
control for a CPU core and then drops it again before SMP bring-up has
finished. cleanup_srcu_struct() then tries to flush delayed SRCU work
and hits the WARN_ON(!wq_online) path, which can leave the machine
hanging before the serial console appears.

Keep the supplier-removal protection, but move it to a single shared
static SRCU domain for the reset core. That preserves the rcdev lifetime
protection needed for supplier unregister without requiring per-handle
init_srcu_struct()/cleanup_srcu_struct() on normal get/put paths.

Fixes: 78ebbff6d1a0 ("reset: handle removing supplier before consumers")
---
 drivers/reset/core.c | 48 ++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 28 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 38e189d04d09..ab4989084824 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -29,6 +29,11 @@
 
 static DEFINE_MUTEX(reset_list_mutex);
 static LIST_HEAD(reset_controller_list);
+/*
+ * Use one shared SRCU domain so reset handles don't need per-instance
+ * init/cleanup on early boot paths.
+ */
+DEFINE_STATIC_SRCU(reset_control_srcu);
 
 /* Protects reset_gpio_lookup_list */
 static DEFINE_MUTEX(reset_gpio_lookup_mutex);
@@ -39,7 +44,6 @@ static DEFINE_IDA(reset_gpio_ida);
  * struct reset_control - a reset control
  * @rcdev: a pointer to the reset controller device
  *         this reset control belongs to
- * @srcu: protects the rcdev pointer from removal during consumer access
  * @list: list entry for the rcdev's reset controller list
  * @id: ID of the reset controller in the reset
  *      controller device
@@ -55,7 +59,6 @@ static DEFINE_IDA(reset_gpio_ida);
  */
 struct reset_control {
 	struct reset_controller_dev __rcu *rcdev;
-	struct srcu_struct srcu;
 	struct list_head list;
 	unsigned int id;
 	struct kref refcnt;
@@ -188,7 +191,7 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev)
 		 */
 		list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) {
 			rcu_assign_pointer(rstc->rcdev, NULL);
-			synchronize_srcu(&rstc->srcu);
+			synchronize_srcu(&reset_control_srcu);
 			reset_controller_remove(rcdev, rstc);
 		}
 	}
@@ -382,9 +385,9 @@ int reset_control_reset(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_reset(rstc_to_array(rstc));
 
-	guard(srcu)(&rstc->srcu);
+	guard(srcu)(&reset_control_srcu);
 
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 	if (!rcdev)
 		return -ENODEV;
 
@@ -503,9 +506,9 @@ int reset_control_assert(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_assert(rstc_to_array(rstc));
 
-	guard(srcu)(&rstc->srcu);
+	guard(srcu)(&reset_control_srcu);
 
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 	if (!rcdev)
 		return -ENODEV;
 
@@ -599,9 +602,9 @@ int reset_control_deassert(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_deassert(rstc_to_array(rstc));
 
-	guard(srcu)(&rstc->srcu);
+	guard(srcu)(&reset_control_srcu);
 
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 	if (!rcdev)
 		return -ENODEV;
 
@@ -679,9 +682,9 @@ int reset_control_status(struct reset_control *rstc)
 	if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc))
 		return -EINVAL;
 
-	guard(srcu)(&rstc->srcu);
+	guard(srcu)(&reset_control_srcu);
 
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 	if (!rcdev)
 		return -ENODEV;
 
@@ -731,9 +734,9 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (rstc->acquired)
 		return 0;
 
-	guard(srcu)(&rstc->srcu);
+	guard(srcu)(&reset_control_srcu);
 
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 	if (!rcdev)
 		return -ENODEV;
 
@@ -831,7 +834,6 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
 	bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
 	struct reset_control *rstc;
-	int ret;
 
 	lockdep_assert_held(&rcdev->lock);
 
@@ -862,14 +864,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	if (!rstc)
 		return ERR_PTR(-ENOMEM);
 
-	ret = init_srcu_struct(&rstc->srcu);
-	if (ret) {
-		kfree(rstc);
-		return ERR_PTR(ret);
-	}
-
 	if (!try_module_get(rcdev->owner)) {
-		cleanup_srcu_struct(&rstc->srcu);
 		kfree(rstc);
 		return ERR_PTR(-ENODEV);
 	}
@@ -892,7 +887,7 @@ static void __reset_control_release(struct kref *kref)
 						  refcnt);
 	struct reset_controller_dev *rcdev;
 
-	lockdep_assert_held(&rstc->srcu);
+	lockdep_assert_held(&reset_control_srcu);
 
 	rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true);
 	if (rcdev) {
@@ -911,8 +906,8 @@ static void reset_control_put_internal(struct reset_control *rstc)
 	if (IS_ERR_OR_NULL(rstc))
 		return;
 
-	scoped_guard(srcu, &rstc->srcu) {
-		rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	scoped_guard(srcu, &reset_control_srcu) {
+		rcdev = srcu_dereference(rstc->rcdev, &reset_control_srcu);
 		if (!rcdev)
 			/* Already released. */
 			return;
@@ -921,11 +916,8 @@ static void reset_control_put_internal(struct reset_control *rstc)
 		ret = kref_put(&rstc->refcnt, __reset_control_release);
 	}
 
-	if (ret) {
-		synchronize_srcu(&rstc->srcu);
-		cleanup_srcu_struct(&rstc->srcu);
+	if (ret)
 		kfree(rstc);
-	}
 }
 
 static void reset_gpio_aux_device_release(struct device *dev)
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-21 21:15 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 15:48 [PATCH] reset: use a shared SRCU domain for reset controls Steven Price
2026-04-20  8:18 ` Steven Price
2026-04-23 10:27 ` Philipp Zabel
2026-04-23 12:45   ` Steven Price
2026-04-23 14:15     ` Philipp Zabel
2026-04-24  9:04       ` Steven Price
2026-05-14  9:07         ` Steven Price
2026-05-21 21:15           ` Heiko Stuebner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.