From nobody Thu Apr 2 22:06:52 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 270283BF696 for ; Thu, 26 Mar 2026 11:25:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774524337; cv=none; b=tKstSmtW8cVEf4lvV587umO9zSt/8RYXPsDaUvHd0Jz+TjdsVggqdpdNtClGPoZHAydd46cZGEGBMQYfujhhHjI7LJCaJLk9nKEgYZEzSjIjnC7vPZ4P2kUbdkvqcHdZci8UushkTYfXvq9TXTrgleQCt3YO6EeQRzl6jxYhYNU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774524337; c=relaxed/simple; bh=SXKjAxsdGUmKwSrMydXTRmH3TVk5HoSMhyyX86S/7tw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qWck9W5OvQ99+E+w5/VbTz46DqMzZzaFrVv+uS8U1tfV+tc6gjiZZXvys3S1EonZPTr/r1LZ27fDZmWRco3/Eu70VwNZxjw+irL7G6LGgll6Py04VuNSxKdqTYE+Cqr03He3BJr3VJZnIXJ6Ei0BL7yKpyRapovzF0x6lsLPkPA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=I/J7PeCy; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="I/J7PeCy" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-12737f276a2so743490c88.1 for ; Thu, 26 Mar 2026 04:25:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774524334; x=1775129134; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=IJTelMffLYeUWQfdwTRKWfmopMGExMPo1dZE2xtXqZU=; b=I/J7PeCy4nXM2lBumXeUy20uA2cA9YShVMtdIaVUlBSWw2JU3JZvoSf1mHqUKgjapH ukiMS001/JMnztcY9tVFAsjsIUqZjOaiD1YeOGZBTlgreUpWjO9J8C+MMhVgYZNBjRhR vAcGlxb5cHr2py+Hqij1jvL/cvdGqugQ+je/QTWrEUo1s7CaDu1aLYFRg1j0akfDVFIg zrpD43PYiq8fAVNxH6dLu3aLJ5T+oafZwtiUM+dsDtO+V2rcbSXOfmoM2IRIgtXqipbz iN/tMw5GrrU0ke4yAAqfxaq66OwAfVKzVF7oq8283sK0G4tycKANV/49wlKkXLeEx50l Bwzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774524334; x=1775129134; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=IJTelMffLYeUWQfdwTRKWfmopMGExMPo1dZE2xtXqZU=; b=IIUFsgLHuVHTZz84NzN/+S+76+vWINBp2gxvuvmOOltKqNOdCvN9vXucAd8D+wIGfr 9HG4eFIrb2TumaVyw5ZLQ0F5t93sT+sx3OlV+xnExEwK+CmQXOr+LnWB5BIYvLI604ib nJ179j8PXBxZWTkx5rhUoIlL5+VCB7pjdSg13Zd6CxNN+IuSOAnRuakgThoU2GhJo2T+ gUxywX7JbSccRm9XDN1kdxAvluwmxsbvi8bQMhzdvfgaAWMOChvgBBSoYXAxvY6oocMM utP53Owhf01onOhjzkjPc8ivkRUkBVhBaD8jj/1QJjqKbWAxaKApNtbI6Z/7vel+08s2 tjlw== X-Forwarded-Encrypted: i=1; AJvYcCUz/iNTOb6UU4cMxKIFBiYyV98MKb8deIOe+HCzI/Ur7Ypn6LjzHA7nwACHpdYDa+Ltg3V8w2tKUw9/kso=@vger.kernel.org X-Gm-Message-State: AOJu0YxdN/lK+wBgn+qq9ngnhX1w0t7vRuxMzUWjoF/IrHtZOssU9I5I /M5V0whHUTpcUb6GYgt6xOfWXVVm9jwcGEBiniCXIDw3fDEEY6dENVSbuduSE8Ln6vmwl3vgynT rjsXm3v+Bc0bVgA== X-Received: from dlaj3.prod.google.com ([2002:a05:701b:2803:b0:128:cff2:6560]) (user=wusamuel job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6189:b0:128:d4be:7433 with SMTP id a92af1059eb24-12a96f0977emr3562318c88.34.1774524334043; Thu, 26 Mar 2026 04:25:34 -0700 (PDT) Date: Thu, 26 Mar 2026 04:25:19 -0700 In-Reply-To: <20260326112521.2827500-1-wusamuel@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260326112521.2827500-1-wusamuel@google.com> X-Mailer: git-send-email 2.53.0.1018.g2bb0e51243-goog Message-ID: <20260326112521.2827500-2-wusamuel@google.com> Subject: [PATCH v2 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources From: Samuel Wu To: "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 , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan Cc: memxor@gmail.com, Samuel Wu , kernel-team@android.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, driver-core@lists.linux.dev, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. 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 --- drivers/base/power/power.h | 7 ++++ drivers/base/power/wakeup.c | 72 +++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 922ed457db19..ced563286ac5 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); +struct list_head *bpf_wakeup_sources_get_head(void); +#endif diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index b8e48a023bf0..27a26a30b6ee 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -1168,11 +1168,79 @@ static const struct file_operations wakeup_sources_= stats_fops =3D { .release =3D seq_release_private, }; =20 -static int __init wakeup_sources_debugfs_init(void) +#ifdef CONFIG_BPF_SYSCALL +#include + +__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 ver= ifier + * requires a pointer (PTR_TO_BTF_ID) to strictly track the state of acqui= red + * 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 NU= LL). + */ +__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 sourc= es + * @lock: The opaque pointer returned by bpf_wakeup_sources_read_lock() + * + * The BPF verifier guarantees that @lock is a valid, unreleased pointer f= rom + * 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 struct list_head *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 =3D { + .owner =3D THIS_MODULE, + .set =3D &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; } =20 -postcore_initcall(wakeup_sources_debugfs_init); +postcore_initcall(wakeup_sources_init); --=20 2.53.0.1018.g2bb0e51243-goog From nobody Thu Apr 2 22:06:52 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0E2C43CAE85 for ; Thu, 26 Mar 2026 11:25:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774524343; cv=none; b=Kz59P1Ki1Emf3BU0gQ00BCtiprKJuFm4tbV2irxUOYLZrOQTzGm9MyBadTH06QtjbHiHunHvWxt55/i1FNoVQYvixBoj62+y5QfcePlcpeH6p12stTy6KhIaOj6STJV7CZkGSs3hT2Ynm7XA/arxSj/auAt43nwB71UjeGSOiS0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774524343; c=relaxed/simple; bh=/V+0IdnH4j/38ObS2x8JnYWV0Up1Buj0iBonshlPv0o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jtX1N2BzW/mwFcucqLNIKmWR/hxpZBySnhIeTAFDEH3cFne3NPqCLzXwOLeq4cf2DrnjGDS5bUtAMIXXTOZcWan/ocpXanmiNrnK/wW/6NDdTdoh64PppzZZb0Yz3v5d3vBr+6UbZX1iMoD2thKHmHQ+obLufeJd270YKwVFCL4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=WXtZg27Y; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="WXtZg27Y" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bdf75bc88fso716969eec.0 for ; Thu, 26 Mar 2026 04:25:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1774524340; x=1775129140; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=AADrlL5HtSz7LV90mafOdNvanEoEV46ktmXue5e6GMM=; b=WXtZg27YRsX/r/r2lesffReMGwYDo4GTU3cfg6urrUWcSExB5B6w8R+CIPaBFHc9mx o4qKTGrM1bQ8kQjqoJ7yiZfd4iiK++hoB0sU47tJfpZijELtNQ1WL8W/pBZCvjDCOEqH vh7imIF2gyK7IXWlxr15RlQK3dQIRz5JHoFrFAK1EiQ/iI4fY1GV2wVOZVkd7Utga2pp xPLaFcNLHg7B47GArr+C+ZeUAn/4erRMc5fzN+F2S4P/mfZ2Y8u0BmdQujKnQVJ96bnQ A9ck7hRRhVH5p/JLS2/Q52wUYGqh4GR5sKUtz0aulYDZbLfL39ZRh9ULq72zZzoLrTbc a3Hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774524340; x=1775129140; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AADrlL5HtSz7LV90mafOdNvanEoEV46ktmXue5e6GMM=; b=fFnluPxT5V5XKZvvWkv7ky/jgB0hU4xekQcXQBONinv2vFcQGfu9/kkEhnMCSk9DGH QsN43+mn9BeTPkuXMD43RU0XAKFHTcQ1eXUNiTwGX59kwq37y9gXyW5G2+R/iJkaqn2r W5LaElc1z855GNKtmKzgNqsVWMVwNAlg757OvZcDp3rjKcM+aJ74p7ZXPTjDoa3zbMj0 6rAxmHcWByq/do5MZmjxX8WdHKBQAOj16Xdsc9WBnq+HCYEPs5Tn6oKLg7221KEkL1/Y 8d9b32dEnLGBRSlfcVjsUrj/bbwQd/vAmvRciPZ48hKImHEfRagDcFq6X1BEQCOtGM4R roRQ== X-Forwarded-Encrypted: i=1; AJvYcCVqtUCQJZbLZJLoG/a1RCfepnJFzO8DlzQ3rYLkFKW3eMVU64u90sUf7Ch73z8MJX/LSAmfEBt5r0ppRfk=@vger.kernel.org X-Gm-Message-State: AOJu0YxN3iW0KZuCFbAbEapUtlDvpGSLc3If4Z9ZHzRFpAscidpDmJtp AnqP+ppTnZtMBAX/gsUEWKDBvh+r/57NUnydjDPysSaVM9MTkAhx/StVqrRVF9oR5e2gadcyw9g +dSbP2wd0jVqQ6g== X-Received: from dyls26.prod.google.com ([2002:a05:7300:6a9a:b0:2be:9dd7:57f6]) (user=wusamuel job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:3c05:b0:2ba:a3f2:958c with SMTP id 5a478bee46e88-2c15d12dc96mr4152044eec.0.1774524339916; Thu, 26 Mar 2026 04:25:39 -0700 (PDT) Date: Thu, 26 Mar 2026 04:25:20 -0700 In-Reply-To: <20260326112521.2827500-1-wusamuel@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260326112521.2827500-1-wusamuel@google.com> X-Mailer: git-send-email 2.53.0.1018.g2bb0e51243-goog Message-ID: <20260326112521.2827500-3-wusamuel@google.com> Subject: [PATCH v2 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs From: Samuel Wu To: "Rafael J. Wysocki" , Pavel Machek , Len Brown , Greg Kroah-Hartman , Danilo Krummrich , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan Cc: memxor@gmail.com, Samuel Wu , kernel-team@android.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, driver-core@lists.linux.dev, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- tools/testing/selftests/bpf/config | 3 +- .../selftests/bpf/prog_tests/wakeup_source.c | 101 +++++++++++++++++ .../selftests/bpf/progs/test_wakeup_source.c | 107 ++++++++++++++++++ .../selftests/bpf/progs/wakeup_source.h | 22 ++++ .../selftests/bpf/progs/wakeup_source_fail.c | 63 +++++++++++ 5 files changed, 295 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/b= pf/config index 24855381290d..bac60b444551 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -130,4 +130,5 @@ CONFIG_INFINIBAND=3Dy CONFIG_SMC=3Dy CONFIG_SMC_HS_CTRL_BPF=3Dy CONFIG_DIBS=3Dy -CONFIG_DIBS_LO=3Dy \ No newline at end of file +CONFIG_DIBS_LO=3Dy +CONFIG_PM_WAKELOCKS=3Dy 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..ff2899cbf3a8 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2026 Google LLC */ + +#include +#include +#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 =3D open("/sys/power/wake_lock", O_WRONLY); + if (!ASSERT_OK_FD(fd, "open /sys/power/wake_lock")) + return -1; + + bytes =3D 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 =3D 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 =3D ctx; + struct wakeup_event_t *e =3D data; + + if (strcmp(e->name, rb_ctx->name) =3D=3D 0) { + rb_ctx->found =3D true; + rb_ctx->active_time_ns =3D e->active_time_ns; + rb_ctx->total_time_ns =3D e->total_time_ns; + } + return 0; +} + +void test_wakeup_source(void) +{ + if (test__start_subtest("iterate_and_verify_times")) { + struct test_wakeup_source *skel; + struct ring_buffer *rb =3D NULL; + struct rb_ctx rb_ctx =3D { + .name =3D "bpf_selftest_ws_times", + .found =3D false, + }; + int err; + + skel =3D test_wakeup_source__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + rb =3D 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 =3D 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..fa35156e31d2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_wakeup_source.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2026 Google LLC */ + +#include "vmlinux.h" +#include +#include +#include "bpf_experimental.h" +#include "bpf_misc.h" +#include "wakeup_source.h" + +#define MAX_LOOP_ITER 1000 +#define RB_SIZE (16384 * 4) + +struct wakeup_source___local { + const char *name; + unsigned long active_count; + unsigned long event_count; + unsigned long wakeup_count; + unsigned long expire_count; + ktime_t last_time; + ktime_t max_time; + ktime_t total_time; + ktime_t start_prevent_time; + ktime_t prevent_sleep_time; + struct list_head entry; + bool active:1; + bool autosleep_enabled:1; +} __attribute__((preserve_access_index)); + +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; +struct list_head *bpf_wakeup_sources_get_head(void) __ksym; + +SEC("syscall") +__success __retval(0) +int iterate_wakeupsources(void *ctx) +{ + struct list_head *head =3D bpf_wakeup_sources_get_head(); + struct list_head *pos =3D head; + struct bpf_ws_lock *lock; + int i; + + lock =3D 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 =3D=3D h= ead) + break; + + struct wakeup_event_t *e =3D bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + + if (!e) + break; + + struct wakeup_source___local *ws =3D container_of( + pos, struct wakeup_source___local, entry); + s64 active_time =3D 0; + bool active =3D BPF_CORE_READ_BITFIELD_PROBED(ws, active); + bool autosleep_enable =3D BPF_CORE_READ_BITFIELD_PROBED(ws, autosleep_en= abled); + s64 last_time =3D BPF_CORE_READ(ws, last_time); + s64 max_time =3D BPF_CORE_READ(ws, max_time); + s64 prevent_sleep_time =3D BPF_CORE_READ(ws, prevent_sleep_time); + s64 total_time =3D BPF_CORE_READ(ws, total_time); + + if (active) { + s64 curr_time =3D bpf_ktime_get_ns(); + s64 prevent_time =3D BPF_CORE_READ(ws, start_prevent_time); + + if (curr_time > last_time) + active_time =3D curr_time - last_time; + + total_time +=3D active_time; + if (active_time > max_time) + max_time =3D active_time; + if (autosleep_enable && curr_time > prevent_time) + prevent_sleep_time +=3D curr_time - prevent_time; + } + + e->active_count =3D BPF_CORE_READ(ws, active_count); + e->active_time_ns =3D active_time; + e->event_count =3D BPF_CORE_READ(ws, event_count); + e->expire_count =3D BPF_CORE_READ(ws, expire_count); + e->last_time_ns =3D last_time; + e->max_time_ns =3D max_time; + e->prevent_sleep_time_ns =3D prevent_sleep_time; + e->total_time_ns =3D total_time; + e->wakeup_count =3D BPF_CORE_READ(ws, wakeup_count); + + if (bpf_probe_read_kernel_str( + e->name, WAKEUP_NAME_LEN, BPF_CORE_READ(ws, name)) < 0) + e->name[0] =3D '\0'; + + bpf_ringbuf_submit(e, 0); + } + + bpf_wakeup_sources_read_unlock(lock); + return 0; +} + +char _license[] SEC("license") =3D "GPL"; diff --git a/tools/testing/selftests/bpf/progs/wakeup_source.h b/tools/test= ing/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..a569c9d53d21 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2026 Google LLC */ + +#include +#include +#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; + +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 =3D 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 =3D bpf_wakeup_sources_read_lock(); + if (!lock) + return 0; + + val =3D *(int *)lock; + + bpf_wakeup_sources_read_unlock(lock); + return val; +} + +SEC("syscall") +__failure __msg("type=3Dscalar expected=3Dfp") +int wakeup_source_unlock_no_lock(void *ctx) +{ + struct bpf_ws_lock *lock =3D (void *)0x1; + + bpf_wakeup_sources_read_unlock(lock); + + return 0; +} + +SEC("syscall") +__failure __msg("Possibly NULL pointer passed to trusted arg0") +int wakeup_source_unlock_null(void *ctx) +{ + bpf_wakeup_sources_read_unlock(NULL); + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.53.0.1018.g2bb0e51243-goog