Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/linux/cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,4 +923,19 @@ struct cgroup *task_get_cgroup1(struct task_struct *tsk, int hierarchy_id);

struct cgroup_of_peak *of_peak(struct kernfs_open_file *of);

/* A cgroup's base CPU-time counters in microseconds, as cpu.stat prints them */
struct cpu_cgroup_cputime {
u64 usage_usec;
u64 user_usec;
u64 system_usec;
u64 nice_usec;
u64 forceidle_usec; /* 0 without CONFIG_SCHED_CORE */
};

/* A task_group's own throttled time in nanoseconds; see cpu.stat.local */
struct task_group;
#ifdef CONFIG_CFS_BANDWIDTH
u64 throttled_time_self(struct task_group *tg);
#endif

#endif /* _LINUX_CGROUP_H */
2 changes: 2 additions & 0 deletions kernel/cgroup/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o freezer.o

obj-$(CONFIG_BPF_SYSCALL) += bpf_cpu.o

obj-$(CONFIG_CGROUP_FREEZER) += legacy_freezer.o
obj-$(CONFIG_CGROUP_PIDS) += pids.o
obj-$(CONFIG_CGROUP_RDMA) += rdma.o
Expand Down
80 changes: 80 additions & 0 deletions kernel/cgroup/bpf_cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CPU Controller-related BPF kfuncs
*
* bpf_cpu_cgroup_cputime() is defined in rstat.c, which owns the locking it
* needs, and only registered here.
*
* Author: Ziyang Men <ziyang.meme@gmail.com>
*/

#include <linux/bpf.h>
#include <linux/btf_ids.h>
#include <linux/cgroup.h>

#include "cgroup-internal.h"

__bpf_kfunc_start_defs();

/**
* bpf_cpu_cgroup_flush_stats - Flush a cgroup's base CPU-time statistics
* @cgrp: cgroup to flush
*
* Propagate the cgroup's base CPU-time statistics up the cgroup tree.
*/
__bpf_kfunc void bpf_cpu_cgroup_flush_stats(struct cgroup *cgrp)
{
css_rstat_flush(&cgrp->self);
}

/**
* bpf_cpu_cgroup_throttled_self - Read a cgroup's own throttled time
* @cgrp: cgroup to read from
*
* Return: The throttled time in microseconds, or 0 if config is off.
*/
__bpf_kfunc u64 bpf_cpu_cgroup_throttled_self(struct cgroup *cgrp)
{
/* cpu_cgrp_id needs the cpu controller, which CFS bandwidth depends on */
#ifdef CONFIG_CFS_BANDWIDTH
struct cgroup_subsys_state *css;

guard(rcu)();

css = rcu_dereference(cgrp->subsys[cpu_cgrp_id]);
if (!css)
return 0;

return div_u64(throttled_time_self((struct task_group *)css),
NSEC_PER_USEC);
#else
return 0;
#endif
}

__bpf_kfunc_end_defs();

/* KF_SLEEPABLE keeps the rstat spinlock out of NMI */
BTF_KFUNCS_START(bpf_cpu_cgroup_kfunc_ids)
BTF_ID_FLAGS(func, bpf_cpu_cgroup_flush_stats, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_cpu_cgroup_cputime, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_cpu_cgroup_throttled_self)
BTF_KFUNCS_END(bpf_cpu_cgroup_kfunc_ids)

static const struct btf_kfunc_id_set bpf_cpu_cgroup_kfunc_set = {
.owner = THIS_MODULE,
.set = &bpf_cpu_cgroup_kfunc_ids,
};

static int __init bpf_cpu_cgroup_kfunc_init(void)
{
int err;

err = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC,
&bpf_cpu_cgroup_kfunc_set);
if (err)
pr_warn("error while registering cpu cgroup kfuncs: %d\n", err);

return err;
}
late_initcall(bpf_cpu_cgroup_kfunc_init);
3 changes: 3 additions & 0 deletions kernel/cgroup/cgroup-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ int css_rstat_init(struct cgroup_subsys_state *css);
void css_rstat_exit(struct cgroup_subsys_state *css);
int ss_rstat_init(struct cgroup_subsys *ss);
void cgroup_base_stat_cputime_show(struct seq_file *seq);
#ifdef CONFIG_BPF_SYSCALL
void bpf_cpu_cgroup_cputime(struct cgroup *cgrp, struct cpu_cgroup_cputime *out);
#endif

/*
* namespace.c
Expand Down
42 changes: 42 additions & 0 deletions kernel/cgroup/rstat.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,48 @@ void cgroup_base_stat_cputime_show(struct seq_file *seq)
cgroup_force_idle_show(seq, &bstat);
}

#ifdef CONFIG_BPF_SYSCALL

__bpf_kfunc_start_defs();

/**
* bpf_cpu_cgroup_cputime - Read a cgroup's base CPU-time data
* @cgrp: cgroup to read from
* @out: the data in microseconds. Zero it first: the verifier reads the
* whole struct.
*
* Adjust once and fill all values.
*/
__bpf_kfunc void bpf_cpu_cgroup_cputime(struct cgroup *cgrp,
struct cpu_cgroup_cputime *out)
{
struct cgroup_base_stat bstat;

if (cgroup_parent(cgrp)) {
__css_rstat_lock(&cgrp->self, -1);
bstat = cgrp->bstat;
cputime_adjust(&cgrp->bstat.cputime, &cgrp->prev_cputime,
&bstat.cputime.utime, &bstat.cputime.stime);
__css_rstat_unlock(&cgrp->self, -1);
} else {
root_cgroup_cputime(&bstat);
}

out->usage_usec = div_u64(bstat.cputime.sum_exec_runtime, NSEC_PER_USEC);
out->user_usec = div_u64(bstat.cputime.utime, NSEC_PER_USEC);
out->system_usec = div_u64(bstat.cputime.stime, NSEC_PER_USEC);
out->nice_usec = div_u64(bstat.ntime, NSEC_PER_USEC);
#ifdef CONFIG_SCHED_CORE
out->forceidle_usec = div_u64(bstat.forceidle_sum, NSEC_PER_USEC);
#else
out->forceidle_usec = 0;
#endif
}

__bpf_kfunc_end_defs();

#endif /* CONFIG_BPF_SYSCALL */

/* Add bpf kfuncs for css_rstat_updated() and css_rstat_flush() */
BTF_KFUNCS_START(bpf_rstat_kfunc_ids)
BTF_ID_FLAGS(func, css_rstat_updated)
Expand Down
2 changes: 1 addition & 1 deletion kernel/sched/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -10027,7 +10027,7 @@ static int cpu_cfs_stat_show(struct seq_file *sf, void *v)
return 0;
}

static u64 throttled_time_self(struct task_group *tg)
u64 throttled_time_self(struct task_group *tg)
{
int i;
u64 total = 0;
Expand Down
22 changes: 22 additions & 0 deletions tools/testing/selftests/bpf/cgroup_iter_cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#ifndef __CGROUP_ITER_CPU_H
#define __CGROUP_ITER_CPU_H

struct cpu_query {
/* base cpu time, from cpu.stat */
__u64 usage_usec;
__u64 user_usec;
__u64 system_usec;
__u64 nice_usec;
__u64 forceidle_usec;
/* CFS bandwidth throttling, from cpu.stat and cpu.stat.local */
__u64 nr_periods;
__u64 nr_throttled;
__u64 throttled_usec;
__u64 nr_bursts;
__u64 burst_usec;
__u64 throttled_self_usec;
};

#endif /* __CGROUP_ITER_CPU_H */
3 changes: 3 additions & 0 deletions tools/testing/selftests/bpf/config
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ CONFIG_BPF_STREAM_PARSER=y
CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set
CONFIG_CGROUP_BPF=y
CONFIG_CGROUP_SCHED=y
CONFIG_FAIR_GROUP_SCHED=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_USER_API=y
Expand Down
Loading
Loading