Skip to content

Commit 1a922fe

Browse files
sargundavem330
authored andcommitted
samples, bpf: Refactor test_current_task_under_cgroup - separate out helpers
This patch modifies test_current_task_under_cgroup_user. The test has several helpers around creating a temporary environment for cgroup testing, and moving the current task around cgroups. This set of helpers can then be used in other tests. Signed-off-by: Sargun Dhillon <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 69a9d09 commit 1a922fe

File tree

4 files changed

+218
-85
lines changed

4 files changed

+218
-85
lines changed

samples/bpf/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o
5959
xdp1-objs := bpf_load.o libbpf.o xdp1_user.o
6060
# reuse xdp1 source intentionally
6161
xdp2-objs := bpf_load.o libbpf.o xdp1_user.o
62-
test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \
62+
test_current_task_under_cgroup-objs := bpf_load.o libbpf.o cgroup_helpers.o \
6363
test_current_task_under_cgroup_user.o
6464
trace_event-objs := bpf_load.o libbpf.o trace_event_user.o
6565
sampleip-objs := bpf_load.o libbpf.o sampleip_user.o

samples/bpf/cgroup_helpers.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#define _GNU_SOURCE
2+
#include <sched.h>
3+
#include <sys/mount.h>
4+
#include <sys/stat.h>
5+
#include <sys/types.h>
6+
#include <linux/limits.h>
7+
#include <stdio.h>
8+
#include <linux/sched.h>
9+
#include <fcntl.h>
10+
#include <unistd.h>
11+
#include <ftw.h>
12+
13+
14+
#include "cgroup_helpers.h"
15+
16+
/*
17+
* To avoid relying on the system setup, when setup_cgroup_env is called
18+
* we create a new mount namespace, and cgroup namespace. The cgroup2
19+
* root is mounted at CGROUP_MOUNT_PATH
20+
*
21+
* Unfortunately, most people don't have cgroupv2 enabled at this point in time.
22+
* It's easier to create our own mount namespace and manage it ourselves.
23+
*
24+
* We assume /mnt exists.
25+
*/
26+
27+
#define WALK_FD_LIMIT 16
28+
#define CGROUP_MOUNT_PATH "/mnt"
29+
#define CGROUP_WORK_DIR "/cgroup-test-work-dir"
30+
#define format_cgroup_path(buf, path) \
31+
snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \
32+
CGROUP_WORK_DIR, path)
33+
34+
/**
35+
* setup_cgroup_environment() - Setup the cgroup environment
36+
*
37+
* After calling this function, cleanup_cgroup_environment should be called
38+
* once testing is complete.
39+
*
40+
* This function will print an error to stderr and return 1 if it is unable
41+
* to setup the cgroup environment. If setup is successful, 0 is returned.
42+
*/
43+
int setup_cgroup_environment(void)
44+
{
45+
char cgroup_workdir[PATH_MAX + 1];
46+
47+
format_cgroup_path(cgroup_workdir, "");
48+
49+
if (unshare(CLONE_NEWNS)) {
50+
log_err("unshare");
51+
return 1;
52+
}
53+
54+
if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
55+
log_err("mount fakeroot");
56+
return 1;
57+
}
58+
59+
if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) {
60+
log_err("mount cgroup2");
61+
return 1;
62+
}
63+
64+
/* Cleanup existing failed runs, now that the environment is setup */
65+
cleanup_cgroup_environment();
66+
67+
if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
68+
log_err("mkdir cgroup work dir");
69+
return 1;
70+
}
71+
72+
return 0;
73+
}
74+
75+
static int nftwfunc(const char *filename, const struct stat *statptr,
76+
int fileflags, struct FTW *pfwt)
77+
{
78+
if ((fileflags & FTW_D) && rmdir(filename))
79+
log_err("Removing cgroup: %s", filename);
80+
return 0;
81+
}
82+
83+
84+
static int join_cgroup_from_top(char *cgroup_path)
85+
{
86+
char cgroup_procs_path[PATH_MAX + 1];
87+
pid_t pid = getpid();
88+
int fd, rc = 0;
89+
90+
snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
91+
"%s/cgroup.procs", cgroup_path);
92+
93+
fd = open(cgroup_procs_path, O_WRONLY);
94+
if (fd < 0) {
95+
log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
96+
return 1;
97+
}
98+
99+
if (dprintf(fd, "%d\n", pid) < 0) {
100+
log_err("Joining Cgroup");
101+
rc = 1;
102+
}
103+
104+
close(fd);
105+
return rc;
106+
}
107+
108+
/**
109+
* join_cgroup() - Join a cgroup
110+
* @path: The cgroup path, relative to the workdir, to join
111+
*
112+
* This function expects a cgroup to already be created, relative to the cgroup
113+
* work dir, and it joins it. For example, passing "/my-cgroup" as the path
114+
* would actually put the calling process into the cgroup
115+
* "/cgroup-test-work-dir/my-cgroup"
116+
*
117+
* On success, it returns 0, otherwise on failure it returns 1.
118+
*/
119+
int join_cgroup(char *path)
120+
{
121+
char cgroup_path[PATH_MAX + 1];
122+
123+
format_cgroup_path(cgroup_path, path);
124+
return join_cgroup_from_top(cgroup_path);
125+
}
126+
127+
/**
128+
* cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment
129+
*
130+
* This is an idempotent function to delete all temporary cgroups that
131+
* have been created during the test, including the cgroup testing work
132+
* directory.
133+
*
134+
* At call time, it moves the calling process to the root cgroup, and then
135+
* runs the deletion process. It is idempotent, and should not fail, unless
136+
* a process is lingering.
137+
*
138+
* On failure, it will print an error to stderr, and try to continue.
139+
*/
140+
void cleanup_cgroup_environment(void)
141+
{
142+
char cgroup_workdir[PATH_MAX + 1];
143+
144+
format_cgroup_path(cgroup_workdir, "");
145+
join_cgroup_from_top(CGROUP_MOUNT_PATH);
146+
nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
147+
}
148+
149+
/**
150+
* create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD
151+
* @path: The cgroup path, relative to the workdir, to join
152+
*
153+
* This function creates a cgroup under the top level workdir and returns the
154+
* file descriptor. It is idempotent.
155+
*
156+
* On success, it returns the file descriptor. On failure it returns 0.
157+
* If there is a failure, it prints the error to stderr.
158+
*/
159+
int create_and_get_cgroup(char *path)
160+
{
161+
char cgroup_path[PATH_MAX + 1];
162+
int fd;
163+
164+
format_cgroup_path(cgroup_path, path);
165+
if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
166+
log_err("mkdiring cgroup");
167+
return 0;
168+
}
169+
170+
fd = open(cgroup_path, O_RDONLY);
171+
if (fd < 0) {
172+
log_err("Opening Cgroup");
173+
return 0;
174+
}
175+
176+
return fd;
177+
}

samples/bpf/cgroup_helpers.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef __CGROUP_HELPERS_H
2+
#define __CGROUP_HELPERS_H
3+
#include <errno.h>
4+
#include <string.h>
5+
6+
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
7+
#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
8+
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
9+
10+
11+
int create_and_get_cgroup(char *path);
12+
int join_cgroup(char *path);
13+
int setup_cgroup_environment(void);
14+
void cleanup_cgroup_environment(void);
15+
16+
#endif

samples/bpf/test_current_task_under_cgroup_user.c

Lines changed: 24 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -11,98 +11,39 @@
1111
#include <unistd.h>
1212
#include "libbpf.h"
1313
#include "bpf_load.h"
14-
#include <string.h>
15-
#include <fcntl.h>
16-
#include <errno.h>
1714
#include <linux/bpf.h>
18-
#include <sched.h>
19-
#include <sys/mount.h>
20-
#include <sys/stat.h>
21-
#include <sys/types.h>
22-
#include <linux/limits.h>
15+
#include "cgroup_helpers.h"
2316

24-
#define CGROUP_MOUNT_PATH "/mnt"
25-
#define CGROUP_PATH "/mnt/my-cgroup"
26-
27-
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
28-
#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
29-
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
30-
31-
static int join_cgroup(char *path)
32-
{
33-
int fd, rc = 0;
34-
pid_t pid = getpid();
35-
char cgroup_path[PATH_MAX + 1];
36-
37-
snprintf(cgroup_path, sizeof(cgroup_path), "%s/cgroup.procs", path);
38-
39-
fd = open(cgroup_path, O_WRONLY);
40-
if (fd < 0) {
41-
log_err("Opening Cgroup");
42-
return 1;
43-
}
44-
45-
if (dprintf(fd, "%d\n", pid) < 0) {
46-
log_err("Joining Cgroup");
47-
rc = 1;
48-
}
49-
close(fd);
50-
return rc;
51-
}
17+
#define CGROUP_PATH "/my-cgroup"
5218

5319
int main(int argc, char **argv)
5420
{
55-
char filename[256];
56-
int cg2, idx = 0;
5721
pid_t remote_pid, local_pid = getpid();
22+
int cg2, idx = 0, rc = 0;
23+
char filename[256];
5824

5925
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
6026
if (load_bpf_file(filename)) {
6127
printf("%s", bpf_log_buf);
6228
return 1;
6329
}
6430

65-
/*
66-
* This is to avoid interfering with existing cgroups. Unfortunately,
67-
* most people don't have cgroupv2 enabled at this point in time.
68-
* It's easier to create our own mount namespace and manage it
69-
* ourselves.
70-
*/
71-
if (unshare(CLONE_NEWNS)) {
72-
log_err("unshare");
73-
return 1;
74-
}
75-
76-
if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
77-
log_err("mount fakeroot");
78-
return 1;
79-
}
80-
81-
if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) {
82-
log_err("mount cgroup2");
83-
return 1;
84-
}
31+
if (setup_cgroup_environment())
32+
goto err;
8533

86-
if (mkdir(CGROUP_PATH, 0777) && errno != EEXIST) {
87-
log_err("mkdir cgroup");
88-
return 1;
89-
}
34+
cg2 = create_and_get_cgroup(CGROUP_PATH);
9035

91-
cg2 = open(CGROUP_PATH, O_RDONLY);
92-
if (cg2 < 0) {
93-
log_err("opening target cgroup");
94-
goto cleanup_cgroup_err;
95-
}
36+
if (!cg2)
37+
goto err;
9638

9739
if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) {
9840
log_err("Adding target cgroup to map");
99-
goto cleanup_cgroup_err;
100-
}
101-
if (join_cgroup("/mnt/my-cgroup")) {
102-
log_err("Leaving target cgroup");
103-
goto cleanup_cgroup_err;
41+
goto err;
10442
}
10543

44+
if (join_cgroup(CGROUP_PATH))
45+
goto err;
46+
10647
/*
10748
* The installed helper program catched the sync call, and should
10849
* write it to the map.
@@ -115,12 +56,12 @@ int main(int argc, char **argv)
11556
fprintf(stderr,
11657
"BPF Helper didn't write correct PID to map, but: %d\n",
11758
remote_pid);
118-
goto leave_cgroup_err;
59+
goto err;
11960
}
12061

12162
/* Verify the negative scenario; leave the cgroup */
122-
if (join_cgroup(CGROUP_MOUNT_PATH))
123-
goto leave_cgroup_err;
63+
if (join_cgroup("/"))
64+
goto err;
12465

12566
remote_pid = 0;
12667
bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY);
@@ -130,16 +71,15 @@ int main(int argc, char **argv)
13071

13172
if (local_pid == remote_pid) {
13273
fprintf(stderr, "BPF cgroup negative test did not work\n");
133-
goto cleanup_cgroup_err;
74+
goto err;
13475
}
13576

136-
rmdir(CGROUP_PATH);
137-
return 0;
77+
goto out;
78+
err:
79+
rc = 1;
13880

139-
/* Error condition, cleanup */
140-
leave_cgroup_err:
141-
join_cgroup(CGROUP_MOUNT_PATH);
142-
cleanup_cgroup_err:
143-
rmdir(CGROUP_PATH);
144-
return 1;
81+
out:
82+
close(cg2);
83+
cleanup_cgroup_environment();
84+
return rc;
14585
}

0 commit comments

Comments
 (0)