File Index
Files and directories
Sign in
FILE

ringbuf.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/ringbuf.c C
  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
 16#include "test_ringbuf.lskel.h"
 17#include "test_ringbuf_n.lskel.h"
 18#include "test_ringbuf_map_key.lskel.h"
 19#include "test_ringbuf_write.lskel.h"
 20#include "test_ringbuf_overwrite.lskel.h"
 21
 22#define EDONE 7777
 23
 24static int duration = 0;
 25
 26struct sample {
 27	int pid;
 28	int seq;
 29	long value;
 30	char comm[16];
 31};
 32
 33static int sample_cnt;
 34
 35static void atomic_inc(int *cnt)
 36{
 37	__atomic_add_fetch(cnt, 1, __ATOMIC_SEQ_CST);
 38}
 39
 40static int atomic_xchg(int *cnt, int val)
 41{
 42	return __atomic_exchange_n(cnt, val, __ATOMIC_SEQ_CST);
 43}
 44
 45static int process_sample(void *ctx, void *data, size_t len)
 46{
 47	struct sample *s = data;
 48
 49	atomic_inc(&sample_cnt);
 50
 51	switch (s->seq) {
 52	case 0:
 53		CHECK(s->value != 333, "sample1_value", "exp %ld, got %ld\n",
 54		      333L, s->value);
 55		return 0;
 56	case 1:
 57		CHECK(s->value != 777, "sample2_value", "exp %ld, got %ld\n",
 58		      777L, s->value);
 59		return -EDONE;
 60	default:
 61		/* we don't care about the rest */
 62		return 0;
 63	}
 64}
 65
 66static struct test_ringbuf_map_key_lskel *skel_map_key;
 67static struct test_ringbuf_lskel *skel;
 68static struct ring_buffer *ringbuf;
 69
 70static void trigger_samples()
 71{
 72	skel->bss->dropped = 0;
 73	skel->bss->total = 0;
 74	skel->bss->discarded = 0;
 75
 76	/* trigger exactly two samples */
 77	skel->bss->value = 333;
 78	syscall(__NR_getpgid);
 79	skel->bss->value = 777;
 80	syscall(__NR_getpgid);
 81}
 82
 83static void *poll_thread(void *input)
 84{
 85	long timeout = (long)input;
 86
 87	return (void *)(long)ring_buffer__poll(ringbuf, timeout);
 88}
 89
 90static void ringbuf_write_subtest(void)
 91{
 92	struct test_ringbuf_write_lskel *skel;
 93	int page_size = getpagesize();
 94	size_t *mmap_ptr;
 95	int err, rb_fd;
 96
 97	skel = test_ringbuf_write_lskel__open();
 98	if (!ASSERT_OK_PTR(skel, "skel_open"))
 99		return;
100
101	skel->maps.ringbuf.max_entries = 0x40000;
102
103	err = test_ringbuf_write_lskel__load(skel);
104	if (!ASSERT_OK(err, "skel_load"))
105		goto cleanup;
106
107	rb_fd = skel->maps.ringbuf.map_fd;
108
109	mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0);
110	if (!ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos"))
111		goto cleanup;
112	*mmap_ptr = 0x30000;
113	ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw");
114
115	skel->bss->pid = getpid();
116
117	ringbuf = ring_buffer__new(rb_fd, process_sample, NULL, NULL);
118	if (!ASSERT_OK_PTR(ringbuf, "ringbuf_new"))
119		goto cleanup;
120
121	err = test_ringbuf_write_lskel__attach(skel);
122	if (!ASSERT_OK(err, "skel_attach"))
123		goto cleanup_ringbuf;
124
125	skel->bss->discarded = 0;
126	skel->bss->passed = 0;
127
128	/* trigger exactly two samples */
129	syscall(__NR_getpgid);
130	syscall(__NR_getpgid);
131
132	ASSERT_EQ(skel->bss->discarded, 2, "discarded");
133	ASSERT_EQ(skel->bss->passed, 0, "passed");
134
135	test_ringbuf_write_lskel__detach(skel);
136cleanup_ringbuf:
137	ring_buffer__free(ringbuf);
138cleanup:
139	test_ringbuf_write_lskel__destroy(skel);
140}
141
142static void ringbuf_subtest(void)
143{
144	const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample);
145	pthread_t thread;
146	long bg_ret = -1;
147	int err, cnt, rb_fd;
148	int page_size = getpagesize();
149	void *mmap_ptr, *tmp_ptr;
150	struct ring *ring;
151	int map_fd;
152	unsigned long avail_data, ring_size, cons_pos, prod_pos;
153
154	skel = test_ringbuf_lskel__open();
155	if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
156		return;
157
158	skel->maps.ringbuf.max_entries = page_size;
159
160	err = test_ringbuf_lskel__load(skel);
161	if (CHECK(err != 0, "skel_load", "skeleton load failed\n"))
162		goto cleanup;
163
164	rb_fd = skel->maps.ringbuf.map_fd;
165	/* good read/write cons_pos */
166	mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0);
167	ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos");
168	tmp_ptr = mremap(mmap_ptr, page_size, 2 * page_size, MREMAP_MAYMOVE);
169	if (!ASSERT_ERR_PTR(tmp_ptr, "rw_extend"))
170		goto cleanup;
171	ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_cons_pos_protect");
172	ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw");
173
174	/* bad writeable prod_pos */
175	mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, page_size);
176	err = -errno;
177	ASSERT_ERR_PTR(mmap_ptr, "wr_prod_pos");
178	ASSERT_EQ(err, -EPERM, "wr_prod_pos_err");
179
180	/* bad writeable data pages */
181	mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, 2 * page_size);
182	err = -errno;
183	ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_one");
184	ASSERT_EQ(err, -EPERM, "wr_data_page_one_err");
185	mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, 3 * page_size);
186	ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_two");
187	mmap_ptr = mmap(NULL, 2 * page_size, PROT_WRITE, MAP_SHARED, rb_fd, 2 * page_size);
188	ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_all");
189
190	/* good read-only pages */
191	mmap_ptr = mmap(NULL, 4 * page_size, PROT_READ, MAP_SHARED, rb_fd, 0);
192	if (!ASSERT_OK_PTR(mmap_ptr, "ro_prod_pos"))
193		goto cleanup;
194
195	ASSERT_ERR(mprotect(mmap_ptr, 4 * page_size, PROT_WRITE), "write_protect");
196	ASSERT_ERR(mprotect(mmap_ptr, 4 * page_size, PROT_EXEC), "exec_protect");
197	ASSERT_ERR_PTR(mremap(mmap_ptr, 0, 4 * page_size, MREMAP_MAYMOVE), "ro_remap");
198	ASSERT_OK(munmap(mmap_ptr, 4 * page_size), "unmap_ro");
199
200	/* good read-only pages with initial offset */
201	mmap_ptr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, rb_fd, page_size);
202	if (!ASSERT_OK_PTR(mmap_ptr, "ro_prod_pos"))
203		goto cleanup;
204
205	ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_WRITE), "write_protect");
206	ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_protect");
207	ASSERT_ERR_PTR(mremap(mmap_ptr, 0, 3 * page_size, MREMAP_MAYMOVE), "ro_remap");
208	ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_ro");
209
210	/* only trigger BPF program for current process */
211	skel->bss->pid = getpid();
212
213	ringbuf = ring_buffer__new(skel->maps.ringbuf.map_fd,
214				   process_sample, NULL, NULL);
215	if (CHECK(!ringbuf, "ringbuf_create", "failed to create ringbuf\n"))
216		goto cleanup;
217
218	err = test_ringbuf_lskel__attach(skel);
219	if (CHECK(err, "skel_attach", "skeleton attachment failed: %d\n", err))
220		goto cleanup;
221
222	trigger_samples();
223
224	ring = ring_buffer__ring(ringbuf, 0);
225	if (!ASSERT_OK_PTR(ring, "ring_buffer__ring_idx_0"))
226		goto cleanup;
227
228	map_fd = ring__map_fd(ring);
229	ASSERT_EQ(map_fd, skel->maps.ringbuf.map_fd, "ring_map_fd");
230
231	/* 2 submitted + 1 discarded records */
232	CHECK(skel->bss->avail_data != 3 * rec_sz,
233	      "err_avail_size", "exp %ld, got %ld\n",
234	      3L * rec_sz, skel->bss->avail_data);
235	CHECK(skel->bss->ring_size != page_size,
236	      "err_ring_size", "exp %ld, got %ld\n",
237	      (long)page_size, skel->bss->ring_size);
238	CHECK(skel->bss->cons_pos != 0,
239	      "err_cons_pos", "exp %ld, got %ld\n",
240	      0L, skel->bss->cons_pos);
241	CHECK(skel->bss->prod_pos != 3 * rec_sz,
242	      "err_prod_pos", "exp %ld, got %ld\n",
243	      3L * rec_sz, skel->bss->prod_pos);
244
245	/* verify getting this data directly via the ring object yields the same
246	 * results
247	 */
248	avail_data = ring__avail_data_size(ring);
249	ASSERT_EQ(avail_data, 3 * rec_sz, "ring_avail_size");
250	ring_size = ring__size(ring);
251	ASSERT_EQ(ring_size, page_size, "ring_ring_size");
252	cons_pos = ring__consumer_pos(ring);
253	ASSERT_EQ(cons_pos, 0, "ring_cons_pos");
254	prod_pos = ring__producer_pos(ring);
255	ASSERT_EQ(prod_pos, 3 * rec_sz, "ring_prod_pos");
256
257	/* poll for samples */
258	err = ring_buffer__poll(ringbuf, -1);
259
260	/* -EDONE is used as an indicator that we are done */
261	if (CHECK(err != -EDONE, "err_done", "done err: %d\n", err))
262		goto cleanup;
263	cnt = atomic_xchg(&sample_cnt, 0);
264	CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt);
265
266	/* we expect extra polling to return nothing */
267	err = ring_buffer__poll(ringbuf, 0);
268	if (CHECK(err != 0, "extra_samples", "poll result: %d\n", err))
269		goto cleanup;
270	cnt = atomic_xchg(&sample_cnt, 0);
271	CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
272
273	CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n",
274	      0L, skel->bss->dropped);
275	CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n",
276	      2L, skel->bss->total);
277	CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n",
278	      1L, skel->bss->discarded);
279
280	/* now validate consumer position is updated and returned */
281	trigger_samples();
282	CHECK(skel->bss->cons_pos != 3 * rec_sz,
283	      "err_cons_pos", "exp %ld, got %ld\n",
284	      3L * rec_sz, skel->bss->cons_pos);
285	err = ring_buffer__poll(ringbuf, -1);
286	CHECK(err <= 0, "poll_err", "err %d\n", err);
287	cnt = atomic_xchg(&sample_cnt, 0);
288	CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt);
289
290	/* start poll in background w/ long timeout */
291	err = pthread_create(&thread, NULL, poll_thread, (void *)(long)10000);
292	if (CHECK(err, "bg_poll", "pthread_create failed: %d\n", err))
293		goto cleanup;
294
295	/* turn off notifications now */
296	skel->bss->flags = BPF_RB_NO_WAKEUP;
297
298	/* give background thread a bit of a time */
299	usleep(50000);
300	trigger_samples();
301	/* sleeping arbitrarily is bad, but no better way to know that
302	 * epoll_wait() **DID NOT** unblock in background thread
303	 */
304	usleep(50000);
305	/* background poll should still be blocked */
306	err = pthread_tryjoin_np(thread, (void **)&bg_ret);
307	if (CHECK(err != EBUSY, "try_join", "err %d\n", err))
308		goto cleanup;
309
310	/* BPF side did everything right */
311	CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n",
312	      0L, skel->bss->dropped);
313	CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n",
314	      2L, skel->bss->total);
315	CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n",
316	      1L, skel->bss->discarded);
317	cnt = atomic_xchg(&sample_cnt, 0);
318	CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
319
320	/* clear flags to return to "adaptive" notification mode */
321	skel->bss->flags = 0;
322
323	/* produce new samples, no notification should be triggered, because
324	 * consumer is now behind
325	 */
326	trigger_samples();
327
328	/* background poll should still be blocked */
329	err = pthread_tryjoin_np(thread, (void **)&bg_ret);
330	if (CHECK(err != EBUSY, "try_join", "err %d\n", err))
331		goto cleanup;
332
333	/* still no samples, because consumer is behind */
334	cnt = atomic_xchg(&sample_cnt, 0);
335	CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
336
337	skel->bss->dropped = 0;
338	skel->bss->total = 0;
339	skel->bss->discarded = 0;
340
341	skel->bss->value = 333;
342	syscall(__NR_getpgid);
343	/* now force notifications */
344	skel->bss->flags = BPF_RB_FORCE_WAKEUP;
345	skel->bss->value = 777;
346	syscall(__NR_getpgid);
347
348	/* now we should get a pending notification */
349	usleep(50000);
350	err = pthread_tryjoin_np(thread, (void **)&bg_ret);
351	if (CHECK(err, "join_bg", "err %d\n", err))
352		goto cleanup;
353
354	if (CHECK(bg_ret <= 0, "bg_ret", "epoll_wait result: %ld", bg_ret))
355		goto cleanup;
356
357	/* due to timing variations, there could still be non-notified
358	 * samples, so consume them here to collect all the samples
359	 */
360	err = ring_buffer__consume(ringbuf);
361	CHECK(err < 0, "rb_consume", "failed: %d\b", err);
362
363	/* also consume using ring__consume to make sure it works the same */
364	err = ring__consume(ring);
365	ASSERT_GE(err, 0, "ring_consume");
366
367	/* 3 rounds, 2 samples each */
368	cnt = atomic_xchg(&sample_cnt, 0);
369	CHECK(cnt != 6, "cnt", "exp %d samples, got %d\n", 6, cnt);
370
371	/* BPF side did everything right */
372	CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n",
373	      0L, skel->bss->dropped);
374	CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n",
375	      2L, skel->bss->total);
376	CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n",
377	      1L, skel->bss->discarded);
378
379	test_ringbuf_lskel__detach(skel);
380cleanup:
381	ring_buffer__free(ringbuf);
382	test_ringbuf_lskel__destroy(skel);
383}
384
385/*
386 * Test ring_buffer__consume_n() by producing N_TOT_SAMPLES samples in the ring
387 * buffer, via getpid(), and consuming them in chunks of N_SAMPLES.
388 */
389#define N_TOT_SAMPLES	32
390#define N_SAMPLES	4
391
392/* Sample value to verify the callback validity */
393#define SAMPLE_VALUE	42L
394
395static int process_n_sample(void *ctx, void *data, size_t len)
396{
397	struct sample *s = data;
398
399	ASSERT_EQ(s->value, SAMPLE_VALUE, "sample_value");
400
401	return 0;
402}
403
404static void ringbuf_n_subtest(void)
405{
406	struct test_ringbuf_n_lskel *skel_n;
407	int err, i;
408
409	skel_n = test_ringbuf_n_lskel__open();
410	if (!ASSERT_OK_PTR(skel_n, "test_ringbuf_n_lskel__open"))
411		return;
412
413	skel_n->maps.ringbuf.max_entries = getpagesize();
414	skel_n->bss->pid = getpid();
415
416	err = test_ringbuf_n_lskel__load(skel_n);
417	if (!ASSERT_OK(err, "test_ringbuf_n_lskel__load"))
418		goto cleanup;
419
420	ringbuf = ring_buffer__new(skel_n->maps.ringbuf.map_fd,
421				   process_n_sample, NULL, NULL);
422	if (!ASSERT_OK_PTR(ringbuf, "ring_buffer__new"))
423		goto cleanup;
424
425	err = test_ringbuf_n_lskel__attach(skel_n);
426	if (!ASSERT_OK(err, "test_ringbuf_n_lskel__attach"))
427		goto cleanup_ringbuf;
428
429	/* Produce N_TOT_SAMPLES samples in the ring buffer by calling getpid() */
430	skel_n->bss->value = SAMPLE_VALUE;
431	for (i = 0; i < N_TOT_SAMPLES; i++)
432		syscall(__NR_getpgid);
433
434	/* Consume all samples from the ring buffer in batches of N_SAMPLES */
435	for (i = 0; i < N_TOT_SAMPLES; i += err) {
436		err = ring_buffer__consume_n(ringbuf, N_SAMPLES);
437		if (!ASSERT_EQ(err, N_SAMPLES, "rb_consume"))
438			goto cleanup_ringbuf;
439	}
440
441cleanup_ringbuf:
442	ring_buffer__free(ringbuf);
443cleanup:
444	test_ringbuf_n_lskel__destroy(skel_n);
445}
446
447static int process_map_key_sample(void *ctx, void *data, size_t len)
448{
449	struct sample *s;
450	int err, val;
451
452	s = data;
453	switch (s->seq) {
454	case 1:
455		ASSERT_EQ(s->value, 42, "sample_value");
456		err = bpf_map_lookup_elem(skel_map_key->maps.hash_map.map_fd,
457					  s, &val);
458		ASSERT_OK(err, "hash_map bpf_map_lookup_elem");
459		ASSERT_EQ(val, 1, "hash_map val");
460		return -EDONE;
461	default:
462		return 0;
463	}
464}
465
466static void ringbuf_map_key_subtest(void)
467{
468	int err;
469
470	skel_map_key = test_ringbuf_map_key_lskel__open();
471	if (!ASSERT_OK_PTR(skel_map_key, "test_ringbuf_map_key_lskel__open"))
472		return;
473
474	skel_map_key->maps.ringbuf.max_entries = getpagesize();
475	skel_map_key->bss->pid = getpid();
476
477	err = test_ringbuf_map_key_lskel__load(skel_map_key);
478	if (!ASSERT_OK(err, "test_ringbuf_map_key_lskel__load"))
479		goto cleanup;
480
481	ringbuf = ring_buffer__new(skel_map_key->maps.ringbuf.map_fd,
482				   process_map_key_sample, NULL, NULL);
483	if (!ASSERT_OK_PTR(ringbuf, "ring_buffer__new"))
484		goto cleanup;
485
486	err = test_ringbuf_map_key_lskel__attach(skel_map_key);
487	if (!ASSERT_OK(err, "test_ringbuf_map_key_lskel__attach"))
488		goto cleanup_ringbuf;
489
490	syscall(__NR_getpgid);
491	ASSERT_EQ(skel_map_key->bss->seq, 1, "skel_map_key->bss->seq");
492	err = ring_buffer__poll(ringbuf, -1);
493	ASSERT_EQ(err, -EDONE, "ring_buffer__poll");
494
495cleanup_ringbuf:
496	ring_buffer__free(ringbuf);
497cleanup:
498	test_ringbuf_map_key_lskel__destroy(skel_map_key);
499}
500
501static void ringbuf_overwrite_mode_subtest(void)
502{
503	unsigned long size, len1, len2, len3, len4, len5;
504	unsigned long expect_avail_data, expect_prod_pos, expect_over_pos;
505	struct test_ringbuf_overwrite_lskel *skel;
506	int page_size = getpagesize();
507	int err;
508
509	skel = test_ringbuf_overwrite_lskel__open();
510	if (!ASSERT_OK_PTR(skel, "skel_open"))
511		return;
512
513	size = page_size;
514	len1 = page_size / 2;
515	len2 = page_size / 4;
516	len3 = size - len1 - len2 - BPF_RINGBUF_HDR_SZ * 3;
517	len4 = len3 - 8;
518	len5 = len3; /* retry with len3 */
519
520	skel->maps.ringbuf.max_entries = size;
521	skel->rodata->LEN1 = len1;
522	skel->rodata->LEN2 = len2;
523	skel->rodata->LEN3 = len3;
524	skel->rodata->LEN4 = len4;
525	skel->rodata->LEN5 = len5;
526
527	skel->bss->pid = getpid();
528
529	err = test_ringbuf_overwrite_lskel__load(skel);
530	if (!ASSERT_OK(err, "skel_load"))
531		goto cleanup;
532
533	err = test_ringbuf_overwrite_lskel__attach(skel);
534	if (!ASSERT_OK(err, "skel_attach"))
535		goto cleanup;
536
537	syscall(__NR_getpgid);
538
539	ASSERT_EQ(skel->bss->reserve1_fail, 0, "reserve 1");
540	ASSERT_EQ(skel->bss->reserve2_fail, 0, "reserve 2");
541	ASSERT_EQ(skel->bss->reserve3_fail, 1, "reserve 3");
542	ASSERT_EQ(skel->bss->reserve4_fail, 0, "reserve 4");
543	ASSERT_EQ(skel->bss->reserve5_fail, 0, "reserve 5");
544
545	ASSERT_EQ(skel->bss->ring_size, size, "check_ring_size");
546
547	expect_avail_data = len2 + len4 + len5 + 3 * BPF_RINGBUF_HDR_SZ;
548	ASSERT_EQ(skel->bss->avail_data, expect_avail_data, "check_avail_size");
549
550	ASSERT_EQ(skel->bss->cons_pos, 0, "check_cons_pos");
551
552	expect_prod_pos = len1 + len2 + len4 + len5 + 4 * BPF_RINGBUF_HDR_SZ;
553	ASSERT_EQ(skel->bss->prod_pos, expect_prod_pos, "check_prod_pos");
554
555	expect_over_pos = len1 + BPF_RINGBUF_HDR_SZ;
556	ASSERT_EQ(skel->bss->over_pos, expect_over_pos, "check_over_pos");
557
558	test_ringbuf_overwrite_lskel__detach(skel);
559cleanup:
560	test_ringbuf_overwrite_lskel__destroy(skel);
561}
562
563void test_ringbuf(void)
564{
565	if (test__start_subtest("ringbuf"))
566		ringbuf_subtest();
567	if (test__start_subtest("ringbuf_n"))
568		ringbuf_n_subtest();
569	if (test__start_subtest("ringbuf_map_key"))
570		ringbuf_map_key_subtest();
571	if (test__start_subtest("ringbuf_write"))
572		ringbuf_write_subtest();
573	if (test__start_subtest("ringbuf_overwrite_mode"))
574		ringbuf_overwrite_mode_subtest();
575}
~/ Read-only file preview.