PM: wakeup: Add kfuncs to traverse over wakeup_sources

Iterating through wakeup sources via sysfs or debugfs can be inefficient
or restricted. Introduce BPF kfuncs to allow high-performance and safe
in-kernel traversal of the wakeup_sources list. There is at least a 30x
speedup for walking 150 wakeup sources and all their attributes.

The new kfuncs include:
- bpf_wakeup_sources_get_head() to obtain the list head.
- bpf_wakeup_sources_read_lock/unlock() to manage the SRCU lock.

For verifier safety, the underlying SRCU index is wrapped in an opaque
'struct bpf_ws_lock' pointer. This enables the use of KF_ACQUIRE and
KF_RELEASE flags, allowing the BPF verifier to strictly enforce paired
lock/unlock cycles and prevent resource leaks.

Signed-off-by: Samuel Wu <wusamuel@google.com>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Acked-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20260511174559.659782-2-wusamuel@google.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Samuel Wu 2026-05-11 10:45:56 -07:00 committed by Alexei Starovoitov
parent e651f3ce13
commit 5ff4495544
2 changed files with 76 additions and 2 deletions

View File

@ -168,3 +168,10 @@ static inline void device_pm_init(struct device *dev)
device_pm_sleep_init(dev);
pm_runtime_init(dev);
}
#ifdef CONFIG_BPF_SYSCALL
struct bpf_ws_lock { };
struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void);
void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock);
void *bpf_wakeup_sources_get_head(void);
#endif

View File

@ -1168,11 +1168,78 @@ static const struct file_operations wakeup_sources_stats_fops = {
.release = seq_release_private,
};
static int __init wakeup_sources_debugfs_init(void)
#ifdef CONFIG_BPF_SYSCALL
#include <linux/btf.h>
__bpf_kfunc_start_defs();
/**
* bpf_wakeup_sources_read_lock - Acquire the SRCU lock for wakeup sources
*
* The underlying SRCU lock returns an integer index. However, the BPF verifier
* requires a pointer (PTR_TO_BTF_ID) to strictly track the state of acquired
* resources using KF_ACQUIRE and KF_RELEASE semantics. We use an opaque
* structure pointer (struct bpf_ws_lock *) to satisfy the verifier while
* safely encoding the integer index within the pointer address itself.
*
* Return: An opaque pointer encoding the SRCU lock index + 1 (to avoid NULL).
*/
__bpf_kfunc struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void)
{
return (struct bpf_ws_lock *)(long)(wakeup_sources_read_lock() + 1);
}
/**
* bpf_wakeup_sources_read_unlock - Release the SRCU lock for wakeup sources
* @lock: The opaque pointer returned by bpf_wakeup_sources_read_lock()
*
* The BPF verifier guarantees that @lock is a valid, unreleased pointer from
* the acquire function. We decode the pointer back into the integer SRCU index
* by subtracting 1 and release the lock.
*/
__bpf_kfunc void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock)
{
wakeup_sources_read_unlock((int)(long)lock - 1);
}
/**
* bpf_wakeup_sources_get_head - Get the head of the wakeup sources list
*
* Return: The head of the wakeup sources list.
*/
__bpf_kfunc void *bpf_wakeup_sources_get_head(void)
{
return &wakeup_sources;
}
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(wakeup_source_kfunc_ids)
BTF_ID_FLAGS(func, bpf_wakeup_sources_read_lock, KF_ACQUIRE)
BTF_ID_FLAGS(func, bpf_wakeup_sources_read_unlock, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_wakeup_sources_get_head)
BTF_KFUNCS_END(wakeup_source_kfunc_ids)
static const struct btf_kfunc_id_set wakeup_source_kfunc_set = {
.set = &wakeup_source_kfunc_ids,
};
static void __init wakeup_sources_bpf_init(void)
{
if (register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &wakeup_source_kfunc_set))
pm_pr_dbg("Wakeup: failed to register BTF kfuncs\n");
}
#else
static inline void wakeup_sources_bpf_init(void) {}
#endif /* CONFIG_BPF_SYSCALL */
static int __init wakeup_sources_init(void)
{
debugfs_create_file("wakeup_sources", 0444, NULL, NULL,
&wakeup_sources_stats_fops);
wakeup_sources_bpf_init();
return 0;
}
postcore_initcall(wakeup_sources_debugfs_init);
postcore_initcall(wakeup_sources_init);