1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3
4 #define _GNU_SOURCE
5 #include <linux/compiler.h>
6 #include <linux/ring_buffer.h>
7 #include <linux/build_bug.h>
8 #include <pthread.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <sys/mman.h>
12 #include <sys/syscall.h>
13 #include <sys/sysinfo.h>
14 #include <test_progs.h>
15 #include <uapi/linux/bpf.h>
16 #include <unistd.h>
17
18 #include "user_ringbuf_fail.skel.h"
19 #include "user_ringbuf_success.skel.h"
20
21 #include "../progs/test_user_ringbuf.h"
22
23 static const long c_sample_size = sizeof(struct sample) + BPF_RINGBUF_HDR_SZ;
24 static long c_ringbuf_size, c_max_entries;
25
drain_current_samples(void)26 static void drain_current_samples(void)
27 {
28 syscall(__NR_getpgid);
29 }
30
write_samples(struct user_ring_buffer * ringbuf,uint32_t num_samples)31 static int write_samples(struct user_ring_buffer *ringbuf, uint32_t num_samples)
32 {
33 int i, err = 0;
34
35 /* Write some number of samples to the ring buffer. */
36 for (i = 0; i < num_samples; i++) {
37 struct sample *entry;
38 int read;
39
40 entry = user_ring_buffer__reserve(ringbuf, sizeof(*entry));
41 if (!entry) {
42 err = -errno;
43 goto done;
44 }
45
46 entry->pid = getpid();
47 entry->seq = i;
48 entry->value = i * i;
49
50 read = snprintf(entry->comm, sizeof(entry->comm), "%u", i);
51 if (read <= 0) {
52 /* Assert on the error path to avoid spamming logs with
53 * mostly success messages.
54 */
55 ASSERT_GT(read, 0, "snprintf_comm");
56 err = read;
57 user_ring_buffer__discard(ringbuf, entry);
58 goto done;
59 }
60
61 user_ring_buffer__submit(ringbuf, entry);
62 }
63
64 done:
65 drain_current_samples();
66
67 return err;
68 }
69
open_load_ringbuf_skel(void)70 static struct user_ringbuf_success *open_load_ringbuf_skel(void)
71 {
72 struct user_ringbuf_success *skel;
73 int err;
74
75 skel = user_ringbuf_success__open();
76 if (!ASSERT_OK_PTR(skel, "skel_open"))
77 return NULL;
78
79 err = bpf_map__set_max_entries(skel->maps.user_ringbuf, c_ringbuf_size);
80 if (!ASSERT_OK(err, "set_max_entries"))
81 goto cleanup;
82
83 err = bpf_map__set_max_entries(skel->maps.kernel_ringbuf, c_ringbuf_size);
84 if (!ASSERT_OK(err, "set_max_entries"))
85 goto cleanup;
86
87 err = user_ringbuf_success__load(skel);
88 if (!ASSERT_OK(err, "skel_load"))
89 goto cleanup;
90
91 return skel;
92
93 cleanup:
94 user_ringbuf_success__destroy(skel);
95 return NULL;
96 }
97
test_user_ringbuf_mappings(void)98 static void test_user_ringbuf_mappings(void)
99 {
100 int err, rb_fd;
101 int page_size = getpagesize();
102 void *mmap_ptr;
103 struct user_ringbuf_success *skel;
104
105 skel = open_load_ringbuf_skel();
106 if (!skel)
107 return;
108
109 rb_fd = bpf_map__fd(skel->maps.user_ringbuf);
110 /* cons_pos can be mapped R/O, can't add +X with mprotect. */
111 mmap_ptr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, rb_fd, 0);
112 ASSERT_OK_PTR(mmap_ptr, "ro_cons_pos");
113 ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_WRITE), "write_cons_pos_protect");
114 ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_cons_pos_protect");
115 ASSERT_ERR_PTR(mremap(mmap_ptr, 0, 4 * page_size, MREMAP_MAYMOVE), "wr_prod_pos");
116 err = -errno;
117 ASSERT_ERR(err, "wr_prod_pos_err");
118 ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_ro_cons");
119
120 /* prod_pos can be mapped RW, can't add +X with mprotect. */
121 mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
122 rb_fd, page_size);
123 ASSERT_OK_PTR(mmap_ptr, "rw_prod_pos");
124 ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_prod_pos_protect");
125 err = -errno;
126 ASSERT_ERR(err, "wr_prod_pos_err");
127 ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw_prod");
128
129 /* data pages can be mapped RW, can't add +X with mprotect. */
130 mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd,
131 2 * page_size);
132 ASSERT_OK_PTR(mmap_ptr, "rw_data");
133 ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_data_protect");
134 err = -errno;
135 ASSERT_ERR(err, "exec_data_err");
136 ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw_data");
137
138 user_ringbuf_success__destroy(skel);
139 }
140
load_skel_create_ringbufs(struct user_ringbuf_success ** skel_out,struct ring_buffer ** kern_ringbuf_out,ring_buffer_sample_fn callback,struct user_ring_buffer ** user_ringbuf_out)141 static int load_skel_create_ringbufs(struct user_ringbuf_success **skel_out,
142 struct ring_buffer **kern_ringbuf_out,
143 ring_buffer_sample_fn callback,
144 struct user_ring_buffer **user_ringbuf_out)
145 {
146 struct user_ringbuf_success *skel;
147 struct ring_buffer *kern_ringbuf = NULL;
148 struct user_ring_buffer *user_ringbuf = NULL;
149 int err = -ENOMEM, rb_fd;
150
151 skel = open_load_ringbuf_skel();
152 if (!skel)
153 return err;
154
155 /* only trigger BPF program for current process */
156 skel->bss->pid = getpid();
157
158 if (kern_ringbuf_out) {
159 rb_fd = bpf_map__fd(skel->maps.kernel_ringbuf);
160 kern_ringbuf = ring_buffer__new(rb_fd, callback, skel, NULL);
161 if (!ASSERT_OK_PTR(kern_ringbuf, "kern_ringbuf_create"))
162 goto cleanup;
163
164 *kern_ringbuf_out = kern_ringbuf;
165 }
166
167 if (user_ringbuf_out) {
168 rb_fd = bpf_map__fd(skel->maps.user_ringbuf);
169 user_ringbuf = user_ring_buffer__new(rb_fd, NULL);
170 if (!ASSERT_OK_PTR(user_ringbuf, "user_ringbuf_create"))
171 goto cleanup;
172
173 *user_ringbuf_out = user_ringbuf;
174 ASSERT_EQ(skel->bss->read, 0, "no_reads_after_load");
175 }
176
177 err = user_ringbuf_success__attach(skel);
178 if (!ASSERT_OK(err, "skel_attach"))
179 goto cleanup;
180
181 *skel_out = skel;
182 return 0;
183
184 cleanup:
185 if (kern_ringbuf_out)
186 *kern_ringbuf_out = NULL;
187 if (user_ringbuf_out)
188 *user_ringbuf_out = NULL;
189 ring_buffer__free(kern_ringbuf);
190 user_ring_buffer__free(user_ringbuf);
191 user_ringbuf_success__destroy(skel);
192 return err;
193 }
194
load_skel_create_user_ringbuf(struct user_ringbuf_success ** skel_out,struct user_ring_buffer ** ringbuf_out)195 static int load_skel_create_user_ringbuf(struct user_ringbuf_success **skel_out,
196 struct user_ring_buffer **ringbuf_out)
197 {
198 return load_skel_create_ringbufs(skel_out, NULL, NULL, ringbuf_out);
199 }
200
manually_write_test_invalid_sample(struct user_ringbuf_success * skel,__u32 size,__u64 producer_pos,int err)201 static void manually_write_test_invalid_sample(struct user_ringbuf_success *skel,
202 __u32 size, __u64 producer_pos, int err)
203 {
204 void *data_ptr;
205 __u64 *producer_pos_ptr;
206 int rb_fd, page_size = getpagesize();
207
208 rb_fd = bpf_map__fd(skel->maps.user_ringbuf);
209
210 ASSERT_EQ(skel->bss->read, 0, "num_samples_before_bad_sample");
211
212 /* Map the producer_pos as RW. */
213 producer_pos_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
214 MAP_SHARED, rb_fd, page_size);
215 ASSERT_OK_PTR(producer_pos_ptr, "producer_pos_ptr");
216
217 /* Map the data pages as RW. */
218 data_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, 2 * page_size);
219 ASSERT_OK_PTR(data_ptr, "rw_data");
220
221 memset(data_ptr, 0, BPF_RINGBUF_HDR_SZ);
222 *(__u32 *)data_ptr = size;
223
224 /* Synchronizes with smp_load_acquire() in __bpf_user_ringbuf_peek() in the kernel. */
225 smp_store_release(producer_pos_ptr, producer_pos + BPF_RINGBUF_HDR_SZ);
226
227 drain_current_samples();
228 ASSERT_EQ(skel->bss->read, 0, "num_samples_after_bad_sample");
229 ASSERT_EQ(skel->bss->err, err, "err_after_bad_sample");
230
231 ASSERT_OK(munmap(producer_pos_ptr, page_size), "unmap_producer_pos");
232 ASSERT_OK(munmap(data_ptr, page_size), "unmap_data_ptr");
233 }
234
test_user_ringbuf_post_misaligned(void)235 static void test_user_ringbuf_post_misaligned(void)
236 {
237 struct user_ringbuf_success *skel;
238 struct user_ring_buffer *ringbuf;
239 int err;
240 __u32 size = (1 << 5) + 7;
241
242 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
243 if (!ASSERT_OK(err, "misaligned_skel"))
244 return;
245
246 manually_write_test_invalid_sample(skel, size, size, -EINVAL);
247 user_ring_buffer__free(ringbuf);
248 user_ringbuf_success__destroy(skel);
249 }
250
test_user_ringbuf_post_producer_wrong_offset(void)251 static void test_user_ringbuf_post_producer_wrong_offset(void)
252 {
253 struct user_ringbuf_success *skel;
254 struct user_ring_buffer *ringbuf;
255 int err;
256 __u32 size = (1 << 5);
257
258 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
259 if (!ASSERT_OK(err, "wrong_offset_skel"))
260 return;
261
262 manually_write_test_invalid_sample(skel, size, size - 8, -EINVAL);
263 user_ring_buffer__free(ringbuf);
264 user_ringbuf_success__destroy(skel);
265 }
266
test_user_ringbuf_post_larger_than_ringbuf_sz(void)267 static void test_user_ringbuf_post_larger_than_ringbuf_sz(void)
268 {
269 struct user_ringbuf_success *skel;
270 struct user_ring_buffer *ringbuf;
271 int err;
272 __u32 size = c_ringbuf_size;
273
274 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
275 if (!ASSERT_OK(err, "huge_sample_skel"))
276 return;
277
278 manually_write_test_invalid_sample(skel, size, size, -E2BIG);
279 user_ring_buffer__free(ringbuf);
280 user_ringbuf_success__destroy(skel);
281 }
282
test_user_ringbuf_basic(void)283 static void test_user_ringbuf_basic(void)
284 {
285 struct user_ringbuf_success *skel;
286 struct user_ring_buffer *ringbuf;
287 int err;
288
289 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
290 if (!ASSERT_OK(err, "ringbuf_basic_skel"))
291 return;
292
293 ASSERT_EQ(skel->bss->read, 0, "num_samples_read_before");
294
295 err = write_samples(ringbuf, 2);
296 if (!ASSERT_OK(err, "write_samples"))
297 goto cleanup;
298
299 ASSERT_EQ(skel->bss->read, 2, "num_samples_read_after");
300
301 cleanup:
302 user_ring_buffer__free(ringbuf);
303 user_ringbuf_success__destroy(skel);
304 }
305
test_user_ringbuf_sample_full_ring_buffer(void)306 static void test_user_ringbuf_sample_full_ring_buffer(void)
307 {
308 struct user_ringbuf_success *skel;
309 struct user_ring_buffer *ringbuf;
310 int err;
311 void *sample;
312
313 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
314 if (!ASSERT_OK(err, "ringbuf_full_sample_skel"))
315 return;
316
317 sample = user_ring_buffer__reserve(ringbuf, c_ringbuf_size - BPF_RINGBUF_HDR_SZ);
318 if (!ASSERT_OK_PTR(sample, "full_sample"))
319 goto cleanup;
320
321 user_ring_buffer__submit(ringbuf, sample);
322 ASSERT_EQ(skel->bss->read, 0, "num_samples_read_before");
323 drain_current_samples();
324 ASSERT_EQ(skel->bss->read, 1, "num_samples_read_after");
325
326 cleanup:
327 user_ring_buffer__free(ringbuf);
328 user_ringbuf_success__destroy(skel);
329 }
330
test_user_ringbuf_post_alignment_autoadjust(void)331 static void test_user_ringbuf_post_alignment_autoadjust(void)
332 {
333 struct user_ringbuf_success *skel;
334 struct user_ring_buffer *ringbuf;
335 struct sample *sample;
336 int err;
337
338 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
339 if (!ASSERT_OK(err, "ringbuf_align_autoadjust_skel"))
340 return;
341
342 /* libbpf should automatically round any sample up to an 8-byte alignment. */
343 sample = user_ring_buffer__reserve(ringbuf, sizeof(*sample) + 1);
344 ASSERT_OK_PTR(sample, "reserve_autoaligned");
345 user_ring_buffer__submit(ringbuf, sample);
346
347 ASSERT_EQ(skel->bss->read, 0, "num_samples_read_before");
348 drain_current_samples();
349 ASSERT_EQ(skel->bss->read, 1, "num_samples_read_after");
350
351 user_ring_buffer__free(ringbuf);
352 user_ringbuf_success__destroy(skel);
353 }
354
test_user_ringbuf_overfill(void)355 static void test_user_ringbuf_overfill(void)
356 {
357 struct user_ringbuf_success *skel;
358 struct user_ring_buffer *ringbuf;
359 int err;
360
361 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
362 if (err)
363 return;
364
365 err = write_samples(ringbuf, c_max_entries * 5);
366 ASSERT_ERR(err, "write_samples");
367 ASSERT_EQ(skel->bss->read, c_max_entries, "max_entries");
368
369 user_ring_buffer__free(ringbuf);
370 user_ringbuf_success__destroy(skel);
371 }
372
test_user_ringbuf_discards_properly_ignored(void)373 static void test_user_ringbuf_discards_properly_ignored(void)
374 {
375 struct user_ringbuf_success *skel;
376 struct user_ring_buffer *ringbuf;
377 int err, num_discarded = 0;
378 __u64 *token;
379
380 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
381 if (err)
382 return;
383
384 ASSERT_EQ(skel->bss->read, 0, "num_samples_read_before");
385
386 while (1) {
387 /* Write samples until the buffer is full. */
388 token = user_ring_buffer__reserve(ringbuf, sizeof(*token));
389 if (!token)
390 break;
391
392 user_ring_buffer__discard(ringbuf, token);
393 num_discarded++;
394 }
395
396 if (!ASSERT_GE(num_discarded, 0, "num_discarded"))
397 goto cleanup;
398
399 /* Should not read any samples, as they are all discarded. */
400 ASSERT_EQ(skel->bss->read, 0, "num_pre_kick");
401 drain_current_samples();
402 ASSERT_EQ(skel->bss->read, 0, "num_post_kick");
403
404 /* Now that the ring buffer has been drained, we should be able to
405 * reserve another token.
406 */
407 token = user_ring_buffer__reserve(ringbuf, sizeof(*token));
408
409 if (!ASSERT_OK_PTR(token, "new_token"))
410 goto cleanup;
411
412 user_ring_buffer__discard(ringbuf, token);
413 cleanup:
414 user_ring_buffer__free(ringbuf);
415 user_ringbuf_success__destroy(skel);
416 }
417
test_user_ringbuf_loop(void)418 static void test_user_ringbuf_loop(void)
419 {
420 struct user_ringbuf_success *skel;
421 struct user_ring_buffer *ringbuf;
422 uint32_t total_samples = 8192;
423 uint32_t remaining_samples = total_samples;
424 int err;
425
426 if (!ASSERT_LT(c_max_entries, total_samples, "compare_c_max_entries"))
427 return;
428
429 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
430 if (err)
431 return;
432
433 do {
434 uint32_t curr_samples;
435
436 curr_samples = remaining_samples > c_max_entries
437 ? c_max_entries : remaining_samples;
438 err = write_samples(ringbuf, curr_samples);
439 if (err != 0) {
440 /* Assert inside of if statement to avoid flooding logs
441 * on the success path.
442 */
443 ASSERT_OK(err, "write_samples");
444 goto cleanup;
445 }
446
447 remaining_samples -= curr_samples;
448 ASSERT_EQ(skel->bss->read, total_samples - remaining_samples,
449 "current_batched_entries");
450 } while (remaining_samples > 0);
451 ASSERT_EQ(skel->bss->read, total_samples, "total_batched_entries");
452
453 cleanup:
454 user_ring_buffer__free(ringbuf);
455 user_ringbuf_success__destroy(skel);
456 }
457
send_test_message(struct user_ring_buffer * ringbuf,enum test_msg_op op,s64 operand_64,s32 operand_32)458 static int send_test_message(struct user_ring_buffer *ringbuf,
459 enum test_msg_op op, s64 operand_64,
460 s32 operand_32)
461 {
462 struct test_msg *msg;
463
464 msg = user_ring_buffer__reserve(ringbuf, sizeof(*msg));
465 if (!msg) {
466 /* Assert on the error path to avoid spamming logs with mostly
467 * success messages.
468 */
469 ASSERT_OK_PTR(msg, "reserve_msg");
470 return -ENOMEM;
471 }
472
473 msg->msg_op = op;
474
475 switch (op) {
476 case TEST_MSG_OP_INC64:
477 case TEST_MSG_OP_MUL64:
478 msg->operand_64 = operand_64;
479 break;
480 case TEST_MSG_OP_INC32:
481 case TEST_MSG_OP_MUL32:
482 msg->operand_32 = operand_32;
483 break;
484 default:
485 PRINT_FAIL("Invalid operand %d\n", op);
486 user_ring_buffer__discard(ringbuf, msg);
487 return -EINVAL;
488 }
489
490 user_ring_buffer__submit(ringbuf, msg);
491
492 return 0;
493 }
494
kick_kernel_read_messages(void)495 static void kick_kernel_read_messages(void)
496 {
497 syscall(__NR_prctl);
498 }
499
handle_kernel_msg(void * ctx,void * data,size_t len)500 static int handle_kernel_msg(void *ctx, void *data, size_t len)
501 {
502 struct user_ringbuf_success *skel = ctx;
503 struct test_msg *msg = data;
504
505 switch (msg->msg_op) {
506 case TEST_MSG_OP_INC64:
507 skel->bss->user_mutated += msg->operand_64;
508 return 0;
509 case TEST_MSG_OP_INC32:
510 skel->bss->user_mutated += msg->operand_32;
511 return 0;
512 case TEST_MSG_OP_MUL64:
513 skel->bss->user_mutated *= msg->operand_64;
514 return 0;
515 case TEST_MSG_OP_MUL32:
516 skel->bss->user_mutated *= msg->operand_32;
517 return 0;
518 default:
519 fprintf(stderr, "Invalid operand %d\n", msg->msg_op);
520 return -EINVAL;
521 }
522 }
523
drain_kernel_messages_buffer(struct ring_buffer * kern_ringbuf,struct user_ringbuf_success * skel)524 static void drain_kernel_messages_buffer(struct ring_buffer *kern_ringbuf,
525 struct user_ringbuf_success *skel)
526 {
527 int cnt;
528
529 cnt = ring_buffer__consume(kern_ringbuf);
530 ASSERT_EQ(cnt, 8, "consume_kern_ringbuf");
531 ASSERT_OK(skel->bss->err, "consume_kern_ringbuf_err");
532 }
533
test_user_ringbuf_msg_protocol(void)534 static void test_user_ringbuf_msg_protocol(void)
535 {
536 struct user_ringbuf_success *skel;
537 struct user_ring_buffer *user_ringbuf;
538 struct ring_buffer *kern_ringbuf;
539 int err, i;
540 __u64 expected_kern = 0;
541
542 err = load_skel_create_ringbufs(&skel, &kern_ringbuf, handle_kernel_msg, &user_ringbuf);
543 if (!ASSERT_OK(err, "create_ringbufs"))
544 return;
545
546 for (i = 0; i < 64; i++) {
547 enum test_msg_op op = i % TEST_MSG_OP_NUM_OPS;
548 __u64 operand_64 = TEST_OP_64;
549 __u32 operand_32 = TEST_OP_32;
550
551 err = send_test_message(user_ringbuf, op, operand_64, operand_32);
552 if (err) {
553 /* Only assert on a failure to avoid spamming success logs. */
554 ASSERT_OK(err, "send_test_message");
555 goto cleanup;
556 }
557
558 switch (op) {
559 case TEST_MSG_OP_INC64:
560 expected_kern += operand_64;
561 break;
562 case TEST_MSG_OP_INC32:
563 expected_kern += operand_32;
564 break;
565 case TEST_MSG_OP_MUL64:
566 expected_kern *= operand_64;
567 break;
568 case TEST_MSG_OP_MUL32:
569 expected_kern *= operand_32;
570 break;
571 default:
572 PRINT_FAIL("Unexpected op %d\n", op);
573 goto cleanup;
574 }
575
576 if (i % 8 == 0) {
577 kick_kernel_read_messages();
578 ASSERT_EQ(skel->bss->kern_mutated, expected_kern, "expected_kern");
579 ASSERT_EQ(skel->bss->err, 0, "bpf_prog_err");
580 drain_kernel_messages_buffer(kern_ringbuf, skel);
581 }
582 }
583
584 cleanup:
585 ring_buffer__free(kern_ringbuf);
586 user_ring_buffer__free(user_ringbuf);
587 user_ringbuf_success__destroy(skel);
588 }
589
kick_kernel_cb(void * arg)590 static void *kick_kernel_cb(void *arg)
591 {
592 /* Kick the kernel, causing it to drain the ring buffer and then wake
593 * up the test thread waiting on epoll.
594 */
595 syscall(__NR_prlimit64);
596
597 return NULL;
598 }
599
spawn_kick_thread_for_poll(void)600 static int spawn_kick_thread_for_poll(void)
601 {
602 pthread_t thread;
603
604 return pthread_create(&thread, NULL, kick_kernel_cb, NULL);
605 }
606
test_user_ringbuf_blocking_reserve(void)607 static void test_user_ringbuf_blocking_reserve(void)
608 {
609 struct user_ringbuf_success *skel;
610 struct user_ring_buffer *ringbuf;
611 int err, num_written = 0;
612 __u64 *token;
613
614 err = load_skel_create_user_ringbuf(&skel, &ringbuf);
615 if (err)
616 return;
617
618 ASSERT_EQ(skel->bss->read, 0, "num_samples_read_before");
619
620 while (1) {
621 /* Write samples until the buffer is full. */
622 token = user_ring_buffer__reserve(ringbuf, sizeof(*token));
623 if (!token)
624 break;
625
626 *token = 0xdeadbeef;
627
628 user_ring_buffer__submit(ringbuf, token);
629 num_written++;
630 }
631
632 if (!ASSERT_GE(num_written, 0, "num_written"))
633 goto cleanup;
634
635 /* Should not have read any samples until the kernel is kicked. */
636 ASSERT_EQ(skel->bss->read, 0, "num_pre_kick");
637
638 /* We correctly time out after 1 second, without a sample. */
639 token = user_ring_buffer__reserve_blocking(ringbuf, sizeof(*token), 1000);
640 if (!ASSERT_EQ(token, NULL, "pre_kick_timeout_token"))
641 goto cleanup;
642
643 err = spawn_kick_thread_for_poll();
644 if (!ASSERT_EQ(err, 0, "deferred_kick_thread\n"))
645 goto cleanup;
646
647 /* After spawning another thread that asynchronously kicks the kernel to
648 * drain the messages, we're able to block and successfully get a
649 * sample once we receive an event notification.
650 */
651 token = user_ring_buffer__reserve_blocking(ringbuf, sizeof(*token), 10000);
652
653 if (!ASSERT_OK_PTR(token, "block_token"))
654 goto cleanup;
655
656 ASSERT_GT(skel->bss->read, 0, "num_post_kill");
657 ASSERT_LE(skel->bss->read, num_written, "num_post_kill");
658 ASSERT_EQ(skel->bss->err, 0, "err_post_poll");
659 user_ring_buffer__discard(ringbuf, token);
660
661 cleanup:
662 user_ring_buffer__free(ringbuf);
663 user_ringbuf_success__destroy(skel);
664 }
665
666 #define SUCCESS_TEST(_func) { _func, #_func }
667
668 static struct {
669 void (*test_callback)(void);
670 const char *test_name;
671 } success_tests[] = {
672 SUCCESS_TEST(test_user_ringbuf_mappings),
673 SUCCESS_TEST(test_user_ringbuf_post_misaligned),
674 SUCCESS_TEST(test_user_ringbuf_post_producer_wrong_offset),
675 SUCCESS_TEST(test_user_ringbuf_post_larger_than_ringbuf_sz),
676 SUCCESS_TEST(test_user_ringbuf_basic),
677 SUCCESS_TEST(test_user_ringbuf_sample_full_ring_buffer),
678 SUCCESS_TEST(test_user_ringbuf_post_alignment_autoadjust),
679 SUCCESS_TEST(test_user_ringbuf_overfill),
680 SUCCESS_TEST(test_user_ringbuf_discards_properly_ignored),
681 SUCCESS_TEST(test_user_ringbuf_loop),
682 SUCCESS_TEST(test_user_ringbuf_msg_protocol),
683 SUCCESS_TEST(test_user_ringbuf_blocking_reserve),
684 };
685
test_user_ringbuf(void)686 void test_user_ringbuf(void)
687 {
688 int i;
689
690 c_ringbuf_size = getpagesize(); /* 1 page */
691 c_max_entries = c_ringbuf_size / c_sample_size;
692
693 for (i = 0; i < ARRAY_SIZE(success_tests); i++) {
694 if (!test__start_subtest(success_tests[i].test_name))
695 continue;
696
697 success_tests[i].test_callback();
698 }
699
700 RUN_TESTS(user_ringbuf_fail);
701 }
702