1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2020 Facebook */
3*4882a593Smuzhiyun #include <asm/barrier.h>
4*4882a593Smuzhiyun #include <linux/perf_event.h>
5*4882a593Smuzhiyun #include <linux/ring_buffer.h>
6*4882a593Smuzhiyun #include <sys/epoll.h>
7*4882a593Smuzhiyun #include <sys/mman.h>
8*4882a593Smuzhiyun #include <argp.h>
9*4882a593Smuzhiyun #include <stdlib.h>
10*4882a593Smuzhiyun #include "bench.h"
11*4882a593Smuzhiyun #include "ringbuf_bench.skel.h"
12*4882a593Smuzhiyun #include "perfbuf_bench.skel.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun static struct {
15*4882a593Smuzhiyun bool back2back;
16*4882a593Smuzhiyun int batch_cnt;
17*4882a593Smuzhiyun bool sampled;
18*4882a593Smuzhiyun int sample_rate;
19*4882a593Smuzhiyun int ringbuf_sz; /* per-ringbuf, in bytes */
20*4882a593Smuzhiyun bool ringbuf_use_output; /* use slower output API */
21*4882a593Smuzhiyun int perfbuf_sz; /* per-CPU size, in pages */
22*4882a593Smuzhiyun } args = {
23*4882a593Smuzhiyun .back2back = false,
24*4882a593Smuzhiyun .batch_cnt = 500,
25*4882a593Smuzhiyun .sampled = false,
26*4882a593Smuzhiyun .sample_rate = 500,
27*4882a593Smuzhiyun .ringbuf_sz = 512 * 1024,
28*4882a593Smuzhiyun .ringbuf_use_output = false,
29*4882a593Smuzhiyun .perfbuf_sz = 128,
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun enum {
33*4882a593Smuzhiyun ARG_RB_BACK2BACK = 2000,
34*4882a593Smuzhiyun ARG_RB_USE_OUTPUT = 2001,
35*4882a593Smuzhiyun ARG_RB_BATCH_CNT = 2002,
36*4882a593Smuzhiyun ARG_RB_SAMPLED = 2003,
37*4882a593Smuzhiyun ARG_RB_SAMPLE_RATE = 2004,
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static const struct argp_option opts[] = {
41*4882a593Smuzhiyun { "rb-b2b", ARG_RB_BACK2BACK, NULL, 0, "Back-to-back mode"},
42*4882a593Smuzhiyun { "rb-use-output", ARG_RB_USE_OUTPUT, NULL, 0, "Use bpf_ringbuf_output() instead of bpf_ringbuf_reserve()"},
43*4882a593Smuzhiyun { "rb-batch-cnt", ARG_RB_BATCH_CNT, "CNT", 0, "Set BPF-side record batch count"},
44*4882a593Smuzhiyun { "rb-sampled", ARG_RB_SAMPLED, NULL, 0, "Notification sampling"},
45*4882a593Smuzhiyun { "rb-sample-rate", ARG_RB_SAMPLE_RATE, "RATE", 0, "Notification sample rate"},
46*4882a593Smuzhiyun {},
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun
parse_arg(int key,char * arg,struct argp_state * state)49*4882a593Smuzhiyun static error_t parse_arg(int key, char *arg, struct argp_state *state)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun switch (key) {
52*4882a593Smuzhiyun case ARG_RB_BACK2BACK:
53*4882a593Smuzhiyun args.back2back = true;
54*4882a593Smuzhiyun break;
55*4882a593Smuzhiyun case ARG_RB_USE_OUTPUT:
56*4882a593Smuzhiyun args.ringbuf_use_output = true;
57*4882a593Smuzhiyun break;
58*4882a593Smuzhiyun case ARG_RB_BATCH_CNT:
59*4882a593Smuzhiyun args.batch_cnt = strtol(arg, NULL, 10);
60*4882a593Smuzhiyun if (args.batch_cnt < 0) {
61*4882a593Smuzhiyun fprintf(stderr, "Invalid batch count.");
62*4882a593Smuzhiyun argp_usage(state);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun break;
65*4882a593Smuzhiyun case ARG_RB_SAMPLED:
66*4882a593Smuzhiyun args.sampled = true;
67*4882a593Smuzhiyun break;
68*4882a593Smuzhiyun case ARG_RB_SAMPLE_RATE:
69*4882a593Smuzhiyun args.sample_rate = strtol(arg, NULL, 10);
70*4882a593Smuzhiyun if (args.sample_rate < 0) {
71*4882a593Smuzhiyun fprintf(stderr, "Invalid perfbuf sample rate.");
72*4882a593Smuzhiyun argp_usage(state);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun break;
75*4882a593Smuzhiyun default:
76*4882a593Smuzhiyun return ARGP_ERR_UNKNOWN;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /* exported into benchmark runner */
82*4882a593Smuzhiyun const struct argp bench_ringbufs_argp = {
83*4882a593Smuzhiyun .options = opts,
84*4882a593Smuzhiyun .parser = parse_arg,
85*4882a593Smuzhiyun };
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* RINGBUF-LIBBPF benchmark */
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun static struct counter buf_hits;
90*4882a593Smuzhiyun
bufs_trigger_batch()91*4882a593Smuzhiyun static inline void bufs_trigger_batch()
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun (void)syscall(__NR_getpgid);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
bufs_validate()96*4882a593Smuzhiyun static void bufs_validate()
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun if (env.consumer_cnt != 1) {
99*4882a593Smuzhiyun fprintf(stderr, "rb-libbpf benchmark doesn't support multi-consumer!\n");
100*4882a593Smuzhiyun exit(1);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (args.back2back && env.producer_cnt > 1) {
104*4882a593Smuzhiyun fprintf(stderr, "back-to-back mode makes sense only for single-producer case!\n");
105*4882a593Smuzhiyun exit(1);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
bufs_sample_producer(void * input)109*4882a593Smuzhiyun static void *bufs_sample_producer(void *input)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun if (args.back2back) {
112*4882a593Smuzhiyun /* initial batch to get everything started */
113*4882a593Smuzhiyun bufs_trigger_batch();
114*4882a593Smuzhiyun return NULL;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun while (true)
118*4882a593Smuzhiyun bufs_trigger_batch();
119*4882a593Smuzhiyun return NULL;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun static struct ringbuf_libbpf_ctx {
123*4882a593Smuzhiyun struct ringbuf_bench *skel;
124*4882a593Smuzhiyun struct ring_buffer *ringbuf;
125*4882a593Smuzhiyun } ringbuf_libbpf_ctx;
126*4882a593Smuzhiyun
ringbuf_libbpf_measure(struct bench_res * res)127*4882a593Smuzhiyun static void ringbuf_libbpf_measure(struct bench_res *res)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun res->hits = atomic_swap(&buf_hits.value, 0);
132*4882a593Smuzhiyun res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
ringbuf_setup_skeleton()135*4882a593Smuzhiyun static struct ringbuf_bench *ringbuf_setup_skeleton()
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct ringbuf_bench *skel;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun setup_libbpf();
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun skel = ringbuf_bench__open();
142*4882a593Smuzhiyun if (!skel) {
143*4882a593Smuzhiyun fprintf(stderr, "failed to open skeleton\n");
144*4882a593Smuzhiyun exit(1);
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun skel->rodata->batch_cnt = args.batch_cnt;
148*4882a593Smuzhiyun skel->rodata->use_output = args.ringbuf_use_output ? 1 : 0;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun if (args.sampled)
151*4882a593Smuzhiyun /* record data + header take 16 bytes */
152*4882a593Smuzhiyun skel->rodata->wakeup_data_size = args.sample_rate * 16;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun bpf_map__resize(skel->maps.ringbuf, args.ringbuf_sz);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (ringbuf_bench__load(skel)) {
157*4882a593Smuzhiyun fprintf(stderr, "failed to load skeleton\n");
158*4882a593Smuzhiyun exit(1);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun return skel;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun
buf_process_sample(void * ctx,void * data,size_t len)164*4882a593Smuzhiyun static int buf_process_sample(void *ctx, void *data, size_t len)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun atomic_inc(&buf_hits.value);
167*4882a593Smuzhiyun return 0;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
ringbuf_libbpf_setup()170*4882a593Smuzhiyun static void ringbuf_libbpf_setup()
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
173*4882a593Smuzhiyun struct bpf_link *link;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun ctx->skel = ringbuf_setup_skeleton();
176*4882a593Smuzhiyun ctx->ringbuf = ring_buffer__new(bpf_map__fd(ctx->skel->maps.ringbuf),
177*4882a593Smuzhiyun buf_process_sample, NULL, NULL);
178*4882a593Smuzhiyun if (!ctx->ringbuf) {
179*4882a593Smuzhiyun fprintf(stderr, "failed to create ringbuf\n");
180*4882a593Smuzhiyun exit(1);
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);
184*4882a593Smuzhiyun if (IS_ERR(link)) {
185*4882a593Smuzhiyun fprintf(stderr, "failed to attach program!\n");
186*4882a593Smuzhiyun exit(1);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
ringbuf_libbpf_consumer(void * input)190*4882a593Smuzhiyun static void *ringbuf_libbpf_consumer(void *input)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun while (ring_buffer__poll(ctx->ringbuf, -1) >= 0) {
195*4882a593Smuzhiyun if (args.back2back)
196*4882a593Smuzhiyun bufs_trigger_batch();
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun fprintf(stderr, "ringbuf polling failed!\n");
199*4882a593Smuzhiyun return NULL;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun /* RINGBUF-CUSTOM benchmark */
203*4882a593Smuzhiyun struct ringbuf_custom {
204*4882a593Smuzhiyun __u64 *consumer_pos;
205*4882a593Smuzhiyun __u64 *producer_pos;
206*4882a593Smuzhiyun __u64 mask;
207*4882a593Smuzhiyun void *data;
208*4882a593Smuzhiyun int map_fd;
209*4882a593Smuzhiyun };
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun static struct ringbuf_custom_ctx {
212*4882a593Smuzhiyun struct ringbuf_bench *skel;
213*4882a593Smuzhiyun struct ringbuf_custom ringbuf;
214*4882a593Smuzhiyun int epoll_fd;
215*4882a593Smuzhiyun struct epoll_event event;
216*4882a593Smuzhiyun } ringbuf_custom_ctx;
217*4882a593Smuzhiyun
ringbuf_custom_measure(struct bench_res * res)218*4882a593Smuzhiyun static void ringbuf_custom_measure(struct bench_res *res)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun res->hits = atomic_swap(&buf_hits.value, 0);
223*4882a593Smuzhiyun res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
ringbuf_custom_setup()226*4882a593Smuzhiyun static void ringbuf_custom_setup()
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
229*4882a593Smuzhiyun const size_t page_size = getpagesize();
230*4882a593Smuzhiyun struct bpf_link *link;
231*4882a593Smuzhiyun struct ringbuf_custom *r;
232*4882a593Smuzhiyun void *tmp;
233*4882a593Smuzhiyun int err;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun ctx->skel = ringbuf_setup_skeleton();
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
238*4882a593Smuzhiyun if (ctx->epoll_fd < 0) {
239*4882a593Smuzhiyun fprintf(stderr, "failed to create epoll fd: %d\n", -errno);
240*4882a593Smuzhiyun exit(1);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun r = &ctx->ringbuf;
244*4882a593Smuzhiyun r->map_fd = bpf_map__fd(ctx->skel->maps.ringbuf);
245*4882a593Smuzhiyun r->mask = args.ringbuf_sz - 1;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun /* Map writable consumer page */
248*4882a593Smuzhiyun tmp = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
249*4882a593Smuzhiyun r->map_fd, 0);
250*4882a593Smuzhiyun if (tmp == MAP_FAILED) {
251*4882a593Smuzhiyun fprintf(stderr, "failed to mmap consumer page: %d\n", -errno);
252*4882a593Smuzhiyun exit(1);
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun r->consumer_pos = tmp;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun /* Map read-only producer page and data pages. */
257*4882a593Smuzhiyun tmp = mmap(NULL, page_size + 2 * args.ringbuf_sz, PROT_READ, MAP_SHARED,
258*4882a593Smuzhiyun r->map_fd, page_size);
259*4882a593Smuzhiyun if (tmp == MAP_FAILED) {
260*4882a593Smuzhiyun fprintf(stderr, "failed to mmap data pages: %d\n", -errno);
261*4882a593Smuzhiyun exit(1);
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun r->producer_pos = tmp;
264*4882a593Smuzhiyun r->data = tmp + page_size;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun ctx->event.events = EPOLLIN;
267*4882a593Smuzhiyun err = epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, r->map_fd, &ctx->event);
268*4882a593Smuzhiyun if (err < 0) {
269*4882a593Smuzhiyun fprintf(stderr, "failed to epoll add ringbuf: %d\n", -errno);
270*4882a593Smuzhiyun exit(1);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);
274*4882a593Smuzhiyun if (IS_ERR(link)) {
275*4882a593Smuzhiyun fprintf(stderr, "failed to attach program\n");
276*4882a593Smuzhiyun exit(1);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun #define RINGBUF_BUSY_BIT (1 << 31)
281*4882a593Smuzhiyun #define RINGBUF_DISCARD_BIT (1 << 30)
282*4882a593Smuzhiyun #define RINGBUF_META_LEN 8
283*4882a593Smuzhiyun
roundup_len(__u32 len)284*4882a593Smuzhiyun static inline int roundup_len(__u32 len)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun /* clear out top 2 bits */
287*4882a593Smuzhiyun len <<= 2;
288*4882a593Smuzhiyun len >>= 2;
289*4882a593Smuzhiyun /* add length prefix */
290*4882a593Smuzhiyun len += RINGBUF_META_LEN;
291*4882a593Smuzhiyun /* round up to 8 byte alignment */
292*4882a593Smuzhiyun return (len + 7) / 8 * 8;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
ringbuf_custom_process_ring(struct ringbuf_custom * r)295*4882a593Smuzhiyun static void ringbuf_custom_process_ring(struct ringbuf_custom *r)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun unsigned long cons_pos, prod_pos;
298*4882a593Smuzhiyun int *len_ptr, len;
299*4882a593Smuzhiyun bool got_new_data;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun cons_pos = smp_load_acquire(r->consumer_pos);
302*4882a593Smuzhiyun while (true) {
303*4882a593Smuzhiyun got_new_data = false;
304*4882a593Smuzhiyun prod_pos = smp_load_acquire(r->producer_pos);
305*4882a593Smuzhiyun while (cons_pos < prod_pos) {
306*4882a593Smuzhiyun len_ptr = r->data + (cons_pos & r->mask);
307*4882a593Smuzhiyun len = smp_load_acquire(len_ptr);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun /* sample not committed yet, bail out for now */
310*4882a593Smuzhiyun if (len & RINGBUF_BUSY_BIT)
311*4882a593Smuzhiyun return;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun got_new_data = true;
314*4882a593Smuzhiyun cons_pos += roundup_len(len);
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun atomic_inc(&buf_hits.value);
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun if (got_new_data)
319*4882a593Smuzhiyun smp_store_release(r->consumer_pos, cons_pos);
320*4882a593Smuzhiyun else
321*4882a593Smuzhiyun break;
322*4882a593Smuzhiyun };
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
ringbuf_custom_consumer(void * input)325*4882a593Smuzhiyun static void *ringbuf_custom_consumer(void *input)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
328*4882a593Smuzhiyun int cnt;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun do {
331*4882a593Smuzhiyun if (args.back2back)
332*4882a593Smuzhiyun bufs_trigger_batch();
333*4882a593Smuzhiyun cnt = epoll_wait(ctx->epoll_fd, &ctx->event, 1, -1);
334*4882a593Smuzhiyun if (cnt > 0)
335*4882a593Smuzhiyun ringbuf_custom_process_ring(&ctx->ringbuf);
336*4882a593Smuzhiyun } while (cnt >= 0);
337*4882a593Smuzhiyun fprintf(stderr, "ringbuf polling failed!\n");
338*4882a593Smuzhiyun return 0;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /* PERFBUF-LIBBPF benchmark */
342*4882a593Smuzhiyun static struct perfbuf_libbpf_ctx {
343*4882a593Smuzhiyun struct perfbuf_bench *skel;
344*4882a593Smuzhiyun struct perf_buffer *perfbuf;
345*4882a593Smuzhiyun } perfbuf_libbpf_ctx;
346*4882a593Smuzhiyun
perfbuf_measure(struct bench_res * res)347*4882a593Smuzhiyun static void perfbuf_measure(struct bench_res *res)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun res->hits = atomic_swap(&buf_hits.value, 0);
352*4882a593Smuzhiyun res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
perfbuf_setup_skeleton()355*4882a593Smuzhiyun static struct perfbuf_bench *perfbuf_setup_skeleton()
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun struct perfbuf_bench *skel;
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun setup_libbpf();
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun skel = perfbuf_bench__open();
362*4882a593Smuzhiyun if (!skel) {
363*4882a593Smuzhiyun fprintf(stderr, "failed to open skeleton\n");
364*4882a593Smuzhiyun exit(1);
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun skel->rodata->batch_cnt = args.batch_cnt;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun if (perfbuf_bench__load(skel)) {
370*4882a593Smuzhiyun fprintf(stderr, "failed to load skeleton\n");
371*4882a593Smuzhiyun exit(1);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun return skel;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun static enum bpf_perf_event_ret
perfbuf_process_sample_raw(void * input_ctx,int cpu,struct perf_event_header * e)378*4882a593Smuzhiyun perfbuf_process_sample_raw(void *input_ctx, int cpu,
379*4882a593Smuzhiyun struct perf_event_header *e)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun switch (e->type) {
382*4882a593Smuzhiyun case PERF_RECORD_SAMPLE:
383*4882a593Smuzhiyun atomic_inc(&buf_hits.value);
384*4882a593Smuzhiyun break;
385*4882a593Smuzhiyun case PERF_RECORD_LOST:
386*4882a593Smuzhiyun break;
387*4882a593Smuzhiyun default:
388*4882a593Smuzhiyun return LIBBPF_PERF_EVENT_ERROR;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun return LIBBPF_PERF_EVENT_CONT;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun
perfbuf_libbpf_setup()393*4882a593Smuzhiyun static void perfbuf_libbpf_setup()
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
396*4882a593Smuzhiyun struct perf_event_attr attr;
397*4882a593Smuzhiyun struct perf_buffer_raw_opts pb_opts = {
398*4882a593Smuzhiyun .event_cb = perfbuf_process_sample_raw,
399*4882a593Smuzhiyun .ctx = (void *)(long)0,
400*4882a593Smuzhiyun .attr = &attr,
401*4882a593Smuzhiyun };
402*4882a593Smuzhiyun struct bpf_link *link;
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun ctx->skel = perfbuf_setup_skeleton();
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun memset(&attr, 0, sizeof(attr));
407*4882a593Smuzhiyun attr.config = PERF_COUNT_SW_BPF_OUTPUT,
408*4882a593Smuzhiyun attr.type = PERF_TYPE_SOFTWARE;
409*4882a593Smuzhiyun attr.sample_type = PERF_SAMPLE_RAW;
410*4882a593Smuzhiyun /* notify only every Nth sample */
411*4882a593Smuzhiyun if (args.sampled) {
412*4882a593Smuzhiyun attr.sample_period = args.sample_rate;
413*4882a593Smuzhiyun attr.wakeup_events = args.sample_rate;
414*4882a593Smuzhiyun } else {
415*4882a593Smuzhiyun attr.sample_period = 1;
416*4882a593Smuzhiyun attr.wakeup_events = 1;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun if (args.sample_rate > args.batch_cnt) {
420*4882a593Smuzhiyun fprintf(stderr, "sample rate %d is too high for given batch count %d\n",
421*4882a593Smuzhiyun args.sample_rate, args.batch_cnt);
422*4882a593Smuzhiyun exit(1);
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun ctx->perfbuf = perf_buffer__new_raw(bpf_map__fd(ctx->skel->maps.perfbuf),
426*4882a593Smuzhiyun args.perfbuf_sz, &pb_opts);
427*4882a593Smuzhiyun if (!ctx->perfbuf) {
428*4882a593Smuzhiyun fprintf(stderr, "failed to create perfbuf\n");
429*4882a593Smuzhiyun exit(1);
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun link = bpf_program__attach(ctx->skel->progs.bench_perfbuf);
433*4882a593Smuzhiyun if (IS_ERR(link)) {
434*4882a593Smuzhiyun fprintf(stderr, "failed to attach program\n");
435*4882a593Smuzhiyun exit(1);
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun
perfbuf_libbpf_consumer(void * input)439*4882a593Smuzhiyun static void *perfbuf_libbpf_consumer(void *input)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun while (perf_buffer__poll(ctx->perfbuf, -1) >= 0) {
444*4882a593Smuzhiyun if (args.back2back)
445*4882a593Smuzhiyun bufs_trigger_batch();
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun fprintf(stderr, "perfbuf polling failed!\n");
448*4882a593Smuzhiyun return NULL;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun /* PERFBUF-CUSTOM benchmark */
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun /* copies of internal libbpf definitions */
454*4882a593Smuzhiyun struct perf_cpu_buf {
455*4882a593Smuzhiyun struct perf_buffer *pb;
456*4882a593Smuzhiyun void *base; /* mmap()'ed memory */
457*4882a593Smuzhiyun void *buf; /* for reconstructing segmented data */
458*4882a593Smuzhiyun size_t buf_size;
459*4882a593Smuzhiyun int fd;
460*4882a593Smuzhiyun int cpu;
461*4882a593Smuzhiyun int map_key;
462*4882a593Smuzhiyun };
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun struct perf_buffer {
465*4882a593Smuzhiyun perf_buffer_event_fn event_cb;
466*4882a593Smuzhiyun perf_buffer_sample_fn sample_cb;
467*4882a593Smuzhiyun perf_buffer_lost_fn lost_cb;
468*4882a593Smuzhiyun void *ctx; /* passed into callbacks */
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun size_t page_size;
471*4882a593Smuzhiyun size_t mmap_size;
472*4882a593Smuzhiyun struct perf_cpu_buf **cpu_bufs;
473*4882a593Smuzhiyun struct epoll_event *events;
474*4882a593Smuzhiyun int cpu_cnt; /* number of allocated CPU buffers */
475*4882a593Smuzhiyun int epoll_fd; /* perf event FD */
476*4882a593Smuzhiyun int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
477*4882a593Smuzhiyun };
478*4882a593Smuzhiyun
perfbuf_custom_consumer(void * input)479*4882a593Smuzhiyun static void *perfbuf_custom_consumer(void *input)
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
482*4882a593Smuzhiyun struct perf_buffer *pb = ctx->perfbuf;
483*4882a593Smuzhiyun struct perf_cpu_buf *cpu_buf;
484*4882a593Smuzhiyun struct perf_event_mmap_page *header;
485*4882a593Smuzhiyun size_t mmap_mask = pb->mmap_size - 1;
486*4882a593Smuzhiyun struct perf_event_header *ehdr;
487*4882a593Smuzhiyun __u64 data_head, data_tail;
488*4882a593Smuzhiyun size_t ehdr_size;
489*4882a593Smuzhiyun void *base;
490*4882a593Smuzhiyun int i, cnt;
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun while (true) {
493*4882a593Smuzhiyun if (args.back2back)
494*4882a593Smuzhiyun bufs_trigger_batch();
495*4882a593Smuzhiyun cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, -1);
496*4882a593Smuzhiyun if (cnt <= 0) {
497*4882a593Smuzhiyun fprintf(stderr, "perf epoll failed: %d\n", -errno);
498*4882a593Smuzhiyun exit(1);
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun for (i = 0; i < cnt; ++i) {
502*4882a593Smuzhiyun cpu_buf = pb->events[i].data.ptr;
503*4882a593Smuzhiyun header = cpu_buf->base;
504*4882a593Smuzhiyun base = ((void *)header) + pb->page_size;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun data_head = ring_buffer_read_head(header);
507*4882a593Smuzhiyun data_tail = header->data_tail;
508*4882a593Smuzhiyun while (data_head != data_tail) {
509*4882a593Smuzhiyun ehdr = base + (data_tail & mmap_mask);
510*4882a593Smuzhiyun ehdr_size = ehdr->size;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun if (ehdr->type == PERF_RECORD_SAMPLE)
513*4882a593Smuzhiyun atomic_inc(&buf_hits.value);
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun data_tail += ehdr_size;
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun ring_buffer_write_tail(header, data_tail);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun return NULL;
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun const struct bench bench_rb_libbpf = {
524*4882a593Smuzhiyun .name = "rb-libbpf",
525*4882a593Smuzhiyun .validate = bufs_validate,
526*4882a593Smuzhiyun .setup = ringbuf_libbpf_setup,
527*4882a593Smuzhiyun .producer_thread = bufs_sample_producer,
528*4882a593Smuzhiyun .consumer_thread = ringbuf_libbpf_consumer,
529*4882a593Smuzhiyun .measure = ringbuf_libbpf_measure,
530*4882a593Smuzhiyun .report_progress = hits_drops_report_progress,
531*4882a593Smuzhiyun .report_final = hits_drops_report_final,
532*4882a593Smuzhiyun };
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun const struct bench bench_rb_custom = {
535*4882a593Smuzhiyun .name = "rb-custom",
536*4882a593Smuzhiyun .validate = bufs_validate,
537*4882a593Smuzhiyun .setup = ringbuf_custom_setup,
538*4882a593Smuzhiyun .producer_thread = bufs_sample_producer,
539*4882a593Smuzhiyun .consumer_thread = ringbuf_custom_consumer,
540*4882a593Smuzhiyun .measure = ringbuf_custom_measure,
541*4882a593Smuzhiyun .report_progress = hits_drops_report_progress,
542*4882a593Smuzhiyun .report_final = hits_drops_report_final,
543*4882a593Smuzhiyun };
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun const struct bench bench_pb_libbpf = {
546*4882a593Smuzhiyun .name = "pb-libbpf",
547*4882a593Smuzhiyun .validate = bufs_validate,
548*4882a593Smuzhiyun .setup = perfbuf_libbpf_setup,
549*4882a593Smuzhiyun .producer_thread = bufs_sample_producer,
550*4882a593Smuzhiyun .consumer_thread = perfbuf_libbpf_consumer,
551*4882a593Smuzhiyun .measure = perfbuf_measure,
552*4882a593Smuzhiyun .report_progress = hits_drops_report_progress,
553*4882a593Smuzhiyun .report_final = hits_drops_report_final,
554*4882a593Smuzhiyun };
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun const struct bench bench_pb_custom = {
557*4882a593Smuzhiyun .name = "pb-custom",
558*4882a593Smuzhiyun .validate = bufs_validate,
559*4882a593Smuzhiyun .setup = perfbuf_libbpf_setup,
560*4882a593Smuzhiyun .producer_thread = bufs_sample_producer,
561*4882a593Smuzhiyun .consumer_thread = perfbuf_custom_consumer,
562*4882a593Smuzhiyun .measure = perfbuf_measure,
563*4882a593Smuzhiyun .report_progress = hits_drops_report_progress,
564*4882a593Smuzhiyun .report_final = hits_drops_report_final,
565*4882a593Smuzhiyun };
566*4882a593Smuzhiyun
567