Skip to content

introduce BPF_F_SHARE_PE #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
sudo: required
language: bash
dist: bionic
services:
- docker

env:
global:
- PROJECT_NAME='libbpf'
- AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")"
- REPO_ROOT="$TRAVIS_BUILD_DIR"
- CI_ROOT="$REPO_ROOT/travis-ci"
- VMTEST_ROOT="$CI_ROOT/vmtest"

addons:
apt:
packages:
- qemu-kvm
- zstd
- binutils-dev
- elfutils
- libcap-dev
- libelf-dev
- libdw-dev
- python3-docutils

jobs:
include:
- stage: Builds & Tests
name: Kernel LATEST + selftests
language: bash
env: KERNEL=LATEST
script: $CI_ROOT/vmtest/run_vmtest.sh || travis_terminate 1
3 changes: 3 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ enum {

/* Enable memory-mapping BPF map */
BPF_F_MMAPABLE = (1U << 10),

/* Share perf_event among processes */
BPF_F_SHARE_PE = (1U << 11),
};

/* Flags for BPF_PROG_QUERY. */
Expand Down
31 changes: 29 additions & 2 deletions kernel/bpf/arraymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "map_in_map.h"

#define ARRAY_CREATE_FLAG_MASK \
(BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK)
(BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | BPF_F_SHARE_PE)

static void bpf_array_free_percpu(struct bpf_array *array)
{
Expand Down Expand Up @@ -64,6 +64,10 @@ int array_map_alloc_check(union bpf_attr *attr)
attr->map_flags & BPF_F_MMAPABLE)
return -EINVAL;

if (attr->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
attr->map_flags & BPF_F_SHARE_PE)
return -EINVAL;

if (attr->value_size > KMALLOC_MAX_SIZE)
/* if value_size is bigger, the user space won't be able to
* access the elements.
Expand Down Expand Up @@ -778,6 +782,26 @@ static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
}
}

static void perf_event_fd_array_map_free(struct bpf_map *map)
{
struct bpf_event_entry *ee;
struct bpf_array *array;
int i;

if ((map->map_flags & BPF_F_SHARE_PE) == 0) {
fd_array_map_free(map);
return;
}

array = container_of(map, struct bpf_array, map);
for (i = 0; i < array->map.max_entries; i++) {
ee = READ_ONCE(array->ptrs[i]);
if (ee)
fd_array_map_delete_elem(map, &i);
}
bpf_map_area_free(array);
}

static void *prog_fd_array_get_ptr(struct bpf_map *map,
struct file *map_file, int fd)
{
Expand Down Expand Up @@ -1134,6 +1158,9 @@ static void perf_event_fd_array_release(struct bpf_map *map,
struct bpf_event_entry *ee;
int i;

if (map->map_flags & BPF_F_SHARE_PE)
return;

rcu_read_lock();
for (i = 0; i < array->map.max_entries; i++) {
ee = READ_ONCE(array->ptrs[i]);
Expand All @@ -1148,7 +1175,7 @@ const struct bpf_map_ops perf_event_array_map_ops = {
.map_meta_equal = bpf_map_meta_equal,
.map_alloc_check = fd_array_map_alloc_check,
.map_alloc = array_map_alloc,
.map_free = fd_array_map_free,
.map_free = perf_event_fd_array_map_free,
.map_get_next_key = array_map_get_next_key,
.map_lookup_elem = fd_array_map_lookup_elem,
.map_delete_elem = fd_array_map_delete_elem,
Expand Down
3 changes: 3 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ enum {

/* Enable memory-mapping BPF map */
BPF_F_MMAPABLE = (1U << 10),

/* Share perf_event among processes */
BPF_F_SHARE_PE = (1U << 11),
};

/* Flags for BPF_PROG_QUERY. */
Expand Down
68 changes: 68 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/perf_event_share.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019 Facebook */
#include <test_progs.h>
#include <linux/bpf.h>
#include "test_perf_event_share.skel.h"

static int duration;

static void test_one_map(struct bpf_map *map, struct bpf_program *prog,
bool has_share_pe)
{
int err, key = 0, pfd = -1, mfd = bpf_map__fd(map);
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts);
struct perf_event_attr attr = {
.size = sizeof(struct perf_event_attr),
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_CPU_CLOCK,
};

pfd = syscall(__NR_perf_event_open, &attr, 0 /* pid */,
-1 /* cpu 0 */, -1 /* group id */, 0 /* flags */);
if (CHECK(pfd < 0, "perf_event_open", "failed\n"))
return;

err = bpf_map_update_elem(mfd, &key, &pfd, BPF_ANY);
if (CHECK(err < 0, "bpf_map_update_elem", "failed\n"))
goto cleanup;

err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
if (CHECK(err < 0, "bpf_prog_test_run_opts", "failed\n"))
goto cleanup;
if (CHECK(opts.retval != 0, "bpf_perf_event_read_value",
"failed with %d\n", opts.retval))
goto cleanup;

/* closing mfd, prog still holds a reference on map */
close(mfd);

err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
if (CHECK(err < 0, "bpf_prog_test_run_opts", "failed\n"))
goto cleanup;

if (has_share_pe) {
CHECK(opts.retval != 0, "bpf_perf_event_read_value",
"failed with %d\n", opts.retval);
} else {
CHECK(opts.retval != -ENOENT, "bpf_perf_event_read_value",
"should have failed with %d, but got %d\n", -ENOENT,
opts.retval);
}

cleanup:
close(pfd);
}

void test_perf_event_share(void)
{
struct test_perf_event_share *skel;

skel = test_perf_event_share__open_and_load();
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
return;

test_one_map(skel->maps.array_1, skel->progs.read_array_1, false);
test_one_map(skel->maps.array_2, skel->progs.read_array_2, true);

test_perf_event_share__destroy(skel);
}
44 changes: 44 additions & 0 deletions tools/testing/selftests/bpf/progs/test_perf_event_share.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Facebook
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} array_1 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
__uint(map_flags, BPF_F_SHARE_PE); /* array_2 has BPF_F_SHARE_PE */
} array_2 SEC(".maps");

SEC("raw_tp/sched_switch")
int BPF_PROG(read_array_1)
{
struct bpf_perf_event_value val;
long ret;

ret = bpf_perf_event_read_value(&array_1, 0, &val, sizeof(val));
bpf_printk("read_array_1 returns %ld", ret);
return ret;
}

SEC("raw_tp/task_rename")
int BPF_PROG(read_array_2)
{
struct bpf_perf_event_value val;
long ret;

ret = bpf_perf_event_read_value(&array_2, 0, &val, sizeof(val));
bpf_printk("read_array_2 returns %ld", ret);
return ret;
}

char LICENSE[] SEC("license") = "GPL";
84 changes: 84 additions & 0 deletions travis-ci/managers/debian.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash

PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
DEBIAN_RELEASE="${DEBIAN_RELEASE:-testing}"
CONT_NAME="${CONT_NAME:-libbpf-debian-$DEBIAN_RELEASE}"
ENV_VARS="${ENV_VARS:-}"
DOCKER_RUN="${DOCKER_RUN:-docker run}"
REPO_ROOT="${REPO_ROOT:-$PWD}"
ADDITIONAL_DEPS=(clang pkg-config gcc-8)
CFLAGS="-g -O2 -Werror -Wall"

function info() {
echo -e "\033[33;1m$1\033[0m"
}

function error() {
echo -e "\033[31;1m$1\033[0m"
}

function docker_exec() {
docker exec $ENV_VARS -it $CONT_NAME "$@"
}

set -e

source "$(dirname $0)/travis_wait.bash"

for phase in "${PHASES[@]}"; do
case $phase in
SETUP)
info "Setup phase"
info "Using Debian $DEBIAN_RELEASE"

sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
docker --version

docker pull debian:$DEBIAN_RELEASE
info "Starting container $CONT_NAME"
$DOCKER_RUN -v $REPO_ROOT:/build:rw \
-w /build --privileged=true --name $CONT_NAME \
-dit --net=host debian:$DEBIAN_RELEASE /bin/bash
docker_exec bash -c "echo deb-src http://deb.debian.org/debian $DEBIAN_RELEASE main >>/etc/apt/sources.list"
docker_exec apt-get -y update
docker_exec apt-get -y build-dep libelf-dev
docker_exec apt-get -y install libelf-dev
docker_exec apt-get -y install "${ADDITIONAL_DEPS[@]}"
;;
RUN|RUN_CLANG|RUN_GCC8|RUN_ASAN|RUN_CLANG_ASAN|RUN_GCC8_ASAN)
if [[ "$phase" = *"CLANG"* ]]; then
ENV_VARS="-e CC=clang -e CXX=clang++"
CC="clang"
elif [[ "$phase" = *"GCC8"* ]]; then
ENV_VARS="-e CC=gcc-8 -e CXX=g++-8"
CC="gcc-8"
else
CFLAGS="${CFLAGS} -Wno-stringop-truncation"
fi
if [[ "$phase" = *"ASAN"* ]]; then
CFLAGS="${CFLAGS} -fsanitize=address,undefined"
fi
docker_exec mkdir build install
docker_exec ${CC:-cc} --version
info "build"
docker_exec make -j$((4*$(nproc))) CFLAGS="${CFLAGS}" -C ./src -B OBJDIR=../build
info "ldd build/libbpf.so:"
docker_exec ldd build/libbpf.so
if ! docker_exec ldd build/libbpf.so | grep -q libelf; then
error "No reference to libelf.so in libbpf.so!"
exit 1
fi
info "install"
docker_exec make -j$((4*$(nproc))) -C src OBJDIR=../build DESTDIR=../install install
docker_exec rm -rf build install
;;
CLEANUP)
info "Cleanup phase"
docker stop $CONT_NAME
docker rm -f $CONT_NAME
;;
*)
echo >&2 "Unknown phase '$phase'"
exit 1
esac
done
61 changes: 61 additions & 0 deletions travis-ci/managers/travis_wait.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash
# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed
# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts.

travis_jigger() {
local cmd_pid="${1}"
shift
local timeout="${1}"
shift
local count=0

echo -e "\\n"

while [[ "${count}" -lt "${timeout}" ]]; do
count="$((count + 1))"
echo -ne "Still running (${count} of ${timeout}): ${*}\\r"
sleep 60
done

echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n"
kill -9 "${cmd_pid}"
}

travis_wait() {
local timeout="${1}"

if [[ "${timeout}" =~ ^[0-9]+$ ]]; then
shift
else
timeout=20
fi

local cmd=("${@}")
local log_file="travis_wait_${$}.log"

"${cmd[@]}" &>"${log_file}" &
local cmd_pid="${!}"

travis_jigger "${!}" "${timeout}" "${cmd[@]}" &
local jigger_pid="${!}"
local result

{
set +e
wait "${cmd_pid}" 2>/dev/null
result="${?}"
ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}"
set -e
}

if [[ "${result}" -eq 0 ]]; then
echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
else
echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
fi

echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n"
cat "${log_file}"

return "${result}"
}
Loading