diff options
| author | Sargun Dhillon <sargun@sargun.me> | 2016-12-02 05:42:18 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-12-03 16:07:11 -0500 |
| commit | 1a922fee66c8a691bfec738b6a5694b2dbb2177d (patch) | |
| tree | a2196acc64ef60499075d0711c4a0eecb0abef09 /samples | |
| parent | 69a9d09b2251aeb968e76f39f2b89774312daa3d (diff) | |
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 <sargun@sargun.me>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
| -rw-r--r-- | samples/bpf/Makefile | 2 | ||||
| -rw-r--r-- | samples/bpf/cgroup_helpers.c | 177 | ||||
| -rw-r--r-- | samples/bpf/cgroup_helpers.h | 16 | ||||
| -rw-r--r-- | samples/bpf/test_current_task_under_cgroup_user.c | 108 |
4 files changed, 218 insertions, 85 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 13c3e1869890..57d84949e814 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile | |||
| @@ -59,7 +59,7 @@ test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o | |||
| 59 | xdp1-objs := bpf_load.o libbpf.o xdp1_user.o | 59 | xdp1-objs := bpf_load.o libbpf.o xdp1_user.o |
| 60 | # reuse xdp1 source intentionally | 60 | # reuse xdp1 source intentionally |
| 61 | xdp2-objs := bpf_load.o libbpf.o xdp1_user.o | 61 | 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 \ |
| 63 | test_current_task_under_cgroup_user.o | 63 | test_current_task_under_cgroup_user.o |
| 64 | trace_event-objs := bpf_load.o libbpf.o trace_event_user.o | 64 | trace_event-objs := bpf_load.o libbpf.o trace_event_user.o |
| 65 | sampleip-objs := bpf_load.o libbpf.o sampleip_user.o | 65 | sampleip-objs := bpf_load.o libbpf.o sampleip_user.o |
diff --git a/samples/bpf/cgroup_helpers.c b/samples/bpf/cgroup_helpers.c new file mode 100644 index 000000000000..9d1be9426401 --- /dev/null +++ b/samples/bpf/cgroup_helpers.c | |||
| @@ -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 | } | ||
diff --git a/samples/bpf/cgroup_helpers.h b/samples/bpf/cgroup_helpers.h new file mode 100644 index 000000000000..78c55207b6bd --- /dev/null +++ b/samples/bpf/cgroup_helpers.h | |||
| @@ -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 | ||
diff --git a/samples/bpf/test_current_task_under_cgroup_user.c b/samples/bpf/test_current_task_under_cgroup_user.c index 30b0bce884f9..95aaaa846130 100644 --- a/samples/bpf/test_current_task_under_cgroup_user.c +++ b/samples/bpf/test_current_task_under_cgroup_user.c | |||
| @@ -11,50 +11,16 @@ | |||
| 11 | #include <unistd.h> | 11 | #include <unistd.h> |
| 12 | #include "libbpf.h" | 12 | #include "libbpf.h" |
| 13 | #include "bpf_load.h" | 13 | #include "bpf_load.h" |
| 14 | #include <string.h> | ||
| 15 | #include <fcntl.h> | ||
| 16 | #include <errno.h> | ||
| 17 | #include <linux/bpf.h> | 14 | #include <linux/bpf.h> |
| 18 | #include <sched.h> | 15 | #include "cgroup_helpers.h" |
| 19 | #include <sys/mount.h> | ||
| 20 | #include <sys/stat.h> | ||
| 21 | #include <sys/types.h> | ||
| 22 | #include <linux/limits.h> | ||
| 23 | 16 | ||
| 24 | #define CGROUP_MOUNT_PATH "/mnt" | 17 | #define CGROUP_PATH "/my-cgroup" |
| 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 | } | ||
| 52 | 18 | ||
| 53 | int main(int argc, char **argv) | 19 | int main(int argc, char **argv) |
| 54 | { | 20 | { |
| 55 | char filename[256]; | ||
| 56 | int cg2, idx = 0; | ||
| 57 | pid_t remote_pid, local_pid = getpid(); | 21 | pid_t remote_pid, local_pid = getpid(); |
| 22 | int cg2, idx = 0, rc = 0; | ||
| 23 | char filename[256]; | ||
| 58 | 24 | ||
| 59 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | 25 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); |
| 60 | if (load_bpf_file(filename)) { | 26 | if (load_bpf_file(filename)) { |
| @@ -62,47 +28,22 @@ int main(int argc, char **argv) | |||
| 62 | return 1; | 28 | return 1; |
| 63 | } | 29 | } |
| 64 | 30 | ||
| 65 | /* | 31 | if (setup_cgroup_environment()) |
| 66 | * This is to avoid interfering with existing cgroups. Unfortunately, | 32 | goto err; |
| 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 | } | ||
| 85 | 33 | ||
| 86 | if (mkdir(CGROUP_PATH, 0777) && errno != EEXIST) { | 34 | cg2 = create_and_get_cgroup(CGROUP_PATH); |
| 87 | log_err("mkdir cgroup"); | ||
| 88 | return 1; | ||
| 89 | } | ||
| 90 | 35 | ||
| 91 | cg2 = open(CGROUP_PATH, O_RDONLY); | 36 | if (!cg2) |
| 92 | if (cg2 < 0) { | 37 | goto err; |
| 93 | log_err("opening target cgroup"); | ||
| 94 | goto cleanup_cgroup_err; | ||
| 95 | } | ||
| 96 | 38 | ||
| 97 | if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { | 39 | if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { |
| 98 | log_err("Adding target cgroup to map"); | 40 | log_err("Adding target cgroup to map"); |
| 99 | goto cleanup_cgroup_err; | 41 | goto err; |
| 100 | } | ||
| 101 | if (join_cgroup("/mnt/my-cgroup")) { | ||
| 102 | log_err("Leaving target cgroup"); | ||
| 103 | goto cleanup_cgroup_err; | ||
| 104 | } | 42 | } |
| 105 | 43 | ||
| 44 | if (join_cgroup(CGROUP_PATH)) | ||
| 45 | goto err; | ||
| 46 | |||
| 106 | /* | 47 | /* |
| 107 | * The installed helper program catched the sync call, and should | 48 | * The installed helper program catched the sync call, and should |
| 108 | * write it to the map. | 49 | * write it to the map. |
| @@ -115,12 +56,12 @@ int main(int argc, char **argv) | |||
| 115 | fprintf(stderr, | 56 | fprintf(stderr, |
| 116 | "BPF Helper didn't write correct PID to map, but: %d\n", | 57 | "BPF Helper didn't write correct PID to map, but: %d\n", |
| 117 | remote_pid); | 58 | remote_pid); |
| 118 | goto leave_cgroup_err; | 59 | goto err; |
| 119 | } | 60 | } |
| 120 | 61 | ||
| 121 | /* Verify the negative scenario; leave the cgroup */ | 62 | /* Verify the negative scenario; leave the cgroup */ |
| 122 | if (join_cgroup(CGROUP_MOUNT_PATH)) | 63 | if (join_cgroup("/")) |
| 123 | goto leave_cgroup_err; | 64 | goto err; |
| 124 | 65 | ||
| 125 | remote_pid = 0; | 66 | remote_pid = 0; |
| 126 | bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); | 67 | bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); |
| @@ -130,16 +71,15 @@ int main(int argc, char **argv) | |||
| 130 | 71 | ||
| 131 | if (local_pid == remote_pid) { | 72 | if (local_pid == remote_pid) { |
| 132 | fprintf(stderr, "BPF cgroup negative test did not work\n"); | 73 | fprintf(stderr, "BPF cgroup negative test did not work\n"); |
| 133 | goto cleanup_cgroup_err; | 74 | goto err; |
| 134 | } | 75 | } |
| 135 | 76 | ||
| 136 | rmdir(CGROUP_PATH); | 77 | goto out; |
| 137 | return 0; | 78 | err: |
| 79 | rc = 1; | ||
| 138 | 80 | ||
| 139 | /* Error condition, cleanup */ | 81 | out: |
| 140 | leave_cgroup_err: | 82 | close(cg2); |
| 141 | join_cgroup(CGROUP_MOUNT_PATH); | 83 | cleanup_cgroup_environment(); |
| 142 | cleanup_cgroup_err: | 84 | return rc; |
| 143 | rmdir(CGROUP_PATH); | ||
| 144 | return 1; | ||
| 145 | } | 85 | } |
