* [PATCH v4 0/2] Support BPF traversal of wakeup sources
@ 2026-05-11 17:45 Samuel Wu
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Samuel Wu @ 2026-05-11 17:45 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown, Pavel Machek, Greg Kroah-Hartman,
Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Song Liu, Yonghong Song, Jiri Olsa,
Shuah Khan
Cc: Samuel Wu, kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest
This patchset adds requisite kfuncs for BPF programs to safely traverse
wakeup_sources, and puts a config flag around the sysfs interface.
Currently, a traversal of wakeup sources require going through
/sys/class/wakeup/* or /d/wakeup_sources/*. The repeated syscalls to query
sysfs is inefficient, as there can be hundreds of wakeup_sources, with each
wakeup source also having multiple attributes. debugfs is unstable and
insecure.
Adding kfuncs to lock/unlock wakeup sources allows BPF program to safely
traverse the wakeup sources list, and a kfunc to get head of wakeup
sources list is needed to start traversing the list.
On a quiescent Pixel 6 traversing 150 wakeup_sources, I am seeing ~34x
speedup (sampled 75 times in table below). For a device under load, the
speedup is greater.
+-------+----+----------+----------+
| | n | AVG (ms) | STD (ms) |
+-------+----+----------+----------+
| sysfs | 75 | 44.9 | 12.6 |
+-------+----+----------+----------+
| BPF | 75 | 1.3 | 0.7 |
+-------+----+----------+----------+
The initial attempts for BPF traversal of wakeup_sources was with BPF
iterators [1]. However, BPF already allows for traversing of a simple list
with bpf_for(), and this current patchset has the added benefit of being
~2-3x more performant than BPF iterators.
[1]: https://lore.kernel.org/all/20260225210820.177674-1-wusamuel@google.com/
Changes in v4:
- Removed `.owner = THIS_MODULE` for btf_kfunc_id_set per Greg
- Add a graceful exit in selftest if bpf_wakeup_sources_get_head() is not
present due to kernel configs without CONFIG_PM_SLEEP (e.g. s390)
- Relaxed substr match in wakeup_source_unlock_null() selftest
- v3 link: https://lore.kernel.org/all/20260331153413.2469218-1-wusamuel@google.com/
Changes in v3:
- Changed return type of bpf_wakeup_sources_get_head() to `void *` per Alexei
- Added failure test for direct dereference of wakeup source head
- Use bpf_core_cast() instead of macros in BPF program per Kumar
- v2 link: https://lore.kernel.org/all/20260326112521.2827500-1-wusamuel@google.com/
Changes in v2:
- Dropped CONFIG_PM_WAKEUP_STATS_SYSFS patch for future patchset
- Added declarations for kfuncs to .h to fix sparse and checkpatch warnings
- Added kfunc to get address of wakeup_source's head
- Added example bpf prog selftest for traversal of wakeup sources per Kumar
- Added *_fail.c selftest per Kumar
- More concise commit message in patch 1/2
- v1 link: https://lore.kernel.org/all/20260320160055.4114055-1-wusamuel@google.com/
Samuel Wu (2):
PM: wakeup: Add kfuncs to traverse over wakeup_sources
selftests/bpf: Add tests for wakeup_sources kfuncs
drivers/base/power/power.h | 7 ++
drivers/base/power/wakeup.c | 71 ++++++++++-
tools/testing/selftests/bpf/config | 3 +-
.../selftests/bpf/prog_tests/wakeup_source.c | 118 ++++++++++++++++++
.../selftests/bpf/progs/test_wakeup_source.c | 92 ++++++++++++++
.../selftests/bpf/progs/wakeup_source.h | 22 ++++
.../selftests/bpf/progs/wakeup_source_fail.c | 76 +++++++++++
7 files changed, 386 insertions(+), 3 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/test_wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source.h
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source_fail.c
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources
2026-05-11 17:45 [PATCH v4 0/2] Support BPF traversal of wakeup sources Samuel Wu
@ 2026-05-11 17:45 ` Samuel Wu
2026-05-11 18:27 ` bot+bpf-ci
2026-05-12 23:36 ` sashiko-bot
2026-05-11 17:45 ` [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs Samuel Wu
2026-05-11 20:44 ` [PATCH v4 0/2] Support BPF traversal of wakeup sources Kumar Kartikeya Dwivedi
2 siblings, 2 replies; 9+ messages in thread
From: Samuel Wu @ 2026-05-11 17:45 UTC (permalink / raw)
To: Rafael J. Wysocki, Pavel Machek, Len Brown, Greg Kroah-Hartman,
Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Song Liu, Yonghong Song, Jiri Olsa,
Shuah Khan
Cc: Samuel Wu, kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest
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>
---
drivers/base/power/power.h | 7 ++++
drivers/base/power/wakeup.c | 71 +++++++++++++++++++++++++++++++++++--
2 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 922ed457db19..8823aceeac8b 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -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
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index b8e48a023bf0..80b497de2deb 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -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);
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs
2026-05-11 17:45 [PATCH v4 0/2] Support BPF traversal of wakeup sources Samuel Wu
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
@ 2026-05-11 17:45 ` Samuel Wu
2026-05-12 23:58 ` sashiko-bot
2026-05-11 20:44 ` [PATCH v4 0/2] Support BPF traversal of wakeup sources Kumar Kartikeya Dwivedi
2 siblings, 1 reply; 9+ messages in thread
From: Samuel Wu @ 2026-05-11 17:45 UTC (permalink / raw)
To: Rafael J. Wysocki, Pavel Machek, Len Brown, Greg Kroah-Hartman,
Danilo Krummrich, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi, Song Liu, Yonghong Song, Jiri Olsa,
Shuah Khan
Cc: Samuel Wu, kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest
Introduce a set of BPF selftests to verify the safety and functionality
of wakeup_source kfuncs.
The suite includes:
1. A functional test (test_wakeup_source.c) that iterates over the
global wakeup_sources list. It uses CO-RE to read timing statistics
and validates them in user-space via the BPF ring buffer.
2. A negative test suite (wakeup_source_fail.c) ensuring the BPF
verifier correctly enforces reference tracking and type safety.
3. Enable CONFIG_PM_WAKELOCKS in the test config, allowing creation of
wakeup sources via /sys/power/wake_lock.
A shared header (wakeup_source.h) is introduced to ensure consistent
memory layout for the Ring Buffer data between BPF and user-space.
Signed-off-by: Samuel Wu <wusamuel@google.com>
---
tools/testing/selftests/bpf/config | 3 +-
.../selftests/bpf/prog_tests/wakeup_source.c | 118 ++++++++++++++++++
.../selftests/bpf/progs/test_wakeup_source.c | 92 ++++++++++++++
.../selftests/bpf/progs/wakeup_source.h | 22 ++++
.../selftests/bpf/progs/wakeup_source_fail.c | 76 +++++++++++
5 files changed, 310 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/test_wakeup_source.c
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source.h
create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source_fail.c
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index 24855381290d..bac60b444551 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -130,4 +130,5 @@ CONFIG_INFINIBAND=y
CONFIG_SMC=y
CONFIG_SMC_HS_CTRL_BPF=y
CONFIG_DIBS=y
-CONFIG_DIBS_LO=y
\ No newline at end of file
+CONFIG_DIBS_LO=y
+CONFIG_PM_WAKELOCKS=y
diff --git a/tools/testing/selftests/bpf/prog_tests/wakeup_source.c b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
new file mode 100644
index 000000000000..ebfdc03271b9
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include <fcntl.h>
+#include "test_wakeup_source.skel.h"
+#include "wakeup_source_fail.skel.h"
+#include "progs/wakeup_source.h"
+
+static int lock_ws(const char *name)
+{
+ int fd;
+ ssize_t bytes;
+
+ fd = open("/sys/power/wake_lock", O_WRONLY);
+ if (!ASSERT_OK_FD(fd, "open /sys/power/wake_lock"))
+ return -1;
+
+ bytes = write(fd, name, strlen(name));
+ close(fd);
+ if (!ASSERT_EQ(bytes, strlen(name), "write to wake_lock"))
+ return -1;
+
+ return 0;
+}
+
+static void unlock_ws(const char *name)
+{
+ int fd;
+
+ fd = open("/sys/power/wake_unlock", O_WRONLY);
+ if (fd < 0)
+ return;
+
+ write(fd, name, strlen(name));
+ close(fd);
+}
+
+struct rb_ctx {
+ const char *name;
+ bool found;
+ long long active_time_ns;
+ long long total_time_ns;
+};
+
+static int process_sample(void *ctx, void *data, size_t len)
+{
+ struct rb_ctx *rb_ctx = ctx;
+ struct wakeup_event_t *e = data;
+
+ if (strcmp(e->name, rb_ctx->name) == 0) {
+ rb_ctx->found = true;
+ rb_ctx->active_time_ns = e->active_time_ns;
+ rb_ctx->total_time_ns = e->total_time_ns;
+ }
+ return 0;
+}
+
+void test_wakeup_source(void)
+{
+ struct btf *btf;
+ int id;
+
+ btf = btf__load_vmlinux_btf();
+ if (!ASSERT_OK_PTR(btf, "btf_vmlinux"))
+ return;
+
+ id = btf__find_by_name_kind(btf, "bpf_wakeup_sources_get_head", BTF_KIND_FUNC);
+ btf__free(btf);
+
+ if (id < 0) {
+ printf("%s:SKIP:bpf_wakeup_sources_get_head kfunc not found in BTF\n", __func__);
+ test__skip();
+ return;
+ }
+
+ if (test__start_subtest("iterate_and_verify_times")) {
+ struct test_wakeup_source *skel;
+ struct ring_buffer *rb = NULL;
+ struct rb_ctx rb_ctx = {
+ .name = "bpf_selftest_ws_times",
+ .found = false,
+ };
+ int err;
+
+ skel = test_wakeup_source__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ return;
+
+ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), process_sample, &rb_ctx, NULL);
+ if (!ASSERT_OK_PTR(rb, "ring_buffer__new"))
+ goto destroy;
+
+ /* Create a temporary wakeup source */
+ if (!ASSERT_OK(lock_ws(rb_ctx.name), "lock_ws"))
+ goto unlock;
+
+ err = bpf_prog_test_run_opts(bpf_program__fd(
+ skel->progs.iterate_wakeupsources), NULL);
+ ASSERT_OK(err, "bpf_prog_test_run");
+
+ ring_buffer__consume(rb);
+
+ ASSERT_TRUE(rb_ctx.found, "found_test_ws_in_rb");
+ ASSERT_GT(rb_ctx.active_time_ns, 0, "active_time_gt_0");
+ ASSERT_GT(rb_ctx.total_time_ns, 0, "total_time_gt_0");
+
+unlock:
+ unlock_ws(rb_ctx.name);
+destroy:
+ if (rb)
+ ring_buffer__free(rb);
+ test_wakeup_source__destroy(skel);
+ }
+
+ RUN_TESTS(wakeup_source_fail);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_wakeup_source.c b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
new file mode 100644
index 000000000000..fd2fb6aebd82
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_experimental.h"
+#include "bpf_misc.h"
+#include "wakeup_source.h"
+
+#define MAX_LOOP_ITER 1000
+#define RB_SIZE (16384 * 4)
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, RB_SIZE);
+} rb SEC(".maps");
+
+struct bpf_ws_lock;
+struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
+void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
+void *bpf_wakeup_sources_get_head(void) __ksym;
+
+SEC("syscall")
+__success __retval(0)
+int iterate_wakeupsources(void *ctx)
+{
+ struct list_head *head = bpf_wakeup_sources_get_head();
+ struct list_head *pos = head;
+ struct bpf_ws_lock *lock;
+ int i;
+
+ lock = bpf_wakeup_sources_read_lock();
+ if (!lock)
+ return 0;
+
+ bpf_for(i, 0, MAX_LOOP_ITER) {
+ if (bpf_core_read(&pos, sizeof(pos), &pos->next) || !pos || pos == head)
+ break;
+
+ struct wakeup_event_t *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+
+ if (!e)
+ break;
+
+ struct wakeup_source *ws = bpf_core_cast(
+ (void *)pos - bpf_core_field_offset(struct wakeup_source, entry),
+ struct wakeup_source);
+ s64 active_time = 0;
+ bool active = BPF_CORE_READ_BITFIELD(ws, active);
+ bool autosleep_enable = BPF_CORE_READ_BITFIELD(ws, autosleep_enabled);
+ s64 last_time = ws->last_time;
+ s64 max_time = ws->max_time;
+ s64 prevent_sleep_time = ws->prevent_sleep_time;
+ s64 total_time = ws->total_time;
+
+ if (active) {
+ s64 curr_time = bpf_ktime_get_ns();
+ s64 prevent_time = ws->start_prevent_time;
+
+ if (curr_time > last_time)
+ active_time = curr_time - last_time;
+
+ total_time += active_time;
+ if (active_time > max_time)
+ max_time = active_time;
+ if (autosleep_enable && curr_time > prevent_time)
+ prevent_sleep_time += curr_time - prevent_time;
+ }
+
+ e->active_count = ws->active_count;
+ e->active_time_ns = active_time;
+ e->event_count = ws->event_count;
+ e->expire_count = ws->expire_count;
+ e->last_time_ns = last_time;
+ e->max_time_ns = max_time;
+ e->prevent_sleep_time_ns = prevent_sleep_time;
+ e->total_time_ns = total_time;
+ e->wakeup_count = ws->wakeup_count;
+
+ if (bpf_probe_read_kernel_str(
+ e->name, WAKEUP_NAME_LEN, ws->name) < 0)
+ e->name[0] = '\0';
+
+ bpf_ringbuf_submit(e, 0);
+ }
+
+ bpf_wakeup_sources_read_unlock(lock);
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source.h b/tools/testing/selftests/bpf/progs/wakeup_source.h
new file mode 100644
index 000000000000..cd74de92c82f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/wakeup_source.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2026 Google LLC */
+
+#ifndef __WAKEUP_SOURCE_H__
+#define __WAKEUP_SOURCE_H__
+
+#define WAKEUP_NAME_LEN 128
+
+struct wakeup_event_t {
+ unsigned long active_count;
+ long long active_time_ns;
+ unsigned long event_count;
+ unsigned long expire_count;
+ long long last_time_ns;
+ long long max_time_ns;
+ long long prevent_sleep_time_ns;
+ long long total_time_ns;
+ unsigned long wakeup_count;
+ char name[WAKEUP_NAME_LEN];
+};
+
+#endif /* __WAKEUP_SOURCE_H__ */
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
new file mode 100644
index 000000000000..b8bbb61d4d4e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+struct bpf_ws_lock;
+
+struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
+void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
+void *bpf_wakeup_sources_get_head(void) __ksym;
+
+SEC("syscall")
+__failure __msg("BPF_EXIT instruction in main prog would lead to reference leak")
+int wakeup_source_lock_no_unlock(void *ctx)
+{
+ struct bpf_ws_lock *lock;
+
+ lock = bpf_wakeup_sources_read_lock();
+ if (!lock)
+ return 0;
+
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("access beyond struct")
+int wakeup_source_access_lock_fields(void *ctx)
+{
+ struct bpf_ws_lock *lock;
+ int val;
+
+ lock = bpf_wakeup_sources_read_lock();
+ if (!lock)
+ return 0;
+
+ val = *(int *)lock;
+
+ bpf_wakeup_sources_read_unlock(lock);
+ return val;
+}
+
+SEC("syscall")
+__failure __msg("type=scalar expected=fp")
+int wakeup_source_unlock_no_lock(void *ctx)
+{
+ struct bpf_ws_lock *lock = (void *)0x1;
+
+ bpf_wakeup_sources_read_unlock(lock);
+
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("Possibly NULL pointer passed to trusted")
+int wakeup_source_unlock_null(void *ctx)
+{
+ bpf_wakeup_sources_read_unlock(NULL);
+
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("R0 invalid mem access 'scalar'")
+int wakeup_source_unsafe_dereference(void *ctx)
+{
+ struct list_head *head = bpf_wakeup_sources_get_head();
+
+ if (head->next)
+ return 1;
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
@ 2026-05-11 18:27 ` bot+bpf-ci
2026-05-11 20:06 ` Samuel Wu
2026-05-12 23:36 ` sashiko-bot
1 sibling, 1 reply; 9+ messages in thread
From: bot+bpf-ci @ 2026-05-11 18:27 UTC (permalink / raw)
To: wusamuel, rafael, pavel, lenb, gregkh, dakr, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, shuah
Cc: wusamuel, kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3181 bytes --]
> diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
> index 922ed457db19..8823aceeac8b 100644
> --- a/drivers/base/power/power.h
> +++ b/drivers/base/power/power.h
[ ... ]
> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> index b8e48a023bf0..80b497de2deb 100644
> --- a/drivers/base/power/wakeup.c
> +++ b/drivers/base/power/wakeup.c
[ ... ]
> @@ -1168,11 +1168,78 @@ static const struct file_operations wakeup_sources_stats_fops = {
> .release = seq_release_private,
> };
>
> +#ifdef CONFIG_BPF_SYSCALL
> +#include <linux/btf.h>
> +
> +__bpf_kfunc_start_defs();
[ ... ]
> +/**
> + * 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;
> +}
What stops a BPF program from using bpf_wakeup_sources_get_head() to get
the wakeup_sources pointer and iterating this list without taking the SRCU
lock?
The current API design requires BPF programs to manually call the lock/unlock
functions, but there's no enforcement that they must do so before accessing
the list. A program could call bpf_wakeup_sources_get_head() and directly
iterate the list without proper SRCU protection, which could lead to race
conditions or crashes.
An open-coded iterator would solve this by internalizing the lock:
Iterator new() acquires SRCU lock
Iterator next() returns next wakeup_source
Iterator destroy() releases SRCU lock
This design pattern ensures that the list can never be traversed without
proper locking. Puranjay Mohan raised this concern in v2, but it was not
addressed in v3 or v4.
Reference: https://lore.kernel.org/all/m2y0jeg5sg.fsf@kernel.org/
> +
> +__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)
Should bpf_wakeup_sources_read_lock have the KF_RET_NULL flag?
The function returns an SRCU index cast to a pointer (incremented by 1).
If wakeup_sources_read_lock() were to fail, the result would need to be
checked for null. Without KF_RET_NULL, the BPF verifier won't require
null checks on the returned pointer.
Looking at bpf_wakeup_sources_read_lock():
__bpf_kfunc struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void)
{
return (struct bpf_ws_lock *)(long)(wakeup_sources_read_lock() + 1);
}
If the underlying wakeup_sources_read_lock() could return a value that,
when incremented, equals zero, or if there's any error path, BPF programs
would need to check for NULL before using the lock pointer.
Puranjay Mohan raised this in v2, but it was not addressed in v3 or v4.
Reference: https://lore.kernel.org/all/m2y0jeg5sg.fsf@kernel.org/
> +BTF_ID_FLAGS(func, bpf_wakeup_sources_get_head)
> +BTF_KFUNCS_END(wakeup_source_kfunc_ids)
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/25688185901
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources
2026-05-11 18:27 ` bot+bpf-ci
@ 2026-05-11 20:06 ` Samuel Wu
0 siblings, 0 replies; 9+ messages in thread
From: Samuel Wu @ 2026-05-11 20:06 UTC (permalink / raw)
To: bot+bpf-ci
Cc: rafael, pavel, lenb, gregkh, dakr, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, shuah,
kernel-team, linux-kernel, linux-pm, driver-core, bpf,
linux-kselftest, martin.lau, clm, ihor.solodrai
On Mon, May 11, 2026 at 11:27 AM <bot+bpf-ci@kernel.org> wrote:
[ ... ]
> > +/**
> > + * 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;
> > +}
>
> What stops a BPF program from using bpf_wakeup_sources_get_head() to get
> the wakeup_sources pointer and iterating this list without taking the SRCU
> lock?
>
> The current API design requires BPF programs to manually call the lock/unlock
> functions, but there's no enforcement that they must do so before accessing
> the list. A program could call bpf_wakeup_sources_get_head() and directly
> iterate the list without proper SRCU protection, which could lead to race
> conditions or crashes.
>
> An open-coded iterator would solve this by internalizing the lock:
>
> Iterator new() acquires SRCU lock
> Iterator next() returns next wakeup_source
> Iterator destroy() releases SRCU lock
>
> This design pattern ensures that the list can never be traversed without
> proper locking. Puranjay Mohan raised this concern in v2, but it was not
> addressed in v3 or v4.
>
> Reference: https://lore.kernel.org/all/m2y0jeg5sg.fsf@kernel.org/
This was already discussed; the response is at [1] and the BPF
iterator attempt is at [2]. In short, BPF iterators and their
tradeoffs were explored, but the current form was chosen as the best
option.
[1]: https://lore.kernel.org/all/CAP01T74KNVT2SVRR+XPbFb1vy85W=Jkp4_yD6xvTSg2avQo8AQ@mail.gmail.com/
[2]: https://lore.kernel.org/all/20260225210820.177674-1-wusamuel@google.com/
> > +
> > +__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)
>
> Should bpf_wakeup_sources_read_lock have the KF_RET_NULL flag?
>
> The function returns an SRCU index cast to a pointer (incremented by 1).
> If wakeup_sources_read_lock() were to fail, the result would need to be
> checked for null. Without KF_RET_NULL, the BPF verifier won't require
> null checks on the returned pointer.
>
> Looking at bpf_wakeup_sources_read_lock():
>
> __bpf_kfunc struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void)
> {
> return (struct bpf_ws_lock *)(long)(wakeup_sources_read_lock() + 1);
> }
>
> If the underlying wakeup_sources_read_lock() could return a value that,
> when incremented, equals zero, or if there's any error path, BPF programs
> would need to check for NULL before using the lock pointer.
>
> Puranjay Mohan raised this in v2, but it was not addressed in v3 or v4.
>
> Reference: https://lore.kernel.org/all/m2y0jeg5sg.fsf@kernel.org/
>
This is fine, the underlying srcu index return value is guaranteed to
be non-negative, so the return value of bpf_wakeup_sources_read_lock()
will be positive and not need the NULL check.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 0/2] Support BPF traversal of wakeup sources
2026-05-11 17:45 [PATCH v4 0/2] Support BPF traversal of wakeup sources Samuel Wu
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
2026-05-11 17:45 ` [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs Samuel Wu
@ 2026-05-11 20:44 ` Kumar Kartikeya Dwivedi
2026-05-13 0:51 ` Alexei Starovoitov
2 siblings, 1 reply; 9+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-05-11 20:44 UTC (permalink / raw)
To: Samuel Wu
Cc: Rafael J. Wysocki, Len Brown, Pavel Machek, Greg Kroah-Hartman,
Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, Jiri Olsa, Shuah Khan, kernel-team, linux-kernel,
linux-pm, driver-core, bpf, linux-kselftest
On Mon, 11 May 2026 at 19:50, Samuel Wu <wusamuel@google.com> wrote:
>
> This patchset adds requisite kfuncs for BPF programs to safely traverse
> wakeup_sources, and puts a config flag around the sysfs interface.
>
> Currently, a traversal of wakeup sources require going through
> /sys/class/wakeup/* or /d/wakeup_sources/*. The repeated syscalls to query
> sysfs is inefficient, as there can be hundreds of wakeup_sources, with each
> wakeup source also having multiple attributes. debugfs is unstable and
> insecure.
>
> Adding kfuncs to lock/unlock wakeup sources allows BPF program to safely
> traverse the wakeup sources list, and a kfunc to get head of wakeup
> sources list is needed to start traversing the list.
>
> On a quiescent Pixel 6 traversing 150 wakeup_sources, I am seeing ~34x
> speedup (sampled 75 times in table below). For a device under load, the
> speedup is greater.
> +-------+----+----------+----------+
> | | n | AVG (ms) | STD (ms) |
> +-------+----+----------+----------+
> | sysfs | 75 | 44.9 | 12.6 |
> +-------+----+----------+----------+
> | BPF | 75 | 1.3 | 0.7 |
> +-------+----+----------+----------+
>
> The initial attempts for BPF traversal of wakeup_sources was with BPF
> iterators [1]. However, BPF already allows for traversing of a simple list
> with bpf_for(), and this current patchset has the added benefit of being
> ~2-3x more performant than BPF iterators.
This looks good to me, you can add for the set:
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>
> [...]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
2026-05-11 18:27 ` bot+bpf-ci
@ 2026-05-12 23:36 ` sashiko-bot
1 sibling, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-05-12 23:36 UTC (permalink / raw)
To: Samuel Wu; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The kfunc `bpf_wakeup_sources_get_head()` returns `void *` instead of `struct list_head *`, which the BPF verifier downgrades to an unknown scalar, destroying BTF type tracking.
- [High] The verifier cannot enforce that the SRCU lock is actually held during list traversal, allowing trivial use-after-free vulnerabilities and violating BPF architectural patterns.
- [Medium] Wrapping the integer SRCU index in a fake `struct bpf_ws_lock *` pointer circumvents verifier safeguards and introduces invalid trusted pointers.
--
commit 6dcbdc1d5aa4a12914c7281f95390e19498129d0
Author: Samuel Wu <wusamuel@google.com>
PM: wakeup: Add kfuncs to traverse over wakeup_sources
This patch introduces BPF kfuncs to allow in-kernel traversal of the
wakeup_sources list. It adds functions to get the list head and manage
the SRCU lock, using an opaque pointer to hold the SRCU index for the
verifier.
> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> index b8e48a023bf0c..80b497de2deb0 100644
> --- a/drivers/base/power/wakeup.c
> +++ b/drivers/base/power/wakeup.c
[ ... ]
> +/**
> + * 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);
> +}
The verifier treats the returned struct bpf_ws_lock * as a trusted
PTR_TO_BTF_ID. By casting the integer index returned by srcu_read_lock()
into a pointer, we end up fabricating invalid kernel pointers pointing to
near-zero addresses like 0x1 or 0x2.
Does this circumvent verifier safeguards by introducing invalid trusted
pointers?
[ ... ]
> +/**
> + * 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);
> +}
The patch uses KF_ACQUIRE and KF_RELEASE to ensure the lock is eventually
unlocked. However, does the BPF verifier have a mechanism to tie this
custom lock state to the list pointer returned by
bpf_wakeup_sources_get_head()?
It seems a BPF program could fetch the list head and traverse it without
ever acquiring the lock, or continue accessing elements after releasing
the lock, resulting in a use-after-free.
Would it be safer to implement a dedicated BPF iterator
(bpf_iter_wakeup_source) that internally manages the SRCU lock and safely
yields elements, rather than exposing the raw lock and list head?
> +/**
> + * 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;
> +}
Since this kfunc returns void * instead of struct list_head *, the BPF
verifier evaluates it as returning an unknown scalar.
Because the return value becomes a scalar, BPF programs cannot natively
or safely dereference the list head. They would be forced to cast the
scalar back into a pointer using bpf_rdonly_cast(), which the verifier
marks as PTR_UNTRUSTED.
Since untrusted pointers cause field dereferences to be rewritten into
slow PROBE_MEM operations, does this defeat the goal of high-performance
in-kernel traversal?
Could the return type be changed to struct list_head * to be natively
tracked as a PTR_TO_BTF_ID?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260511174559.659782-1-wusamuel@google.com?part=1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs
2026-05-11 17:45 ` [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs Samuel Wu
@ 2026-05-12 23:58 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-05-12 23:58 UTC (permalink / raw)
To: Samuel Wu; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] BPF verifier rejection due to direct memory access on scalar register
- [High] Incorrect expected failure messages will cause negative tests to fail
- [Medium] Early return upon skeleton load failure skips the negative test suite
- [Medium] Architecture portability issue due to layout mismatch in shared structure
- [Medium] Uninitialized memory leak in ring buffer
--
commit 4b176327f91f44d20f491d6779d845f94357d717
Author: Samuel Wu <wusamuel@google.com>
selftests/bpf: Add tests for wakeup_sources kfuncs
This commit introduces a set of BPF selftests to verify the safety and
functionality of wakeup_source kfuncs. It includes a functional test iterating
over the global wakeup_sources list and a negative test suite for verifier
checks.
> diff --git a/tools/testing/selftests/bpf/prog_tests/wakeup_source.c b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
> index 0000000000000..ebfdc03271b9a 100644
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
[ ... ]
> @@ -73,46 +73,46 @@ void test_wakeup_source(void)
> return;
> }
>
> if (test__start_subtest("iterate_and_verify_times")) {
> struct test_wakeup_source *skel;
> struct ring_buffer *rb = NULL;
> struct rb_ctx rb_ctx = {
> .name = "bpf_selftest_ws_times",
> .found = false,
> };
> int err;
>
> skel = test_wakeup_source__open_and_load();
> if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
> return;
If test_wakeup_source__open_and_load() fails, the code executes an early
return. Does this silently skip running the negative test suite via
RUN_TESTS(wakeup_source_fail) at the end of the function?
[ ... ]
> diff --git a/tools/testing/selftests/bpf/progs/test_wakeup_source.c b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
> index 0000000000000..fd2fb6aebd82b 100644
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
[ ... ]
> @@ -32,61 +32,61 @@ int iterate_wakeupsources(void *ctx)
>
> bpf_for(i, 0, MAX_LOOP_ITER) {
> if (bpf_core_read(&pos, sizeof(pos), &pos->next) || !pos || pos == head)
> break;
>
> struct wakeup_event_t *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
>
> if (!e)
> break;
>
> struct wakeup_source *ws = bpf_core_cast(
> (void *)pos - bpf_core_field_offset(struct wakeup_source, entry),
> struct wakeup_source);
> s64 active_time = 0;
> bool active = BPF_CORE_READ_BITFIELD(ws, active);
> bool autosleep_enable = BPF_CORE_READ_BITFIELD(ws, autosleep_enabled);
> s64 last_time = ws->last_time;
Since pos is read from memory via bpf_core_read(), the verifier tracks it as
a SCALAR_VALUE. Because ws is derived from pos via pointer arithmetic,
will the BPF verifier reject direct memory dereferences like ws->last_time
with an "invalid mem access 'scalar'" error?
Should these accesses be wrapped in BPF_CORE_READ() instead?
[ ... ]
> @@ -76,17 +76,17 @@ int iterate_wakeupsources(void *ctx)
> e->last_time_ns = last_time;
> e->max_time_ns = max_time;
> e->prevent_sleep_time_ns = prevent_sleep_time;
> e->total_time_ns = total_time;
> e->wakeup_count = ws->wakeup_count;
>
> if (bpf_probe_read_kernel_str(
> e->name, WAKEUP_NAME_LEN, ws->name) < 0)
> e->name[0] = '\0';
>
> bpf_ringbuf_submit(e, 0);
bpf_ringbuf_reserve() does not zero-initialize the memory, and
bpf_probe_read_kernel_str() does not pad the remainder of the buffer with
zeroes if the string is shorter than WAKEUP_NAME_LEN.
Could this expose uninitialized kernel memory from previous ring buffer uses
to user-space through the remainder of e->name?
[ ... ]
> diff --git a/tools/testing/selftests/bpf/progs/wakeup_source.h b/tools/testing/selftests/bpf/progs/wakeup_source.h
> index 0000000000000..cd74de92c82f8 100644
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/wakeup_source.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright 2026 Google LLC */
> +
> +#ifndef __WAKEUP_SOURCE_H__
> +#define __WAKEUP_SOURCE_H__
> +
> +#define WAKEUP_NAME_LEN 128
> +
> +struct wakeup_event_t {
> + unsigned long active_count;
> + long long active_time_ns;
Because this structure is shared between BPF and user-space, could using
architecture-dependent types like unsigned long cause a size and layout
mismatch?
BPF programs are compiled for a 64-bit target where unsigned long is 8 bytes,
but the user-space selftest may be compiled on a 32-bit host (like ARM32) where
it is 4 bytes.
Should explicitly sized types like __u64 be used for shared structures?
[ ... ]
> diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
> index 0000000000000..b8bbb61d4d4e0 100644
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
[ ... ]
> @@ -44,19 +44,19 @@ int wakeup_source_access_lock_fields(void *ctx)
> val = *(int *)lock;
>
> bpf_wakeup_sources_read_unlock(lock);
> return val;
> }
>
> SEC("syscall")
> __failure __msg("type=scalar expected=fp")
> int wakeup_source_unlock_no_lock(void *ctx)
Is this the correct expected failure message for a kfunc argument?
The check_kfunc_args() function typically emits messages like "arg#0 pointer
type mismatch" when an incorrect type is passed, whereas "expected=fp" is
usually emitted by check_func_arg() for standard helpers.
Will this test fail because the verifier output doesn't match the expectation?
> {
> struct bpf_ws_lock *lock = (void *)0x1;
>
> bpf_wakeup_sources_read_unlock(lock);
>
> return 0;
> }
[ ... ]
> @@ -60,17 +60,17 @@ int wakeup_source_unlock_null(void *ctx)
> bpf_wakeup_sources_read_unlock(NULL);
>
> return 0;
> }
>
> SEC("syscall")
> __failure __msg("R0 invalid mem access 'scalar'")
> int wakeup_source_unsafe_dereference(void *ctx)
> {
> struct list_head *head = bpf_wakeup_sources_get_head();
>
> if (head->next)
Since bpf_wakeup_sources_get_head() returns a BTF pointer, the verifier tracks
it as PTR_TO_BTF_ID.
Because the verifier allows direct dereferences of trusted pointers (resulting
in PROBE_MEM), will this program successfully load instead of failing with the
"invalid mem access 'scalar'" error?
> return 1;
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260511174559.659782-1-wusamuel@google.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 0/2] Support BPF traversal of wakeup sources
2026-05-11 20:44 ` [PATCH v4 0/2] Support BPF traversal of wakeup sources Kumar Kartikeya Dwivedi
@ 2026-05-13 0:51 ` Alexei Starovoitov
0 siblings, 0 replies; 9+ messages in thread
From: Alexei Starovoitov @ 2026-05-13 0:51 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi
Cc: Samuel Wu, Rafael J. Wysocki, Len Brown, Pavel Machek,
Greg Kroah-Hartman, Danilo Krummrich, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau,
Eduard Zingerman, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan,
Android Kernel Team, LKML, Linux Power Management, driver-core,
bpf, open list:KERNEL SELFTEST FRAMEWORK
On Mon, May 11, 2026 at 1:44 PM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> On Mon, 11 May 2026 at 19:50, Samuel Wu <wusamuel@google.com> wrote:
> >
> > This patchset adds requisite kfuncs for BPF programs to safely traverse
> > wakeup_sources, and puts a config flag around the sysfs interface.
> >
> > Currently, a traversal of wakeup sources require going through
> > /sys/class/wakeup/* or /d/wakeup_sources/*. The repeated syscalls to query
> > sysfs is inefficient, as there can be hundreds of wakeup_sources, with each
> > wakeup source also having multiple attributes. debugfs is unstable and
> > insecure.
> >
> > Adding kfuncs to lock/unlock wakeup sources allows BPF program to safely
> > traverse the wakeup sources list, and a kfunc to get head of wakeup
> > sources list is needed to start traversing the list.
> >
> > On a quiescent Pixel 6 traversing 150 wakeup_sources, I am seeing ~34x
> > speedup (sampled 75 times in table below). For a device under load, the
> > speedup is greater.
> > +-------+----+----------+----------+
> > | | n | AVG (ms) | STD (ms) |
> > +-------+----+----------+----------+
> > | sysfs | 75 | 44.9 | 12.6 |
> > +-------+----+----------+----------+
> > | BPF | 75 | 1.3 | 0.7 |
> > +-------+----+----------+----------+
> >
> > The initial attempts for BPF traversal of wakeup_sources was with BPF
> > iterators [1]. However, BPF already allows for traversing of a simple list
> > with bpf_for(), and this current patchset has the added benefit of being
> > ~2-3x more performant than BPF iterators.
>
> This looks good to me, you can add for the set:
> Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Rafael,
how do you want to route it?
If you ack it we can take it into bpf-next.
I'd think patch 1 shouldn't conflict with other 'wakeup' changes.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-13 0:51 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11 17:45 [PATCH v4 0/2] Support BPF traversal of wakeup sources Samuel Wu
2026-05-11 17:45 ` [PATCH v4 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources Samuel Wu
2026-05-11 18:27 ` bot+bpf-ci
2026-05-11 20:06 ` Samuel Wu
2026-05-12 23:36 ` sashiko-bot
2026-05-11 17:45 ` [PATCH v4 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs Samuel Wu
2026-05-12 23:58 ` sashiko-bot
2026-05-11 20:44 ` [PATCH v4 0/2] Support BPF traversal of wakeup sources Kumar Kartikeya Dwivedi
2026-05-13 0:51 ` Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox