tools/bpf: add a selftest for bpf_get_current_cgroup_id() helper
Syscall name_to_handle_at() can be used to get cgroup id for a particular cgroup path in user space. The selftest got cgroup id from both user and kernel, and compare to ensure they are equal to each other. Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
c7ddbbaf1e
commit
f269099a7e
1
tools/testing/selftests/bpf/.gitignore
vendored
1
tools/testing/selftests/bpf/.gitignore
vendored
@ -18,3 +18,4 @@ urandom_read
|
||||
test_btf
|
||||
test_sockmap
|
||||
test_lirc_mode2_user
|
||||
get_cgroup_id_user
|
||||
|
@ -24,7 +24,7 @@ urandom_read: urandom_read.c
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
|
||||
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
|
||||
test_sock test_btf test_sockmap test_lirc_mode2_user
|
||||
test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user
|
||||
|
||||
TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
|
||||
test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
|
||||
@ -34,7 +34,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
|
||||
sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \
|
||||
test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \
|
||||
test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \
|
||||
test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o
|
||||
test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
|
||||
get_cgroup_id_kern.o
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_PROGS := test_kmod.sh \
|
||||
@ -63,6 +64,7 @@ $(OUTPUT)/test_sock: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sock_addr: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockmap: cgroup_helpers.c
|
||||
$(OUTPUT)/test_progs: trace_helpers.c
|
||||
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
|
||||
|
||||
.PHONY: force
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/sched.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
@ -176,3 +177,59 @@ int create_and_get_cgroup(char *path)
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_cgroup_id() - Get cgroup id for a particular cgroup path
|
||||
* @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(char *path)
|
||||
{
|
||||
int dirfd, err, flags, mount_id, fhsize;
|
||||
union {
|
||||
unsigned long long cgid;
|
||||
unsigned char raw_bytes[8];
|
||||
} id;
|
||||
char cgroup_workdir[PATH_MAX + 1];
|
||||
struct file_handle *fhp, *fhp2;
|
||||
unsigned long long ret = 0;
|
||||
|
||||
format_cgroup_path(cgroup_workdir, path);
|
||||
|
||||
dirfd = AT_FDCWD;
|
||||
flags = 0;
|
||||
fhsize = sizeof(*fhp);
|
||||
fhp = calloc(1, fhsize);
|
||||
if (!fhp) {
|
||||
log_err("calloc");
|
||||
return 0;
|
||||
}
|
||||
err = name_to_handle_at(dirfd, cgroup_workdir, fhp, &mount_id, flags);
|
||||
if (err >= 0 || fhp->handle_bytes != 8) {
|
||||
log_err("name_to_handle_at");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
fhsize = sizeof(struct file_handle) + fhp->handle_bytes;
|
||||
fhp2 = realloc(fhp, fhsize);
|
||||
if (!fhp2) {
|
||||
log_err("realloc");
|
||||
goto free_mem;
|
||||
}
|
||||
err = name_to_handle_at(dirfd, cgroup_workdir, fhp2, &mount_id, flags);
|
||||
fhp = fhp2;
|
||||
if (err < 0) {
|
||||
log_err("name_to_handle_at");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
memcpy(id.raw_bytes, fhp->f_handle, 8);
|
||||
ret = id.cgid;
|
||||
|
||||
free_mem:
|
||||
free(fhp);
|
||||
return ret;
|
||||
}
|
||||
|
@ -13,5 +13,6 @@ int create_and_get_cgroup(char *path);
|
||||
int join_cgroup(char *path);
|
||||
int setup_cgroup_environment(void);
|
||||
void cleanup_cgroup_environment(void);
|
||||
unsigned long long get_cgroup_id(char *path);
|
||||
|
||||
#endif
|
||||
|
28
tools/testing/selftests/bpf/get_cgroup_id_kern.c
Normal file
28
tools/testing/selftests/bpf/get_cgroup_id_kern.c
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
struct bpf_map_def SEC("maps") cg_ids = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
SEC("tracepoint/syscalls/sys_enter_nanosleep")
|
||||
int trace(void *ctx)
|
||||
{
|
||||
__u32 key = 0;
|
||||
__u64 *val;
|
||||
|
||||
val = bpf_map_lookup_elem(&cg_ids, &key);
|
||||
if (val)
|
||||
*val = bpf_get_current_cgroup_id();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */
|
141
tools/testing/selftests/bpf/get_cgroup_id_user.c
Normal file
141
tools/testing/selftests/bpf/get_cgroup_id_user.c
Normal file
@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 Facebook
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "cgroup_helpers.h"
|
||||
#include "bpf_rlimit.h"
|
||||
|
||||
#define CHECK(condition, tag, format...) ({ \
|
||||
int __ret = !!(condition); \
|
||||
if (__ret) { \
|
||||
printf("%s:FAIL:%s ", __func__, tag); \
|
||||
printf(format); \
|
||||
} else { \
|
||||
printf("%s:PASS:%s\n", __func__, tag); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
static int bpf_find_map(const char *test, struct bpf_object *obj,
|
||||
const char *name)
|
||||
{
|
||||
struct bpf_map *map;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, name);
|
||||
if (!map)
|
||||
return -1;
|
||||
return bpf_map__fd(map);
|
||||
}
|
||||
|
||||
#define TEST_CGROUP "/test-bpf-get-cgroup-id/"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *probe_name = "syscalls/sys_enter_nanosleep";
|
||||
const char *file = "get_cgroup_id_kern.o";
|
||||
int err, bytes, efd, prog_fd, pmu_fd;
|
||||
struct perf_event_attr attr = {};
|
||||
int cgroup_fd, cgidmap_fd;
|
||||
struct bpf_object *obj;
|
||||
__u64 kcgid = 0, ucgid;
|
||||
int exit_code = 1;
|
||||
char buf[256];
|
||||
__u32 key = 0;
|
||||
|
||||
err = setup_cgroup_environment();
|
||||
if (CHECK(err, "setup_cgroup_environment", "err %d errno %d\n", err,
|
||||
errno))
|
||||
return 1;
|
||||
|
||||
cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
|
||||
if (CHECK(cgroup_fd < 0, "create_and_get_cgroup", "err %d errno %d\n",
|
||||
cgroup_fd, errno))
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
err = join_cgroup(TEST_CGROUP);
|
||||
if (CHECK(err, "join_cgroup", "err %d errno %d\n", err, errno))
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err, "bpf_prog_load", "err %d errno %d\n", err, errno))
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
cgidmap_fd = bpf_find_map(__func__, obj, "cg_ids");
|
||||
if (CHECK(cgidmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
|
||||
cgidmap_fd, errno))
|
||||
goto close_prog;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"/sys/kernel/debug/tracing/events/%s/id", probe_name);
|
||||
efd = open(buf, O_RDONLY, 0);
|
||||
if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
|
||||
goto close_prog;
|
||||
bytes = read(efd, buf, sizeof(buf));
|
||||
close(efd);
|
||||
if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "read",
|
||||
"bytes %d errno %d\n", bytes, errno))
|
||||
goto close_prog;
|
||||
|
||||
attr.config = strtol(buf, NULL, 0);
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.sample_type = PERF_SAMPLE_RAW;
|
||||
attr.sample_period = 1;
|
||||
attr.wakeup_events = 1;
|
||||
|
||||
/* attach to this pid so the all bpf invocations will be in the
|
||||
* cgroup associated with this pid.
|
||||
*/
|
||||
pmu_fd = syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
|
||||
if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd,
|
||||
errno))
|
||||
goto close_prog;
|
||||
|
||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", err,
|
||||
errno))
|
||||
goto close_pmu;
|
||||
|
||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
|
||||
if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", err,
|
||||
errno))
|
||||
goto close_pmu;
|
||||
|
||||
/* trigger some syscalls */
|
||||
sleep(1);
|
||||
|
||||
err = bpf_map_lookup_elem(cgidmap_fd, &key, &kcgid);
|
||||
if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno))
|
||||
goto close_pmu;
|
||||
|
||||
ucgid = get_cgroup_id(TEST_CGROUP);
|
||||
if (CHECK(kcgid != ucgid, "compare_cgroup_id",
|
||||
"kern cgid %llx user cgid %llx", kcgid, ucgid))
|
||||
goto close_pmu;
|
||||
|
||||
exit_code = 0;
|
||||
printf("%s:PASS\n", argv[0]);
|
||||
|
||||
close_pmu:
|
||||
close(pmu_fd);
|
||||
close_prog:
|
||||
bpf_object__close(obj);
|
||||
cleanup_cgroup_env:
|
||||
cleanup_cgroup_environment();
|
||||
return exit_code;
|
||||
}
|
Loading…
Reference in New Issue
Block a user