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
prev 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®