selftests/bpf: add bpf_spin_lock C test
authorAlexei Starovoitov <ast@kernel.org>
Thu, 31 Jan 2019 23:40:08 +0000 (15:40 -0800)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 1 Feb 2019 19:55:39 +0000 (20:55 +0100)
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>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_spin_lock.c [new file with mode: 0644]

index 8993e9c8f4101ef76319ca6c40e27710e1690def..302b8e70dec9089b4d47410c938967d90633993b 100644 (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.
index 6c77cf7bedce84fac91e68858d818ad2063fafeb..6a0ce0f055c5bda7aba08807f84c44d0b28bc21c 100644 (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
index d8940b8b2f8d6634e63fd40d02ce2a9eced30f04..d2e71d6973404cf6ef047f9f3fab0f1065bd8446 100644 (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;
diff --git a/tools/testing/selftests/bpf/test_spin_lock.c b/tools/testing/selftests/bpf/test_spin_lock.c
new file mode 100644 (file)
index 0000000..40f9043
--- /dev/null
@@ -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";