Skip to content

Commit bdda12d

Browse files
haoluo1022Nobody
authored andcommitted
selftests/bpf: Tests using sleepable tracepoints to monitor cgroup events
Tests the functionalities of sleepable tracing prog, sleepable tracepoints (i.e. cgroup_mkdir_s and cgroup_rmdir_s) and cgroup iter prog all together. The added selftest resembles a real-world application, where bpf is used to export cgroup-level performance stats. There are two sets of progs in the test: cgroup_monitor and cgroup_sched_lat - Cgroup_monitor monitors cgroup creation and deletion using sleepable tracing; for each cgroup created, creates a directory in bpffs; creates a cgroup iter link and pins it in that directory. - Cgroup_sched_lat is the program that collects cgroup's scheduling latencies and store them in hash map. Cgroup_sched_lat implements a cgroup iter prog, which reads the stats from the map and seq_prints them. This cgroup iter prog is the prog pinned by cgroup_monitor in each bpffs directory. The cgroup_sched_lat in this test can be adapted for exporting similar cgroup-level performance stats. Signed-off-by: Hao Luo <[email protected]>
1 parent ee9a4a3 commit bdda12d

File tree

4 files changed

+504
-0
lines changed

4 files changed

+504
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Google */
3+
#define _GNU_SOURCE
4+
#include <sys/stat.h> /* mkdir */
5+
#include <fcntl.h> /* name_to_handle_at */
6+
#include <stdlib.h>
7+
#include <test_progs.h>
8+
#include "cgroup_monitor.skel.h"
9+
#include "cgroup_sched_lat.skel.h"
10+
11+
static char mkdir_prog_path[64];
12+
static char rmdir_prog_path[64];
13+
static char dump_prog_path[64];
14+
15+
/* Get cgroup id from a full path to cgroup */
16+
static int get_cgroup_id(const char *cgroup)
17+
{
18+
int mount_id = 0;
19+
struct {
20+
struct file_handle fh;
21+
__u64 cgid;
22+
} fh = {};
23+
24+
fh.fh.handle_bytes = sizeof(fh.cgid);
25+
if (name_to_handle_at(AT_FDCWD, cgroup, &fh.fh, &mount_id, 0))
26+
return -1;
27+
28+
return fh.cgid;
29+
}
30+
31+
static void spin_on_cpu(int seconds)
32+
{
33+
time_t start, now;
34+
35+
start = time(NULL);
36+
do {
37+
now = time(NULL);
38+
} while (now - start < seconds);
39+
}
40+
41+
static void do_work(const char *cgroup)
42+
{
43+
int i, cpu = 0, pid;
44+
char cmd[128];
45+
46+
/* make cgroup threaded */
47+
snprintf(cmd, 128, "echo threaded > %s/cgroup.type", cgroup);
48+
system(cmd);
49+
50+
/* try to enable cpu controller. this may fail if cpu controller is not
51+
* available in cgroup.controllers or there is a cgroup v1 already
52+
* mounted in the system.
53+
*/
54+
snprintf(cmd, 128, "echo \"+cpu\" > %s/cgroup.subtree_control", cgroup);
55+
system(cmd);
56+
57+
/* launch two children, both running in child cgroup */
58+
for (i = 0; i < 2; ++i) {
59+
pid = fork();
60+
if (pid == 0) {
61+
/* attach to cgroup */
62+
snprintf(cmd, 128, "echo %d > %s/cgroup.procs", getpid(), cgroup);
63+
system(cmd);
64+
65+
/* pin process to target cpu */
66+
snprintf(cmd, 128, "taskset -pc %d %d", cpu, getpid());
67+
system(cmd);
68+
69+
spin_on_cpu(3); /* spin on cpu for 3 seconds */
70+
exit(0);
71+
}
72+
}
73+
74+
/* pin parent process to target cpu */
75+
snprintf(cmd, 128, "taskset -pc %d %d", cpu, getpid());
76+
system(cmd);
77+
78+
spin_on_cpu(3); /* spin on cpu for 3 seconds */
79+
wait(NULL);
80+
}
81+
82+
/* Check reading cgroup stats from auto pinned objects
83+
* @root: root directory in bpffs set up for this test
84+
* @cgroup: cgroup path
85+
*/
86+
static void check_cgroup_stats(const char *root, const char *cgroup)
87+
{
88+
unsigned long queue_self, queue_other;
89+
char buf[64], path[64];
90+
int id, cgroup_id;
91+
FILE *file;
92+
93+
id = get_cgroup_id(cgroup);
94+
if (!ASSERT_GE(id, 0, "get_cgroup_id"))
95+
return;
96+
97+
snprintf(path, sizeof(path), "%s/%d/stats", root, id);
98+
file = fopen(path, "r");
99+
if (!ASSERT_OK_PTR(file, "open"))
100+
return;
101+
102+
ASSERT_OK_PTR(fgets(buf, sizeof(buf), file), "cat");
103+
ASSERT_EQ(sscanf(buf, "cgroup_id: %8d", &cgroup_id), 1, "output");
104+
ASSERT_EQ(id, cgroup_id, "cgroup_id");
105+
106+
ASSERT_OK_PTR(fgets(buf, sizeof(buf), file), "cat");
107+
ASSERT_EQ(sscanf(buf, "queue_self: %8lu", &queue_self), 1, "output");
108+
109+
ASSERT_OK_PTR(fgets(buf, sizeof(buf), file), "cat");
110+
ASSERT_EQ(sscanf(buf, "queue_other: %8lu", &queue_other), 1, "output");
111+
fclose(file);
112+
}
113+
114+
/* Set up bpf progs for monitoring cgroup activities. */
115+
static void setup_cgroup_monitor(const char *root)
116+
{
117+
struct cgroup_monitor *skel = NULL;
118+
119+
skel = cgroup_monitor__open_and_load();
120+
if (!ASSERT_OK_PTR(skel, "cgroup_monitor_skel_load"))
121+
return;
122+
123+
cgroup_monitor__attach(skel);
124+
125+
snprintf(skel->bss->root, sizeof(skel->bss->root), "%s", root);
126+
127+
snprintf(mkdir_prog_path, 64, "%s/mkdir_prog", root);
128+
bpf_obj_pin(bpf_link__fd(skel->links.mkdir_prog), mkdir_prog_path);
129+
130+
snprintf(rmdir_prog_path, 64, "%s/rmdir_prog", root);
131+
bpf_obj_pin(bpf_link__fd(skel->links.rmdir_prog), rmdir_prog_path);
132+
133+
cgroup_monitor__destroy(skel);
134+
}
135+
136+
void test_cgroup_stats(void)
137+
{
138+
char bpf_tmpl[] = "/sys/fs/bpf/XXXXXX";
139+
char cgrp_tmpl[] = "/sys/fs/cgroup/XXXXXX";
140+
struct cgroup_sched_lat *skel = NULL;
141+
char *root, *cgroup;
142+
143+
/* prepare test directories */
144+
system("mount -t cgroup2 none /sys/fs/cgroup");
145+
system("mount -t bpf bpffs /sys/fs/bpf");
146+
root = mkdtemp(bpf_tmpl);
147+
chmod(root, 0777);
148+
149+
/* set up progs for monitoring cgroup events */
150+
setup_cgroup_monitor(root);
151+
152+
/* set up progs for profiling cgroup stats */
153+
skel = cgroup_sched_lat__open_and_load();
154+
if (!ASSERT_OK_PTR(skel, "cgroup_sched_lat_skel_load"))
155+
goto cleanup_root;
156+
157+
snprintf(dump_prog_path, 64, "%s/prog", root);
158+
bpf_obj_pin(bpf_program__fd(skel->progs.dump_cgroup), dump_prog_path);
159+
chmod(dump_prog_path, 0644);
160+
161+
cgroup_sched_lat__attach(skel);
162+
163+
/* thanks to cgroup monitoring progs, a directory corresponding to the
164+
* cgroup is created in bpffs.
165+
*/
166+
cgroup = mkdtemp(cgrp_tmpl);
167+
168+
/* collect some cgroup-level stats and check reading them from bpffs */
169+
do_work(cgroup);
170+
check_cgroup_stats(root, cgroup);
171+
172+
/* thanks to cgroup monitoring progs, removing cgroups also removes
173+
* the created directory in bpffs.
174+
*/
175+
rmdir(cgroup);
176+
177+
/* clean up cgroup monitoring progs */
178+
cgroup_sched_lat__detach(skel);
179+
cgroup_sched_lat__destroy(skel);
180+
unlink(dump_prog_path);
181+
cleanup_root:
182+
/* remove test directories in bpffs */
183+
unlink(mkdir_prog_path);
184+
unlink(rmdir_prog_path);
185+
rmdir(root);
186+
return;
187+
}

tools/testing/selftests/bpf/progs/bpf_iter.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define bpf_iter__bpf_map_elem bpf_iter__bpf_map_elem___not_used
1717
#define bpf_iter__bpf_sk_storage_map bpf_iter__bpf_sk_storage_map___not_used
1818
#define bpf_iter__sockmap bpf_iter__sockmap___not_used
19+
#define bpf_iter__cgroup bpf_iter__cgroup__not_used
1920
#define btf_ptr btf_ptr___not_used
2021
#define BTF_F_COMPACT BTF_F_COMPACT___not_used
2122
#define BTF_F_NONAME BTF_F_NONAME___not_used
@@ -37,6 +38,7 @@
3738
#undef bpf_iter__bpf_map_elem
3839
#undef bpf_iter__bpf_sk_storage_map
3940
#undef bpf_iter__sockmap
41+
#undef bpf_iter__cgroup
4042
#undef btf_ptr
4143
#undef BTF_F_COMPACT
4244
#undef BTF_F_NONAME
@@ -132,6 +134,11 @@ struct bpf_iter__sockmap {
132134
struct sock *sk;
133135
};
134136

137+
struct bpf_iter__cgroup {
138+
struct bpf_iter_meta *meta;
139+
struct cgroup *cgroup;
140+
} __attribute__((preserve_access_index));
141+
135142
struct btf_ptr {
136143
void *ptr;
137144
__u32 type_id;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Google */
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
/* root is the directory path. */
10+
char root[64];
11+
12+
SEC("tp_btf.s/cgroup_mkdir_s")
13+
int BPF_PROG(mkdir_prog, struct cgroup *cgrp)
14+
{
15+
static char dirname[64];
16+
static char prog_path[64];
17+
static char iter_path[64];
18+
static union bpf_iter_link_info info;
19+
static union bpf_attr get_attr;
20+
static union bpf_attr link_attr;
21+
static union bpf_attr pin_attr;
22+
int link_fd, prog_fd, ret;
23+
__u64 id;
24+
25+
/* create directory in bpffs named by cgroup's id. */
26+
id = cgrp->kn->id;
27+
BPF_SNPRINTF(dirname, sizeof(dirname), "%s/%lu", root, id);
28+
ret = bpf_mkdir(dirname, sizeof(dirname), 0755);
29+
if (ret)
30+
return ret;
31+
32+
/* get cgroup iter prog pinned by test progs. */
33+
BPF_SNPRINTF(prog_path, sizeof(prog_path), "%s/prog", root);
34+
get_attr.bpf_fd = 0;
35+
get_attr.pathname = (__u64)prog_path;
36+
get_attr.file_flags = BPF_F_RDONLY;
37+
prog_fd = bpf_sys_bpf(BPF_OBJ_GET, &get_attr, sizeof(get_attr));
38+
if (prog_fd < 0)
39+
return prog_fd;
40+
41+
/* create a link, parameterized by cgroup id. */
42+
info.cgroup.cgroup_id = id;
43+
link_attr.link_create.prog_fd = prog_fd;
44+
link_attr.link_create.attach_type = BPF_TRACE_ITER;
45+
link_attr.link_create.target_fd = 0;
46+
link_attr.link_create.flags = 0;
47+
link_attr.link_create.iter_info = (__u64)&info;
48+
link_attr.link_create.iter_info_len = sizeof(info);
49+
ret = bpf_sys_bpf(BPF_LINK_CREATE, &link_attr, sizeof(link_attr));
50+
if (ret < 0) {
51+
bpf_sys_close(prog_fd);
52+
return ret;
53+
}
54+
link_fd = ret;
55+
56+
/* pin the link in the created directory */
57+
BPF_SNPRINTF(iter_path, sizeof(iter_path), "%s/stats", dirname);
58+
pin_attr.pathname = (__u64)iter_path;
59+
pin_attr.bpf_fd = link_fd;
60+
pin_attr.file_flags = 0;
61+
ret = bpf_sys_bpf(BPF_OBJ_PIN, &pin_attr, sizeof(pin_attr));
62+
63+
bpf_sys_close(prog_fd);
64+
bpf_sys_close(link_fd);
65+
return ret;
66+
}
67+
68+
SEC("tp_btf.s/cgroup_rmdir_s")
69+
int BPF_PROG(rmdir_prog, struct cgroup *cgrp)
70+
{
71+
static char dirname[64];
72+
static char path[64];
73+
74+
BPF_SNPRINTF(dirname, sizeof(dirname), "%s/%lu", root, cgrp->kn->id);
75+
BPF_SNPRINTF(path, sizeof(path), "%s/stats", dirname);
76+
bpf_unlink(path, sizeof(path));
77+
return bpf_rmdir(dirname, sizeof(dirname));
78+
}

0 commit comments

Comments
 (0)