From nobody Tue Jun 16 08:57:47 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E652D33A9FE for ; Fri, 17 Apr 2026 15:48:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776440897; cv=none; b=BPsOPwB+Il+sEwtipl8P6bShR8eIIYTJ7aRCIqwqqWwpa6odd3bzmBoQhZCa6UAp9dFTWqulko0aAQmShf0oAIaMm/uQ8DcifSP8YtFQokbWLpSDqtvyIwuApSkbrpuf3E222GJ5NYs7JJQWcrcMs7fKYWgebDbpsT+RmIJ/ZN0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776440897; c=relaxed/simple; bh=MjZaOaqTGVn3kO47/oWzT8MqTouV4owVL6pimKfjQpQ=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=bcrYoGnV04zC6JRreaMoNSZv8zq0LvyCxp4wIJsVtsUdHWvpAlfP7erkQSBOesLqeKuudTlwMXJNeYqnbz1bfyHd4bK/iCPIp4FN5RRAFIUlDXxR7poCYYABc6h9aFhM9yFe/WcyHbgQ2rM2n654br7TGQufvskAq4PH78lcBag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=RqXa6hjb; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="RqXa6hjb" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C5CE61CC4; Fri, 17 Apr 2026 08:48:09 -0700 (PDT) Received: from e112269-lin.cambridge.arm.com (e112269-lin.cambridge.arm.com [10.1.194.64]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A9FAD3F7D8; Fri, 17 Apr 2026 08:48:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776440895; bh=MjZaOaqTGVn3kO47/oWzT8MqTouV4owVL6pimKfjQpQ=; h=From:To:Cc:Subject:Date:From; b=RqXa6hjb2T81cGTg/HCtA6D8AaIoqWZEad+Uza4QIN+4ciocQ6J3agyu7akNxi6bV iL1sAZbMIMiD2L/3jQTynmG6N8M9xECs9dPF5F/JnWgiFTbBeFsCNtYtF+fyHoJPTC 1tW14e5Yzoiq8jl+8Be1/uHu3Qj9oFn061M0i8YE= From: Steven Price To: Philipp Zabel Cc: linux-kernel@vger.kernel.org, Bartosz Golaszewski , Steven Price Subject: [PATCH] reset: use a shared SRCU domain for reset controls Date: Fri, 17 Apr 2026 16:48:09 +0100 Message-Id: <20260417154809.1984386-1-steven.price@arm.com> X-Mailer: git-send-email 2.39.5 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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") Tested-by: Steven Price --- 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 @@ =20 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); =20 /* 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_controlle= r_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)); =20 - guard(srcu)(&rstc->srcu); + guard(srcu)(&reset_control_srcu); =20 - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) return -ENODEV; =20 @@ -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)); =20 - guard(srcu)(&rstc->srcu); + guard(srcu)(&reset_control_srcu); =20 - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) return -ENODEV; =20 @@ -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)); =20 - guard(srcu)(&rstc->srcu); + guard(srcu)(&reset_control_srcu); =20 - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) return -ENODEV; =20 @@ -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; =20 - guard(srcu)(&rstc->srcu); + guard(srcu)(&reset_control_srcu); =20 - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) return -ENODEV; =20 @@ -731,9 +734,9 @@ int reset_control_acquire(struct reset_control *rstc) if (rstc->acquired) return 0; =20 - guard(srcu)(&rstc->srcu); + guard(srcu)(&reset_control_srcu); =20 - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) return -ENODEV; =20 @@ -831,7 +834,6 @@ __reset_control_get_internal(struct reset_controller_de= v *rcdev, bool shared =3D flags & RESET_CONTROL_FLAGS_BIT_SHARED; bool acquired =3D flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; struct reset_control *rstc; - int ret; =20 lockdep_assert_held(&rcdev->lock); =20 @@ -862,14 +864,7 @@ __reset_control_get_internal(struct reset_controller_d= ev *rcdev, if (!rstc) return ERR_PTR(-ENOMEM); =20 - ret =3D 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; =20 - lockdep_assert_held(&rstc->srcu); + lockdep_assert_held(&reset_control_srcu); =20 rcdev =3D rcu_replace_pointer(rstc->rcdev, NULL, true); if (rcdev) { @@ -911,8 +906,8 @@ static void reset_control_put_internal(struct reset_con= trol *rstc) if (IS_ERR_OR_NULL(rstc)) return; =20 - scoped_guard(srcu, &rstc->srcu) { - rcdev =3D srcu_dereference(rstc->rcdev, &rstc->srcu); + scoped_guard(srcu, &reset_control_srcu) { + rcdev =3D srcu_dereference(rstc->rcdev, &reset_control_srcu); if (!rcdev) /* Already released. */ return; @@ -921,11 +916,8 @@ static void reset_control_put_internal(struct reset_co= ntrol *rstc) ret =3D kref_put(&rstc->refcnt, __reset_control_release); } =20 - if (ret) { - synchronize_srcu(&rstc->srcu); - cleanup_srcu_struct(&rstc->srcu); + if (ret) kfree(rstc); - } } =20 static void reset_gpio_aux_device_release(struct device *dev) --=20 2.39.5