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 volatile int sample_cnt; 29 30 static int process_sample(void *ctx, void *data, size_t len) 31 { 32 struct sample *s = data; 33 34 sample_cnt++; 35 36 switch (s->seq) { 37 case 0: 38 CHECK(s->value != 333, "sample1_value", "exp %ld, got %ld\n", 39 333L, s->value); 40 return 0; 41 case 1: 42 CHECK(s->value != 777, "sample2_value", "exp %ld, got %ld\n", 43 777L, s->value); 44 return -EDONE; 45 default: 46 /* we don't care about the rest */ 47 return 0; 48 } 49 } 50 51 static struct test_ringbuf *skel; 52 static struct ring_buffer *ringbuf; 53 54 static void trigger_samples() 55 { 56 skel->bss->dropped = 0; 57 skel->bss->total = 0; 58 skel->bss->discarded = 0; 59 60 /* trigger exactly two samples */ 61 skel->bss->value = 333; 62 syscall(__NR_getpgid); 63 skel->bss->value = 777; 64 syscall(__NR_getpgid); 65 } 66 67 static void *poll_thread(void *input) 68 { 69 long timeout = (long)input; 70 71 return (void *)(long)ring_buffer__poll(ringbuf, timeout); 72 } 73 74 void test_ringbuf(void) 75 { 76 const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample); 77 pthread_t thread; 78 long bg_ret = -1; 79 int err; 80 81 skel = test_ringbuf__open_and_load(); 82 if (CHECK(!skel, "skel_open_load", "skeleton open&load failed\n")) 83 return; 84 85 /* only trigger BPF program for current process */ 86 skel->bss->pid = getpid(); 87 88 ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.ringbuf), 89 process_sample, NULL, NULL); 90 if (CHECK(!ringbuf, "ringbuf_create", "failed to create ringbuf\n")) 91 goto cleanup; 92 93 err = test_ringbuf__attach(skel); 94 if (CHECK(err, "skel_attach", "skeleton attachment failed: %d\n", err)) 95 goto cleanup; 96 97 trigger_samples(); 98 99 /* 2 submitted + 1 discarded records */ 100 CHECK(skel->bss->avail_data != 3 * rec_sz, 101 "err_avail_size", "exp %ld, got %ld\n", 102 3L * rec_sz, skel->bss->avail_data); 103 CHECK(skel->bss->ring_size != 4096, 104 "err_ring_size", "exp %ld, got %ld\n", 105 4096L, skel->bss->ring_size); 106 CHECK(skel->bss->cons_pos != 0, 107 "err_cons_pos", "exp %ld, got %ld\n", 108 0L, skel->bss->cons_pos); 109 CHECK(skel->bss->prod_pos != 3 * rec_sz, 110 "err_prod_pos", "exp %ld, got %ld\n", 111 3L * rec_sz, skel->bss->prod_pos); 112 113 /* poll for samples */ 114 err = ring_buffer__poll(ringbuf, -1); 115 116 /* -EDONE is used as an indicator that we are done */ 117 if (CHECK(err != -EDONE, "err_done", "done err: %d\n", err)) 118 goto cleanup; 119 120 /* we expect extra polling to return nothing */ 121 err = ring_buffer__poll(ringbuf, 0); 122 if (CHECK(err != 0, "extra_samples", "poll result: %d\n", err)) 123 goto cleanup; 124 125 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 126 0L, skel->bss->dropped); 127 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 128 2L, skel->bss->total); 129 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 130 1L, skel->bss->discarded); 131 132 /* now validate consumer position is updated and returned */ 133 trigger_samples(); 134 CHECK(skel->bss->cons_pos != 3 * rec_sz, 135 "err_cons_pos", "exp %ld, got %ld\n", 136 3L * rec_sz, skel->bss->cons_pos); 137 err = ring_buffer__poll(ringbuf, -1); 138 CHECK(err <= 0, "poll_err", "err %d\n", err); 139 140 /* start poll in background w/ long timeout */ 141 err = pthread_create(&thread, NULL, poll_thread, (void *)(long)10000); 142 if (CHECK(err, "bg_poll", "pthread_create failed: %d\n", err)) 143 goto cleanup; 144 145 /* turn off notifications now */ 146 skel->bss->flags = BPF_RB_NO_WAKEUP; 147 148 /* give background thread a bit of a time */ 149 usleep(50000); 150 trigger_samples(); 151 /* sleeping arbitrarily is bad, but no better way to know that 152 * epoll_wait() **DID NOT** unblock in background thread 153 */ 154 usleep(50000); 155 /* background poll should still be blocked */ 156 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 157 if (CHECK(err != EBUSY, "try_join", "err %d\n", err)) 158 goto cleanup; 159 160 /* BPF side did everything right */ 161 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 162 0L, skel->bss->dropped); 163 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 164 2L, skel->bss->total); 165 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 166 1L, skel->bss->discarded); 167 168 /* clear flags to return to "adaptive" notification mode */ 169 skel->bss->flags = 0; 170 171 /* produce new samples, no notification should be triggered, because 172 * consumer is now behind 173 */ 174 trigger_samples(); 175 176 /* background poll should still be blocked */ 177 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 178 if (CHECK(err != EBUSY, "try_join", "err %d\n", err)) 179 goto cleanup; 180 181 /* now force notifications */ 182 skel->bss->flags = BPF_RB_FORCE_WAKEUP; 183 sample_cnt = 0; 184 trigger_samples(); 185 186 /* now we should get a pending notification */ 187 usleep(50000); 188 err = pthread_tryjoin_np(thread, (void **)&bg_ret); 189 if (CHECK(err, "join_bg", "err %d\n", err)) 190 goto cleanup; 191 192 if (CHECK(bg_ret != 1, "bg_ret", "epoll_wait result: %ld", bg_ret)) 193 goto cleanup; 194 195 /* 3 rounds, 2 samples each */ 196 CHECK(sample_cnt != 6, "wrong_sample_cnt", 197 "expected to see %d samples, got %d\n", 6, sample_cnt); 198 199 /* BPF side did everything right */ 200 CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", 201 0L, skel->bss->dropped); 202 CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", 203 2L, skel->bss->total); 204 CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n", 205 1L, skel->bss->discarded); 206 207 test_ringbuf__detach(skel); 208 cleanup: 209 ring_buffer__free(ringbuf); 210 test_ringbuf__destroy(skel); 211 } 212