From patchwork Fri May 20 01:21:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yosry Ahmed X-Patchwork-Id: 12856174 X-Patchwork-Delegate: bpf@iogearbox.net 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 CB0CBC43217 for ; Fri, 20 May 2022 01:21:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344411AbiETBVn (ORCPT ); Thu, 19 May 2022 21:21:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344388AbiETBVl (ORCPT ); Thu, 19 May 2022 21:21:41 -0400 Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4B73629834 for ; Thu, 19 May 2022 18:21:40 -0700 (PDT) Received: by mail-pf1-x449.google.com with SMTP id m21-20020aa78a15000000b005182fda1b15so2879637pfa.21 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=YeuwuR6Kfbg5ig3AIj1AwaBAJrOCJoucYAwuiKd5rvxIcI2w0jsNsfW/1xlVSlnk5o LXzLUsjfRN168HS3/YOp/oKOHdybmOinwBxedqUfNEMefoVQ/CPebnWYl56WALG1+wEv EzVbaKLCXDuJJ4ykyV/WF9fr0wc1HzK5E6mnBww6LhwnGaeqfC1zMHfjDAVfxg0bEXLi KO3qt74WmzN5D8NMhoDIoRAqjKGXt56AzVHw9J8loWy2c7oKjnACVlGcZqaLuhFMbW1A FhgML8icgYJnEV6SW5WMId1GzHvXj8RlIX5kil2hqmpxpyLqqvr6dtnsjJ13072AqMdx Q5ng== X-Gm-Message-State: AOAM531VEBG038Bjv6bpaZQ8292078vFqLFUNASDVT2vTd/L9u2mCXaq iX/ZwqfNPqoPkpxeAkC8x60Ykbsulpul+Zu3 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: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net 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 Reported-by: kernel test robot 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(struct cgroup *pos, return pos; } +/* 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 *cgrp, bool may_sleep) struct cgroup_subsys_state *css; cgroup_base_stat_flush(pos, cpu); + bpf_rstat_flush(pos, cgroup_parent(pos), cpu); rcu_read_lock(); list_for_each_entry_rcu(css, &pos->rstat_css_list, From patchwork Fri May 20 01:21:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yosry Ahmed X-Patchwork-Id: 12856176 X-Patchwork-Delegate: bpf@iogearbox.net 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 D599DC433F5 for ; Fri, 20 May 2022 01:22:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344464AbiETBV4 (ORCPT ); Thu, 19 May 2022 21:21:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55628 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344395AbiETBVn (ORCPT ); Thu, 19 May 2022 21:21:43 -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 F3C4227CC7 for ; Thu, 19 May 2022 18:21:41 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id e18-20020a170902ef5200b0016153d857a6so3352423plx.5 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=ANnMhkVjXsNYNasVpTkjmm6drklJJhe6+TPkMBQRDUyMUtog5zYvi74xsktOq8+bRK eSwnx1BJSi/4Sg0S+or4BIdl6nNpgyC1C8FO6KjquCI75wKBCz1Udj+vCNGMwRb3lkzX izuW3WCNB4xmaG5ew6oEgkxLoYdJ5vQV00ciOQzVaoQYJayVObVdL7DHgfErsnj+4E7V 94PjNeRESutNFrtCXwTfBrW6HKBmTvRf5JK1Rae8V1SwNsrn82UwEcR1YjZKTlJkDOAz GQlBlYA3oHX3VFzIJ03mYTKxFOlzj6ux1vo1+Ys4B6gDLALEnNHCkUT79jCliz1txUty V8IA== X-Gm-Message-State: AOAM532ETkgx0Fa3xXSSjzDxU3YPAFw2e3Q1kxExVMZWxY9tapiHVabR 28r2qpmtIy5CfioFr5wk+liJmDvHMDzEy1VK 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: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net 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 @@ #include +#include +#include +#include + + static DEFINE_SPINLOCK(cgroup_rstat_lock); static DEFINE_PER_CPU(raw_spinlock_t, cgroup_rstat_cpu_lock); @@ -141,7 +146,12 @@ static struct cgroup *cgroup_rstat_cpu_pop_updated(struct cgroup *pos, return pos; } -/* 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 *seq) "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 = { + .owner = THIS_MODULE, + .check_set = &bpf_rstat_check_kfunc_ids, + .sleepable_set = &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); From patchwork Fri May 20 01:21:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yosry Ahmed X-Patchwork-Id: 12856175 X-Patchwork-Delegate: bpf@iogearbox.net 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 9D8A6C433FE for ; Fri, 20 May 2022 01:21:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344409AbiETBVy (ORCPT ); Thu, 19 May 2022 21:21:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55762 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344414AbiETBVo (ORCPT ); Thu, 19 May 2022 21:21:44 -0400 Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB8D429831 for ; Thu, 19 May 2022 18:21:43 -0700 (PDT) Received: by mail-pl1-x64a.google.com with SMTP id d4-20020a170902c18400b001619cd42b0aso3349131pld.19 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=6zl1hxgnnzRBNPwWKu6aJgEG7AmYVrzt8YR12pgi+Ma8pVin7JoZxEfdd20ScVwqay t+U6Q06wCeOAM3SLixgjmPS7+EHGVzzK9zyj0OEwx28ugTuo5S6pCLkyT4qgdLkDsxag fW73J1VSodfhtdpTk/JLtdLnEw8rtAmtM2Ozo8mzQ/gkZFjhi0T4lQTAEJ+vTp2z2+lj swSKJim35u8klnl7gILTmEGVi3ANiSMoUYc1gBrOdY8rujz9PyQodMJYPPPq1pY4Ax+a vQrOEFaPodlJAtpuAPl1A6ymkahFZ/HXRZN+vD4CjOqWJCaBxJ5PMU17F0kTjOfynhYv sVpw== X-Gm-Message-State: AOAM532/nJrNC8tobGklT7l38Xsv6N+dUrSxuQA2cVQP8txEmwvUn53s qCbMi0iwGqacsAVVNf8w13EvRT7FeSNVE/uV 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: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net 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; 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); struct bpf_iter_aux_info { struct bpf_map *map; + struct cgroup *cgroup; }; 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; }; /* 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) += bpf_struct_ops.o obj-${CONFIG_BPF_LSM} += bpf_lsm.o endif obj-$(CONFIG_BPF_PRELOAD) += preload/ +ifeq ($(CONFIG_CGROUPS),y) +obj-$(CONFIG_BPF_SYSCALL) += cgroup_iter.o +endif obj-$(CONFIG_BPF_SYSCALL) += 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 == 0) + ++*pos; + + return *(struct cgroup **)seq->private; +} + +static void *cgroup_iter_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*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 = 0; + + ctx.meta = &meta; + ctx.cgroup = v; + meta.seq = seq; + prog = bpf_iter_get_info(&meta, false); + if (prog) + ret = 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 = { + .start = cgroup_iter_seq_start, + .next = cgroup_iter_seq_next, + .stop = cgroup_iter_seq_stop, + .show = 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 = aux->cgroup; + return 0; +} + +static const struct bpf_iter_seq_info cgroup_iter_seq_info = { + .seq_ops = &cgroup_iter_seq_ops, + .init_seq_private = cgroup_iter_seq_init, + .seq_priv_size = 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 = cgroup_get_from_id(linfo->cgroup.cgroup_id); + if (!cgroup) + return -EBUSY; + + aux->cgroup = 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 *aux, + struct seq_file *seq) +{ + char *buf; + + seq_printf(seq, "cgroup_id:\t%llu\n", cgroup_id(aux->cgroup)); + + buf = 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 = 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 = { + .target = "cgroup", + .attach_target = bpf_iter_attach_cgroup, + .detach_target = bpf_iter_detach_cgroup, + .show_fdinfo = bpf_iter_cgroup_show_fdinfo, + .fill_link_info = bpf_iter_cgroup_fill_link_info, + .ctx_arg_info_size = 1, + .ctx_arg_info = { + { offsetof(struct bpf_iter__cgroup, cgroup), + PTR_TO_BTF_ID }, + }, + .seq_info = &cgroup_iter_seq_info, +}; + +static int __init bpf_cgroup_iter_init(void) +{ + bpf_cgroup_reg_info.ctx_arg_info[0].btf_id = 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; }; /* 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 { From patchwork Fri May 20 01:21:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yosry Ahmed X-Patchwork-Id: 12856178 X-Patchwork-Delegate: bpf@iogearbox.net 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 039C3C433FE for ; Fri, 20 May 2022 01:22:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344507AbiETBWB (ORCPT ); Thu, 19 May 2022 21:22:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56532 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344438AbiETBVv (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 98BC32AC7B for ; Thu, 19 May 2022 18:21:45 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id e4-20020a17090301c400b00161995eef69so3347525plh.12 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=PboT0Ve94m1RHdy5/4MjRTnHBpwMkHtZpxrqlLroOy8fdI8lb8NFIeJTSWqLq48DM7 /3zqSI3FBFp/UpFhWda6CQn5w4HruXdIlOrYxk99oPG+mPoMENytAcfvIIL1cBbBzH3+ 3AnXH6dnPwmr5riSqepmlAed+dn15Smf5wQKaQbM62cJg50CYmE3HkWCk1bDIQWvN51C 0ELfHZZTLrrCKSxt/8c3WN95Ffp8s1YwO1ErBnvjS+MeaCRhrUavMurv144zINPnuic9 PzJOyV6oX2sMXtGmh/rTEyU5/1CkytozzoMPnHolbH1RX48VU0sDvAvJoFAdlW7ToYPs XhYg== X-Gm-Message-State: AOAM5323vLkvHsVW4kr17Y2JWyCRp1GCPhk7o2/D6OrSERqr1ihW6SAA 0gA7JGsiVbhoMRRdEWkFNm65VIYqNL31bYyV 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: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net 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/selftests/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()) #define format_classid_path(buf) \ snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \ CGROUP_WORK_DIR) -/** - * 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 *controllers) { char path[PATH_MAX + 1]; - char buf[PATH_MAX]; + char enable[PATH_MAX + 1]; char *c, *c2; int fd, cfd; ssize_t len; - snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path); - fd = 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 = open(path, O_RDONLY); + if (fd < 0) { + log_err("Opening cgroup.controllers: %s", path); + return 1; + } - len = read(fd, buf, sizeof(buf) - 1); - if (len < 0) { + len = read(fd, enable, sizeof(enable) - 1); + if (len < 0) { + close(fd); + log_err("Reading cgroup.controllers: %s", path); + return 1; + } else if (len == 0) /* No controllers to enable */ + return 0; + enable[len] = 0; close(fd); - log_err("Reading cgroup.controllers: %s", path); - return 1; - } - buf[len] = 0; - close(fd); - - /* No controllers available? We're probably on cgroup v1. */ - if (len == 0) - return 0; + } else + strncpy(enable, controllers, sizeof(enable)); snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path); cfd = open(path, O_RDWR); @@ -84,7 +86,7 @@ static int enable_all_controllers(char *cgroup_path) return 1; } - for (c = strtok_r(buf, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) { + for (c = strtok_r(enable, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) { if (dprintf(cfd, "+%s\n", c) <= 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; } +/** + * 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 = open(file_path, O_RDWR); + if (fd < 0) { + log_err("Opening cgroup.subtree_control: %s", file_path); + return 1; + } + + if (dprintf(fd, "%s", buf) <= 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; } - 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; return 0; @@ -173,7 +234,7 @@ static int join_cgroup_from_top(const char *cgroup_path) /** * 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 cgroup * 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_path) * * 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]; - format_cgroup_path(cgroup_path, path); + format_parent_cgroup_path(cgroup_path, relative_path); return join_cgroup_from_top(cgroup_path); } @@ -214,7 +291,7 @@ void cleanup_cgroup_environment(void) /** * 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; - format_cgroup_path(cgroup_path, path); + format_cgroup_path(cgroup_path, relative_path); if (mkdir(cgroup_path, 0777) && errno != EEXIST) { - log_err("mkdiring cgroup %s .. %s", path, cgroup_path); + log_err("mkdiring cgroup %s .. %s", relative_path, cgroup_path); return -1; } @@ -244,13 +321,13 @@ int create_and_get_cgroup(const char *path) /** * 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 = 0; - format_cgroup_path(cgroup_workdir, path); + format_cgroup_path(cgroup_workdir, relative_path); dirfd = AT_FDCWD; flags = 0; diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/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__) /* 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); -int join_cgroup(const char *path); +int join_cgroup(const char *relative_path); +int join_parent_cgroup(const char *relative_path); 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); -#endif /* __CGROUP_HELPERS_H */ \ No newline at end of file +#endif /* __CGROUP_HELPERS_H */ From patchwork Fri May 20 01:21:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yosry Ahmed X-Patchwork-Id: 12856177 X-Patchwork-Delegate: bpf@iogearbox.net 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 CC535C433EF for ; Fri, 20 May 2022 01:22:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344506AbiETBV6 (ORCPT ); Thu, 19 May 2022 21:21:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344449AbiETBVz (ORCPT ); Thu, 19 May 2022 21:21:55 -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 2315227CD5 for ; Thu, 19 May 2022 18:21:47 -0700 (PDT) Received: by mail-pj1-x1049.google.com with SMTP id rj11-20020a17090b3e8b00b001df51eb1831so5985972pjb.3 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=2B+zjfI5N4RF6mAMvevX1YCXgaJCWsVwBCD5U3w9ayda2zsiA7DrhEqppngyObs9ls GA8G/bY5yoEgkIC71gFqqyw1r9fULv5NfRAhPm+aeHqFZTxammWSshLMjAK1p/LbxBmF bq11BhQ0Os9KrimtxzlsO6wKO2lLwQfy2Rgi+W3scgkgVogc2SqMqw3TiB9oAMl8GmfS FpeF+eXJVE1CLVETp7j1xD5SvacGPPBlHyISBijb7LZfmE6Pqi3vW3EsJYmkL+9zykPy X4oWFMjqSnc7MKOvdLqLvYvPtjaEFqLGQsu0UqZGPnVEwO1fZ/L8j7z+WoF+wn8/fVKa AevQ== X-Gm-Message-State: AOAM530STOpUlDjL7CFwElIcoJiClSahkFOvKZmOtcLFae7+QcreouEM nq1uEv/PLyTWNSCjTVk7Evw/ptkzUD5rWHOa 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: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net 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_hierarchical_stats.c create mode 100644 tools/testing/selftests/bpf/progs/cgroup_vmscan.c diff --git a/tools/testing/selftests/bpf/prog_tests/test_cgroup_hierarchical_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 = #n, .path = #p"/"#n} + +static struct { + const char *name, *path; + unsigned long long id; + int fd; +} cgroups[] = { + 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 = open(path, O_RDONLY); + if (fd < 0) { + log_err("Open %s", path); + return -errno; + } + len = read(fd, buf, size); + if (len < 0) + log_err("Read %s", path); + else + buf[len] = 0; + close(fd); + return len < 0 ? -errno : 0; +} + +static int setup_bpffs(void) +{ + int err; + + /* Mount bpffs */ + err = mount("bpf", BPFFS_ROOT, "bpf", 0, NULL); + mounted_bpffs = !err; + if (CHECK(err && errno != 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 = 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 = setup_cgroup_environment(); + if (CHECK(err, "setup_cgroup_environment", "failed: %d\n", err)) + return err; + + for (i = 0; i < N_CGROUPS; i++) { + int fd; + + fd = create_and_get_cgroup(cgroups[i].path); + if (!ASSERT_GE(fd, 0, "create_and_get_cgroup")) + return fd; + + cgroups[i].fd = fd; + cgroups[i].id = get_cgroup_id(cgroups[i].path); + if (i < N_NON_LEAF_CGROUPS) { + err = enable_controllers(cgroups[i].path, "memory"); + if (!ASSERT_OK(err, "enable_controllers")) + return err; + } + } + return 0; +} + +static void cleanup_cgroups(void) +{ + for (int i = 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 = malloc(size); + for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) + *ptr = 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 = 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 = N_NON_LEAF_CGROUPS; i < N_CGROUPS; i++) { + pid_t pid = fork(); + + if (pid == 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 = 0, id = 0; + int err; + + /* For every cgroup, read the file generated by cgroup_iter */ + snprintf(path, 128, "%s%s", BPFFS_VMSCAN, file_name); + err = 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 = 0; i < N_CGROUPS; i++) + vmscan_readings[i] = get_cgroup_vmscan(cgroups[i].id, + cgroups[i].name); + + /* Read stats for root too */ + vmscan_root = get_cgroup_vmscan(CG_ROOT_ID, CG_ROOT_NAME); + + /* Check that child1 == child1_1 + child1_2 */ + ASSERT_EQ(vmscan_readings[1], vmscan_readings[3] + vmscan_readings[4], + "child1_vmscan"); + /* Check that child2 == child2_1 + child2_2 */ + ASSERT_EQ(vmscan_readings[2], vmscan_readings[5] + vmscan_readings[6], + "child2_vmscan"); + /* Check that test == child1 + child2 */ + ASSERT_EQ(vmscan_readings[0], vmscan_readings[1] + vmscan_readings[2], + "test_vmscan"); + /* Check that root >= 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 = {}; + struct bpf_link *link; + char path[128]; + int err; + + /* Create an iter link, parameterized by cgroup id */ + linfo.cgroup.cgroup_id = cgroup_id; + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + link = 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 = 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 = 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 = 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 = 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 = bpf_program__attach(obj->progs.vmscan_start); + if (!ASSERT_OK_PTR(obj, "attach raw_tracepoint")) + return libbpf_get_error(obj); + + link = bpf_program__attach(obj->progs.vmscan_end); + if (!ASSERT_OK_PTR(obj, "attach raw_tracepoint")) + return libbpf_get_error(obj); + + *skel = obj; + return 0; +} + +void destroy_progs(struct cgroup_vmscan *skel) +{ + char path[128]; + int i; + + for (i = 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 = 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/selftests/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_used #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; }; +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/testing/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") = "GPL"; + +/* + * Start times are stored per-task, not per-cgroup, as multiple tasks in one + * 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] != 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 = {.state = state, .prev = 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 pending) +{ + struct vmscan init = {.state = state, .pending = 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 = bpf_get_current_task_btf(); + __u64 *start_time_ptr; + + start_time_ptr = 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 = 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 = bpf_get_current_task_btf(); + struct cgroup *cgrp = task_memcg(current); + __u64 *start_time_ptr; + __u64 current_elapsed, cg_id; + __u64 end_time = bpf_ktime_get_ns(); + + /* cgrp may not have memory controller enabled */ + if (!cgrp) + return 0; + + cg_id = cgroup_id(cgrp); + start_time_ptr = 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 = end_time - *start_time_ptr; + pcpu_stat = 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 = cgroup_id(cgrp); + __u64 parent_cg_id = parent ? cgroup_id(parent) : 0; + __u64 *pcpu_vmscan; + __u64 state; + __u64 delta = 0; + + if (!memory_subsys_enabled(cgrp)) + return 0; + + /* Add CPU changes on this level since the last flush */ + pcpu_stat = bpf_map_lookup_percpu_elem(&pcpu_cgroup_vmscan_elapsed, + &cg_id, cpu); + if (pcpu_stat) { + state = pcpu_stat->state; + delta += state - pcpu_stat->prev; + pcpu_stat->prev = state; + } + + total_stat = 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 += total_stat->pending; + total_stat->pending = 0; + } + + /* Propagate changes to this cgroup's total */ + total_stat->state += 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 = bpf_map_lookup_elem(&cgroup_vmscan_elapsed, + &parent_cg_id); + if (parent_stat) + parent_stat->pending += 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 = meta->seq; + struct vmscan *total_stat; + __u64 cg_id = cgroup_id(cgrp); + + /* Flush the stats to make sure we get the most updated numbers */ + cgroup_rstat_flush(cgrp); + + total_stat = 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; +} +