Loading tools/testing/selftests/bpf/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading tools/testing/selftests/bpf/bpf_helpers.h +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading tools/testing/selftests/bpf/test_progs.c +42 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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)); Loading Loading @@ -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; Loading tools/testing/selftests/bpf/test_spin_lock.c 0 → 100644 +108 −0 Original line number Diff line number Diff line // 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"; Loading
tools/testing/selftests/bpf/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
tools/testing/selftests/bpf/bpf_helpers.h +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
tools/testing/selftests/bpf/test_progs.c +42 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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)); Loading Loading @@ -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; Loading
tools/testing/selftests/bpf/test_spin_lock.c 0 → 100644 +108 −0 Original line number Diff line number Diff line // 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";