1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <linux/compiler.h> 4 #include <asm/barrier.h> 5 #include <test_progs.h> 6 #include <sys/mman.h> 7 #include <sys/epoll.h> 8 #include <time.h> 9 #include <sched.h> 10 #include <signal.h> 11 #include <pthread.h> 12 #include <sys/sysinfo.h> 13 #include <linux/perf_event.h> 14 #include <linux/ring_buffer.h> 15 #include "test_ringbuf.skel.h" 16 17 #define EDONE 7777 18 19 static int duration = 0; 20 21 struct sample { 22 int pid; 23 int seq; 24 long value; 25 char comm[16]; 26 }; 27 28 static int sample_cnt; 29 30 static void atomic_inc(int *cnt) 31 { 32 __atomic_add_fetch(cnt, 1, __ATOMIC_SEQ_CST); 33 } 34 35 static int atomic_xchg(int *cnt, int val) 36 { 37 return __atomic_exchange_n(cnt, val, __ATOMIC_SEQ_CST); 38 } 39 40 static int process_sample(void *ctx, void *data, size_t len) 41 { 42 struct sample *s = data; 43 44 atomic_inc(&sample_cnt); 45 46 switch (s->seq) { 47 case 0: 48 CHECK(s->value != 333, "sample1_value", "exp %ld, got %ld\n", 49 333L, s->value); 50 return 0; 51 case 1: 52 CHECK(s->value != 777, "sample2_value", "exp %ld, got %ld\n", 53 777L, s->value); 54 return -EDONE; 55 default: 56 /* we don't care about the rest */ 57 return 0; 58 } 59 } 60 61 static struct test_ringbuf *skel; 62 static struct ring_buffer *ringbuf; 63 64 static void trigger_samples() 65 { 66 skel->bss->dropped = 0; 67 skel->bss->total = 0; 68 skel->bss->discarded = 0; 69 70 /* trigger exactly two samples */ 71 skel->bss->value = 333; 72 syscall(__NR_getpgid); 73 skel->bss->value = 777; 74 syscall(__NR_getpgid); 75 } 76 77 static void *poll_thread(void *input) 78 { 79 long timeout = (long)input; 80 81 return (void *)(long)ring_buffer__poll(ringbuf, timeout); 82 } 83 84 void test_ringbuf(void) 85 { 86 const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample); 87 pthread_t thread; 88 long bg_ret = -1; 89 int err, cnt; 90 int page_size = getpagesize(); 91 92 skel = test_ringbuf__open(); 93 if (CHECK(!skel, "skel_open", "skeleton open failed\n")) 94 return; 95 96 err = bpf_map__set_max_entries(skel->maps.ringbuf, page_size); 97 if (CHECK(err != 0, "bpf_map__set_max_entries", "bpf_map__set_max_entries failed\n")) 98 goto cleanup; 99 100 err = test_ringbuf__load(skel); 101 if (CHECK(err != 0, "skel_load", "skeleton load failed\n")) 102 goto cleanup; 103 104 /* only trigger BPF program for current process */ 105 skel->bss->pid = getpid(); 106 107 ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.ringbuf), 108 process_sample, NULL, NULL); 109 if (CHECK(!ringbuf, "ringbuf_create", "failed to create ringbuf\n")) 110 goto cleanup; 111 112 err = test_ringbuf__attach(skel); 113 if (CHECK(err, "skel_attach", "skeleton attachment failed: %d\n", err)) 114 goto cleanup; 115 116 trigger_samples(); 117 118 /* 2 submitted + 1 discarded records */ 119 CHECK(skel->bss->avail_data != 3 * rec_sz, 120 "err_avail_size", "exp %ld, got %ld\n", 121 3L * rec_sz, skel->bss->avail_data); 122 CHECK(skel->bss->ring_size != page_size, 123 "err_ring_size", "exp %ld, got %ld\n", 124 (long)page_size, skel->bss->ring_size); 125 CHECK(skel->bss->cons_pos != 0, 126 "err_cons_pos", "exp %ld, got %ld\n", 127 0L, skel->bss->cons_pos); 128 CHECK(skel->bss->prod_pos != 3 * rec_sz, 129 "err_prod_pos", "exp %ld, got %ld\n", 130 3L * rec_sz, skel->bss->prod_pos); 131 132 /* poll for samples */ 133 err = ring_buffer__poll(ringbuf, -1); 134 135 /* -EDONE is used as an indicator that we are done */ 136 if (CHECK(err != -EDONE, "err_done", "done err: %d\n", err)) 137 goto cleanup; 138 cnt = atomic_xchg(&sample_cnt, 0); 139 CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt); 140 141 /* we expect extra polling to return nothing */ 142 err = ring_buffer__poll(ringbuf, 0); 143 if (CHECK(err != 0, "extra_samples", "poll result: %d\n", err)) 144 goto cleanup; 145 cnt = atomic_xchg(&sample_cnt, 0); 146 CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt); 147 148 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 149 0L, skel->bss->dropped); 150 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 151 2L, skel->bss->total); 152 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 153 1L, skel->bss->discarded); 154 155 /* now validate consumer position is updated and returned */ 156 trigger_samples(); 157 CHECK(skel->bss->cons_pos != 3 * rec_sz, 158 "err_cons_pos", "exp %ld, got %ld\n", 159 3L * rec_sz, skel->bss->cons_pos); 160 err = ring_buffer__poll(ringbuf, -1); 161 CHECK(err <= 0, "poll_err", "err %d\n", err); 162 cnt = atomic_xchg(&sample_cnt, 0); 163 CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt); 164 165 /* start poll in background w/ long timeout */ 166 err = pthread_create(&thread, NULL, poll_thread, (void *)(long)10000); 167 if (CHECK(err, "bg_poll", "pthread_create failed: %d\n", err)) 168 goto cleanup; 169 170 /* turn off notifications now */ 171 skel->bss->flags = BPF_RB_NO_WAKEUP; 172 173 /* give background thread a bit of a time */ 174 usleep(50000); 175 trigger_samples(); 176 /* sleeping arbitrarily is bad, but no better way to know that 177 * epoll_wait() **DID NOT** unblock in background thread 178 */ 179 usleep(50000); 180 /* background poll should still be blocked */ 181 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 182 if (CHECK(err != EBUSY, "try_join", "err %d\n", err)) 183 goto cleanup; 184 185 /* BPF side did everything right */ 186 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 187 0L, skel->bss->dropped); 188 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 189 2L, skel->bss->total); 190 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 191 1L, skel->bss->discarded); 192 cnt = atomic_xchg(&sample_cnt, 0); 193 CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt); 194 195 /* clear flags to return to "adaptive" notification mode */ 196 skel->bss->flags = 0; 197 198 /* produce new samples, no notification should be triggered, because 199 * consumer is now behind 200 */ 201 trigger_samples(); 202 203 /* background poll should still be blocked */ 204 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 205 if (CHECK(err != EBUSY, "try_join", "err %d\n", err)) 206 goto cleanup; 207 208 /* still no samples, because consumer is behind */ 209 cnt = atomic_xchg(&sample_cnt, 0); 210 CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt); 211 212 skel->bss->dropped = 0; 213 skel->bss->total = 0; 214 skel->bss->discarded = 0; 215 216 skel->bss->value = 333; 217 syscall(__NR_getpgid); 218 /* now force notifications */ 219 skel->bss->flags = BPF_RB_FORCE_WAKEUP; 220 skel->bss->value = 777; 221 syscall(__NR_getpgid); 222 223 /* now we should get a pending notification */ 224 usleep(50000); 225 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 226 if (CHECK(err, "join_bg", "err %d\n", err)) 227 goto cleanup; 228 229 if (CHECK(bg_ret <= 0, "bg_ret", "epoll_wait result: %ld", bg_ret)) 230 goto cleanup; 231 232 /* due to timing variations, there could still be non-notified 233 * samples, so consume them here to collect all the samples 234 */ 235 err = ring_buffer__consume(ringbuf); 236 CHECK(err < 0, "rb_consume", "failed: %d\b", err); 237 238 /* 3 rounds, 2 samples each */ 239 cnt = atomic_xchg(&sample_cnt, 0); 240 CHECK(cnt != 6, "cnt", "exp %d samples, got %d\n", 6, cnt); 241 242 /* BPF side did everything right */ 243 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 244 0L, skel->bss->dropped); 245 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 246 2L, skel->bss->total); 247 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 248 1L, skel->bss->discarded); 249 250 test_ringbuf__detach(skel); 251 cleanup: 252 ring_buffer__free(ringbuf); 253 test_ringbuf__destroy(skel); 254 } 255