selftests/bpf: add bpf_spin_lock C test

add bpf_spin_lock C based test that requires latest llvm with BTF support

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
Alexei Starovoitov 2019-01-31 15:40:08 -08:00 committed by Daniel Borkmann
parent b4d4556c32
commit ab963beb9f
4 changed files with 155 additions and 2 deletions

View File

@ -35,7 +35,7 @@ BPF_OBJ_FILES = \
sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o test_xdp_vlan.o \
xdp_dummy.o test_map_in_map.o
xdp_dummy.o test_map_in_map.o test_spin_lock.o
# Objects are built with default compilation flags and with sub-register
# code-gen enabled.

View File

@ -172,6 +172,10 @@ static int (*bpf_skb_vlan_pop)(void *ctx) =
(void *) BPF_FUNC_skb_vlan_pop;
static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) =
(void *) BPF_FUNC_rc_pointer_rel;
static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) =
(void *) BPF_FUNC_spin_lock;
static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) =
(void *) BPF_FUNC_spin_unlock;
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions

View File

@ -28,7 +28,7 @@ typedef __u16 __sum16;
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <linux/bpf.h>
#include <linux/err.h>
#include <bpf/bpf.h>
@ -1985,6 +1985,46 @@ static void test_flow_dissector(void)
bpf_object__close(obj);
}
static void *test_spin_lock(void *arg)
{
__u32 duration, retval;
int err, prog_fd = *(u32 *) arg;
err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4),
NULL, NULL, &retval, &duration);
CHECK(err || retval, "",
"err %d errno %d retval %d duration %d\n",
err, errno, retval, duration);
pthread_exit(arg);
}
static void test_spinlock(void)
{
const char *file = "./test_spin_lock.o";
pthread_t thread_id[4];
struct bpf_object *obj;
int prog_fd;
int err = 0, i;
void *ret;
err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
if (err) {
printf("test_spin_lock:bpf_prog_load errno %d\n", errno);
goto close_prog;
}
for (i = 0; i < 4; i++)
assert(pthread_create(&thread_id[i], NULL,
&test_spin_lock, &prog_fd) == 0);
for (i = 0; i < 4; i++)
assert(pthread_join(thread_id[i], &ret) == 0 &&
ret == (void *)&prog_fd);
goto close_prog_noerr;
close_prog:
error_cnt++;
close_prog_noerr:
bpf_object__close(obj);
}
int main(void)
{
srand(time(NULL));
@ -2013,6 +2053,7 @@ int main(void)
test_queue_stack_map(QUEUE);
test_queue_stack_map(STACK);
test_flow_dissector();
test_spinlock();
printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;

View File

@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <linux/version.h>
#include "bpf_helpers.h"
struct hmap_elem {
volatile int cnt;
struct bpf_spin_lock lock;
int test_padding;
};
struct bpf_map_def SEC("maps") hmap = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(int),
.value_size = sizeof(struct hmap_elem),
.max_entries = 1,
};
BPF_ANNOTATE_KV_PAIR(hmap, int, struct hmap_elem);
struct cls_elem {
struct bpf_spin_lock lock;
volatile int cnt;
};
struct bpf_map_def SEC("maps") cls_map = {
.type = BPF_MAP_TYPE_CGROUP_STORAGE,
.key_size = sizeof(struct bpf_cgroup_storage_key),
.value_size = sizeof(struct cls_elem),
};
BPF_ANNOTATE_KV_PAIR(cls_map, struct bpf_cgroup_storage_key,
struct cls_elem);
struct bpf_vqueue {
struct bpf_spin_lock lock;
/* 4 byte hole */
unsigned long long lasttime;
int credit;
unsigned int rate;
};
struct bpf_map_def SEC("maps") vqueue = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(struct bpf_vqueue),
.max_entries = 1,
};
BPF_ANNOTATE_KV_PAIR(vqueue, int, struct bpf_vqueue);
#define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
SEC("spin_lock_demo")
int bpf_sping_lock_test(struct __sk_buff *skb)
{
volatile int credit = 0, max_credit = 100, pkt_len = 64;
struct hmap_elem zero = {}, *val;
unsigned long long curtime;
struct bpf_vqueue *q;
struct cls_elem *cls;
int key = 0;
int err = 0;
val = bpf_map_lookup_elem(&hmap, &key);
if (!val) {
bpf_map_update_elem(&hmap, &key, &zero, 0);
val = bpf_map_lookup_elem(&hmap, &key);
if (!val) {
err = 1;
goto err;
}
}
/* spin_lock in hash map run time test */
bpf_spin_lock(&val->lock);
if (val->cnt)
val->cnt--;
else
val->cnt++;
if (val->cnt != 0 && val->cnt != 1)
err = 1;
bpf_spin_unlock(&val->lock);
/* spin_lock in array. virtual queue demo */
q = bpf_map_lookup_elem(&vqueue, &key);
if (!q)
goto err;
curtime = bpf_ktime_get_ns();
bpf_spin_lock(&q->lock);
q->credit += CREDIT_PER_NS(curtime - q->lasttime, q->rate);
q->lasttime = curtime;
if (q->credit > max_credit)
q->credit = max_credit;
q->credit -= pkt_len;
credit = q->credit;
bpf_spin_unlock(&q->lock);
/* spin_lock in cgroup local storage */
cls = bpf_get_local_storage(&cls_map, 0);
bpf_spin_lock(&cls->lock);
cls->cnt++;
bpf_spin_unlock(&cls->lock);
err:
return err;
}
char _license[] SEC("license") = "GPL";