567 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			567 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /* Copyright (c) 2020 Facebook */
 | |
| #include <asm/barrier.h>
 | |
| #include <linux/perf_event.h>
 | |
| #include <linux/ring_buffer.h>
 | |
| #include <sys/epoll.h>
 | |
| #include <sys/mman.h>
 | |
| #include <argp.h>
 | |
| #include <stdlib.h>
 | |
| #include "bench.h"
 | |
| #include "ringbuf_bench.skel.h"
 | |
| #include "perfbuf_bench.skel.h"
 | |
| 
 | |
| static struct {
 | |
| 	bool back2back;
 | |
| 	int batch_cnt;
 | |
| 	bool sampled;
 | |
| 	int sample_rate;
 | |
| 	int ringbuf_sz; /* per-ringbuf, in bytes */
 | |
| 	bool ringbuf_use_output; /* use slower output API */
 | |
| 	int perfbuf_sz; /* per-CPU size, in pages */
 | |
| } args = {
 | |
| 	.back2back = false,
 | |
| 	.batch_cnt = 500,
 | |
| 	.sampled = false,
 | |
| 	.sample_rate = 500,
 | |
| 	.ringbuf_sz = 512 * 1024,
 | |
| 	.ringbuf_use_output = false,
 | |
| 	.perfbuf_sz = 128,
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	ARG_RB_BACK2BACK = 2000,
 | |
| 	ARG_RB_USE_OUTPUT = 2001,
 | |
| 	ARG_RB_BATCH_CNT = 2002,
 | |
| 	ARG_RB_SAMPLED = 2003,
 | |
| 	ARG_RB_SAMPLE_RATE = 2004,
 | |
| };
 | |
| 
 | |
| static const struct argp_option opts[] = {
 | |
| 	{ "rb-b2b", ARG_RB_BACK2BACK, NULL, 0, "Back-to-back mode"},
 | |
| 	{ "rb-use-output", ARG_RB_USE_OUTPUT, NULL, 0, "Use bpf_ringbuf_output() instead of bpf_ringbuf_reserve()"},
 | |
| 	{ "rb-batch-cnt", ARG_RB_BATCH_CNT, "CNT", 0, "Set BPF-side record batch count"},
 | |
| 	{ "rb-sampled", ARG_RB_SAMPLED, NULL, 0, "Notification sampling"},
 | |
| 	{ "rb-sample-rate", ARG_RB_SAMPLE_RATE, "RATE", 0, "Notification sample rate"},
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static error_t parse_arg(int key, char *arg, struct argp_state *state)
 | |
| {
 | |
| 	switch (key) {
 | |
| 	case ARG_RB_BACK2BACK:
 | |
| 		args.back2back = true;
 | |
| 		break;
 | |
| 	case ARG_RB_USE_OUTPUT:
 | |
| 		args.ringbuf_use_output = true;
 | |
| 		break;
 | |
| 	case ARG_RB_BATCH_CNT:
 | |
| 		args.batch_cnt = strtol(arg, NULL, 10);
 | |
| 		if (args.batch_cnt < 0) {
 | |
| 			fprintf(stderr, "Invalid batch count.");
 | |
| 			argp_usage(state);
 | |
| 		}
 | |
| 		break;
 | |
| 	case ARG_RB_SAMPLED:
 | |
| 		args.sampled = true;
 | |
| 		break;
 | |
| 	case ARG_RB_SAMPLE_RATE:
 | |
| 		args.sample_rate = strtol(arg, NULL, 10);
 | |
| 		if (args.sample_rate < 0) {
 | |
| 			fprintf(stderr, "Invalid perfbuf sample rate.");
 | |
| 			argp_usage(state);
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		return ARGP_ERR_UNKNOWN;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* exported into benchmark runner */
 | |
| const struct argp bench_ringbufs_argp = {
 | |
| 	.options = opts,
 | |
| 	.parser = parse_arg,
 | |
| };
 | |
| 
 | |
| /* RINGBUF-LIBBPF benchmark */
 | |
| 
 | |
| static struct counter buf_hits;
 | |
| 
 | |
| static inline void bufs_trigger_batch()
 | |
| {
 | |
| 	(void)syscall(__NR_getpgid);
 | |
| }
 | |
| 
 | |
| static void bufs_validate()
 | |
| {
 | |
| 	if (env.consumer_cnt != 1) {
 | |
| 		fprintf(stderr, "rb-libbpf benchmark doesn't support multi-consumer!\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	if (args.back2back && env.producer_cnt > 1) {
 | |
| 		fprintf(stderr, "back-to-back mode makes sense only for single-producer case!\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void *bufs_sample_producer(void *input)
 | |
| {
 | |
| 	if (args.back2back) {
 | |
| 		/* initial batch to get everything started */
 | |
| 		bufs_trigger_batch();
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	while (true)
 | |
| 		bufs_trigger_batch();
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static struct ringbuf_libbpf_ctx {
 | |
| 	struct ringbuf_bench *skel;
 | |
| 	struct ring_buffer *ringbuf;
 | |
| } ringbuf_libbpf_ctx;
 | |
| 
 | |
| static void ringbuf_libbpf_measure(struct bench_res *res)
 | |
| {
 | |
| 	struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
 | |
| 
 | |
| 	res->hits = atomic_swap(&buf_hits.value, 0);
 | |
| 	res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
 | |
| }
 | |
| 
 | |
| static struct ringbuf_bench *ringbuf_setup_skeleton()
 | |
| {
 | |
| 	struct ringbuf_bench *skel;
 | |
| 
 | |
| 	setup_libbpf();
 | |
| 
 | |
| 	skel = ringbuf_bench__open();
 | |
| 	if (!skel) {
 | |
| 		fprintf(stderr, "failed to open skeleton\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	skel->rodata->batch_cnt = args.batch_cnt;
 | |
| 	skel->rodata->use_output = args.ringbuf_use_output ? 1 : 0;
 | |
| 
 | |
| 	if (args.sampled)
 | |
| 		/* record data + header take 16 bytes */
 | |
| 		skel->rodata->wakeup_data_size = args.sample_rate * 16;
 | |
| 
 | |
| 	bpf_map__resize(skel->maps.ringbuf, args.ringbuf_sz);
 | |
| 
 | |
| 	if (ringbuf_bench__load(skel)) {
 | |
| 		fprintf(stderr, "failed to load skeleton\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	return skel;
 | |
| }
 | |
| 
 | |
| static int buf_process_sample(void *ctx, void *data, size_t len)
 | |
| {
 | |
| 	atomic_inc(&buf_hits.value);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void ringbuf_libbpf_setup()
 | |
| {
 | |
| 	struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
 | |
| 	struct bpf_link *link;
 | |
| 
 | |
| 	ctx->skel = ringbuf_setup_skeleton();
 | |
| 	ctx->ringbuf = ring_buffer__new(bpf_map__fd(ctx->skel->maps.ringbuf),
 | |
| 					buf_process_sample, NULL, NULL);
 | |
| 	if (!ctx->ringbuf) {
 | |
| 		fprintf(stderr, "failed to create ringbuf\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);
 | |
| 	if (!link) {
 | |
| 		fprintf(stderr, "failed to attach program!\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void *ringbuf_libbpf_consumer(void *input)
 | |
| {
 | |
| 	struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;
 | |
| 
 | |
| 	while (ring_buffer__poll(ctx->ringbuf, -1) >= 0) {
 | |
| 		if (args.back2back)
 | |
| 			bufs_trigger_batch();
 | |
| 	}
 | |
| 	fprintf(stderr, "ringbuf polling failed!\n");
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /* RINGBUF-CUSTOM benchmark */
 | |
| struct ringbuf_custom {
 | |
| 	__u64 *consumer_pos;
 | |
| 	__u64 *producer_pos;
 | |
| 	__u64 mask;
 | |
| 	void *data;
 | |
| 	int map_fd;
 | |
| };
 | |
| 
 | |
| static struct ringbuf_custom_ctx {
 | |
| 	struct ringbuf_bench *skel;
 | |
| 	struct ringbuf_custom ringbuf;
 | |
| 	int epoll_fd;
 | |
| 	struct epoll_event event;
 | |
| } ringbuf_custom_ctx;
 | |
| 
 | |
| static void ringbuf_custom_measure(struct bench_res *res)
 | |
| {
 | |
| 	struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
 | |
| 
 | |
| 	res->hits = atomic_swap(&buf_hits.value, 0);
 | |
| 	res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
 | |
| }
 | |
| 
 | |
| static void ringbuf_custom_setup()
 | |
| {
 | |
| 	struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
 | |
| 	const size_t page_size = getpagesize();
 | |
| 	struct bpf_link *link;
 | |
| 	struct ringbuf_custom *r;
 | |
| 	void *tmp;
 | |
| 	int err;
 | |
| 
 | |
| 	ctx->skel = ringbuf_setup_skeleton();
 | |
| 
 | |
| 	ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 | |
| 	if (ctx->epoll_fd < 0) {
 | |
| 		fprintf(stderr, "failed to create epoll fd: %d\n", -errno);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	r = &ctx->ringbuf;
 | |
| 	r->map_fd = bpf_map__fd(ctx->skel->maps.ringbuf);
 | |
| 	r->mask = args.ringbuf_sz - 1;
 | |
| 
 | |
| 	/* Map writable consumer page */
 | |
| 	tmp = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
 | |
| 		   r->map_fd, 0);
 | |
| 	if (tmp == MAP_FAILED) {
 | |
| 		fprintf(stderr, "failed to mmap consumer page: %d\n", -errno);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	r->consumer_pos = tmp;
 | |
| 
 | |
| 	/* Map read-only producer page and data pages. */
 | |
| 	tmp = mmap(NULL, page_size + 2 * args.ringbuf_sz, PROT_READ, MAP_SHARED,
 | |
| 		   r->map_fd, page_size);
 | |
| 	if (tmp == MAP_FAILED) {
 | |
| 		fprintf(stderr, "failed to mmap data pages: %d\n", -errno);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	r->producer_pos = tmp;
 | |
| 	r->data = tmp + page_size;
 | |
| 
 | |
| 	ctx->event.events = EPOLLIN;
 | |
| 	err = epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, r->map_fd, &ctx->event);
 | |
| 	if (err < 0) {
 | |
| 		fprintf(stderr, "failed to epoll add ringbuf: %d\n", -errno);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);
 | |
| 	if (!link) {
 | |
| 		fprintf(stderr, "failed to attach program\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #define RINGBUF_BUSY_BIT (1 << 31)
 | |
| #define RINGBUF_DISCARD_BIT (1 << 30)
 | |
| #define RINGBUF_META_LEN 8
 | |
| 
 | |
| static inline int roundup_len(__u32 len)
 | |
| {
 | |
| 	/* clear out top 2 bits */
 | |
| 	len <<= 2;
 | |
| 	len >>= 2;
 | |
| 	/* add length prefix */
 | |
| 	len += RINGBUF_META_LEN;
 | |
| 	/* round up to 8 byte alignment */
 | |
| 	return (len + 7) / 8 * 8;
 | |
| }
 | |
| 
 | |
| static void ringbuf_custom_process_ring(struct ringbuf_custom *r)
 | |
| {
 | |
| 	unsigned long cons_pos, prod_pos;
 | |
| 	int *len_ptr, len;
 | |
| 	bool got_new_data;
 | |
| 
 | |
| 	cons_pos = smp_load_acquire(r->consumer_pos);
 | |
| 	while (true) {
 | |
| 		got_new_data = false;
 | |
| 		prod_pos = smp_load_acquire(r->producer_pos);
 | |
| 		while (cons_pos < prod_pos) {
 | |
| 			len_ptr = r->data + (cons_pos & r->mask);
 | |
| 			len = smp_load_acquire(len_ptr);
 | |
| 
 | |
| 			/* sample not committed yet, bail out for now */
 | |
| 			if (len & RINGBUF_BUSY_BIT)
 | |
| 				return;
 | |
| 
 | |
| 			got_new_data = true;
 | |
| 			cons_pos += roundup_len(len);
 | |
| 
 | |
| 			atomic_inc(&buf_hits.value);
 | |
| 		}
 | |
| 		if (got_new_data)
 | |
| 			smp_store_release(r->consumer_pos, cons_pos);
 | |
| 		else
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void *ringbuf_custom_consumer(void *input)
 | |
| {
 | |
| 	struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;
 | |
| 	int cnt;
 | |
| 
 | |
| 	do {
 | |
| 		if (args.back2back)
 | |
| 			bufs_trigger_batch();
 | |
| 		cnt = epoll_wait(ctx->epoll_fd, &ctx->event, 1, -1);
 | |
| 		if (cnt > 0)
 | |
| 			ringbuf_custom_process_ring(&ctx->ringbuf);
 | |
| 	} while (cnt >= 0);
 | |
| 	fprintf(stderr, "ringbuf polling failed!\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* PERFBUF-LIBBPF benchmark */
 | |
| static struct perfbuf_libbpf_ctx {
 | |
| 	struct perfbuf_bench *skel;
 | |
| 	struct perf_buffer *perfbuf;
 | |
| } perfbuf_libbpf_ctx;
 | |
| 
 | |
| static void perfbuf_measure(struct bench_res *res)
 | |
| {
 | |
| 	struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
 | |
| 
 | |
| 	res->hits = atomic_swap(&buf_hits.value, 0);
 | |
| 	res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);
 | |
| }
 | |
| 
 | |
| static struct perfbuf_bench *perfbuf_setup_skeleton()
 | |
| {
 | |
| 	struct perfbuf_bench *skel;
 | |
| 
 | |
| 	setup_libbpf();
 | |
| 
 | |
| 	skel = perfbuf_bench__open();
 | |
| 	if (!skel) {
 | |
| 		fprintf(stderr, "failed to open skeleton\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	skel->rodata->batch_cnt = args.batch_cnt;
 | |
| 
 | |
| 	if (perfbuf_bench__load(skel)) {
 | |
| 		fprintf(stderr, "failed to load skeleton\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	return skel;
 | |
| }
 | |
| 
 | |
| static enum bpf_perf_event_ret
 | |
| perfbuf_process_sample_raw(void *input_ctx, int cpu,
 | |
| 			   struct perf_event_header *e)
 | |
| {
 | |
| 	switch (e->type) {
 | |
| 	case PERF_RECORD_SAMPLE:
 | |
| 		atomic_inc(&buf_hits.value);
 | |
| 		break;
 | |
| 	case PERF_RECORD_LOST:
 | |
| 		break;
 | |
| 	default:
 | |
| 		return LIBBPF_PERF_EVENT_ERROR;
 | |
| 	}
 | |
| 	return LIBBPF_PERF_EVENT_CONT;
 | |
| }
 | |
| 
 | |
| static void perfbuf_libbpf_setup()
 | |
| {
 | |
| 	struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
 | |
| 	struct perf_event_attr attr;
 | |
| 	struct perf_buffer_raw_opts pb_opts = {
 | |
| 		.event_cb = perfbuf_process_sample_raw,
 | |
| 		.ctx = (void *)(long)0,
 | |
| 		.attr = &attr,
 | |
| 	};
 | |
| 	struct bpf_link *link;
 | |
| 
 | |
| 	ctx->skel = perfbuf_setup_skeleton();
 | |
| 
 | |
| 	memset(&attr, 0, sizeof(attr));
 | |
| 	attr.config = PERF_COUNT_SW_BPF_OUTPUT,
 | |
| 	attr.type = PERF_TYPE_SOFTWARE;
 | |
| 	attr.sample_type = PERF_SAMPLE_RAW;
 | |
| 	/* notify only every Nth sample */
 | |
| 	if (args.sampled) {
 | |
| 		attr.sample_period = args.sample_rate;
 | |
| 		attr.wakeup_events = args.sample_rate;
 | |
| 	} else {
 | |
| 		attr.sample_period = 1;
 | |
| 		attr.wakeup_events = 1;
 | |
| 	}
 | |
| 
 | |
| 	if (args.sample_rate > args.batch_cnt) {
 | |
| 		fprintf(stderr, "sample rate %d is too high for given batch count %d\n",
 | |
| 			args.sample_rate, args.batch_cnt);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	ctx->perfbuf = perf_buffer__new_raw(bpf_map__fd(ctx->skel->maps.perfbuf),
 | |
| 					    args.perfbuf_sz, &pb_opts);
 | |
| 	if (!ctx->perfbuf) {
 | |
| 		fprintf(stderr, "failed to create perfbuf\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	link = bpf_program__attach(ctx->skel->progs.bench_perfbuf);
 | |
| 	if (!link) {
 | |
| 		fprintf(stderr, "failed to attach program\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void *perfbuf_libbpf_consumer(void *input)
 | |
| {
 | |
| 	struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
 | |
| 
 | |
| 	while (perf_buffer__poll(ctx->perfbuf, -1) >= 0) {
 | |
| 		if (args.back2back)
 | |
| 			bufs_trigger_batch();
 | |
| 	}
 | |
| 	fprintf(stderr, "perfbuf polling failed!\n");
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /* PERFBUF-CUSTOM benchmark */
 | |
| 
 | |
| /* copies of internal libbpf definitions */
 | |
| struct perf_cpu_buf {
 | |
| 	struct perf_buffer *pb;
 | |
| 	void *base; /* mmap()'ed memory */
 | |
| 	void *buf; /* for reconstructing segmented data */
 | |
| 	size_t buf_size;
 | |
| 	int fd;
 | |
| 	int cpu;
 | |
| 	int map_key;
 | |
| };
 | |
| 
 | |
| struct perf_buffer {
 | |
| 	perf_buffer_event_fn event_cb;
 | |
| 	perf_buffer_sample_fn sample_cb;
 | |
| 	perf_buffer_lost_fn lost_cb;
 | |
| 	void *ctx; /* passed into callbacks */
 | |
| 
 | |
| 	size_t page_size;
 | |
| 	size_t mmap_size;
 | |
| 	struct perf_cpu_buf **cpu_bufs;
 | |
| 	struct epoll_event *events;
 | |
| 	int cpu_cnt; /* number of allocated CPU buffers */
 | |
| 	int epoll_fd; /* perf event FD */
 | |
| 	int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
 | |
| };
 | |
| 
 | |
| static void *perfbuf_custom_consumer(void *input)
 | |
| {
 | |
| 	struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;
 | |
| 	struct perf_buffer *pb = ctx->perfbuf;
 | |
| 	struct perf_cpu_buf *cpu_buf;
 | |
| 	struct perf_event_mmap_page *header;
 | |
| 	size_t mmap_mask = pb->mmap_size - 1;
 | |
| 	struct perf_event_header *ehdr;
 | |
| 	__u64 data_head, data_tail;
 | |
| 	size_t ehdr_size;
 | |
| 	void *base;
 | |
| 	int i, cnt;
 | |
| 
 | |
| 	while (true) {
 | |
| 		if (args.back2back)
 | |
| 			bufs_trigger_batch();
 | |
| 		cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, -1);
 | |
| 		if (cnt <= 0) {
 | |
| 			fprintf(stderr, "perf epoll failed: %d\n", -errno);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		for (i = 0; i < cnt; ++i) {
 | |
| 			cpu_buf = pb->events[i].data.ptr;
 | |
| 			header = cpu_buf->base;
 | |
| 			base = ((void *)header) + pb->page_size;
 | |
| 
 | |
| 			data_head = ring_buffer_read_head(header);
 | |
| 			data_tail = header->data_tail;
 | |
| 			while (data_head != data_tail) {
 | |
| 				ehdr = base + (data_tail & mmap_mask);
 | |
| 				ehdr_size = ehdr->size;
 | |
| 
 | |
| 				if (ehdr->type == PERF_RECORD_SAMPLE)
 | |
| 					atomic_inc(&buf_hits.value);
 | |
| 
 | |
| 				data_tail += ehdr_size;
 | |
| 			}
 | |
| 			ring_buffer_write_tail(header, data_tail);
 | |
| 		}
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| const struct bench bench_rb_libbpf = {
 | |
| 	.name = "rb-libbpf",
 | |
| 	.validate = bufs_validate,
 | |
| 	.setup = ringbuf_libbpf_setup,
 | |
| 	.producer_thread = bufs_sample_producer,
 | |
| 	.consumer_thread = ringbuf_libbpf_consumer,
 | |
| 	.measure = ringbuf_libbpf_measure,
 | |
| 	.report_progress = hits_drops_report_progress,
 | |
| 	.report_final = hits_drops_report_final,
 | |
| };
 | |
| 
 | |
| const struct bench bench_rb_custom = {
 | |
| 	.name = "rb-custom",
 | |
| 	.validate = bufs_validate,
 | |
| 	.setup = ringbuf_custom_setup,
 | |
| 	.producer_thread = bufs_sample_producer,
 | |
| 	.consumer_thread = ringbuf_custom_consumer,
 | |
| 	.measure = ringbuf_custom_measure,
 | |
| 	.report_progress = hits_drops_report_progress,
 | |
| 	.report_final = hits_drops_report_final,
 | |
| };
 | |
| 
 | |
| const struct bench bench_pb_libbpf = {
 | |
| 	.name = "pb-libbpf",
 | |
| 	.validate = bufs_validate,
 | |
| 	.setup = perfbuf_libbpf_setup,
 | |
| 	.producer_thread = bufs_sample_producer,
 | |
| 	.consumer_thread = perfbuf_libbpf_consumer,
 | |
| 	.measure = perfbuf_measure,
 | |
| 	.report_progress = hits_drops_report_progress,
 | |
| 	.report_final = hits_drops_report_final,
 | |
| };
 | |
| 
 | |
| const struct bench bench_pb_custom = {
 | |
| 	.name = "pb-custom",
 | |
| 	.validate = bufs_validate,
 | |
| 	.setup = perfbuf_libbpf_setup,
 | |
| 	.producer_thread = bufs_sample_producer,
 | |
| 	.consumer_thread = perfbuf_custom_consumer,
 | |
| 	.measure = perfbuf_measure,
 | |
| 	.report_progress = hits_drops_report_progress,
 | |
| 	.report_final = hits_drops_report_final,
 | |
| };
 | |
| 
 |