* [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 8:02 [PATCH v7 0/3] drivers/base: Introduce revocable Tzung-Bi Shih
@ 2026-01-16 8:02 ` Tzung-Bi Shih
2026-01-16 15:19 ` Danilo Krummrich
2026-02-04 13:12 ` Bartosz Golaszewski
2026-01-16 8:02 ` [PATCH v7 2/3] revocable: Add Kunit test cases Tzung-Bi Shih
` (2 subsequent siblings)
3 siblings, 2 replies; 18+ messages in thread
From: Tzung-Bi Shih @ 2026-01-16 8:02 UTC (permalink / raw)
To: Benson Leung, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Bartosz Golaszewski, Linus Walleij
Cc: Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, tzungbi, Laurent Pinchart,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
Some resources can be removed asynchronously, for example, resources
provided by a hot-pluggable device like USB. When holding a reference
to such a resource, it's possible for the resource to be removed and
its memory freed, leading to use-after-free errors on subsequent access.
The "revocable" mechanism addresses this by establishing a weak reference
to a resource that might be freed at any time. It allows a resource
consumer to safely attempt to access the resource, guaranteeing that the
access is valid for the duration of its use, or it fails safely if the
resource has already been revoked.
The implementation uses a provider/consumer model built on Sleepable
RCU (SRCU) to guarantee safe memory access:
- A resource provider, such as a driver for a hot-pluggable device,
allocates a struct revocable_provider and initializes it with a pointer
to the resource.
- A resource consumer that wants to access the resource allocates a
struct revocable which acts as a handle containing a reference to the
provider.
- To access the resource, the consumer uses revocable_try_access().
This function enters an SRCU read-side critical section and returns
the pointer to the resource. If the provider has already freed the
resource, it returns NULL. After use, the consumer calls
revocable_withdraw_access() to exit the SRCU critical section. The
REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED() are
convenient helpers for doing that.
- When the provider needs to remove the resource, it calls
revocable_provider_revoke(). This function sets the internal resource
pointer to NULL and then calls synchronize_srcu() to wait for all
current readers to finish before the resource can be completely torn
down.
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v7:
- "2025" -> "2026" in copyright.
- Documentation/
- Rephrase section "Revocable vs. Devres (devm)".
- Include sections for struct revocable_provider and struct revocable.
- Minor rename: "revocable" -> "access_rev" for DEFINE_FREE().
- Add Acked-by tag.
v6: https://lore.kernel.org/chrome-platform/20251106152330.11733-2-tzungbi@kernel.org
- Rename REVOCABLE_TRY_ACCESS_WITH() -> REVOCABLE_TRY_ACCESS_SCOPED().
- Add new REVOCABLE_TRY_ACCESS_WITH().
- Remove Acked-by tags as the API names changed a bit.
v5: https://lore.kernel.org/chrome-platform/20251016054204.1523139-2-tzungbi@kernel.org
- No changes.
v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-2-tzungbi@kernel.org
- Rename:
- revocable_provider_free() -> revocable_provider_revoke().
- REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH().
- revocable_release() -> revocable_withdraw_access().
- rcu_dereference() -> srcu_dereference() to fix a warning from lock debugging.
- Move most docs to kernel-doc, include them in Documentation/, and modify the
commit message accordingly.
- Fix some doc errors.
- Add Acked-by tags.
v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-2-tzungbi@kernel.org
- No changes.
v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-2-tzungbi@kernel.org
- Rename "ref_proxy" -> "revocable".
- Add introduction in kernel-doc format in revocable.c.
- Add MAINTAINERS entry.
- Add copyright.
- Move from lib/ to drivers/base/.
- EXPORT_SYMBOL() -> EXPORT_SYMBOL_GPL().
- Add Documentation/.
- Rename _get() -> try_access(); _put() -> release().
- Fix a sparse warning by removing the redundant __rcu annotations.
- Fix a sparse warning by adding __acquires() and __releases() annotations.
v1: https://lore.kernel.org/chrome-platform/20250814091020.1302888-2-tzungbi@kernel.org
Note (for my reference):
- An optional .release() callback for revocable provider-managed resource
hasn't added.
- `make O=build SPHINXDIRS=driver-api/driver-model/ htmldocs` a way to
verify the Documentation/.
.../driver-api/driver-model/index.rst | 1 +
.../driver-api/driver-model/revocable.rst | 152 +++++++++++
MAINTAINERS | 7 +
drivers/base/Makefile | 2 +-
drivers/base/revocable.c | 242 ++++++++++++++++++
include/linux/revocable.h | 69 +++++
6 files changed, 472 insertions(+), 1 deletion(-)
create mode 100644 Documentation/driver-api/driver-model/revocable.rst
create mode 100644 drivers/base/revocable.c
create mode 100644 include/linux/revocable.h
diff --git a/Documentation/driver-api/driver-model/index.rst b/Documentation/driver-api/driver-model/index.rst
index 4831bdd92e5c..8e1ee21185df 100644
--- a/Documentation/driver-api/driver-model/index.rst
+++ b/Documentation/driver-api/driver-model/index.rst
@@ -14,6 +14,7 @@ Driver Model
overview
platform
porting
+ revocable
.. only:: subproject and html
diff --git a/Documentation/driver-api/driver-model/revocable.rst b/Documentation/driver-api/driver-model/revocable.rst
new file mode 100644
index 000000000000..22a442cc8d7f
--- /dev/null
+++ b/Documentation/driver-api/driver-model/revocable.rst
@@ -0,0 +1,152 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+Revocable Resource Management
+==============================
+
+Overview
+========
+
+.. kernel-doc:: drivers/base/revocable.c
+ :doc: Overview
+
+Revocable vs. Devres (devm)
+===========================
+
+It's important to understand the distinct roles of the Revocable and Devres,
+and how they can complement each other. They address different problems in
+resource management:
+
+* **Devres:** Primarily address **resource leaks**. The lifetime of the
+ resources is tied to the lifetime of the device. The resource is
+ automatically freed when the device is unbound. This cleanup happens
+ irrespective of any potential active users.
+
+* **Revocable:** Primarily addresses **invalid memory access**,
+ such as Use-After-Free (UAF). It's an independent synchronization
+ primitive that decouples consumer access from the resource's actual
+ presence. Consumers interact with a "revocable object" (an intermediary),
+ not the underlying resource directly. This revocable object persists as
+ long as there are active references to it from consumer handles.
+
+**Key Distinctions & How They Complement Each Other:**
+
+1. **Reference Target:** Consumers of a resource managed by the Revocable
+ mechanism hold a reference to the *revocable object*, not the
+ encapsulated resource itself.
+
+2. **Resource Lifetime vs. Access:** The underlying resource's lifetime is
+ independent of the number of references to the revocable object. The
+ resource can be freed at any point. A common scenario is the resource
+ being freed by `devres` when the providing device is unbound.
+
+3. **Safe Access:** Revocable provides a safe way to attempt access. Before
+ using the resource, a consumer uses the Revocable API (e.g.,
+ revocable_try_access()). This function checks if the resource is still
+ valid. It returns a pointer to the resource only if it hasn't been
+ revoked; otherwise, it returns NULL. This prevents UAF by providing a
+ clear signal that the resource is gone.
+
+4. **Complementary Usage:** `devres` and Revocable work well together.
+ `devres` can handle the automatic allocation and deallocation of a
+ resource tied to a device. The Revocable mechanism can be layered on top
+ to provide safe access for consumers whose lifetimes might extend beyond
+ the provider device's lifetime. For instance, a userspace program might
+ keep a character device file open even after the physical device has been
+ removed. In this case:
+
+ * `devres` frees the device-specific resource upon unbinding.
+ * The Revocable mechanism ensures that any subsequent operations on the
+ open file handle, which attempt to access the now-freed resource,
+ will fail gracefully (e.g., revocable_try_access() returns NULL)
+ instead of causing a UAF.
+
+In summary, `devres` ensures resources are *released* to prevent leaks, while
+the Revocable mechanism ensures that attempts to *access* these resources are
+done safely, even if the resource has been released.
+
+API and Usage
+=============
+
+For Resource Providers
+----------------------
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_provider
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_provider_alloc
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: devm_revocable_provider_alloc
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_provider_revoke
+
+For Resource Consumers
+----------------------
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_alloc
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_free
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_try_access
+
+.. kernel-doc:: drivers/base/revocable.c
+ :identifiers: revocable_withdraw_access
+
+.. kernel-doc:: include/linux/revocable.h
+ :identifiers: REVOCABLE_TRY_ACCESS_WITH
+
+Example Usage
+~~~~~~~~~~~~~
+
+.. code-block:: c
+
+ void consumer_use_resource(struct revocable *rev)
+ {
+ struct foo_resource *res;
+
+ REVOCABLE_TRY_ACCESS_WITH(rev, res);
+ // Always check if the resource is valid.
+ if (!res) {
+ pr_warn("Resource is not available\n");
+ return;
+ }
+
+ // At this point, 'res' is guaranteed to be valid until
+ // this block exits.
+ do_something_with(res);
+
+ } // revocable_withdraw_access() is automatically called here.
+
+.. kernel-doc:: include/linux/revocable.h
+ :identifiers: REVOCABLE_TRY_ACCESS_SCOPED
+
+Example Usage
+~~~~~~~~~~~~~
+
+.. code-block:: c
+
+ void consumer_use_resource(struct revocable *rev)
+ {
+ struct foo_resource *res;
+
+ REVOCABLE_TRY_ACCESS_SCOPED(rev, res) {
+ // Always check if the resource is valid.
+ if (!res) {
+ pr_warn("Resource is not available\n");
+ return;
+ }
+
+ // At this point, 'res' is guaranteed to be valid until
+ // this block exits.
+ do_something_with(res);
+ }
+
+ // revocable_withdraw_access() is automatically called here.
+ }
diff --git a/MAINTAINERS b/MAINTAINERS
index a671e3d4e8be..fd683c62012a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22517,6 +22517,13 @@ F: include/uapi/linux/rseq.h
F: kernel/rseq.c
F: tools/testing/selftests/rseq/
+REVOCABLE RESOURCE MANAGEMENT
+M: Tzung-Bi Shih <tzungbi@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/base/revocable.c
+F: include/linux/revocable.h
+
RFKILL
M: Johannes Berg <johannes@sipsolutions.net>
L: linux-wireless@vger.kernel.org
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 8074a10183dc..bdf854694e39 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
cpu.o firmware.o init.o map.o devres.o \
attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o \
- swnode.o faux.o
+ swnode.o faux.o revocable.o
obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-y += power/
diff --git a/drivers/base/revocable.c b/drivers/base/revocable.c
new file mode 100644
index 000000000000..f6cece275aac
--- /dev/null
+++ b/drivers/base/revocable.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Revocable resource management
+ */
+
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/revocable.h>
+#include <linux/slab.h>
+#include <linux/srcu.h>
+
+/**
+ * DOC: Overview
+ *
+ * The "revocable" mechanism is a synchronization primitive designed to manage
+ * safe access to resources that can be asynchronously removed or invalidated.
+ * Its primary purpose is to prevent Use-After-Free (UAF) errors when
+ * interacting with resources whose lifetimes are not guaranteed to outlast
+ * their consumers.
+ *
+ * This is particularly useful in systems where resources can disappear
+ * unexpectedly, such as those provided by hot-pluggable devices like USB.
+ * When a consumer holds a reference to such a resource, the underlying device
+ * might be removed, causing the resource's memory to be freed. Subsequent
+ * access attempts by the consumer would then lead to UAF errors.
+ *
+ * Revocable addresses this by providing a form of "weak reference" and a
+ * controlled access method. It allows a resource consumer to safely attempt to
+ * access the resource. The mechanism guarantees that any access granted is
+ * valid for the duration of its use. If the resource has already been
+ * revoked (i.e., freed), the access attempt will fail safely, typically by
+ * returning NULL, instead of causing a crash.
+ *
+ * The implementation uses a provider/consumer model built on Sleepable
+ * RCU (SRCU) to guarantee safe memory access:
+ *
+ * - A resource provider, such as a driver for a hot-pluggable device,
+ * allocates a struct revocable_provider and initializes it with a pointer
+ * to the resource.
+ *
+ * - A resource consumer that wants to access the resource allocates a
+ * struct revocable which acts as a handle containing a reference to the
+ * provider.
+ *
+ * - To access the resource, the consumer uses revocable_try_access().
+ * This function enters an SRCU read-side critical section and returns
+ * the pointer to the resource. If the provider has already freed the
+ * resource, it returns NULL. After use, the consumer calls
+ * revocable_withdraw_access() to exit the SRCU critical section. The
+ * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED() are
+ * convenient helpers for doing that.
+ *
+ * - When the provider needs to remove the resource, it calls
+ * revocable_provider_revoke(). This function sets the internal resource
+ * pointer to NULL and then calls synchronize_srcu() to wait for all
+ * current readers to finish before the resource can be completely torn
+ * down.
+ */
+
+/**
+ * struct revocable_provider - A handle for resource provider.
+ * @srcu: The SRCU to protect the resource.
+ * @res: The pointer of resource. It can point to anything.
+ * @kref: The refcount for this handle.
+ */
+struct revocable_provider {
+ struct srcu_struct srcu;
+ void __rcu *res;
+ struct kref kref;
+};
+
+/**
+ * struct revocable - A handle for resource consumer.
+ * @rp: The pointer of resource provider.
+ * @idx: The index for the RCU critical section.
+ */
+struct revocable {
+ struct revocable_provider *rp;
+ int idx;
+};
+
+/**
+ * revocable_provider_alloc() - Allocate struct revocable_provider.
+ * @res: The pointer of resource.
+ *
+ * This holds an initial refcount to the struct.
+ *
+ * Return: The pointer of struct revocable_provider. NULL on errors.
+ */
+struct revocable_provider *revocable_provider_alloc(void *res)
+{
+ struct revocable_provider *rp;
+
+ rp = kzalloc(sizeof(*rp), GFP_KERNEL);
+ if (!rp)
+ return NULL;
+
+ init_srcu_struct(&rp->srcu);
+ rcu_assign_pointer(rp->res, res);
+ synchronize_srcu(&rp->srcu);
+ kref_init(&rp->kref);
+
+ return rp;
+}
+EXPORT_SYMBOL_GPL(revocable_provider_alloc);
+
+static void revocable_provider_release(struct kref *kref)
+{
+ struct revocable_provider *rp = container_of(kref,
+ struct revocable_provider, kref);
+
+ cleanup_srcu_struct(&rp->srcu);
+ kfree(rp);
+}
+
+/**
+ * revocable_provider_revoke() - Revoke the managed resource.
+ * @rp: The pointer of resource provider.
+ *
+ * This sets the resource `(struct revocable_provider *)->res` to NULL to
+ * indicate the resource has gone.
+ *
+ * This drops the refcount to the resource provider. If it is the final
+ * reference, revocable_provider_release() will be called to free the struct.
+ */
+void revocable_provider_revoke(struct revocable_provider *rp)
+{
+ rcu_assign_pointer(rp->res, NULL);
+ synchronize_srcu(&rp->srcu);
+ kref_put(&rp->kref, revocable_provider_release);
+}
+EXPORT_SYMBOL_GPL(revocable_provider_revoke);
+
+static void devm_revocable_provider_revoke(void *data)
+{
+ struct revocable_provider *rp = data;
+
+ revocable_provider_revoke(rp);
+}
+
+/**
+ * devm_revocable_provider_alloc() - Dev-managed revocable_provider_alloc().
+ * @dev: The device.
+ * @res: The pointer of resource.
+ *
+ * It is convenient to allocate providers via this function if the @res is
+ * also tied to the lifetime of the @dev. revocable_provider_revoke() will
+ * be called automatically when the device is unbound.
+ *
+ * This holds an initial refcount to the struct.
+ *
+ * Return: The pointer of struct revocable_provider. NULL on errors.
+ */
+struct revocable_provider *devm_revocable_provider_alloc(struct device *dev,
+ void *res)
+{
+ struct revocable_provider *rp;
+
+ rp = revocable_provider_alloc(res);
+ if (!rp)
+ return NULL;
+
+ if (devm_add_action_or_reset(dev, devm_revocable_provider_revoke, rp))
+ return NULL;
+
+ return rp;
+}
+EXPORT_SYMBOL_GPL(devm_revocable_provider_alloc);
+
+/**
+ * revocable_alloc() - Allocate struct revocable.
+ * @rp: The pointer of resource provider.
+ *
+ * This holds a refcount to the resource provider.
+ *
+ * Return: The pointer of struct revocable. NULL on errors.
+ */
+struct revocable *revocable_alloc(struct revocable_provider *rp)
+{
+ struct revocable *rev;
+
+ rev = kzalloc(sizeof(*rev), GFP_KERNEL);
+ if (!rev)
+ return NULL;
+
+ rev->rp = rp;
+ kref_get(&rp->kref);
+
+ return rev;
+}
+EXPORT_SYMBOL_GPL(revocable_alloc);
+
+/**
+ * revocable_free() - Free struct revocable.
+ * @rev: The pointer of struct revocable.
+ *
+ * This drops a refcount to the resource provider. If it is the final
+ * reference, revocable_provider_release() will be called to free the struct.
+ */
+void revocable_free(struct revocable *rev)
+{
+ struct revocable_provider *rp = rev->rp;
+
+ kref_put(&rp->kref, revocable_provider_release);
+ kfree(rev);
+}
+EXPORT_SYMBOL_GPL(revocable_free);
+
+/**
+ * revocable_try_access() - Try to access the resource.
+ * @rev: The pointer of struct revocable.
+ *
+ * This tries to de-reference to the resource and enters a RCU critical
+ * section.
+ *
+ * Return: The pointer to the resource. NULL if the resource has gone.
+ */
+void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu)
+{
+ struct revocable_provider *rp = rev->rp;
+
+ rev->idx = srcu_read_lock(&rp->srcu);
+ return srcu_dereference(rp->res, &rp->srcu);
+}
+EXPORT_SYMBOL_GPL(revocable_try_access);
+
+/**
+ * revocable_withdraw_access() - Stop accessing to the resource.
+ * @rev: The pointer of struct revocable.
+ *
+ * Call this function to indicate the resource is no longer used. It exits
+ * the RCU critical section.
+ */
+void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu)
+{
+ struct revocable_provider *rp = rev->rp;
+
+ srcu_read_unlock(&rp->srcu, rev->idx);
+}
+EXPORT_SYMBOL_GPL(revocable_withdraw_access);
diff --git a/include/linux/revocable.h b/include/linux/revocable.h
new file mode 100644
index 000000000000..659ba01c58db
--- /dev/null
+++ b/include/linux/revocable.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2026 Google LLC
+ */
+
+#ifndef __LINUX_REVOCABLE_H
+#define __LINUX_REVOCABLE_H
+
+#include <linux/compiler.h>
+#include <linux/cleanup.h>
+
+struct device;
+struct revocable;
+struct revocable_provider;
+
+struct revocable_provider *revocable_provider_alloc(void *res);
+void revocable_provider_revoke(struct revocable_provider *rp);
+struct revocable_provider *devm_revocable_provider_alloc(struct device *dev,
+ void *res);
+
+struct revocable *revocable_alloc(struct revocable_provider *rp);
+void revocable_free(struct revocable *rev);
+void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu);
+void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu);
+
+DEFINE_FREE(access_rev, struct revocable *, if (_T) revocable_withdraw_access(_T))
+
+/**
+ * REVOCABLE_TRY_ACCESS_WITH() - A helper for accessing revocable resource
+ * @_rev: The consumer's ``struct revocable *`` handle.
+ * @_res: A pointer variable that will be assigned the resource.
+ *
+ * The macro simplifies the access-release cycle for consumers, ensuring that
+ * revocable_withdraw_access() is always called, even in the case of an early
+ * exit.
+ *
+ * It creates a local variable in the current scope. @_res is populated with
+ * the result of revocable_try_access(). The consumer code **must** check if
+ * @_res is ``NULL`` before using it. The revocable_withdraw_access() function
+ * is automatically called when the scope is exited.
+ *
+ * Note: It shares the same issue with guard() in cleanup.h. No goto statements
+ * are allowed before the helper. Otherwise, the compiler fails with
+ * "jump bypasses initialization of variable with __attribute__((cleanup))".
+ */
+#define REVOCABLE_TRY_ACCESS_WITH(_rev, _res) \
+ struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
+ _res = revocable_try_access(_rev)
+
+#define _REVOCABLE_TRY_ACCESS_SCOPED(_rev, _label, _res) \
+ for (struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
+ (_res = revocable_try_access(_rev)) || true; ({ goto _label; })) \
+ if (0) { \
+_label: \
+ break; \
+ } else
+
+/**
+ * REVOCABLE_TRY_ACCESS_SCOPED() - A helper for accessing revocable resource
+ * @_rev: The consumer's ``struct revocable *`` handle.
+ * @_res: A pointer variable that will be assigned the resource.
+ *
+ * Similar to REVOCABLE_TRY_ACCESS_WITH() but with an explicit scope from a
+ * temporary ``for`` loop.
+ */
+#define REVOCABLE_TRY_ACCESS_SCOPED(_rev, _res) \
+ _REVOCABLE_TRY_ACCESS_SCOPED(_rev, __UNIQUE_ID(label), _res)
+
+#endif /* __LINUX_REVOCABLE_H */
--
2.52.0.457.g6b5491de43-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 8:02 ` [PATCH v7 1/3] revocable: Revocable resource management Tzung-Bi Shih
@ 2026-01-16 15:19 ` Danilo Krummrich
2026-01-16 15:27 ` Bartosz Golaszewski
2026-02-04 13:12 ` Bartosz Golaszewski
1 sibling, 1 reply; 18+ messages in thread
From: Danilo Krummrich @ 2026-01-16 15:19 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Benson Leung, Greg Kroah-Hartman, Rafael J . Wysocki,
Bartosz Golaszewski, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Laurent Pinchart, Wolfram Sang, Simona Vetter, Dan Williams,
Jason Gunthorpe, linux-gpio
On Fri Jan 16, 2026 at 9:02 AM CET, Tzung-Bi Shih wrote:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a671e3d4e8be..fd683c62012a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22517,6 +22517,13 @@ F: include/uapi/linux/rseq.h
> F: kernel/rseq.c
> F: tools/testing/selftests/rseq/
>
> +REVOCABLE RESOURCE MANAGEMENT
> +M: Tzung-Bi Shih <tzungbi@kernel.org>
> +L: linux-kernel@vger.kernel.org
> +S: Maintained
> +F: drivers/base/revocable.c
> +F: include/linux/revocable.h
NIT: I think we should add this include to the DRIVER CORE entry as well.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 15:19 ` Danilo Krummrich
@ 2026-01-16 15:27 ` Bartosz Golaszewski
2026-01-16 15:35 ` Danilo Krummrich
2026-01-16 16:04 ` Laurent Pinchart
0 siblings, 2 replies; 18+ messages in thread
From: Bartosz Golaszewski @ 2026-01-16 15:27 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Tzung-Bi Shih, Benson Leung, Greg Kroah-Hartman,
Rafael J . Wysocki, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Laurent Pinchart, Wolfram Sang, Simona Vetter, Dan Williams,
Jason Gunthorpe, linux-gpio
On Fri, Jan 16, 2026 at 4:20 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Fri Jan 16, 2026 at 9:02 AM CET, Tzung-Bi Shih wrote:
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index a671e3d4e8be..fd683c62012a 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -22517,6 +22517,13 @@ F: include/uapi/linux/rseq.h
> > F: kernel/rseq.c
> > F: tools/testing/selftests/rseq/
> >
> > +REVOCABLE RESOURCE MANAGEMENT
> > +M: Tzung-Bi Shih <tzungbi@kernel.org>
> > +L: linux-kernel@vger.kernel.org
> > +S: Maintained
> > +F: drivers/base/revocable.c
> > +F: include/linux/revocable.h
>
> NIT: I think we should add this include to the DRIVER CORE entry as well.
FWIW: I'm not even sure drivers/base/ is the right place for this.
Except for a few devm_ helpers, nothing here is inherently tied into
the driver model This could be useful outside of device drivers and I
would suggest to put it under lib/ with devres factored out into a
separate source file.
Bartosz
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 15:27 ` Bartosz Golaszewski
@ 2026-01-16 15:35 ` Danilo Krummrich
2026-01-16 16:04 ` Laurent Pinchart
1 sibling, 0 replies; 18+ messages in thread
From: Danilo Krummrich @ 2026-01-16 15:35 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Tzung-Bi Shih, Benson Leung, Greg Kroah-Hartman,
Rafael J . Wysocki, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Laurent Pinchart, Wolfram Sang, Simona Vetter, Dan Williams,
Jason Gunthorpe, linux-gpio
On Fri Jan 16, 2026 at 4:27 PM CET, Bartosz Golaszewski wrote:
> On Fri, Jan 16, 2026 at 4:20 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> On Fri Jan 16, 2026 at 9:02 AM CET, Tzung-Bi Shih wrote:
>> > diff --git a/MAINTAINERS b/MAINTAINERS
>> > index a671e3d4e8be..fd683c62012a 100644
>> > --- a/MAINTAINERS
>> > +++ b/MAINTAINERS
>> > @@ -22517,6 +22517,13 @@ F: include/uapi/linux/rseq.h
>> > F: kernel/rseq.c
>> > F: tools/testing/selftests/rseq/
>> >
>> > +REVOCABLE RESOURCE MANAGEMENT
>> > +M: Tzung-Bi Shih <tzungbi@kernel.org>
>> > +L: linux-kernel@vger.kernel.org
>> > +S: Maintained
>> > +F: drivers/base/revocable.c
>> > +F: include/linux/revocable.h
>>
>> NIT: I think we should add this include to the DRIVER CORE entry as well.
>
> FWIW: I'm not even sure drivers/base/ is the right place for this.
> Except for a few devm_ helpers, nothing here is inherently tied into
> the driver model This could be useful outside of device drivers and I
> would suggest to put it under lib/ with devres factored out into a
> separate source file.
It is indeed correct that in the end revocable is more of a generic
synchronization primitve.
In fact, the Revocable type in Rust is not listed under driver core
infrastructure either. However, it was introduced for use with Devres (which is
still the only user).
I think the, by far, most common use-case where a resource is revoked from an
entity are device resources revoked by devres.
So, I'd say let's factor it out once we see interest by more users?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 15:27 ` Bartosz Golaszewski
2026-01-16 15:35 ` Danilo Krummrich
@ 2026-01-16 16:04 ` Laurent Pinchart
2026-01-16 16:41 ` Danilo Krummrich
1 sibling, 1 reply; 18+ messages in thread
From: Laurent Pinchart @ 2026-01-16 16:04 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Danilo Krummrich, Tzung-Bi Shih, Benson Leung, Greg Kroah-Hartman,
Rafael J . Wysocki, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
On Fri, Jan 16, 2026 at 04:27:27PM +0100, Bartosz Golaszewski wrote:
> On Fri, Jan 16, 2026 at 4:20 PM Danilo Krummrich wrote:
> > On Fri Jan 16, 2026 at 9:02 AM CET, Tzung-Bi Shih wrote:
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index a671e3d4e8be..fd683c62012a 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -22517,6 +22517,13 @@ F: include/uapi/linux/rseq.h
> > > F: kernel/rseq.c
> > > F: tools/testing/selftests/rseq/
> > >
> > > +REVOCABLE RESOURCE MANAGEMENT
> > > +M: Tzung-Bi Shih <tzungbi@kernel.org>
> > > +L: linux-kernel@vger.kernel.org
> > > +S: Maintained
> > > +F: drivers/base/revocable.c
> > > +F: include/linux/revocable.h
> >
> > NIT: I think we should add this include to the DRIVER CORE entry as well.
>
> FWIW: I'm not even sure drivers/base/ is the right place for this.
> Except for a few devm_ helpers, nothing here is inherently tied into
> the driver model This could be useful outside of device drivers and I
> would suggest to put it under lib/ with devres factored out into a
> separate source file.
I agree. Based on the discussions we had at LPC, the revocable resource
management API is not the right solution to handle races between device
removal and userspace access. It is however a possibly useful tool for
races between producers and consumers *inside the kernel*. lib/ is a
better location.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 16:04 ` Laurent Pinchart
@ 2026-01-16 16:41 ` Danilo Krummrich
2026-01-16 18:19 ` Bartosz Golaszewski
0 siblings, 1 reply; 18+ messages in thread
From: Danilo Krummrich @ 2026-01-16 16:41 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Bartosz Golaszewski, Tzung-Bi Shih, Benson Leung,
Greg Kroah-Hartman, Rafael J . Wysocki, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, Jason Gunthorpe, linux-gpio
On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> Based on the discussions we had at LPC, the revocable resource management API
> is not the right solution to handle races between device removal and userspace
> access.
Please see: https://lore.kernel.org/all/DFQ5D44A0348.PZJIGPL972N@kernel.org/
> It is however a possibly useful tool for races between producers and consumers
> *inside the kernel*.
Do you have an example for such a case?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 16:41 ` Danilo Krummrich
@ 2026-01-16 18:19 ` Bartosz Golaszewski
2026-01-16 18:24 ` Jason Gunthorpe
0 siblings, 1 reply; 18+ messages in thread
From: Bartosz Golaszewski @ 2026-01-16 18:19 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Laurent Pinchart, Tzung-Bi Shih, Benson Leung, Greg Kroah-Hartman,
Rafael J . Wysocki, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
On Fri, Jan 16, 2026 at 5:41 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> > Based on the discussions we had at LPC, the revocable resource management API
> > is not the right solution to handle races between device removal and userspace
> > access.
>
> Please see: https://lore.kernel.org/all/DFQ5D44A0348.PZJIGPL972N@kernel.org/
>
> > It is however a possibly useful tool for races between producers and consumers
> > *inside the kernel*.
>
> Do you have an example for such a case?
Isn't the GPIO use-case - which the series on top of it addresses - one?
With fw_devlink=off it's quite easy to trigger all kinds of crashes
with in-kernel users. I suggested using revocable in GPIO myself but I
won't accept it as long as it impacts performance over "naked" SRCU.
Bart
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 18:19 ` Bartosz Golaszewski
@ 2026-01-16 18:24 ` Jason Gunthorpe
2026-01-16 18:31 ` Bartosz Golaszewski
0 siblings, 1 reply; 18+ messages in thread
From: Jason Gunthorpe @ 2026-01-16 18:24 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Danilo Krummrich, Laurent Pinchart, Tzung-Bi Shih, Benson Leung,
Greg Kroah-Hartman, Rafael J . Wysocki, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, linux-gpio
On Fri, Jan 16, 2026 at 07:19:50PM +0100, Bartosz Golaszewski wrote:
> On Fri, Jan 16, 2026 at 5:41 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> > > Based on the discussions we had at LPC, the revocable resource management API
> > > is not the right solution to handle races between device removal and userspace
> > > access.
> >
> > Please see: https://lore.kernel.org/all/DFQ5D44A0348.PZJIGPL972N@kernel.org/
> >
> > > It is however a possibly useful tool for races between producers and consumers
> > > *inside the kernel*.
> >
> > Do you have an example for such a case?
>
> Isn't the GPIO use-case - which the series on top of it addresses - one?
>
> With fw_devlink=off it's quite easy to trigger all kinds of crashes
> with in-kernel users.
Does this series solve that? It looked to me like it just replaces the
existing SRCU with a wrapper?
Jason
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 18:24 ` Jason Gunthorpe
@ 2026-01-16 18:31 ` Bartosz Golaszewski
0 siblings, 0 replies; 18+ messages in thread
From: Bartosz Golaszewski @ 2026-01-16 18:31 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Danilo Krummrich, Laurent Pinchart, Tzung-Bi Shih, Benson Leung,
Greg Kroah-Hartman, Rafael J . Wysocki, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, linux-gpio
On Fri, Jan 16, 2026 at 7:24 PM Jason Gunthorpe <jgg@nvidia.com> wrote:
>
> On Fri, Jan 16, 2026 at 07:19:50PM +0100, Bartosz Golaszewski wrote:
> > On Fri, Jan 16, 2026 at 5:41 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> > > > Based on the discussions we had at LPC, the revocable resource management API
> > > > is not the right solution to handle races between device removal and userspace
> > > > access.
> > >
> > > Please see: https://lore.kernel.org/all/DFQ5D44A0348.PZJIGPL972N@kernel.org/
> > >
> > > > It is however a possibly useful tool for races between producers and consumers
> > > > *inside the kernel*.
> > >
> > > Do you have an example for such a case?
> >
> > Isn't the GPIO use-case - which the series on top of it addresses - one?
> >
> > With fw_devlink=off it's quite easy to trigger all kinds of crashes
> > with in-kernel users.
>
> Does this series solve that? It looked to me like it just replaces the
> existing SRCU with a wrapper?
>
SRCU already *did* solve it. Revocable *is* a wrapper around SRCU that
generalizes the initial solution.
Replacing SRCU with a generalized wrapper is fine but there are
subsystems out there, where the problem is much less trivial. Take I2C
for example: the struct device management is so broken that there
isn't even anything *to revoke* yet. It'll take years of little
reworks before we can even use revocable at all.
I'm not against it as a library of functions. But TBH I looked at the
series and - besides making the code run slower - it also kind of
makes it harder to read. With *naked* SRCU it's very clear what's
going on, when you start hiding the logic, it becomes needlessly
obfuscated.
I want to first see revocable match current GPIO performance and then
we can talk about accepting it.
Bartosz
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 1/3] revocable: Revocable resource management
2026-01-16 8:02 ` [PATCH v7 1/3] revocable: Revocable resource management Tzung-Bi Shih
2026-01-16 15:19 ` Danilo Krummrich
@ 2026-02-04 13:12 ` Bartosz Golaszewski
1 sibling, 0 replies; 18+ messages in thread
From: Bartosz Golaszewski @ 2026-02-04 13:12 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Benson Leung, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Laurent Pinchart, Wolfram Sang, Simona Vetter, Dan Williams,
Jason Gunthorpe, linux-gpio
On Fri, Jan 16, 2026 at 9:03 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> Some resources can be removed asynchronously, for example, resources
> provided by a hot-pluggable device like USB. When holding a reference
> to such a resource, it's possible for the resource to be removed and
> its memory freed, leading to use-after-free errors on subsequent access.
>
> The "revocable" mechanism addresses this by establishing a weak reference
> to a resource that might be freed at any time. It allows a resource
> consumer to safely attempt to access the resource, guaranteeing that the
> access is valid for the duration of its use, or it fails safely if the
> resource has already been revoked.
>
> The implementation uses a provider/consumer model built on Sleepable
> RCU (SRCU) to guarantee safe memory access:
>
> - A resource provider, such as a driver for a hot-pluggable device,
> allocates a struct revocable_provider and initializes it with a pointer
> to the resource.
>
> - A resource consumer that wants to access the resource allocates a
> struct revocable which acts as a handle containing a reference to the
> provider.
>
> - To access the resource, the consumer uses revocable_try_access().
> This function enters an SRCU read-side critical section and returns
> the pointer to the resource. If the provider has already freed the
> resource, it returns NULL. After use, the consumer calls
> revocable_withdraw_access() to exit the SRCU critical section. The
> REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED() are
> convenient helpers for doing that.
>
> - When the provider needs to remove the resource, it calls
> revocable_provider_revoke(). This function sets the internal resource
> pointer to NULL and then calls synchronize_srcu() to wait for all
> current readers to finish before the resource can be completely torn
> down.
>
> Acked-by: Danilo Krummrich <dakr@kernel.org>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> ---
[snip]
> diff --git a/include/linux/revocable.h b/include/linux/revocable.h
> new file mode 100644
> index 000000000000..659ba01c58db
> --- /dev/null
> +++ b/include/linux/revocable.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2026 Google LLC
> + */
> +
> +#ifndef __LINUX_REVOCABLE_H
> +#define __LINUX_REVOCABLE_H
> +
> +#include <linux/compiler.h>
> +#include <linux/cleanup.h>
> +
> +struct device;
> +struct revocable;
> +struct revocable_provider;
> +
> +struct revocable_provider *revocable_provider_alloc(void *res);
> +void revocable_provider_revoke(struct revocable_provider *rp);
> +struct revocable_provider *devm_revocable_provider_alloc(struct device *dev,
> + void *res);
> +
> +struct revocable *revocable_alloc(struct revocable_provider *rp);
> +void revocable_free(struct revocable *rev);
> +void *revocable_try_access(struct revocable *rev) __acquires(&rev->rp->srcu);
> +void revocable_withdraw_access(struct revocable *rev) __releases(&rev->rp->srcu);
> +
> +DEFINE_FREE(access_rev, struct revocable *, if (_T) revocable_withdraw_access(_T))
> +
> +/**
> + * REVOCABLE_TRY_ACCESS_WITH() - A helper for accessing revocable resource
> + * @_rev: The consumer's ``struct revocable *`` handle.
> + * @_res: A pointer variable that will be assigned the resource.
> + *
> + * The macro simplifies the access-release cycle for consumers, ensuring that
> + * revocable_withdraw_access() is always called, even in the case of an early
> + * exit.
> + *
> + * It creates a local variable in the current scope. @_res is populated with
> + * the result of revocable_try_access(). The consumer code **must** check if
> + * @_res is ``NULL`` before using it. The revocable_withdraw_access() function
> + * is automatically called when the scope is exited.
> + *
> + * Note: It shares the same issue with guard() in cleanup.h. No goto statements
> + * are allowed before the helper. Otherwise, the compiler fails with
> + * "jump bypasses initialization of variable with __attribute__((cleanup))".
> + */
> +#define REVOCABLE_TRY_ACCESS_WITH(_rev, _res) \
> + struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
> + _res = revocable_try_access(_rev)
> +
> +#define _REVOCABLE_TRY_ACCESS_SCOPED(_rev, _label, _res) \
> + for (struct revocable *__UNIQUE_ID(name) __free(access_rev) = _rev; \
> + (_res = revocable_try_access(_rev)) || true; ({ goto _label; })) \
> + if (0) { \
> +_label: \
> + break; \
> + } else
> +
> +/**
> + * REVOCABLE_TRY_ACCESS_SCOPED() - A helper for accessing revocable resource
> + * @_rev: The consumer's ``struct revocable *`` handle.
> + * @_res: A pointer variable that will be assigned the resource.
> + *
> + * Similar to REVOCABLE_TRY_ACCESS_WITH() but with an explicit scope from a
> + * temporary ``for`` loop.
> + */
> +#define REVOCABLE_TRY_ACCESS_SCOPED(_rev, _res) \
Seeing this code used in GPIO, I would suggest a coding-style change
before it makes its way into other places:
revocable_try_access_with() looks better and is in line with most
other macros. There's no reason for capitalisation IMO and it makes
the code using it look like there was some issue with it, as if this
was a WARN() or something.
I'd suggest changing all these macros to using lowercase and queuing
it for v7.0.
Bartosz
> + _REVOCABLE_TRY_ACCESS_SCOPED(_rev, __UNIQUE_ID(label), _res)
> +
> +#endif /* __LINUX_REVOCABLE_H */
> --
> 2.52.0.457.g6b5491de43-goog
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v7 2/3] revocable: Add Kunit test cases
2026-01-16 8:02 [PATCH v7 0/3] drivers/base: Introduce revocable Tzung-Bi Shih
2026-01-16 8:02 ` [PATCH v7 1/3] revocable: Revocable resource management Tzung-Bi Shih
@ 2026-01-16 8:02 ` Tzung-Bi Shih
2026-01-16 8:02 ` [PATCH v7 3/3] selftests: revocable: Add kselftest cases Tzung-Bi Shih
2026-01-16 15:17 ` [PATCH v7 0/3] drivers/base: Introduce revocable Greg Kroah-Hartman
3 siblings, 0 replies; 18+ messages in thread
From: Tzung-Bi Shih @ 2026-01-16 8:02 UTC (permalink / raw)
To: Benson Leung, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Bartosz Golaszewski, Linus Walleij
Cc: Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, tzungbi, Laurent Pinchart,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
Add Kunit test cases for the revocable API.
The test cases cover the following scenarios:
- Basic: Verifies that a consumer can successfully access the resource
provided via the provider.
- Revocation: Verifies that after the provider revokes the resource,
the consumer correctly receives a NULL pointer on a subsequent access.
- Try Access Macro: Same as "Revocation" but uses the
REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().
A way to run the test:
$ ./tools/testing/kunit/kunit.py run \
--kconfig_add CONFIG_REVOCABLE_KUNIT_TEST=y \
revocable_test
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v7:
- "2025" -> "2026" in copyright.
- Rename the test name "macro" -> "try_access_macro".
v6: https://lore.kernel.org/chrome-platform/20251106152330.11733-3-tzungbi@kernel.org
- Rename REVOCABLE_TRY_ACCESS_WITH() -> REVOCABLE_TRY_ACCESS_SCOPED().
- Add tests for new REVOCABLE_TRY_ACCESS_WITH().
v5: https://lore.kernel.org/chrome-platform/20251016054204.1523139-3-tzungbi@kernel.org
- No changes.
v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-3-tzungbi@kernel.org
- REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH().
- revocable_release() -> revocable_withdraw_access().
v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-3-tzungbi@kernel.org
- No changes.
v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-3-tzungbi@kernel.org
- New in the series.
MAINTAINERS | 1 +
drivers/base/Kconfig | 8 ++
drivers/base/Makefile | 3 +
drivers/base/revocable_test.c | 139 ++++++++++++++++++++++++++++++++++
4 files changed, 151 insertions(+)
create mode 100644 drivers/base/revocable_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index fd683c62012a..8e237833a17a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22522,6 +22522,7 @@ M: Tzung-Bi Shih <tzungbi@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/base/revocable.c
+F: drivers/base/revocable_test.c
F: include/linux/revocable.h
RFKILL
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 1786d87b29e2..8f7d7b9d81ac 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -250,3 +250,11 @@ config FW_DEVLINK_SYNC_STATE_TIMEOUT
work on.
endmenu
+
+# Kunit test cases
+config REVOCABLE_KUNIT_TEST
+ tristate "Kunit tests for revocable" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Kunit tests for the revocable API.
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index bdf854694e39..4185aaa9bbb9 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -35,3 +35,6 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_TRACING) += trace.o
+
+# Kunit test cases
+obj-$(CONFIG_REVOCABLE_KUNIT_TEST) += revocable_test.o
diff --git a/drivers/base/revocable_test.c b/drivers/base/revocable_test.c
new file mode 100644
index 000000000000..28d46ce1ba0c
--- /dev/null
+++ b/drivers/base/revocable_test.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Kunit tests for the revocable API.
+ *
+ * The test cases cover the following scenarios:
+ *
+ * - Basic: Verifies that a consumer can successfully access the resource
+ * provided via the provider.
+ *
+ * - Revocation: Verifies that after the provider revokes the resource,
+ * the consumer correctly receives a NULL pointer on a subsequent access.
+ *
+ * - Try Access Macro: Same as "Revocation" but uses the
+ * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().
+ */
+
+#include <kunit/test.h>
+#include <linux/revocable.h>
+
+static void revocable_test_basic(struct kunit *test)
+{
+ struct revocable_provider *rp;
+ struct revocable *rev;
+ void *real_res = (void *)0x12345678, *res;
+
+ rp = revocable_provider_alloc(real_res);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
+
+ rev = revocable_alloc(rp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev);
+
+ res = revocable_try_access(rev);
+ KUNIT_EXPECT_PTR_EQ(test, res, real_res);
+ revocable_withdraw_access(rev);
+
+ revocable_free(rev);
+ revocable_provider_revoke(rp);
+}
+
+static void revocable_test_revocation(struct kunit *test)
+{
+ struct revocable_provider *rp;
+ struct revocable *rev;
+ void *real_res = (void *)0x12345678, *res;
+
+ rp = revocable_provider_alloc(real_res);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
+
+ rev = revocable_alloc(rp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev);
+
+ res = revocable_try_access(rev);
+ KUNIT_EXPECT_PTR_EQ(test, res, real_res);
+ revocable_withdraw_access(rev);
+
+ revocable_provider_revoke(rp);
+
+ res = revocable_try_access(rev);
+ KUNIT_EXPECT_PTR_EQ(test, res, NULL);
+ revocable_withdraw_access(rev);
+
+ revocable_free(rev);
+}
+
+static void revocable_test_try_access_macro(struct kunit *test)
+{
+ struct revocable_provider *rp;
+ struct revocable *rev;
+ void *real_res = (void *)0x12345678, *res;
+
+ rp = revocable_provider_alloc(real_res);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
+
+ rev = revocable_alloc(rp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev);
+
+ {
+ REVOCABLE_TRY_ACCESS_WITH(rev, res);
+ KUNIT_EXPECT_PTR_EQ(test, res, real_res);
+ }
+
+ revocable_provider_revoke(rp);
+
+ {
+ REVOCABLE_TRY_ACCESS_WITH(rev, res);
+ KUNIT_EXPECT_PTR_EQ(test, res, NULL);
+ }
+
+ revocable_free(rev);
+}
+
+static void revocable_test_try_access_macro2(struct kunit *test)
+{
+ struct revocable_provider *rp;
+ struct revocable *rev;
+ void *real_res = (void *)0x12345678, *res;
+ bool accessed;
+
+ rp = revocable_provider_alloc(real_res);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp);
+
+ rev = revocable_alloc(rp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rev);
+
+ accessed = false;
+ REVOCABLE_TRY_ACCESS_SCOPED(rev, res) {
+ KUNIT_EXPECT_PTR_EQ(test, res, real_res);
+ accessed = true;
+ }
+ KUNIT_EXPECT_TRUE(test, accessed);
+
+ revocable_provider_revoke(rp);
+
+ accessed = false;
+ REVOCABLE_TRY_ACCESS_SCOPED(rev, res) {
+ KUNIT_EXPECT_PTR_EQ(test, res, NULL);
+ accessed = true;
+ }
+ KUNIT_EXPECT_TRUE(test, accessed);
+
+ revocable_free(rev);
+}
+
+static struct kunit_case revocable_test_cases[] = {
+ KUNIT_CASE(revocable_test_basic),
+ KUNIT_CASE(revocable_test_revocation),
+ KUNIT_CASE(revocable_test_try_access_macro),
+ KUNIT_CASE(revocable_test_try_access_macro2),
+ {}
+};
+
+static struct kunit_suite revocable_test_suite = {
+ .name = "revocable_test",
+ .test_cases = revocable_test_cases,
+};
+
+kunit_test_suite(revocable_test_suite);
--
2.52.0.457.g6b5491de43-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v7 3/3] selftests: revocable: Add kselftest cases
2026-01-16 8:02 [PATCH v7 0/3] drivers/base: Introduce revocable Tzung-Bi Shih
2026-01-16 8:02 ` [PATCH v7 1/3] revocable: Revocable resource management Tzung-Bi Shih
2026-01-16 8:02 ` [PATCH v7 2/3] revocable: Add Kunit test cases Tzung-Bi Shih
@ 2026-01-16 8:02 ` Tzung-Bi Shih
2026-01-16 15:17 ` [PATCH v7 0/3] drivers/base: Introduce revocable Greg Kroah-Hartman
3 siblings, 0 replies; 18+ messages in thread
From: Tzung-Bi Shih @ 2026-01-16 8:02 UTC (permalink / raw)
To: Benson Leung, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Bartosz Golaszewski, Linus Walleij
Cc: Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, tzungbi, Laurent Pinchart,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
Add kselftest cases for the revocable API.
The test consists of three parts:
- A kernel module (revocable_test.ko) that creates a debugfs interface
with `/provider` and `/consumer` files.
- A user-space C program (revocable_test) that uses the kselftest
harness to interact with the debugfs files.
- An orchestrating shell script (test-revocable.sh) that loads the
module, runs the C program, and unloads the module.
The test cases cover the following scenarios:
- Basic: Verifies that a consumer can successfully access the resource
provided via the provider.
- Revocation: Verifies that after the provider revokes the resource,
the consumer correctly receives a NULL pointer on a subsequent access.
- Try Access Macro: Same as "Revocation" but uses the
REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v7:
- "2025" -> "2026" in copyright.
- Rename the test name "macro" -> "try_access_macro".
v6: https://lore.kernel.org/chrome-platform/20251106152330.11733-4-tzungbi@kernel.org
- Rename REVOCABLE_TRY_ACCESS_WITH() -> REVOCABLE_TRY_ACCESS_SCOPED().
- Add tests for new REVOCABLE_TRY_ACCESS_WITH().
v5: https://lore.kernel.org/chrome-platform/20251016054204.1523139-4-tzungbi@kernel.org
- No changes.
v4: https://lore.kernel.org/chrome-platform/20250923075302.591026-4-tzungbi@kernel.org
- REVOCABLE() -> REVOCABLE_TRY_ACCESS_WITH().
- revocable_release() -> revocable_withdraw_access().
v3: https://lore.kernel.org/chrome-platform/20250912081718.3827390-4-tzungbi@kernel.org
- No changes.
v2: https://lore.kernel.org/chrome-platform/20250820081645.847919-4-tzungbi@kernel.org
- New in the series.
A way to run the kselftest (for my reference):
- Update kernel to the DUT.
- `mkdir build` and copy the kernel config to build/.
- `make O=build LLVM=1 -j32` (for generated headers and built-in symbols).
- `make O=build LLVM=1 KDIR=$(pwd) -C tools/testing/selftests/
TARGETS=drivers/base/revocable gen_tar`.
- Copy build/kselftest/kselftest_install/kselftest-packages/kselftest.tar.gz
to the DUT, extract, and execute the run_kselftest.sh.
MAINTAINERS | 1 +
tools/testing/selftests/Makefile | 1 +
.../selftests/drivers/base/revocable/Makefile | 7 +
.../drivers/base/revocable/revocable_test.c | 136 ++++++++++++
.../drivers/base/revocable/test-revocable.sh | 39 ++++
.../base/revocable/test_modules/Makefile | 10 +
.../revocable/test_modules/revocable_test.c | 195 ++++++++++++++++++
7 files changed, 389 insertions(+)
create mode 100644 tools/testing/selftests/drivers/base/revocable/Makefile
create mode 100644 tools/testing/selftests/drivers/base/revocable/revocable_test.c
create mode 100755 tools/testing/selftests/drivers/base/revocable/test-revocable.sh
create mode 100644 tools/testing/selftests/drivers/base/revocable/test_modules/Makefile
create mode 100644 tools/testing/selftests/drivers/base/revocable/test_modules/revocable_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e237833a17a..51426922c88e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22524,6 +22524,7 @@ S: Maintained
F: drivers/base/revocable.c
F: drivers/base/revocable_test.c
F: include/linux/revocable.h
+F: tools/testing/selftests/drivers/base/revocable/
RFKILL
M: Johannes Berg <johannes@sipsolutions.net>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 56e44a98d6a5..11b6515ce3d0 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -17,6 +17,7 @@ TARGETS += damon
TARGETS += devices/error_logs
TARGETS += devices/probe
TARGETS += dmabuf-heaps
+TARGETS += drivers/base/revocable
TARGETS += drivers/dma-buf
TARGETS += drivers/ntsync
TARGETS += drivers/s390x/uvdevice
diff --git a/tools/testing/selftests/drivers/base/revocable/Makefile b/tools/testing/selftests/drivers/base/revocable/Makefile
new file mode 100644
index 000000000000..afa5ca0fa452
--- /dev/null
+++ b/tools/testing/selftests/drivers/base/revocable/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_GEN_MODS_DIR := test_modules
+TEST_GEN_PROGS_EXTENDED := revocable_test
+TEST_PROGS := test-revocable.sh
+
+include ../../../lib.mk
diff --git a/tools/testing/selftests/drivers/base/revocable/revocable_test.c b/tools/testing/selftests/drivers/base/revocable/revocable_test.c
new file mode 100644
index 000000000000..f024164e9273
--- /dev/null
+++ b/tools/testing/selftests/drivers/base/revocable/revocable_test.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2026 Google LLC
+ *
+ * A selftest for the revocable API.
+ *
+ * The test cases cover the following scenarios:
+ *
+ * - Basic: Verifies that a consumer can successfully access the resource
+ * provided via the provider.
+ *
+ * - Revocation: Verifies that after the provider revokes the resource,
+ * the consumer correctly receives a NULL pointer on a subsequent access.
+ *
+ * - Try Access Macro: Same as "Revocation" but uses the
+ * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "../../../kselftest_harness.h"
+
+#define DEBUGFS_PATH "/sys/kernel/debug/revocable_test"
+#define TEST_CMD_RESOURCE_GONE "resource_gone"
+#define TEST_DATA "12345678"
+#define TEST_MAGIC_OFFSET 0x1234
+#define TEST_MAGIC_OFFSET2 0x5678
+
+FIXTURE(revocable_fixture) {
+ int pfd;
+ int cfd;
+};
+
+FIXTURE_SETUP(revocable_fixture) {
+ int ret;
+
+ self->pfd = open(DEBUGFS_PATH "/provider", O_WRONLY);
+ ASSERT_NE(-1, self->pfd)
+ TH_LOG("failed to open provider fd");
+
+ ret = write(self->pfd, TEST_DATA, strlen(TEST_DATA));
+ ASSERT_NE(-1, ret) {
+ close(self->pfd);
+ TH_LOG("failed to write test data");
+ }
+
+ self->cfd = open(DEBUGFS_PATH "/consumer", O_RDONLY);
+ ASSERT_NE(-1, self->cfd)
+ TH_LOG("failed to open consumer fd");
+}
+
+FIXTURE_TEARDOWN(revocable_fixture) {
+ close(self->cfd);
+ close(self->pfd);
+}
+
+/*
+ * ASSERT_* is only available in TEST or TEST_F block. Use
+ * macro for the helper.
+ */
+#define READ_TEST_DATA(_fd, _offset, _data, _msg) \
+ do { \
+ int ret; \
+ \
+ ret = lseek(_fd, _offset, SEEK_SET); \
+ ASSERT_NE(-1, ret) \
+ TH_LOG("failed to lseek"); \
+ \
+ ret = read(_fd, _data, sizeof(_data) - 1); \
+ ASSERT_NE(-1, ret) \
+ TH_LOG(_msg); \
+ data[ret] = '\0'; \
+ } while (0)
+
+TEST_F(revocable_fixture, basic) {
+ char data[16];
+
+ READ_TEST_DATA(self->cfd, 0, data, "failed to read test data");
+ EXPECT_STREQ(TEST_DATA, data);
+}
+
+TEST_F(revocable_fixture, revocation) {
+ char data[16];
+ int ret;
+
+ READ_TEST_DATA(self->cfd, 0, data, "failed to read test data");
+ EXPECT_STREQ(TEST_DATA, data);
+
+ ret = write(self->pfd, TEST_CMD_RESOURCE_GONE,
+ strlen(TEST_CMD_RESOURCE_GONE));
+ ASSERT_NE(-1, ret)
+ TH_LOG("failed to write resource gone cmd");
+
+ READ_TEST_DATA(self->cfd, 0, data,
+ "failed to read test data after resource gone");
+ EXPECT_STREQ("(null)", data);
+}
+
+TEST_F(revocable_fixture, try_access_macro) {
+ char data[16];
+ int ret;
+
+ READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data,
+ "failed to read test data");
+ EXPECT_STREQ(TEST_DATA, data);
+
+ ret = write(self->pfd, TEST_CMD_RESOURCE_GONE,
+ strlen(TEST_CMD_RESOURCE_GONE));
+ ASSERT_NE(-1, ret)
+ TH_LOG("failed to write resource gone cmd");
+
+ READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data,
+ "failed to read test data after resource gone");
+ EXPECT_STREQ("(null)", data);
+}
+
+TEST_F(revocable_fixture, try_access_macro2) {
+ char data[16];
+ int ret;
+
+ READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET2, data,
+ "failed to read test data");
+ EXPECT_STREQ(TEST_DATA, data);
+
+ ret = write(self->pfd, TEST_CMD_RESOURCE_GONE,
+ strlen(TEST_CMD_RESOURCE_GONE));
+ ASSERT_NE(-1, ret)
+ TH_LOG("failed to write resource gone cmd");
+
+ READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET2, data,
+ "failed to read test data after resource gone");
+ EXPECT_STREQ("(null)", data);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/drivers/base/revocable/test-revocable.sh b/tools/testing/selftests/drivers/base/revocable/test-revocable.sh
new file mode 100755
index 000000000000..3a34be28001a
--- /dev/null
+++ b/tools/testing/selftests/drivers/base/revocable/test-revocable.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+mod_name="revocable_test"
+ksft_fail=1
+ksft_skip=4
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "$0: Must be run as root"
+ exit "$ksft_skip"
+fi
+
+if ! which insmod > /dev/null 2>&1; then
+ echo "$0: Need insmod"
+ exit "$ksft_skip"
+fi
+
+if ! which rmmod > /dev/null 2>&1; then
+ echo "$0: Need rmmod"
+ exit "$ksft_skip"
+fi
+
+insmod test_modules/"${mod_name}".ko
+
+if [ ! -d /sys/kernel/debug/revocable_test/ ]; then
+ mount -t debugfs none /sys/kernel/debug/
+
+ if [ ! -d /sys/kernel/debug/revocable_test/ ]; then
+ echo "$0: Error mounting debugfs"
+ exit "$ksft_fail"
+ fi
+fi
+
+./revocable_test
+ret=$?
+
+rmmod "${mod_name}"
+
+exit "${ret}"
diff --git a/tools/testing/selftests/drivers/base/revocable/test_modules/Makefile b/tools/testing/selftests/drivers/base/revocable/test_modules/Makefile
new file mode 100644
index 000000000000..f29e4f909402
--- /dev/null
+++ b/tools/testing/selftests/drivers/base/revocable/test_modules/Makefile
@@ -0,0 +1,10 @@
+TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+KDIR ?= /lib/modules/$(shell uname -r)/build
+
+obj-m += revocable_test.o
+
+all:
+ $(Q)$(MAKE) -C $(KDIR) M=$(TESTMODS_DIR)
+
+clean:
+ $(Q)$(MAKE) -C $(KDIR) M=$(TESTMODS_DIR) clean
diff --git a/tools/testing/selftests/drivers/base/revocable/test_modules/revocable_test.c b/tools/testing/selftests/drivers/base/revocable/test_modules/revocable_test.c
new file mode 100644
index 000000000000..1b0692eb75f3
--- /dev/null
+++ b/tools/testing/selftests/drivers/base/revocable/test_modules/revocable_test.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2026 Google LLC
+ *
+ * A kernel module for testing the revocable API.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/revocable.h>
+#include <linux/slab.h>
+
+#define TEST_CMD_RESOURCE_GONE "resource_gone"
+#define TEST_MAGIC_OFFSET 0x1234
+#define TEST_MAGIC_OFFSET2 0x5678
+
+static struct dentry *debugfs_dir;
+
+struct revocable_test_provider_priv {
+ struct revocable_provider *rp;
+ struct dentry *dentry;
+ char res[16];
+};
+
+static int revocable_test_consumer_open(struct inode *inode, struct file *filp)
+{
+ struct revocable *rev;
+ struct revocable_provider *rp = inode->i_private;
+
+ rev = revocable_alloc(rp);
+ if (!rev)
+ return -ENOMEM;
+ filp->private_data = rev;
+
+ return 0;
+}
+
+static int revocable_test_consumer_release(struct inode *inode,
+ struct file *filp)
+{
+ struct revocable *rev = filp->private_data;
+
+ revocable_free(rev);
+ return 0;
+}
+
+static ssize_t revocable_test_consumer_read(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *offset)
+{
+ char *res;
+ char data[16];
+ size_t len;
+ struct revocable *rev = filp->private_data;
+
+ switch (*offset) {
+ case 0:
+ res = revocable_try_access(rev);
+ snprintf(data, sizeof(data), "%s", res ?: "(null)");
+ revocable_withdraw_access(rev);
+ break;
+ case TEST_MAGIC_OFFSET:
+ {
+ REVOCABLE_TRY_ACCESS_WITH(rev, res);
+ snprintf(data, sizeof(data), "%s", res ?: "(null)");
+ }
+ break;
+ case TEST_MAGIC_OFFSET2:
+ REVOCABLE_TRY_ACCESS_SCOPED(rev, res)
+ snprintf(data, sizeof(data), "%s", res ?: "(null)");
+ break;
+ default:
+ return 0;
+ }
+
+ len = min_t(size_t, strlen(data), count);
+ if (copy_to_user(buf, data, len))
+ return -EFAULT;
+
+ *offset = len;
+ return len;
+}
+
+static const struct file_operations revocable_test_consumer_fops = {
+ .open = revocable_test_consumer_open,
+ .release = revocable_test_consumer_release,
+ .read = revocable_test_consumer_read,
+ .llseek = default_llseek,
+};
+
+static int revocable_test_provider_open(struct inode *inode, struct file *filp)
+{
+ struct revocable_test_provider_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ filp->private_data = priv;
+
+ return 0;
+}
+
+static int revocable_test_provider_release(struct inode *inode,
+ struct file *filp)
+{
+ struct revocable_test_provider_priv *priv = filp->private_data;
+
+ debugfs_remove(priv->dentry);
+ if (priv->rp)
+ revocable_provider_revoke(priv->rp);
+ kfree(priv);
+
+ return 0;
+}
+
+static ssize_t revocable_test_provider_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ size_t copied;
+ char data[64];
+ struct revocable_test_provider_priv *priv = filp->private_data;
+
+ copied = strncpy_from_user(data, buf, sizeof(data));
+ if (copied < 0)
+ return copied;
+ if (copied == sizeof(data))
+ data[sizeof(data) - 1] = '\0';
+
+ /*
+ * Note: The test can't just close the FD for signaling the
+ * resource gone. Subsequent file operations on the opening
+ * FD of debugfs return -EIO after calling debugfs_remove().
+ * See also debugfs_file_get().
+ *
+ * Here is a side command channel for signaling the resource
+ * gone.
+ */
+ if (!strcmp(data, TEST_CMD_RESOURCE_GONE)) {
+ revocable_provider_revoke(priv->rp);
+ priv->rp = NULL;
+ } else {
+ if (priv->res[0] != '\0')
+ return 0;
+
+ strscpy(priv->res, data);
+
+ priv->rp = revocable_provider_alloc(&priv->res);
+ if (!priv->rp)
+ return -ENOMEM;
+
+ priv->dentry = debugfs_create_file("consumer", 0400,
+ debugfs_dir, priv->rp,
+ &revocable_test_consumer_fops);
+ if (!priv->dentry) {
+ revocable_provider_revoke(priv->rp);
+ return -ENOMEM;
+ }
+ }
+
+ return copied;
+}
+
+static const struct file_operations revocable_test_provider_fops = {
+ .open = revocable_test_provider_open,
+ .release = revocable_test_provider_release,
+ .write = revocable_test_provider_write,
+};
+
+static int __init revocable_test_init(void)
+{
+ debugfs_dir = debugfs_create_dir("revocable_test", NULL);
+ if (!debugfs_dir)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("provider", 0200, debugfs_dir, NULL,
+ &revocable_test_provider_fops)) {
+ debugfs_remove_recursive(debugfs_dir);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void __exit revocable_test_exit(void)
+{
+ debugfs_remove_recursive(debugfs_dir);
+}
+
+module_init(revocable_test_init);
+module_exit(revocable_test_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tzung-Bi Shih <tzungbi@kernel.org>");
+MODULE_DESCRIPTION("Revocable Kselftest");
--
2.52.0.457.g6b5491de43-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v7 0/3] drivers/base: Introduce revocable
2026-01-16 8:02 [PATCH v7 0/3] drivers/base: Introduce revocable Tzung-Bi Shih
` (2 preceding siblings ...)
2026-01-16 8:02 ` [PATCH v7 3/3] selftests: revocable: Add kselftest cases Tzung-Bi Shih
@ 2026-01-16 15:17 ` Greg Kroah-Hartman
2026-01-16 16:04 ` Laurent Pinchart
3 siblings, 1 reply; 18+ messages in thread
From: Greg Kroah-Hartman @ 2026-01-16 15:17 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Benson Leung, Rafael J . Wysocki, Danilo Krummrich,
Bartosz Golaszewski, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Laurent Pinchart, Wolfram Sang, Simona Vetter, Dan Williams,
Jason Gunthorpe, linux-gpio
On Fri, Jan 16, 2026 at 08:02:32AM +0000, Tzung-Bi Shih wrote:
> The series is separated from [1] to show the independency and compare
> potential use cases easier. This is the revocable core part. Use cases
> are in other series.
>
> The 1st patch introduces the revocable which is an implementation of ideas
> from the talk [2].
>
> The 2nd and 3rd patches add test cases for revocable in Kunit and selftest.
>
> [1] https://lore.kernel.org/chrome-platform/20251016054204.1523139-1-tzungbi@kernel.org
> [2] https://lpc.events/event/17/contributions/1627/
>
> v7:
> - Rebase onto next-20260115.
Thanks for sticking with this, I've queued this up in the driver-core
testing branch for now, thanks!
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v7 0/3] drivers/base: Introduce revocable
2026-01-16 15:17 ` [PATCH v7 0/3] drivers/base: Introduce revocable Greg Kroah-Hartman
@ 2026-01-16 16:04 ` Laurent Pinchart
2026-01-16 16:16 ` Danilo Krummrich
0 siblings, 1 reply; 18+ messages in thread
From: Laurent Pinchart @ 2026-01-16 16:04 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Tzung-Bi Shih, Benson Leung, Rafael J . Wysocki, Danilo Krummrich,
Bartosz Golaszewski, Linus Walleij, Jonathan Corbet, Shuah Khan,
linux-doc, linux-kernel, chrome-platform, linux-kselftest,
Wolfram Sang, Simona Vetter, Dan Williams, Jason Gunthorpe,
linux-gpio
On Fri, Jan 16, 2026 at 04:17:29PM +0100, Greg KH wrote:
> On Fri, Jan 16, 2026 at 08:02:32AM +0000, Tzung-Bi Shih wrote:
> > The series is separated from [1] to show the independency and compare
> > potential use cases easier. This is the revocable core part. Use cases
> > are in other series.
> >
> > The 1st patch introduces the revocable which is an implementation of ideas
> > from the talk [2].
> >
> > The 2nd and 3rd patches add test cases for revocable in Kunit and selftest.
> >
> > [1] https://lore.kernel.org/chrome-platform/20251016054204.1523139-1-tzungbi@kernel.org
> > [2] https://lpc.events/event/17/contributions/1627/
> >
> > v7:
> > - Rebase onto next-20260115.
>
> Thanks for sticking with this, I've queued this up in the driver-core
> testing branch for now, thanks!
I'd like to see this being successfully used before we merge it. There's
now one patch series for the GPIO subsystem, which I think is a very
good test case. Bartosz is already reviewing it, and there's a
performance issue that needs to be investigated and addressed.
As discussed during LPC, the UAF issue in the CrOS EC that this series
was initially supposed to fix is best solved with reference counting and
refactoring of the CrOS EC driver framework. The revocable mechanism
isn't the right solution for races between device removal and userspace
access.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 0/3] drivers/base: Introduce revocable
2026-01-16 16:04 ` Laurent Pinchart
@ 2026-01-16 16:16 ` Danilo Krummrich
2026-01-16 16:52 ` Jason Gunthorpe
0 siblings, 1 reply; 18+ messages in thread
From: Danilo Krummrich @ 2026-01-16 16:16 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Greg Kroah-Hartman, Tzung-Bi Shih, Benson Leung,
Rafael J . Wysocki, Bartosz Golaszewski, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, Jason Gunthorpe, linux-gpio
On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> The revocable mechanism isn't the right solution for races between device
> removal and userspace access.
I think you have to differenciate, as it depends on the resource:
If the resource is a device resource (e.g. MMIO resource regions) that must not
be held by the driver after its bound device has been unbound, you have to
revoke the resource from the driver, i.e. you can't just fix it with a reference
count.
Effectively, that's what devres does, it releases the resource when the device
is unbound. Revocable takes care of avoiding a UAF of a subsequent access.
We subsystems that invented subsystem specific implementations for a revocable
mechanism for exactly those cases.
For instance, there is drm_dev_enter() drm_dev_exit() and drm_dev_unplug() which
ultimately does the same things as DRM specific implementation.
If the resource is not a device resource, then revocable is clearly not the
correct solution however.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 0/3] drivers/base: Introduce revocable
2026-01-16 16:16 ` Danilo Krummrich
@ 2026-01-16 16:52 ` Jason Gunthorpe
2026-01-16 17:00 ` Danilo Krummrich
0 siblings, 1 reply; 18+ messages in thread
From: Jason Gunthorpe @ 2026-01-16 16:52 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Laurent Pinchart, Greg Kroah-Hartman, Tzung-Bi Shih, Benson Leung,
Rafael J . Wysocki, Bartosz Golaszewski, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, linux-gpio
On Fri, Jan 16, 2026 at 05:16:58PM +0100, Danilo Krummrich wrote:
> On Fri Jan 16, 2026 at 5:04 PM CET, Laurent Pinchart wrote:
> > The revocable mechanism isn't the right solution for races between device
> > removal and userspace access.
>
> I think you have to differenciate, as it depends on the resource:
>
> If the resource is a device resource (e.g. MMIO resource regions) that must not
> be held by the driver after its bound device has been unbound, you have to
> revoke the resource from the driver, i.e. you can't just fix it with a reference
> count.
The C code doesn't really work like that, it works on sync teardown
flows. If you want to write correct C code you need to think about all
the concurrency the driver has and ensure that removal undoes it
I think Laurent is referring to the lack of a sync unregister for
fops.
In this series patches 13-16 are all fops related callbacks.
#17 kind of looks like a cancel_work_sync() is missing (ie what is
preventing this work func from running after the module unloads?)
#19 is all sysfs stuff, and sysfs already has a sync unregister, so
maybe these SRCU's can never fail?
Jason
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v7 0/3] drivers/base: Introduce revocable
2026-01-16 16:52 ` Jason Gunthorpe
@ 2026-01-16 17:00 ` Danilo Krummrich
0 siblings, 0 replies; 18+ messages in thread
From: Danilo Krummrich @ 2026-01-16 17:00 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Laurent Pinchart, Greg Kroah-Hartman, Tzung-Bi Shih, Benson Leung,
Rafael J . Wysocki, Bartosz Golaszewski, Linus Walleij,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
chrome-platform, linux-kselftest, Wolfram Sang, Simona Vetter,
Dan Williams, linux-gpio
On Fri Jan 16, 2026 at 5:52 PM CET, Jason Gunthorpe wrote:
> The C code doesn't really work like that, it works on sync teardown
> flows. If you want to write correct C code you need to think about all
> the concurrency the driver has and ensure that removal undoes it
Again, it depends: Sometimes a synchronized teardown is not possible. Iff a
synchronized teardown is not possible by design, this is where revocable is
useful instead.
However, a synchronized teardown should of course always be preferred.
And just to clarify, since you said "the C code": The Rust code follows exactly
the the same principle, prefer synchronized teardown whenever possible.
(The only difference with Rust is that we can always guard device resources and
iff synchronized teardown is ensured through the type system the guard becomes
zero-cost for accesses.)
^ permalink raw reply [flat|nested] 18+ messages in thread