Skip to content

Commit 81427a6

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-add-support-for-cgroup1-bpf-part'
Yafang Shao says: ==================== bpf: Add support for cgroup1, BPF part This is the BPF part of the series "bpf, cgroup: Add BPF support for cgroup1 hierarchy" with adjustment in the last two patches compared to the previous one. v3->v4: - use subsys_name instead of cgrp_name in get_cgroup_hierarchy_id() (Tejun) - use local bpf_link instead of modifying the skeleton in the selftests v3: https://lwn.net/Articles/949264/ ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 727a92d + 3607692 commit 81427a6

File tree

6 files changed

+352
-19
lines changed

6 files changed

+352
-19
lines changed

kernel/bpf/helpers.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,6 +2228,25 @@ __bpf_kfunc long bpf_task_under_cgroup(struct task_struct *task,
22282228
rcu_read_unlock();
22292229
return ret;
22302230
}
2231+
2232+
/**
2233+
* bpf_task_get_cgroup1 - Acquires the associated cgroup of a task within a
2234+
* specific cgroup1 hierarchy. The cgroup1 hierarchy is identified by its
2235+
* hierarchy ID.
2236+
* @task: The target task
2237+
* @hierarchy_id: The ID of a cgroup1 hierarchy
2238+
*
2239+
* On success, the cgroup is returen. On failure, NULL is returned.
2240+
*/
2241+
__bpf_kfunc struct cgroup *
2242+
bpf_task_get_cgroup1(struct task_struct *task, int hierarchy_id)
2243+
{
2244+
struct cgroup *cgrp = task_get_cgroup1(task, hierarchy_id);
2245+
2246+
if (IS_ERR(cgrp))
2247+
return NULL;
2248+
return cgrp;
2249+
}
22312250
#endif /* CONFIG_CGROUPS */
22322251

22332252
/**
@@ -2534,6 +2553,7 @@ BTF_ID_FLAGS(func, bpf_cgroup_release, KF_RELEASE)
25342553
BTF_ID_FLAGS(func, bpf_cgroup_ancestor, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
25352554
BTF_ID_FLAGS(func, bpf_cgroup_from_id, KF_ACQUIRE | KF_RET_NULL)
25362555
BTF_ID_FLAGS(func, bpf_task_under_cgroup, KF_RCU)
2556+
BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
25372557
#endif
25382558
BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL)
25392559
BTF_ID_FLAGS(func, bpf_throw)

tools/testing/selftests/bpf/cgroup_helpers.c

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@
4545
#define format_parent_cgroup_path(buf, path) \
4646
format_cgroup_path_pid(buf, path, getppid())
4747

48-
#define format_classid_path(buf) \
49-
snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \
50-
CGROUP_WORK_DIR)
48+
#define format_classid_path_pid(buf, pid) \
49+
snprintf(buf, sizeof(buf), "%s%s%d", NETCLS_MOUNT_PATH, \
50+
CGROUP_WORK_DIR, pid)
51+
52+
#define format_classid_path(buf) \
53+
format_classid_path_pid(buf, getpid())
5154

5255
static __thread bool cgroup_workdir_mounted;
5356

@@ -419,26 +422,23 @@ int create_and_get_cgroup(const char *relative_path)
419422
}
420423

421424
/**
422-
* get_cgroup_id() - Get cgroup id for a particular cgroup path
423-
* @relative_path: The cgroup path, relative to the workdir, to join
425+
* get_cgroup_id_from_path - Get cgroup id for a particular cgroup path
426+
* @cgroup_workdir: The absolute cgroup path
424427
*
425428
* On success, it returns the cgroup id. On failure it returns 0,
426429
* which is an invalid cgroup id.
427430
* If there is a failure, it prints the error to stderr.
428431
*/
429-
unsigned long long get_cgroup_id(const char *relative_path)
432+
unsigned long long get_cgroup_id_from_path(const char *cgroup_workdir)
430433
{
431434
int dirfd, err, flags, mount_id, fhsize;
432435
union {
433436
unsigned long long cgid;
434437
unsigned char raw_bytes[8];
435438
} id;
436-
char cgroup_workdir[PATH_MAX + 1];
437439
struct file_handle *fhp, *fhp2;
438440
unsigned long long ret = 0;
439441

440-
format_cgroup_path(cgroup_workdir, relative_path);
441-
442442
dirfd = AT_FDCWD;
443443
flags = 0;
444444
fhsize = sizeof(*fhp);
@@ -474,6 +474,14 @@ unsigned long long get_cgroup_id(const char *relative_path)
474474
return ret;
475475
}
476476

477+
unsigned long long get_cgroup_id(const char *relative_path)
478+
{
479+
char cgroup_workdir[PATH_MAX + 1];
480+
481+
format_cgroup_path(cgroup_workdir, relative_path);
482+
return get_cgroup_id_from_path(cgroup_workdir);
483+
}
484+
477485
int cgroup_setup_and_join(const char *path) {
478486
int cg_fd;
479487

@@ -523,10 +531,20 @@ int setup_classid_environment(void)
523531
return 1;
524532
}
525533

526-
if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls") &&
527-
errno != EBUSY) {
528-
log_err("mount cgroup net_cls");
529-
return 1;
534+
if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls")) {
535+
if (errno != EBUSY) {
536+
log_err("mount cgroup net_cls");
537+
return 1;
538+
}
539+
540+
if (rmdir(NETCLS_MOUNT_PATH)) {
541+
log_err("rmdir cgroup net_cls");
542+
return 1;
543+
}
544+
if (umount(CGROUP_MOUNT_DFLT)) {
545+
log_err("umount cgroup base");
546+
return 1;
547+
}
530548
}
531549

532550
cleanup_classid_environment();
@@ -541,15 +559,16 @@ int setup_classid_environment(void)
541559

542560
/**
543561
* set_classid() - Set a cgroupv1 net_cls classid
544-
* @id: the numeric classid
545562
*
546-
* Writes the passed classid into the cgroup work dir's net_cls.classid
563+
* Writes the classid into the cgroup work dir's net_cls.classid
547564
* file in order to later on trigger socket tagging.
548565
*
566+
* We leverage the current pid as the classid, ensuring unique identification.
567+
*
549568
* On success, it returns 0, otherwise on failure it returns 1. If there
550569
* is a failure, it prints the error to stderr.
551570
*/
552-
int set_classid(unsigned int id)
571+
int set_classid(void)
553572
{
554573
char cgroup_workdir[PATH_MAX - 42];
555574
char cgroup_classid_path[PATH_MAX + 1];
@@ -565,7 +584,7 @@ int set_classid(unsigned int id)
565584
return 1;
566585
}
567586

568-
if (dprintf(fd, "%u\n", id) < 0) {
587+
if (dprintf(fd, "%u\n", getpid()) < 0) {
569588
log_err("Setting cgroup classid");
570589
rc = 1;
571590
}
@@ -607,3 +626,66 @@ void cleanup_classid_environment(void)
607626
join_cgroup_from_top(NETCLS_MOUNT_PATH);
608627
nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
609628
}
629+
630+
/**
631+
* get_classid_cgroup_id - Get the cgroup id of a net_cls cgroup
632+
*/
633+
unsigned long long get_classid_cgroup_id(void)
634+
{
635+
char cgroup_workdir[PATH_MAX + 1];
636+
637+
format_classid_path(cgroup_workdir);
638+
return get_cgroup_id_from_path(cgroup_workdir);
639+
}
640+
641+
/**
642+
* get_cgroup1_hierarchy_id - Retrieves the ID of a cgroup1 hierarchy from the cgroup1 subsys name.
643+
* @subsys_name: The cgroup1 subsys name, which can be retrieved from /proc/self/cgroup. It can be
644+
* a named cgroup like "name=systemd", a controller name like "net_cls", or multi-contollers like
645+
* "net_cls,net_prio".
646+
*/
647+
int get_cgroup1_hierarchy_id(const char *subsys_name)
648+
{
649+
char *c, *c2, *c3, *c4;
650+
bool found = false;
651+
char line[1024];
652+
FILE *file;
653+
int i, id;
654+
655+
if (!subsys_name)
656+
return -1;
657+
658+
file = fopen("/proc/self/cgroup", "r");
659+
if (!file) {
660+
log_err("fopen /proc/self/cgroup");
661+
return -1;
662+
}
663+
664+
while (fgets(line, 1024, file)) {
665+
i = 0;
666+
for (c = strtok_r(line, ":", &c2); c && i < 2; c = strtok_r(NULL, ":", &c2)) {
667+
if (i == 0) {
668+
id = strtol(c, NULL, 10);
669+
} else if (i == 1) {
670+
if (!strcmp(c, subsys_name)) {
671+
found = true;
672+
break;
673+
}
674+
675+
/* Multiple subsystems may share one single mount point */
676+
for (c3 = strtok_r(c, ",", &c4); c3;
677+
c3 = strtok_r(NULL, ",", &c4)) {
678+
if (!strcmp(c, subsys_name)) {
679+
found = true;
680+
break;
681+
}
682+
}
683+
}
684+
i++;
685+
}
686+
if (found)
687+
break;
688+
}
689+
fclose(file);
690+
return found ? id : -1;
691+
}

tools/testing/selftests/bpf/cgroup_helpers.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ int get_root_cgroup(void);
2020
int create_and_get_cgroup(const char *relative_path);
2121
void remove_cgroup(const char *relative_path);
2222
unsigned long long get_cgroup_id(const char *relative_path);
23+
int get_cgroup1_hierarchy_id(const char *subsys_name);
2324

2425
int join_cgroup(const char *relative_path);
2526
int join_root_cgroup(void);
@@ -29,8 +30,9 @@ int setup_cgroup_environment(void);
2930
void cleanup_cgroup_environment(void);
3031

3132
/* cgroupv1 related */
32-
int set_classid(unsigned int id);
33+
int set_classid(void);
3334
int join_classid(void);
35+
unsigned long long get_classid_cgroup_id(void);
3436

3537
int setup_classid_environment(void);
3638
void cleanup_classid_environment(void);
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2023 Yafang Shao <[email protected]> */
3+
4+
#include <sys/types.h>
5+
#include <unistd.h>
6+
#include <test_progs.h>
7+
#include "cgroup_helpers.h"
8+
#include "test_cgroup1_hierarchy.skel.h"
9+
10+
static void bpf_cgroup1(struct test_cgroup1_hierarchy *skel)
11+
{
12+
struct bpf_link *lsm_link, *fentry_link;
13+
int err;
14+
15+
/* Attach LSM prog first */
16+
lsm_link = bpf_program__attach_lsm(skel->progs.lsm_run);
17+
if (!ASSERT_OK_PTR(lsm_link, "lsm_attach"))
18+
return;
19+
20+
/* LSM prog will be triggered when attaching fentry */
21+
fentry_link = bpf_program__attach_trace(skel->progs.fentry_run);
22+
ASSERT_NULL(fentry_link, "fentry_attach_fail");
23+
24+
err = bpf_link__destroy(lsm_link);
25+
ASSERT_OK(err, "destroy_lsm");
26+
}
27+
28+
static void bpf_cgroup1_sleepable(struct test_cgroup1_hierarchy *skel)
29+
{
30+
struct bpf_link *lsm_link, *fentry_link;
31+
int err;
32+
33+
/* Attach LSM prog first */
34+
lsm_link = bpf_program__attach_lsm(skel->progs.lsm_s_run);
35+
if (!ASSERT_OK_PTR(lsm_link, "lsm_attach"))
36+
return;
37+
38+
/* LSM prog will be triggered when attaching fentry */
39+
fentry_link = bpf_program__attach_trace(skel->progs.fentry_run);
40+
ASSERT_NULL(fentry_link, "fentry_attach_fail");
41+
42+
err = bpf_link__destroy(lsm_link);
43+
ASSERT_OK(err, "destroy_lsm");
44+
}
45+
46+
static void bpf_cgroup1_invalid_id(struct test_cgroup1_hierarchy *skel)
47+
{
48+
struct bpf_link *lsm_link, *fentry_link;
49+
int err;
50+
51+
/* Attach LSM prog first */
52+
lsm_link = bpf_program__attach_lsm(skel->progs.lsm_run);
53+
if (!ASSERT_OK_PTR(lsm_link, "lsm_attach"))
54+
return;
55+
56+
/* LSM prog will be triggered when attaching fentry */
57+
fentry_link = bpf_program__attach_trace(skel->progs.fentry_run);
58+
if (!ASSERT_OK_PTR(fentry_link, "fentry_attach_success"))
59+
goto cleanup;
60+
61+
err = bpf_link__destroy(fentry_link);
62+
ASSERT_OK(err, "destroy_lsm");
63+
64+
cleanup:
65+
err = bpf_link__destroy(lsm_link);
66+
ASSERT_OK(err, "destroy_fentry");
67+
}
68+
69+
void test_cgroup1_hierarchy(void)
70+
{
71+
struct test_cgroup1_hierarchy *skel;
72+
__u64 current_cgid;
73+
int hid, err;
74+
75+
skel = test_cgroup1_hierarchy__open();
76+
if (!ASSERT_OK_PTR(skel, "open"))
77+
return;
78+
79+
skel->bss->target_pid = getpid();
80+
81+
err = bpf_program__set_attach_target(skel->progs.fentry_run, 0, "bpf_fentry_test1");
82+
if (!ASSERT_OK(err, "fentry_set_target"))
83+
goto destroy;
84+
85+
err = test_cgroup1_hierarchy__load(skel);
86+
if (!ASSERT_OK(err, "load"))
87+
goto destroy;
88+
89+
/* Setup cgroup1 hierarchy */
90+
err = setup_classid_environment();
91+
if (!ASSERT_OK(err, "setup_classid_environment"))
92+
goto destroy;
93+
94+
err = join_classid();
95+
if (!ASSERT_OK(err, "join_cgroup1"))
96+
goto cleanup;
97+
98+
current_cgid = get_classid_cgroup_id();
99+
if (!ASSERT_GE(current_cgid, 0, "cgroup1 id"))
100+
goto cleanup;
101+
102+
hid = get_cgroup1_hierarchy_id("net_cls");
103+
if (!ASSERT_GE(hid, 0, "cgroup1 id"))
104+
goto cleanup;
105+
skel->bss->target_hid = hid;
106+
107+
if (test__start_subtest("test_cgroup1_hierarchy")) {
108+
skel->bss->target_ancestor_cgid = current_cgid;
109+
bpf_cgroup1(skel);
110+
}
111+
112+
if (test__start_subtest("test_root_cgid")) {
113+
skel->bss->target_ancestor_cgid = 1;
114+
skel->bss->target_ancestor_level = 0;
115+
bpf_cgroup1(skel);
116+
}
117+
118+
if (test__start_subtest("test_invalid_level")) {
119+
skel->bss->target_ancestor_cgid = 1;
120+
skel->bss->target_ancestor_level = 1;
121+
bpf_cgroup1_invalid_id(skel);
122+
}
123+
124+
if (test__start_subtest("test_invalid_cgid")) {
125+
skel->bss->target_ancestor_cgid = 0;
126+
bpf_cgroup1_invalid_id(skel);
127+
}
128+
129+
if (test__start_subtest("test_invalid_hid")) {
130+
skel->bss->target_ancestor_cgid = 1;
131+
skel->bss->target_ancestor_level = 0;
132+
skel->bss->target_hid = -1;
133+
bpf_cgroup1_invalid_id(skel);
134+
}
135+
136+
if (test__start_subtest("test_invalid_cgrp_name")) {
137+
skel->bss->target_hid = get_cgroup1_hierarchy_id("net_cl");
138+
skel->bss->target_ancestor_cgid = current_cgid;
139+
bpf_cgroup1_invalid_id(skel);
140+
}
141+
142+
if (test__start_subtest("test_invalid_cgrp_name2")) {
143+
skel->bss->target_hid = get_cgroup1_hierarchy_id("net_cls,");
144+
skel->bss->target_ancestor_cgid = current_cgid;
145+
bpf_cgroup1_invalid_id(skel);
146+
}
147+
148+
if (test__start_subtest("test_sleepable_prog")) {
149+
skel->bss->target_hid = hid;
150+
skel->bss->target_ancestor_cgid = current_cgid;
151+
bpf_cgroup1_sleepable(skel);
152+
}
153+
154+
cleanup:
155+
cleanup_classid_environment();
156+
destroy:
157+
test_cgroup1_hierarchy__destroy(skel);
158+
}

0 commit comments

Comments
 (0)