mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tzung-Bi Shih <tzungbi@kernel.org>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	Danilo Krummrich <dakr@kernel.org>,
	Johan Hovold <johan@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>,
	Bartosz Golaszewski <brgl@kernel.org>,
	Linus Walleij <linusw@kernel.org>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	driver-core@lists.linux.dev, linux-kernel@vger.kernel.org,
	linux-gpio@vger.kernel.org, tzungbi@kernel.org
Subject: [PATCH v14 5/5] reset: Leverage revocable for accessing struct reset_controller_dev
Date: Sun, 11 Oct 2026 20:12:45 +0800	[thread overview]
Message-ID: <20261011121246.162759-6-tzungbi@kernel.org> (raw)
In-Reply-To: <20261011121246.162759-1-tzungbi@kernel.org>

Commit 78ebbff6d1a0 ("reset: handle removing supplier before consumers")
protected the `rcdev` pointer in `struct reset_control` with SRCU so
that the reset core can handle suppliers unbinding while consumers still
hold reset control handles.

Because `struct reset_control` handles can outlive `rcdev`, each of the
N handles had to embed its own `srcu_struct` and call synchronize_srcu()
on release, and reset_controller_unregister() had to call
synchronize_srcu() N times in a loop.

Leverage dynamic revocable to allocate a single reference-counted
`struct revocable` for `rcdev` shared across all N `struct reset_control`
handles (1-to-N).  This reduces N `srcu_struct` instances per controller
to 1, drops synchronize_srcu() from reset_control_put_internal(), and
replaces the N synchronize_srcu() calls on unregistration with a single
revocable_revoke().

In addition, calling revocable_revoke() outside `rcdev->lock` avoids an
ABBA lock inversion between `rcdev->lock` and SRCU, as
reset_control_acquire() and reset_control_put_internal() acquire
`rcdev->lock` inside the SRCU read-side critical section.

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v14:
- New to the series.
---
 drivers/reset/core.c             | 93 ++++++++------------------------
 include/linux/reset-controller.h |  3 ++
 2 files changed, 26 insertions(+), 70 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 38e189d04d09..788a235c3965 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -24,8 +24,8 @@
 #include <linux/property.h>
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
+#include <linux/revocable.h>
 #include <linux/slab.h>
-#include <linux/srcu.h>
 
 static DEFINE_MUTEX(reset_list_mutex);
 static LIST_HEAD(reset_controller_list);
@@ -37,9 +37,7 @@ 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
+ * @rev: revocable handle for accessing the reset controller device
  * @list: list entry for the rcdev's reset controller list
  * @id: ID of the reset controller in the reset
  *      controller device
@@ -54,8 +52,7 @@ static DEFINE_IDA(reset_gpio_ida);
  * @lock: serializes the internals of reset_control_acquire()
  */
 struct reset_control {
-	struct reset_controller_dev __rcu *rcdev;
-	struct srcu_struct srcu;
+	struct revocable *rev;
 	struct list_head list;
 	unsigned int id;
 	struct kref refcnt;
@@ -141,6 +138,10 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
 			return -EINVAL;
 	}
 
+	rcdev->rev = revocable_alloc(rcdev);
+	if (!rcdev->rev)
+		return -ENOMEM;
+
 	if (rcdev->of_xlate)
 		rcdev->fwnode_reset_n_cells = rcdev->of_reset_n_cells;
 
@@ -181,18 +182,14 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev)
 	scoped_guard(mutex, &reset_list_mutex)
 		list_del(&rcdev->list);
 
+	revocable_revoke(rcdev->rev);
+
 	scoped_guard(mutex, &rcdev->lock) {
-		/*
-		 * Numb but don't free the remaining reset control handles that are
-		 * still held by consumers.
-		 */
-		list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) {
-			rcu_assign_pointer(rstc->rcdev, NULL);
-			synchronize_srcu(&rstc->srcu);
+		list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list)
 			reset_controller_remove(rcdev, rstc);
-		}
 	}
 
+	revocable_put(rcdev->rev);
 	mutex_destroy(&rcdev->lock);
 }
 EXPORT_SYMBOL_GPL(reset_controller_unregister);
@@ -382,11 +379,7 @@ 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);
-
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-	if (!rcdev)
-		return -ENODEV;
+	revocable_try_access_or_return(rstc->rev, rcdev);
 
 	if (!rcdev->ops->reset)
 		return -ENOTSUPP;
@@ -503,11 +496,7 @@ 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);
-
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-	if (!rcdev)
-		return -ENODEV;
+	revocable_try_access_or_return(rstc->rev, rcdev);
 
 	if (rstc->shared) {
 		if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
@@ -599,11 +588,7 @@ 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);
-
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-	if (!rcdev)
-		return -ENODEV;
+	revocable_try_access_or_return(rstc->rev, rcdev);
 
 	if (rstc->shared) {
 		if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
@@ -679,11 +664,7 @@ 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);
-
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-	if (!rcdev)
-		return -ENODEV;
+	revocable_try_access_or_return(rstc->rev, rcdev);
 
 	if (rcdev->ops->status)
 		return rcdev->ops->status(rcdev, rstc->id);
@@ -731,11 +712,7 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (rstc->acquired)
 		return 0;
 
-	guard(srcu)(&rstc->srcu);
-
-	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-	if (!rcdev)
-		return -ENODEV;
+	revocable_try_access_or_return(rstc->rev, rcdev);
 
 	scoped_guard(mutex, &rcdev->lock) {
 		list_for_each_entry(rc, &rcdev->reset_control_head, list) {
@@ -831,7 +808,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,19 +838,13 @@ __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);
 	}
 
-	rcu_assign_pointer(rstc->rcdev, rcdev);
+	revocable_get(rcdev->rev);
+	rstc->rev = rcdev->rev;
 	list_add(&rstc->list, &rcdev->reset_control_head);
 	rstc->id = index;
 	kref_init(&rstc->refcnt);
@@ -890,40 +860,23 @@ static void __reset_control_release(struct kref *kref)
 {
 	struct reset_control *rstc = container_of(kref, struct reset_control,
 						  refcnt);
-	struct reset_controller_dev *rcdev;
-
-	lockdep_assert_held(&rstc->srcu);
-
-	rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true);
-	if (rcdev) {
-		lockdep_assert_held(&rcdev->lock);
-		reset_controller_remove(rcdev, rstc);
-	}
 
 	mutex_destroy(&rstc->lock);
+	revocable_put(rstc->rev);
 }
 
 static void reset_control_put_internal(struct reset_control *rstc)
 {
 	struct reset_controller_dev *rcdev;
-	int ret = 0;
 
 	if (IS_ERR_OR_NULL(rstc))
 		return;
 
-	scoped_guard(srcu, &rstc->srcu) {
-		rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
-		if (!rcdev)
-			/* Already released. */
-			return;
-
-		guard(mutex)(&rcdev->lock);
-		ret = kref_put(&rstc->refcnt, __reset_control_release);
-	}
+	revocable_try_access_or_return_void(rstc->rev, rcdev);
 
-	if (ret) {
-		synchronize_srcu(&rstc->srcu);
-		cleanup_srcu_struct(&rstc->srcu);
+	guard(mutex)(&rcdev->lock);
+	if (kref_put(&rstc->refcnt, __reset_control_release)) {
+		reset_controller_remove(rcdev, rstc);
 		kfree(rstc);
 	}
 }
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index 52a5a4e81f18..9abf5c59fd9a 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -28,6 +28,7 @@ struct reset_control_ops {
 struct module;
 struct device_node;
 struct of_phandle_args;
+struct revocable;
 
 /**
  * struct reset_controller_dev - reset controller entity that might
@@ -48,6 +49,7 @@ struct of_phandle_args;
  *                :c:func:`fwnode_reset_simple_xlate`
  * @nr_resets: number of reset controls in this reset controller device
  * @lock: protects the reset control list from concurrent access
+ * @rev: revocable provider handle for itself
  */
 struct reset_controller_dev {
 	const struct reset_control_ops *ops;
@@ -65,6 +67,7 @@ struct reset_controller_dev {
 			    const struct fwnode_reference_args *reset_spec);
 	unsigned int nr_resets;
 	struct mutex lock;
+	struct revocable *rev;
 };
 
 #if IS_ENABLED(CONFIG_RESET_CONTROLLER)
-- 
2.53.0


      parent reply	other threads:[~2026-10-11 12:14 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 12:12 [PATCH v14 0/5] drivers/base: Introduce revocable Tzung-Bi Shih
2026-10-11 12:12 ` [PATCH v14 1/5] revocable: Revocable resource management Tzung-Bi Shih
2026-10-11 12:12 ` [PATCH v14 2/5] revocable: Add KUnit test cases Tzung-Bi Shih
2026-10-11 12:12 ` [PATCH v14 3/5] gpio: Leverage revocable for accessing struct gpio_chip Tzung-Bi Shih
2026-10-11 12:12 ` [PATCH v14 4/5] reset: Add KUnit test cases Tzung-Bi Shih
2026-10-11 12:12 ` Tzung-Bi Shih [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261011121246.162759-6-tzungbi@kernel.org \
    --to=tzungbi@kernel.org \
    --cc=brgl@kernel.org \
    --cc=corbet@lwn.net \
    --cc=dakr@kernel.org \
    --cc=driver-core@lists.linux.dev \
    --cc=gregkh@linuxfoundation.org \
    --cc=johan@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=rafael@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®