From nobody Thu May 7 19:50:57 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3BEE4C433EF for ; Fri, 20 May 2022 01:21:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344423AbiETBVs (ORCPT ); Thu, 19 May 2022 21:21:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55354 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344389AbiETBVl (ORCPT ); Thu, 19 May 2022 21:21:41 -0400 Received: from mail-pf1-x44a.google.com (mail-pf1-x44a.google.com [IPv6:2607:f8b0:4864:20::44a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 410FE29817 for ; Thu, 19 May 2022 18:21:40 -0700 (PDT) Received: by mail-pf1-x44a.google.com with SMTP id t29-20020a056a00139d00b005107ebaefeaso3446932pfg.20 for ; Thu, 19 May 2022 18:21:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=69cx6mPg1rWURoZo4NX3ijOmJtwQkSNn64F/hEHNYSU=; b=BCreR+XUKTegtpZpREe5xPvg68UCcKsrUAIVbGh5bI2zogHrCn+YHLq0FMySF7Z8+U 3nTv8stgycmvTmhhg+YPkCLz4UYr3ZJz7474U4xhyJE3TKlT4rYlAp0vn5NBMmGoe2ov dgweabl3iJGlpFuwJTqJRwak1koJWau8g/NP85L4CjmiJUzSKn4LzPc2hkHvubatyBQP /96s5b/5xzfSh2L2JFvPZ2hVNpAhSiLyYoCG5VatpXZ/1CZSaKHDY0Xa+XvkgrFotW3c B1tX82bRy1h3iblza4rY3Q+jRM97bMVY3aW48bY0dTk7oUfdIoNGjySPHVcDY3xSepq3 Yujw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=69cx6mPg1rWURoZo4NX3ijOmJtwQkSNn64F/hEHNYSU=; b=sTAwLil3uHl2BEMTTKC7YpJo71kNTz2rpZ93IhOB8YQqsKL0iNFsbzZT7j1ThFyBKa jcAuAo1qIJL8LOniWXDTBGjuwsStJFH/BCZMJ4FTBd7SngPZ7sB33qVkbnBi2gPJg3ag iYWStfT7qLhxUn5n5Zrp3c4YKD94m/E+Kr6CTHw+7xlzRyhDzWE2qiKSijkHZGemq6oc Yl56bL37Vh1hJUTBCrNTEASmzkt+oDp3vby4e8kKe/o3ppGMylVEK3mj8g3YBqC5XvVD P/cgwpu7YO3y8IZE7IvQtOgivq5b/rXZXaRBpI/4VAUuyru4gyaxIX8hLngM3y8n5u58 zkpQ== X-Gm-Message-State: AOAM531A5ZqO8E16kBd1R80eNjquuc0FN5TJLWKVzaMZrWkf0xbLGPUw WZ2V35vMKcOzUViwMqM8Fti6u0qpc07wl4Mv X-Google-Smtp-Source: ABdhPJwhlm4uc6Arbp6IzP0EiWll+Bev9gm1/YHaZOBFW6aHkjOxNlzyA5wFDryaE/HeLy/GmQugBTe+SOD4g6uB X-Received: from yosry.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:2327]) (user=yosryahmed job=sendgmr) by 2002:a05:6a00:724:b0:4fa:a35f:8e0f with SMTP id 4-20020a056a00072400b004faa35f8e0fmr7557670pfm.25.1653009699699; Thu, 19 May 2022 18:21:39 -0700 (PDT) Date: Fri, 20 May 2022 01:21:29 +0000 In-Reply-To: <20220520012133.1217211-1-yosryahmed@google.com> Message-Id: <20220520012133.1217211-2-yosryahmed@google.com> Mime-Version: 1.0 References: <20220520012133.1217211-1-yosryahmed@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH bpf-next v1 1/5] cgroup: bpf: add a hook for bpf progs to attach to rstat flushing From: Yosry Ahmed To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Hao Luo , Tejun Heo , Zefan Li , Johannes Weiner , Shuah Khan , Roman Gushchin , Michal Hocko Cc: Stanislav Fomichev , David Rientjes , Greg Thelen , Shakeel Butt , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, cgroups@vger.kernel.org, Yosry Ahmed Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add an empty bpf_rstat_flush() hook that is called during rstat flushing. bpf programs that make use of rstat and want to flush their stats can attach to bpf_rstat_flush(). Signed-off-by: Yosry Ahmed Reported-by: kernel test robot --- kernel/cgroup/rstat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index 24b5c2ab5598..e7a88d2600bd 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -141,6 +141,12 @@ static struct cgroup *cgroup_rstat_cpu_pop_updated(str= uct cgroup *pos, return pos; } =20 +/* A hook for bpf stat collectors to attach to and flush their stats */ +__weak noinline void bpf_rstat_flush(struct cgroup *cgrp, + struct cgroup *parent, int cpu) +{ +} + /* see cgroup_rstat_flush() */ static void cgroup_rstat_flush_locked(struct cgroup *cgrp, bool may_sleep) __releases(&cgroup_rstat_lock) __acquires(&cgroup_rstat_lock) @@ -168,6 +174,7 @@ static void cgroup_rstat_flush_locked(struct cgroup *cg= rp, bool may_sleep) struct cgroup_subsys_state *css; =20 cgroup_base_stat_flush(pos, cpu); + bpf_rstat_flush(pos, cgroup_parent(pos), cpu); =20 rcu_read_lock(); list_for_each_entry_rcu(css, &pos->rstat_css_list, --=20 2.36.1.124.g0e6072fb45-goog From nobody Thu May 7 19:50:57 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 058F7C433FE for ; Fri, 20 May 2022 01:22:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344450AbiETBV4 (ORCPT ); Thu, 19 May 2022 21:21:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55552 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344407AbiETBVm (ORCPT ); Thu, 19 May 2022 21:21:42 -0400 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F2DEF27CC6 for ; Thu, 19 May 2022 18:21:41 -0700 (PDT) Received: by mail-pj1-x1049.google.com with SMTP id rj11-20020a17090b3e8b00b001df51eb1831so5985972pjb.3 for ; Thu, 19 May 2022 18:21:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=18Er9O41CS051aXSe9usnmRbqjT27fNlHDiukFcMpPk=; b=igx0wUTdQcYupX1yAJkzuiW3tixL4/rG9QHDOE1OuemDauucgS0b4WPm/qNDTqc6tj dC8noNnsek02+MHALHDloPhHjY9PuAcO8cT0fqReQQSFMoKfhUaM0O2JYTV90eF5TW1T 1ZpTQAHA+bZ5+sSSYuH0fi+CeElAYVF0uv+c3gA5rToEvxuK4V4z2uk297hwMXxNn/CK 5nHug49g8iMgqh+7Bw17Kgzdbg90jtNx7QMIWVdBW/1UttBugbiKkD/nel+18aIOmhCW DnVdrCrB08b/fwlSt9/iJh7/Z32kjo9kzvfKYcbNc635DkaxYvbgHuDNWDgJOr4EWfTq +obQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=18Er9O41CS051aXSe9usnmRbqjT27fNlHDiukFcMpPk=; b=0mJF/xV1v78oj6ZpuhbB8fBWb28PQ0vkTWKDQdWgrzOpK5aR3Chp+Mdqkb86H5QwsI RLqZlS4O7LoWrqgdCUoUGNP3Gn28Z4lh2vUypOPirv3AmZXjnhWcNY/m4VTG1eE7tWph XbIVH2FIqVkZIRciontfTtyNwge0IoEFTY9GM7dMm+EsDHgjjJu8h1YzhaHqPBgBZZ7p pB7SFrPN1GXsRCSLd0EDDuuoT4cyrffOZezwilz9d1NeifEF+U4/NFo3thM2oWwnESne 3bimfB8Wu3T9Tosdz3n1ZUYjT6Ui9IIWU/jTU2OhbzIhsUNFskT8SeuUodN13b3Ras/Z nKYA== X-Gm-Message-State: AOAM533QOnjFX2KNVg2S8G/+Gss78ayks43iFi42omdNsDTx8q189pqR b+x6LPqx+ZjXrUf5krvL/DLgIzI28UKUJZT9 X-Google-Smtp-Source: ABdhPJz9ntngBIfCSaZOOirWq+VnWkQ9QbljpHNzGtI7ZAT0+hNeqeX9Vx2fzjW6MS3w55enQ0OqOdn0dJ+ys6A1 X-Received: from yosry.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:2327]) (user=yosryahmed job=sendgmr) by 2002:a62:e518:0:b0:4fa:9333:ddbd with SMTP id n24-20020a62e518000000b004fa9333ddbdmr7319126pff.11.1653009701453; Thu, 19 May 2022 18:21:41 -0700 (PDT) Date: Fri, 20 May 2022 01:21:30 +0000 In-Reply-To: <20220520012133.1217211-1-yosryahmed@google.com> Message-Id: <20220520012133.1217211-3-yosryahmed@google.com> Mime-Version: 1.0 References: <20220520012133.1217211-1-yosryahmed@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH bpf-next v1 2/5] cgroup: bpf: add cgroup_rstat_updated() and cgroup_rstat_flush() kfuncs From: Yosry Ahmed To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Hao Luo , Tejun Heo , Zefan Li , Johannes Weiner , Shuah Khan , Roman Gushchin , Michal Hocko Cc: Stanislav Fomichev , David Rientjes , Greg Thelen , Shakeel Butt , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, cgroups@vger.kernel.org, Yosry Ahmed Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add cgroup_rstat_updated() and cgroup_rstat_flush() kfuncs to bpf tracing programs. bpf programs that make use of rstat can use these functions to inform rstat when they update stats for a cgroup, and when they need to flush the stats. Signed-off-by: Yosry Ahmed Reported-by: kernel test robot --- kernel/cgroup/rstat.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index e7a88d2600bd..a16a851bc0a1 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -3,6 +3,11 @@ =20 #include =20 +#include +#include +#include + + static DEFINE_SPINLOCK(cgroup_rstat_lock); static DEFINE_PER_CPU(raw_spinlock_t, cgroup_rstat_cpu_lock); =20 @@ -141,7 +146,12 @@ static struct cgroup *cgroup_rstat_cpu_pop_updated(str= uct cgroup *pos, return pos; } =20 -/* A hook for bpf stat collectors to attach to and flush their stats */ +/* + * A hook for bpf stat collectors to attach to and flush their stats. + * Together with providing bpf kfuncs for cgroup_rstat_updated() and + * cgroup_rstat_flush(), this enables a complete workflow where bpf progs = that + * collect cgroup stats can integrate with rstat for efficient flushing. + */ __weak noinline void bpf_rstat_flush(struct cgroup *cgrp, struct cgroup *parent, int cpu) { @@ -476,3 +486,26 @@ void cgroup_base_stat_cputime_show(struct seq_file *se= q) "system_usec %llu\n", usage, utime, stime); } + +/* Add bpf kfuncs for cgroup_rstat_updated() and cgroup_rstat_flush() */ +BTF_SET_START(bpf_rstat_check_kfunc_ids) +BTF_ID(func, cgroup_rstat_updated) +BTF_ID(func, cgroup_rstat_flush) +BTF_SET_END(bpf_rstat_check_kfunc_ids) + +BTF_SET_START(bpf_rstat_sleepable_kfunc_ids) +BTF_ID(func, cgroup_rstat_flush) +BTF_SET_END(bpf_rstat_sleepable_kfunc_ids) + +static const struct btf_kfunc_id_set bpf_rstat_kfunc_set =3D { + .owner =3D THIS_MODULE, + .check_set =3D &bpf_rstat_check_kfunc_ids, + .sleepable_set =3D &bpf_rstat_sleepable_kfunc_ids, +}; + +static int __init bpf_rstat_kfunc_init(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, + &bpf_rstat_kfunc_set); +} +late_initcall(bpf_rstat_kfunc_init); --=20 2.36.1.124.g0e6072fb45-goog From nobody Thu May 7 19:50:57 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 68073C43217 for ; Fri, 20 May 2022 01:22:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344453AbiETBWF (ORCPT ); Thu, 19 May 2022 21:22:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56424 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344430AbiETBVu (ORCPT ); Thu, 19 May 2022 21:21:50 -0400 Received: from mail-pg1-x549.google.com (mail-pg1-x549.google.com [IPv6:2607:f8b0:4864:20::549]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AFBE82983A for ; Thu, 19 May 2022 18:21:43 -0700 (PDT) Received: by mail-pg1-x549.google.com with SMTP id g7-20020a655947000000b003c6bf87efdaso3371671pgu.14 for ; Thu, 19 May 2022 18:21:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Qrx145uZheTwW5S3ogXKwTgZQSI1uZw5vSyKKikVhSA=; b=CAgIAB5LDMWdv//SxiQPXDjTJlwlbBN2rmSL+nPOHdEijE1xOdZXxikE5SFGVUzPQT kjSilKK8B+8JGiNJdgJJEQ1CXfsIqBrjW0AukAeEjIP6fvmaYKALizTzEEm9GEJdjGMh e277Rxzchd/dAtUR6iSQr+g0YeVpWZd6HO+Lqnrhii+s/ZSvwAjN+7EmQWlr+9+wg3M1 Z+4FeTeZXJ6zzpQJYpoS6/l2SeuU13lgYxw9pwqsamHmU1v+X0NPvIBBZKg/srA8xqW1 HXJudJNsY/+9ZEGpxckZsze5cBXJHIfINh7bx4uWYb3j4ghE8vYb6avCtaR6pi/MjReR bKNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Qrx145uZheTwW5S3ogXKwTgZQSI1uZw5vSyKKikVhSA=; b=ZBpxSvZJMi9Qg1YzWY+UdKscL5VEHBvg50LLJe4GYYvVfeITP+TNKyaaFK8rHosQz5 mYiDB8OtTjzw6xmWRhkUSkcUtGLsZJ6vbiB+XLZV1j0EOGY/71On5WhtlqBNFPEBUdci QvRy2ph07jdtc0XMqMrcduiKCaaT1WSIr6I90YfZ85uvnXyAY2BQfXgVWZ4anWsGZSwP EMo2PJ1RRz1F06np/sYmzS0vN2Rt+b9ZB8TO3XVPKyUmwMM66KXtC3Svg+SFqb0H7vsi hrlt2OVX0q3e6pNAc7WO/lYeWSpsYhLTHFruJnqZGior/B9/arzFpl6rkjj9LKhARM6k aeTQ== X-Gm-Message-State: AOAM532menuM0Ac/55RGufUFQCsVQKaB50KkB6Iwu9hfZtJMnUA1wtTy hxG+jOWcmzzOJHxpYMkDRvdEY44zcDELgAcF X-Google-Smtp-Source: ABdhPJxVt9kfrm7048uGAWHslUU6OTNJ04ersU8aLYVdcbVg3H9cOlpukOXF+BPyfd0JGwUqB8vULWOmNQBYFxat X-Received: from yosry.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:2327]) (user=yosryahmed job=sendgmr) by 2002:a05:6a00:1348:b0:518:6ef5:ac3 with SMTP id k8-20020a056a00134800b005186ef50ac3mr536051pfu.69.1653009703123; Thu, 19 May 2022 18:21:43 -0700 (PDT) Date: Fri, 20 May 2022 01:21:31 +0000 In-Reply-To: <20220520012133.1217211-1-yosryahmed@google.com> Message-Id: <20220520012133.1217211-4-yosryahmed@google.com> Mime-Version: 1.0 References: <20220520012133.1217211-1-yosryahmed@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH bpf-next v1 3/5] bpf: Introduce cgroup iter From: Yosry Ahmed To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Hao Luo , Tejun Heo , Zefan Li , Johannes Weiner , Shuah Khan , Roman Gushchin , Michal Hocko Cc: Stanislav Fomichev , David Rientjes , Greg Thelen , Shakeel Butt , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, cgroups@vger.kernel.org, Yosry Ahmed Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Hao Luo Introduce a new type of iter prog: cgroup. Unlike other bpf_iter, this iter doesn't iterate a set of kernel objects. Instead, it is supposed to be parameterized by a cgroup id and prints only that cgroup. So one needs to specify a target cgroup id when attaching this iter. The target cgroup's state can be read out via a link of this iter. Signed-off-by: Hao Luo Signed-off-by: Yosry Ahmed --- include/linux/bpf.h | 2 + include/uapi/linux/bpf.h | 6 ++ kernel/bpf/Makefile | 3 + kernel/bpf/cgroup_iter.c | 148 +++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 6 ++ 5 files changed, 165 insertions(+) create mode 100644 kernel/bpf/cgroup_iter.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c107392b0ba7..74c30fe20c23 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -44,6 +44,7 @@ struct kobject; struct mem_cgroup; struct module; struct bpf_func_state; +struct cgroup; =20 extern struct idr btf_idr; extern spinlock_t btf_idr_lock; @@ -1581,6 +1582,7 @@ int bpf_obj_get_user(const char __user *pathname, int= flags); =20 struct bpf_iter_aux_info { struct bpf_map *map; + struct cgroup *cgroup; }; =20 typedef int (*bpf_iter_attach_target_t)(struct bpf_prog *prog, diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0210f85131b3..e5bc40d4bccc 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -91,6 +91,9 @@ union bpf_iter_link_info { struct { __u32 map_fd; } map; + struct { + __u64 cgroup_id; + } cgroup; }; =20 /* BPF syscall commands, see bpf(2) man-page for more details. */ @@ -5965,6 +5968,9 @@ struct bpf_link_info { struct { __u32 map_id; } map; + struct { + __u64 cgroup_id; + } cgroup; }; } iter; struct { diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 057ba8e01e70..3e563b163d49 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -36,6 +36,9 @@ obj-$(CONFIG_BPF_SYSCALL) +=3D bpf_struct_ops.o obj-${CONFIG_BPF_LSM} +=3D bpf_lsm.o endif obj-$(CONFIG_BPF_PRELOAD) +=3D preload/ +ifeq ($(CONFIG_CGROUPS),y) +obj-$(CONFIG_BPF_SYSCALL) +=3D cgroup_iter.o +endif =20 obj-$(CONFIG_BPF_SYSCALL) +=3D relo_core.o $(obj)/relo_core.o: $(srctree)/tools/lib/bpf/relo_core.c FORCE diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c new file mode 100644 index 000000000000..86bdfe135d24 --- /dev/null +++ b/kernel/bpf/cgroup_iter.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Google */ +#include +#include +#include +#include +#include + +struct bpf_iter__cgroup { + __bpf_md_ptr(struct bpf_iter_meta *, meta); + __bpf_md_ptr(struct cgroup *, cgroup); +}; + +static void *cgroup_iter_seq_start(struct seq_file *seq, loff_t *pos) +{ + /* Only one session is supported. */ + if (*pos > 0) + return NULL; + + if (*pos =3D=3D 0) + ++*pos; + + return *(struct cgroup **)seq->private; +} + +static void *cgroup_iter_seq_next(struct seq_file *seq, void *v, loff_t *p= os) +{ + ++*pos; + return NULL; +} + +static int cgroup_iter_seq_show(struct seq_file *seq, void *v) +{ + struct bpf_iter__cgroup ctx; + struct bpf_iter_meta meta; + struct bpf_prog *prog; + int ret =3D 0; + + ctx.meta =3D &meta; + ctx.cgroup =3D v; + meta.seq =3D seq; + prog =3D bpf_iter_get_info(&meta, false); + if (prog) + ret =3D bpf_iter_run_prog(prog, &ctx); + + return ret; +} + +static void cgroup_iter_seq_stop(struct seq_file *seq, void *v) +{ +} + +static const struct seq_operations cgroup_iter_seq_ops =3D { + .start =3D cgroup_iter_seq_start, + .next =3D cgroup_iter_seq_next, + .stop =3D cgroup_iter_seq_stop, + .show =3D cgroup_iter_seq_show, +}; + +BTF_ID_LIST_SINGLE(bpf_cgroup_btf_id, struct, cgroup) + +static int cgroup_iter_seq_init(void *priv_data, struct bpf_iter_aux_info = *aux) +{ + *(struct cgroup **)priv_data =3D aux->cgroup; + return 0; +} + +static const struct bpf_iter_seq_info cgroup_iter_seq_info =3D { + .seq_ops =3D &cgroup_iter_seq_ops, + .init_seq_private =3D cgroup_iter_seq_init, + .seq_priv_size =3D sizeof(struct cgroup *), +}; + +static int bpf_iter_attach_cgroup(struct bpf_prog *prog, + union bpf_iter_link_info *linfo, + struct bpf_iter_aux_info *aux) +{ + struct cgroup *cgroup; + + cgroup =3D cgroup_get_from_id(linfo->cgroup.cgroup_id); + if (!cgroup) + return -EBUSY; + + aux->cgroup =3D cgroup; + return 0; +} + +static void bpf_iter_detach_cgroup(struct bpf_iter_aux_info *aux) +{ + if (aux->cgroup) + cgroup_put(aux->cgroup); +} + +static void bpf_iter_cgroup_show_fdinfo(const struct bpf_iter_aux_info *au= x, + struct seq_file *seq) +{ + char *buf; + + seq_printf(seq, "cgroup_id:\t%llu\n", cgroup_id(aux->cgroup)); + + buf =3D kmalloc(PATH_MAX, GFP_KERNEL); + if (!buf) { + seq_puts(seq, "cgroup_path:\n"); + return; + } + + /* If cgroup_path_ns() fails, buf will be an empty string, cgroup_path + * will print nothing. + * + * Cgroup_path is the path in the calliing process's cgroup namespace. + */ + cgroup_path_ns(aux->cgroup, buf, sizeof(buf), + current->nsproxy->cgroup_ns); + seq_printf(seq, "cgroup_path:\t%s\n", buf); + kfree(buf); +} + +static int bpf_iter_cgroup_fill_link_info(const struct bpf_iter_aux_info *= aux, + struct bpf_link_info *info) +{ + info->iter.cgroup.cgroup_id =3D cgroup_id(aux->cgroup); + return 0; +} + +DEFINE_BPF_ITER_FUNC(cgroup, struct bpf_iter_meta *meta, + struct cgroup *cgroup) + +static struct bpf_iter_reg bpf_cgroup_reg_info =3D { + .target =3D "cgroup", + .attach_target =3D bpf_iter_attach_cgroup, + .detach_target =3D bpf_iter_detach_cgroup, + .show_fdinfo =3D bpf_iter_cgroup_show_fdinfo, + .fill_link_info =3D bpf_iter_cgroup_fill_link_info, + .ctx_arg_info_size =3D 1, + .ctx_arg_info =3D { + { offsetof(struct bpf_iter__cgroup, cgroup), + PTR_TO_BTF_ID }, + }, + .seq_info =3D &cgroup_iter_seq_info, +}; + +static int __init bpf_cgroup_iter_init(void) +{ + bpf_cgroup_reg_info.ctx_arg_info[0].btf_id =3D bpf_cgroup_btf_id[0]; + return bpf_iter_reg_target(&bpf_cgroup_reg_info); +} + +late_initcall(bpf_cgroup_iter_init); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0210f85131b3..e5bc40d4bccc 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -91,6 +91,9 @@ union bpf_iter_link_info { struct { __u32 map_fd; } map; + struct { + __u64 cgroup_id; + } cgroup; }; =20 /* BPF syscall commands, see bpf(2) man-page for more details. */ @@ -5965,6 +5968,9 @@ struct bpf_link_info { struct { __u32 map_id; } map; + struct { + __u64 cgroup_id; + } cgroup; }; } iter; struct { --=20 2.36.1.124.g0e6072fb45-goog From nobody Thu May 7 19:50:57 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CA514C433EF for ; Fri, 20 May 2022 01:22:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344509AbiETBWI (ORCPT ); Thu, 19 May 2022 21:22:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56526 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344440AbiETBVv (ORCPT ); Thu, 19 May 2022 21:21:51 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B6372AC68 for ; Thu, 19 May 2022 18:21:45 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id x4-20020a1709028ec400b0015e84d42eaaso3356558plo.7 for ; Thu, 19 May 2022 18:21:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=7NpPrNa+Bz8HzFusX9ojwAjuFv1Lhyxt4v6rFhund+w=; b=eyJXXtPEk+d/1DG1nBhGcV5radE6gV+k7cAc+PXed5M9Isz+fiheznJqdqKz3LjFHX gjM+Zn3y0to7/5yaXKUwMITmClHfNHUeq5DgOPdMhXNDtJJmwtr7N9hsm4P+AxE/33PR 47mR9OLgu0ceS0+ARA7BecenYLC8hs045MtHkg9GFxYvSmco561MCjFAYvLnF7IhohsA 8kg4FfciGF4nSuxGXH2hxIDElmH0NiFTH8lZehsvuGf2jkfiz7oVxW62Xd3oI1uMTNSY M0V3bIBznmQZQX1RFieoj/ZlkXCMWREM14yxVtLxB9AVL4IMLgE955H+TfzjNt2vNZVS IVRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=7NpPrNa+Bz8HzFusX9ojwAjuFv1Lhyxt4v6rFhund+w=; b=NmuiNcYHzyQIqI9M0pJE3e+1O2Pxqis7X9DZasJ+hO679UraTdord7NO2Ew2vrpHBZ BAzlQt6yVMBgDZTOuu4S/gJ2PH+yZAwOWdChtH9Dja8A87HeUrcBqz+7zZhI0PgZy3oO aA7pUY2/m+WdRBeOgTBoSEjZJ4AHjdt7UoY+zL2kaRM1JpHiYthUHXDUDGTSezUwo3xr OmNeOvRK4QpTqOprdMCHGsAnPbYl13UhjAUKqOIWS1g/chP8EDIIjKQkgnj2hVT61Kjj LJZbngkR5wuPNKisgbYMC20RyFWWOnTKqHni5I1uQeCKhOdv3lRXHXgJmwxIK1clNDBr Rfxg== X-Gm-Message-State: AOAM530Mnmqza2G1Z/f7giemP2FNSsQ5juwJrr8ZLo74p6YMZd5JzObc lCttXPTw+1f43Qt9o1F02G/a/6jqZFOnmwmw X-Google-Smtp-Source: ABdhPJwTCUHAIhmzAsWG6Hx/j6j1+ePR0qgOdomgLGI+Fid8PxjmP1rcK6BkhG60NGCyNuGxBHACGvhVDkyiO+aQ X-Received: from yosry.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:2327]) (user=yosryahmed job=sendgmr) by 2002:a65:554a:0:b0:3f6:885:b291 with SMTP id t10-20020a65554a000000b003f60885b291mr6191939pgr.608.1653009704939; Thu, 19 May 2022 18:21:44 -0700 (PDT) Date: Fri, 20 May 2022 01:21:32 +0000 In-Reply-To: <20220520012133.1217211-1-yosryahmed@google.com> Message-Id: <20220520012133.1217211-5-yosryahmed@google.com> Mime-Version: 1.0 References: <20220520012133.1217211-1-yosryahmed@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH bpf-next v1 4/5] selftests/bpf: extend cgroup helpers From: Yosry Ahmed To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Hao Luo , Tejun Heo , Zefan Li , Johannes Weiner , Shuah Khan , Roman Gushchin , Michal Hocko Cc: Stanislav Fomichev , David Rientjes , Greg Thelen , Shakeel Butt , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, cgroups@vger.kernel.org, Yosry Ahmed Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends bpf selftests cgroup helpers in various ways: - Expose enable_controllers() that allows tests to enable all or a subset of controllers for a specific cgroup. - Add write_cgroup_file(). - Add join_cgroup_parent(). The cgroup workdir is based on the pid, therefore a spawned child cannot join the same cgroup hierarchy of the test through join_cgroup(). join_cgroup_parent() is used in child processes to join a cgroup under the parent's workdir. - Distinguish relative and absolute cgroup paths in function arguments. Now relative paths are called relative_path, and absolute paths are called cgroup_path. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/bpf/cgroup_helpers.c | 159 ++++++++++++++----- tools/testing/selftests/bpf/cgroup_helpers.h | 14 +- 2 files changed, 127 insertions(+), 46 deletions(-) diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/s= elftests/bpf/cgroup_helpers.c index 9d59c3990ca8..48c8f794a347 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -33,49 +33,51 @@ #define CGROUP_MOUNT_DFLT "/sys/fs/cgroup" #define NETCLS_MOUNT_PATH CGROUP_MOUNT_DFLT "/net_cls" #define CGROUP_WORK_DIR "/cgroup-test-work-dir" -#define format_cgroup_path(buf, path) \ + +#define format_cgroup_path_pid(buf, path, pid) \ snprintf(buf, sizeof(buf), "%s%s%d%s", CGROUP_MOUNT_PATH, \ - CGROUP_WORK_DIR, getpid(), path) + CGROUP_WORK_DIR, pid, path) + +#define format_cgroup_path(buf, path) \ + format_cgroup_path_pid(buf, path, getpid()) + +#define format_parent_cgroup_path(buf, path) \ + format_cgroup_path_pid(buf, path, getppid()) =20 #define format_classid_path(buf) \ snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \ CGROUP_WORK_DIR) =20 -/** - * enable_all_controllers() - Enable all available cgroup v2 controllers - * - * Enable all available cgroup v2 controllers in order to increase - * the code coverage. - * - * If successful, 0 is returned. - */ -static int enable_all_controllers(char *cgroup_path) + +static int __enable_controllers(const char *cgroup_path, const char *contr= ollers) { char path[PATH_MAX + 1]; - char buf[PATH_MAX]; + char enable[PATH_MAX + 1]; char *c, *c2; int fd, cfd; ssize_t len; =20 - snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path); - fd =3D open(path, O_RDONLY); - if (fd < 0) { - log_err("Opening cgroup.controllers: %s", path); - return 1; - } + /* If not controllers are passed, enable all available controllers */ + if (!controllers) { + snprintf(path, sizeof(path), "%s/cgroup.controllers", + cgroup_path); + fd =3D open(path, O_RDONLY); + if (fd < 0) { + log_err("Opening cgroup.controllers: %s", path); + return 1; + } =20 - len =3D read(fd, buf, sizeof(buf) - 1); - if (len < 0) { + len =3D read(fd, enable, sizeof(enable) - 1); + if (len < 0) { + close(fd); + log_err("Reading cgroup.controllers: %s", path); + return 1; + } else if (len =3D=3D 0) /* No controllers to enable */ + return 0; + enable[len] =3D 0; close(fd); - log_err("Reading cgroup.controllers: %s", path); - return 1; - } - buf[len] =3D 0; - close(fd); - - /* No controllers available? We're probably on cgroup v1. */ - if (len =3D=3D 0) - return 0; + } else + strncpy(enable, controllers, sizeof(enable)); =20 snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path); cfd =3D open(path, O_RDWR); @@ -84,7 +86,7 @@ static int enable_all_controllers(char *cgroup_path) return 1; } =20 - for (c =3D strtok_r(buf, " ", &c2); c; c =3D strtok_r(NULL, " ", &c2)) { + for (c =3D strtok_r(enable, " ", &c2); c; c =3D strtok_r(NULL, " ", &c2))= { if (dprintf(cfd, "+%s\n", c) <=3D 0) { log_err("Enabling controller %s: %s", c, path); close(cfd); @@ -95,6 +97,63 @@ static int enable_all_controllers(char *cgroup_path) return 0; } =20 +/** + * enable_controllers() - Enable cgroup v2 controllers + * @relative_path: The cgroup path, relative to the workdir + * @controllers: List of controllers to enable in cgroup.controllers format + * + * + * Enable given cgroup v2 controllers, if @controllers is NULL, enable all + * available controllers. + * + * If successful, 0 is returned. + */ +int enable_controllers(const char *relative_path, const char *controllers) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, relative_path); + return __enable_controllers(cgroup_path, controllers); +} + + + +/** + * write_cgroup_file() - Write to a cgroup file + * @relative_path: The cgroup path, relative to the workdir + * @buf: Buffer to write to the file + * + * Write to a file in the given cgroup's directory. + * + * If successful, 0 is returned. + */ +int write_cgroup_file(const char *relative_path, const char *file, + const char *buf) +{ + char cgroup_path[PATH_MAX - 24]; + char file_path[PATH_MAX + 1]; + int fd; + + format_cgroup_path(cgroup_path, relative_path); + + snprintf(file_path, sizeof(file_path), "%s/%s", cgroup_path, file); + fd =3D open(file_path, O_RDWR); + if (fd < 0) { + log_err("Opening cgroup.subtree_control: %s", file_path); + return 1; + } + + if (dprintf(fd, "%s", buf) <=3D 0) { + log_err("Writing to %s", file_path); + close(fd); + return 1; + } + close(fd); + return 0; +} + + + /** * setup_cgroup_environment() - Setup the cgroup environment * @@ -133,7 +192,9 @@ int setup_cgroup_environment(void) return 1; } =20 - if (enable_all_controllers(cgroup_workdir)) + /* Enable all available controllers to increase test coverage */ + if (__enable_controllers(CGROUP_MOUNT_PATH, NULL) || + __enable_controllers(cgroup_workdir, NULL)) return 1; =20 return 0; @@ -173,7 +234,7 @@ static int join_cgroup_from_top(const char *cgroup_path) =20 /** * join_cgroup() - Join a cgroup - * @path: The cgroup path, relative to the workdir, to join + * @relative_path: The cgroup path, relative to the workdir, to join * * This function expects a cgroup to already be created, relative to the c= group * work dir, and it joins it. For example, passing "/my-cgroup" as the path @@ -182,11 +243,27 @@ static int join_cgroup_from_top(const char *cgroup_pa= th) * * On success, it returns 0, otherwise on failure it returns 1. */ -int join_cgroup(const char *path) +int join_cgroup(const char *relative_path) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, relative_path); + return join_cgroup_from_top(cgroup_path); +} + +/** + * join_parent_cgroup() - Join a cgroup in the parent process workdir + * @relative_path: The cgroup path, relative to parent process workdir, to= join + * + * See join_cgroup(). + * + * On success, it returns 0, otherwise on failure it returns 1. + */ +int join_parent_cgroup(const char *relative_path) { char cgroup_path[PATH_MAX + 1]; =20 - format_cgroup_path(cgroup_path, path); + format_parent_cgroup_path(cgroup_path, relative_path); return join_cgroup_from_top(cgroup_path); } =20 @@ -214,7 +291,7 @@ void cleanup_cgroup_environment(void) =20 /** * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get= the FD - * @path: The cgroup path, relative to the workdir, to join + * @relative_path: The cgroup path, relative to the workdir, to join * * This function creates a cgroup under the top level workdir and returns = the * file descriptor. It is idempotent. @@ -222,14 +299,14 @@ void cleanup_cgroup_environment(void) * On success, it returns the file descriptor. On failure it returns -1. * If there is a failure, it prints the error to stderr. */ -int create_and_get_cgroup(const char *path) +int create_and_get_cgroup(const char *relative_path) { char cgroup_path[PATH_MAX + 1]; int fd; =20 - format_cgroup_path(cgroup_path, path); + format_cgroup_path(cgroup_path, relative_path); if (mkdir(cgroup_path, 0777) && errno !=3D EEXIST) { - log_err("mkdiring cgroup %s .. %s", path, cgroup_path); + log_err("mkdiring cgroup %s .. %s", relative_path, cgroup_path); return -1; } =20 @@ -244,13 +321,13 @@ int create_and_get_cgroup(const char *path) =20 /** * get_cgroup_id() - Get cgroup id for a particular cgroup path - * @path: The cgroup path, relative to the workdir, to join + * @relative_path: The cgroup path, relative to the workdir, to join * * On success, it returns the cgroup id. On failure it returns 0, * which is an invalid cgroup id. * If there is a failure, it prints the error to stderr. */ -unsigned long long get_cgroup_id(const char *path) +unsigned long long get_cgroup_id(const char *relative_path) { int dirfd, err, flags, mount_id, fhsize; union { @@ -261,7 +338,7 @@ unsigned long long get_cgroup_id(const char *path) struct file_handle *fhp, *fhp2; unsigned long long ret =3D 0; =20 - format_cgroup_path(cgroup_workdir, path); + format_cgroup_path(cgroup_workdir, relative_path); =20 dirfd =3D AT_FDCWD; flags =3D 0; diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/s= elftests/bpf/cgroup_helpers.h index fcc9cb91b211..6b1d905557c7 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.h +++ b/tools/testing/selftests/bpf/cgroup_helpers.h @@ -10,11 +10,15 @@ __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) =20 /* cgroupv2 related */ -int cgroup_setup_and_join(const char *path); -int create_and_get_cgroup(const char *path); -unsigned long long get_cgroup_id(const char *path); +int enable_controllers(const char *relative_path, const char *controllers); +int write_cgroup_file(const char *relative_path, const char *file, + const char *buf); +int cgroup_setup_and_join(const char *relative_path); +int create_and_get_cgroup(const char *relative_path); +unsigned long long get_cgroup_id(const char *relative_path); =20 -int join_cgroup(const char *path); +int join_cgroup(const char *relative_path); +int join_parent_cgroup(const char *relative_path); =20 int setup_cgroup_environment(void); void cleanup_cgroup_environment(void); @@ -26,4 +30,4 @@ int join_classid(void); int setup_classid_environment(void); void cleanup_classid_environment(void); =20 -#endif /* __CGROUP_HELPERS_H */ \ No newline at end of file +#endif /* __CGROUP_HELPERS_H */ --=20 2.36.1.124.g0e6072fb45-goog From nobody Thu May 7 19:50:57 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB8BAC433F5 for ; Fri, 20 May 2022 01:22:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344514AbiETBWO (ORCPT ); Thu, 19 May 2022 21:22:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56528 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344454AbiETBV4 (ORCPT ); Thu, 19 May 2022 21:21:56 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 69D282C677 for ; Thu, 19 May 2022 18:21:47 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id f2-20020a17090274c200b0016158e7c490so3353622plt.9 for ; Thu, 19 May 2022 18:21:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=sKIQnfquWdecZ9McyX/vUzv09szePilO1xsOhvXjmng=; b=isgv2TCt315YbAOh0HfR4pphfXmJ/2o+6Wca+8ICKvBOFXfyPEGcJt1prDu0BVd2k5 7P8U54t3vvjAb1hdXqaAEdSMXX4wkCdUx2AkSM65AEVy8s9WZAw3rC+dhJKTv1MVMIxy iXtpitmzLriGkuSOpDbePkAA7MHsIljJs/ZFel35PXAsOc7V6S7Y/thwtlYoScHXTyjN ExWHPuj2Zuy9Fawx347aqcUkiNQuLyRkuxW/Dw6YMq8m1F92TPUfW998qS60AtVw//8X N1nqenn4AKF07BlLCAIlDRSXPvSVjeQKVEUHuvdiVBSOHAPawwYV+fA3jAvDEL7L5imO ctBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=sKIQnfquWdecZ9McyX/vUzv09szePilO1xsOhvXjmng=; b=V2fTzOOYB9F8YFk/A7NT5UDC8rEvRc+T/IKl/RRLCiQzXQ6TXgvCis4ztIvZYg5ktZ 3S74pW9T8jwwB8svX6HTgzEFwe1iYcbgjAiPGs36jeduHaGtWF7+LnNBbzkfslhKFELs jyij1GUsqm1kJWXfjq8xnGoT+Et3vEMk+o+VDth4dcMScE+Zt0z4TWpCzeCt0ZferL0Z hYsn/bp08lYFaCrVafYTtq0jsObvY0+Pppoann1FToXfEa/GVCQbGryBSdas+5q34S9r 86+SrMRLdmg2ldLOxB04Jidwo3oq1QLrhfh3bKfuld7Nz6aHvbCzErsn0KMnVXeSnVTm kBEg== X-Gm-Message-State: AOAM532ESxDr+yXFMmqN6Tz43rq8OJ1UGy12jTOKZq2nZrDguK57YvWj 8jz6tgLXKqemqdZ0h77v2r/DC2IUxVv1iN62 X-Google-Smtp-Source: ABdhPJxvj+oMT/Y1pEfemNuKHmAw+Kf5AHjgGvizfR6T4uMhOa4z8z3lx+IYbfPMX0AKaZgdtChZ9Xcla2S3YZb1 X-Received: from yosry.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:2327]) (user=yosryahmed job=sendgmr) by 2002:a65:554d:0:b0:3c6:3f22:72cd with SMTP id t13-20020a65554d000000b003c63f2272cdmr6274189pgr.283.1653009706668; Thu, 19 May 2022 18:21:46 -0700 (PDT) Date: Fri, 20 May 2022 01:21:33 +0000 In-Reply-To: <20220520012133.1217211-1-yosryahmed@google.com> Message-Id: <20220520012133.1217211-6-yosryahmed@google.com> Mime-Version: 1.0 References: <20220520012133.1217211-1-yosryahmed@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH bpf-next v1 5/5] bpf: add a selftest for cgroup hierarchical stats collection From: Yosry Ahmed To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Hao Luo , Tejun Heo , Zefan Li , Johannes Weiner , Shuah Khan , Roman Gushchin , Michal Hocko Cc: Stanislav Fomichev , David Rientjes , Greg Thelen , Shakeel Butt , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, cgroups@vger.kernel.org, Yosry Ahmed Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a selftest that tests the whole workflow for collecting, aggregating, and display cgroup hierarchical stats. TL;DR: - Whenever reclaim happens, vmscan_start and vmscan_end update per-cgroup percpu readings, and tell rstat which (cgroup, cpu) pairs have updates. - When userspace tries to read the stats, vmscan_dump calls rstat to flush the stats. - rstat calls vmscan_flush once for every (cgroup, cpu) pair that has updates, vmscan_flush aggregates cpu readings and propagates updates to parents. Detailed explanation: - The test loads tracing bpf programs, vmscan_start and vmscan_end, to measure the latency of cgroup reclaim. Per-cgroup ratings are stored in percpu maps for efficiency. When a cgroup reading is updated on a cpu, cgroup_rstat_updated(cgroup, cpu) is called to add the cgroup to the rstat updated tree on that cpu. - A cgroup_iter program, vmscan_dump, is loaded and pinned to a file, for each cgroup. Reading this file invokes the program, which calls cgroup_rstat_flush(cgroup) to ask rstat to propagate the updates for all cpus and cgroups that have updates in this cgroup's subtree. Afterwards, the stats are exposed to the user. - An ftrace program, vmscan_flush, is also loaded and attached to bpf_rstat_flush. When rstat flushing is ongoing, vmscan_flush is invoked once for each (cgroup, cpu) pair that has updates. cgroups are popped from the rstat tree in a bottom-up fashion, so calls will always be made for cgroups that have updates before their parents. The program aggregates percpu readings to a total per-cgroup reading, and also propagates them to the parent cgroup. After rstat flushing is over, all cgroups will have correct updated hierarchical readings (including all cpus and all their descendants). Signed-off-by: Yosry Ahmed --- .../test_cgroup_hierarchical_stats.c | 339 ++++++++++++++++++ tools/testing/selftests/bpf/progs/bpf_iter.h | 7 + .../selftests/bpf/progs/cgroup_vmscan.c | 221 ++++++++++++ 3 files changed, 567 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/test_cgroup_hier= archical_stats.c create mode 100644 tools/testing/selftests/bpf/progs/cgroup_vmscan.c diff --git a/tools/testing/selftests/bpf/prog_tests/test_cgroup_hierarchica= l_stats.c b/tools/testing/selftests/bpf/prog_tests/test_cgroup_hierarchical= _stats.c new file mode 100644 index 000000000000..e560c1f6291f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_cgroup_hierarchical_stats= .c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Functions to manage eBPF programs attached to cgroup subsystems + * + * Copyright 2022 Google LLC. + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cgroup_helpers.h" +#include "cgroup_vmscan.skel.h" + +#define PAGE_SIZE 4096 +#define MB(x) (x << 20) + +#define BPFFS_ROOT "/sys/fs/bpf/" +#define BPFFS_VMSCAN BPFFS_ROOT"vmscan/" + +#define CG_ROOT_NAME "root" +#define CG_ROOT_ID 1 + +#define CGROUP_PATH(p, n) {.name =3D #n, .path =3D #p"/"#n} + +static struct { + const char *name, *path; + unsigned long long id; + int fd; +} cgroups[] =3D { + CGROUP_PATH(/, test), + CGROUP_PATH(/test, child1), + CGROUP_PATH(/test, child2), + CGROUP_PATH(/test/child1, child1_1), + CGROUP_PATH(/test/child1, child1_2), + CGROUP_PATH(/test/child2, child2_1), + CGROUP_PATH(/test/child2, child2_2), +}; + +#define N_CGROUPS ARRAY_SIZE(cgroups) +#define N_NON_LEAF_CGROUPS 3 + +bool mounted_bpffs; +static int duration; + +static int read_from_file(const char *path, char *buf, size_t size) +{ + int fd, len; + + fd =3D open(path, O_RDONLY); + if (fd < 0) { + log_err("Open %s", path); + return -errno; + } + len =3D read(fd, buf, size); + if (len < 0) + log_err("Read %s", path); + else + buf[len] =3D 0; + close(fd); + return len < 0 ? -errno : 0; +} + +static int setup_bpffs(void) +{ + int err; + + /* Mount bpffs */ + err =3D mount("bpf", BPFFS_ROOT, "bpf", 0, NULL); + mounted_bpffs =3D !err; + if (CHECK(err && errno !=3D EBUSY, "mount bpffs", + "failed to mount bpffs at %s (%s)\n", BPFFS_ROOT, + strerror(errno))) + return err; + + /* Create a directory to contain stat files in bpffs */ + err =3D mkdir(BPFFS_VMSCAN, 0755); + CHECK(err, "mkdir bpffs", "failed to mkdir %s (%s)\n", + BPFFS_VMSCAN, strerror(errno)); + return err; +} + +static void cleanup_bpffs(void) +{ + /* Remove created directory in bpffs */ + CHECK(rmdir(BPFFS_VMSCAN), "rmdir", "failed to rmdir %s (%s)\n", + BPFFS_VMSCAN, strerror(errno)); + + /* Unmount bpffs, if it wasn't already mounted when we started */ + if (mounted_bpffs) + return; + CHECK(umount(BPFFS_ROOT), "umount", "failed to unmount bpffs (%s)\n", + strerror(errno)); +} + +static int setup_cgroups(void) +{ + int i, err; + + err =3D setup_cgroup_environment(); + if (CHECK(err, "setup_cgroup_environment", "failed: %d\n", err)) + return err; + + for (i =3D 0; i < N_CGROUPS; i++) { + int fd; + + fd =3D create_and_get_cgroup(cgroups[i].path); + if (!ASSERT_GE(fd, 0, "create_and_get_cgroup")) + return fd; + + cgroups[i].fd =3D fd; + cgroups[i].id =3D get_cgroup_id(cgroups[i].path); + if (i < N_NON_LEAF_CGROUPS) { + err =3D enable_controllers(cgroups[i].path, "memory"); + if (!ASSERT_OK(err, "enable_controllers")) + return err; + } + } + return 0; +} + +static void cleanup_cgroups(void) +{ + for (int i =3D 0; i < N_CGROUPS; i++) + close(cgroups[i].fd); + cleanup_cgroup_environment(); +} + + +static int setup_hierarchy(void) +{ + return setup_bpffs() || setup_cgroups(); +} + +static void destroy_hierarchy(void) +{ + cleanup_cgroups(); + cleanup_bpffs(); +} + +static void alloc_anon(size_t size) +{ + char *buf, *ptr; + + buf =3D malloc(size); + for (ptr =3D buf; ptr < buf + size; ptr +=3D PAGE_SIZE) + *ptr =3D 0; + free(buf); +} + +static int induce_vmscan(void) +{ + char size[128]; + int i, err; + + /* + * Set memory.high for test parent cgroup to 1 MB to throttle + * allocations and invoke reclaim in children. + */ + snprintf(size, 128, "%d", MB(1)); + err =3D write_cgroup_file(cgroups[0].path, "memory.high", size); + if (!ASSERT_OK(err, "write memory.high")) + return err; + /* + * In every leaf cgroup, run a memory hog for a few seconds to induce + * reclaim then kill it. + */ + for (i =3D N_NON_LEAF_CGROUPS; i < N_CGROUPS; i++) { + pid_t pid =3D fork(); + + if (pid =3D=3D 0) { + /* Join cgroup in the parent process workdir */ + join_parent_cgroup(cgroups[i].path); + + /* Allocate more memory than memory.high */ + alloc_anon(MB(2)); + exit(0); + } else { + /* Wait for child to cause reclaim then kill it */ + if (!ASSERT_GT(pid, 0, "fork")) + return pid; + sleep(2); + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); + } + } + return 0; +} + +static unsigned long long get_cgroup_vmscan(unsigned long long cgroup_id, + const char *file_name) +{ + char buf[128], path[128]; + unsigned long long vmscan =3D 0, id =3D 0; + int err; + + /* For every cgroup, read the file generated by cgroup_iter */ + snprintf(path, 128, "%s%s", BPFFS_VMSCAN, file_name); + err =3D read_from_file(path, buf, 128); + if (CHECK(err, "read", "failed to read from %s (%s)\n", + path, strerror(errno))) + return 0; + + /* Check the output file formatting */ + ASSERT_EQ(sscanf(buf, "cg_id: %llu, total_vmscan_delay: %llu\n", + &id, &vmscan), 2, "output format"); + + /* Check that the cgroup_id is displayed correctly */ + ASSERT_EQ(cgroup_id, id, "cgroup_id"); + /* Check that the vmscan reading is non-zero */ + ASSERT_NEQ(vmscan, 0, "vmscan_reading"); + return vmscan; +} + +static void check_vmscan_stats(void) +{ + int i; + unsigned long long vmscan_readings[N_CGROUPS], vmscan_root; + + for (i =3D 0; i < N_CGROUPS; i++) + vmscan_readings[i] =3D get_cgroup_vmscan(cgroups[i].id, + cgroups[i].name); + + /* Read stats for root too */ + vmscan_root =3D get_cgroup_vmscan(CG_ROOT_ID, CG_ROOT_NAME); + + /* Check that child1 =3D=3D child1_1 + child1_2 */ + ASSERT_EQ(vmscan_readings[1], vmscan_readings[3] + vmscan_readings[4], + "child1_vmscan"); + /* Check that child2 =3D=3D child2_1 + child2_2 */ + ASSERT_EQ(vmscan_readings[2], vmscan_readings[5] + vmscan_readings[6], + "child2_vmscan"); + /* Check that test =3D=3D child1 + child2 */ + ASSERT_EQ(vmscan_readings[0], vmscan_readings[1] + vmscan_readings[2], + "test_vmscan"); + /* Check that root >=3D test */ + ASSERT_GE(vmscan_root, vmscan_readings[1], "root_vmscan"); +} + +static int setup_cgroup_iter(struct cgroup_vmscan *obj, + unsigned long long cgroup_id, + const char *file_name) +{ + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo =3D {}; + struct bpf_link *link; + char path[128]; + int err; + + /* Create an iter link, parameterized by cgroup id */ + linfo.cgroup.cgroup_id =3D cgroup_id; + opts.link_info =3D &linfo; + opts.link_info_len =3D sizeof(linfo); + link =3D bpf_program__attach_iter(obj->progs.dump_vmscan, &opts); + if (!ASSERT_OK_PTR(link, "attach iter")) + return libbpf_get_error(link); + + /* Pin the link to a bpffs file */ + snprintf(path, 128, "%s%s", BPFFS_VMSCAN, file_name); + err =3D bpf_link__pin(link, path); + CHECK(err, "pin iter", "failed to pin iter at %s", path); + return err; +} + +static int setup_progs(struct cgroup_vmscan **skel) +{ + int i; + struct bpf_link *link; + struct cgroup_vmscan *obj; + + obj =3D cgroup_vmscan__open_and_load(); + if (!ASSERT_OK_PTR(obj, "open_and_load")) + return libbpf_get_error(obj); + + /* Attach cgroup_iter program that will dump the stats to cgroups */ + for (i =3D 0; i < N_CGROUPS; i++) + setup_cgroup_iter(obj, cgroups[i].id, cgroups[i].name); + /* Also dump stats for root */ + setup_cgroup_iter(obj, CG_ROOT_ID, CG_ROOT_NAME); + + /* Attach rstat flusher */ + link =3D bpf_program__attach(obj->progs.vmscan_flush); + if (!ASSERT_OK_PTR(link, "attach rstat")) + return libbpf_get_error(link); + + /* Attach tracing programs that will calculate vmscan delays */ + link =3D bpf_program__attach(obj->progs.vmscan_start); + if (!ASSERT_OK_PTR(obj, "attach raw_tracepoint")) + return libbpf_get_error(obj); + + link =3D bpf_program__attach(obj->progs.vmscan_end); + if (!ASSERT_OK_PTR(obj, "attach raw_tracepoint")) + return libbpf_get_error(obj); + + *skel =3D obj; + return 0; +} + +void destroy_progs(struct cgroup_vmscan *skel) +{ + char path[128]; + int i; + + for (i =3D 0; i < N_CGROUPS; i++) { + /* Delete files in bpffs that cgroup_iters are pinned in */ + snprintf(path, 128, "%s%s", BPFFS_VMSCAN, + cgroups[i].name); + CHECK(remove(path), "remove", "failed to remove %s (%s)\n", + path, strerror(errno)); + } + + /* Delete root file in bpffs */ + snprintf(path, 128, "%s%s", BPFFS_VMSCAN, CG_ROOT_NAME); + CHECK(remove(path), "remove", "failed to remove %s (%s)\n", path, + strerror(errno)); + cgroup_vmscan__destroy(skel); +} + +void test_cgroup_hierarchical_stats(void) +{ + struct cgroup_vmscan *skel =3D NULL; + + if (setup_hierarchy()) + goto hierarchy_cleanup; + if (setup_progs(&skel)) + goto cleanup; + if (induce_vmscan()) + goto cleanup; + check_vmscan_stats(); +cleanup: + destroy_progs(skel); +hierarchy_cleanup: + destroy_hierarchy(); +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter.h b/tools/testing/s= elftests/bpf/progs/bpf_iter.h index 97ec8bc76ae6..df91f1daf74d 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter.h +++ b/tools/testing/selftests/bpf/progs/bpf_iter.h @@ -17,6 +17,7 @@ #define bpf_iter__bpf_sk_storage_map bpf_iter__bpf_sk_storage_map___not_us= ed #define bpf_iter__sockmap bpf_iter__sockmap___not_used #define bpf_iter__bpf_link bpf_iter__bpf_link___not_used +#define bpf_iter__cgroup bpf_iter__cgroup__not_used #define btf_ptr btf_ptr___not_used #define BTF_F_COMPACT BTF_F_COMPACT___not_used #define BTF_F_NONAME BTF_F_NONAME___not_used @@ -39,6 +40,7 @@ #undef bpf_iter__bpf_sk_storage_map #undef bpf_iter__sockmap #undef bpf_iter__bpf_link +#undef bpf_iter__cgroup #undef btf_ptr #undef BTF_F_COMPACT #undef BTF_F_NONAME @@ -139,6 +141,11 @@ struct bpf_iter__bpf_link { struct bpf_link *link; }; =20 +struct bpf_iter__cgroup { + struct bpf_iter_meta *meta; + struct cgroup *cgroup; +} __attribute((preserve_access_index)); + struct btf_ptr { void *ptr; __u32 type_id; diff --git a/tools/testing/selftests/bpf/progs/cgroup_vmscan.c b/tools/test= ing/selftests/bpf/progs/cgroup_vmscan.c new file mode 100644 index 000000000000..9d7c72c213ad --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_vmscan.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Functions to manage eBPF programs attached to cgroup subsystems + * + * Copyright 2022 Google LLC. + */ +#include "bpf_iter.h" +#include +#include + +char _license[] SEC("license") =3D "GPL"; + +/* + * Start times are stored per-task, not per-cgroup, as multiple tasks in o= ne + * cgroup can perform reclain concurrently. + */ +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, __u64); +} vmscan_start_time SEC(".maps"); + +struct vmscan_percpu { + /* Previous percpu state, to figure out if we have new updates */ + __u64 prev; + /* Current percpu state */ + __u64 state; +}; + +struct vmscan { + /* State propagated through children, pending aggregation */ + __u64 pending; + /* Total state, including all cpus and all children */ + __u64 state; +}; + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); + __uint(max_entries, 10); + __type(key, __u64); + __type(value, struct vmscan_percpu); +} pcpu_cgroup_vmscan_elapsed SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10); + __type(key, __u64); + __type(value, struct vmscan); +} cgroup_vmscan_elapsed SEC(".maps"); + +extern void cgroup_rstat_updated(struct cgroup *cgrp, int cpu) __ksym; +extern void cgroup_rstat_flush(struct cgroup *cgrp) __ksym; + +static inline bool memory_subsys_enabled(struct cgroup *cgrp) +{ + return cgrp->subsys[memory_cgrp_id] !=3D NULL; +} + +static inline struct cgroup *task_memcg(struct task_struct *task) +{ + return task->cgroups->subsys[memory_cgrp_id]->cgroup; +} + +static inline uint64_t cgroup_id(struct cgroup *cgrp) +{ + return cgrp->kn->id; +} + +static inline int create_vmscan_percpu_elem(__u64 cg_id, __u64 state) +{ + struct vmscan_percpu pcpu_init =3D {.state =3D state, .prev =3D 0}; + + if (bpf_map_update_elem(&pcpu_cgroup_vmscan_elapsed, &cg_id, + &pcpu_init, BPF_NOEXIST)) { + bpf_printk("failed to create pcpu entry for cgroup %llu\n" + , cg_id); + return 1; + } + return 0; +} + +static inline int create_vmscan_elem(__u64 cg_id, __u64 state, __u64 pendi= ng) +{ + struct vmscan init =3D {.state =3D state, .pending =3D pending}; + + if (bpf_map_update_elem(&cgroup_vmscan_elapsed, &cg_id, + &init, BPF_NOEXIST)) { + bpf_printk("failed to create entry for cgroup %llu\n" + , cg_id); + return 1; + } + return 0; +} + +SEC("tp_btf/mm_vmscan_memcg_reclaim_begin") +int BPF_PROG(vmscan_start, struct lruvec *lruvec, struct scan_control *sc) +{ + struct task_struct *task =3D bpf_get_current_task_btf(); + __u64 *start_time_ptr; + + start_time_ptr =3D bpf_task_storage_get(&vmscan_start_time, task, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!start_time_ptr) { + bpf_printk("error retrieving storage\n"); + return 0; + } + + *start_time_ptr =3D bpf_ktime_get_ns(); + return 0; +} + +SEC("tp_btf/mm_vmscan_memcg_reclaim_end") +int BPF_PROG(vmscan_end, struct lruvec *lruvec, struct scan_control *sc) +{ + struct vmscan_percpu *pcpu_stat; + struct task_struct *current =3D bpf_get_current_task_btf(); + struct cgroup *cgrp =3D task_memcg(current); + __u64 *start_time_ptr; + __u64 current_elapsed, cg_id; + __u64 end_time =3D bpf_ktime_get_ns(); + + /* cgrp may not have memory controller enabled */ + if (!cgrp) + return 0; + + cg_id =3D cgroup_id(cgrp); + start_time_ptr =3D bpf_task_storage_get(&vmscan_start_time, current, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!start_time_ptr) { + bpf_printk("error retrieving storage local storage\n"); + return 0; + } + + current_elapsed =3D end_time - *start_time_ptr; + pcpu_stat =3D bpf_map_lookup_elem(&pcpu_cgroup_vmscan_elapsed, + &cg_id); + if (pcpu_stat) + __sync_fetch_and_add(&pcpu_stat->state, current_elapsed); + else + create_vmscan_percpu_elem(cg_id, current_elapsed); + + cgroup_rstat_updated(cgrp, bpf_get_smp_processor_id()); + return 0; +} + +SEC("fentry/bpf_rstat_flush") +int BPF_PROG(vmscan_flush, struct cgroup *cgrp, struct cgroup *parent, int= cpu) +{ + struct vmscan_percpu *pcpu_stat; + struct vmscan *total_stat, *parent_stat; + __u64 cg_id =3D cgroup_id(cgrp); + __u64 parent_cg_id =3D parent ? cgroup_id(parent) : 0; + __u64 *pcpu_vmscan; + __u64 state; + __u64 delta =3D 0; + + if (!memory_subsys_enabled(cgrp)) + return 0; + + /* Add CPU changes on this level since the last flush */ + pcpu_stat =3D bpf_map_lookup_percpu_elem(&pcpu_cgroup_vmscan_elapsed, + &cg_id, cpu); + if (pcpu_stat) { + state =3D pcpu_stat->state; + delta +=3D state - pcpu_stat->prev; + pcpu_stat->prev =3D state; + } + + total_stat =3D bpf_map_lookup_elem(&cgroup_vmscan_elapsed, &cg_id); + if (!total_stat) { + create_vmscan_elem(cg_id, delta, 0); + goto update_parent; + } + + /* Collect pending stats from subtree */ + if (total_stat->pending) { + delta +=3D total_stat->pending; + total_stat->pending =3D 0; + } + + /* Propagate changes to this cgroup's total */ + total_stat->state +=3D delta; + +update_parent: + /* Skip if there are no changes to propagate, or no parent */ + if (!delta || !parent_cg_id) + return 0; + + /* Propagate changes to cgroup's parent */ + parent_stat =3D bpf_map_lookup_elem(&cgroup_vmscan_elapsed, + &parent_cg_id); + if (parent_stat) + parent_stat->pending +=3D delta; + else + create_vmscan_elem(parent_cg_id, 0, delta); + + return 0; +} + +SEC("iter.s/cgroup") +int BPF_PROG(dump_vmscan, struct bpf_iter_meta *meta, struct cgroup *cgrp) +{ + struct seq_file *seq =3D meta->seq; + struct vmscan *total_stat; + __u64 cg_id =3D cgroup_id(cgrp); + + /* Flush the stats to make sure we get the most updated numbers */ + cgroup_rstat_flush(cgrp); + + total_stat =3D bpf_map_lookup_elem(&cgroup_vmscan_elapsed, &cg_id); + if (!total_stat) { + bpf_printk("error finding stats for cgroup %llu\n", cg_id); + BPF_SEQ_PRINTF(seq, "cg_id: -1, total_vmscan_delay: -1\n"); + return 0; + } + BPF_SEQ_PRINTF(seq, "cg_id: %llu, total_vmscan_delay: %llu\n", + cg_id, total_stat->state); + return 0; +} + --=20 2.36.1.124.g0e6072fb45-goog