root/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c
// SPDX-License-Identifier: GPL-2.0

#include <test_progs.h>

#include "cgroup_helpers.h"

#define PING_CMD        "ping -q -c1 -w1 127.0.0.1 > /dev/null"

static char bpf_log_buf[BPF_LOG_BUF_SIZE];

static int prog_load(void)
{
        struct bpf_insn prog[] = {
                BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = 1 */
                BPF_EXIT_INSN(),
        };
        size_t insns_cnt = ARRAY_SIZE(prog);

        return bpf_test_load_program(BPF_PROG_TYPE_CGROUP_SKB,
                               prog, insns_cnt, "GPL", 0,
                               bpf_log_buf, BPF_LOG_BUF_SIZE);
}

void serial_test_cgroup_attach_autodetach(void)
{
        __u32 duration = 0, prog_cnt = 4, attach_flags;
        int allow_prog[2] = {-1};
        __u32 prog_ids[2] = {0};
        void *ptr = NULL;
        int cg = 0, i;
        int attempts;

        for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
                allow_prog[i] = prog_load();
                if (CHECK(allow_prog[i] < 0, "prog_load",
                          "verifier output:\n%s\n-------\n", bpf_log_buf))
                        goto err;
        }

        if (CHECK_FAIL(setup_cgroup_environment()))
                goto err;

        /* create a cgroup, attach two programs and remember their ids */
        cg = create_and_get_cgroup("/cg_autodetach");
        if (CHECK_FAIL(cg < 0))
                goto err;

        if (CHECK_FAIL(join_cgroup("/cg_autodetach")))
                goto err;

        for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
                if (CHECK(bpf_prog_attach(allow_prog[i], cg,
                                          BPF_CGROUP_INET_EGRESS,
                                          BPF_F_ALLOW_MULTI),
                          "prog_attach", "prog[%d], errno=%d\n", i, errno))
                        goto err;

        /* make sure that programs are attached and run some traffic */
        if (CHECK(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags,
                                 prog_ids, &prog_cnt),
                  "prog_query", "errno=%d\n", errno))
                goto err;
        if (CHECK_FAIL(system(PING_CMD)))
                goto err;

        /* allocate some memory (4Mb) to pin the original cgroup */
        ptr = malloc(4 * (1 << 20));
        if (CHECK_FAIL(!ptr))
                goto err;

        /* close programs and cgroup fd */
        for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
                close(allow_prog[i]);
                allow_prog[i] = -1;
        }

        close(cg);
        cg = 0;

        /* leave the cgroup and remove it. don't detach programs */
        cleanup_cgroup_environment();

        /* wait for the asynchronous auto-detachment.
         * wait for no more than 5 sec and give up.
         */
        for (i = 0; i < ARRAY_SIZE(prog_ids); i++) {
                for (attempts = 5; attempts >= 0; attempts--) {
                        int fd = bpf_prog_get_fd_by_id(prog_ids[i]);

                        if (fd < 0)
                                break;

                        /* don't leave the fd open */
                        close(fd);

                        if (CHECK_FAIL(!attempts))
                                goto err;

                        sleep(1);
                }
        }

err:
        for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
                if (allow_prog[i] >= 0)
                        close(allow_prog[i]);
        if (cg)
                close(cg);
        free(ptr);
        cleanup_cgroup_environment();
}