Path: blob/master/tools/testing/selftests/bpf/benchs/bench_ringbufs.c
50619 views
// SPDX-License-Identifier: GPL-2.01/* Copyright (c) 2020 Facebook */2#include <asm/barrier.h>3#include <linux/perf_event.h>4#include <linux/ring_buffer.h>5#include <sys/epoll.h>6#include <sys/mman.h>7#include <argp.h>8#include <stdlib.h>9#include "bench.h"10#include "ringbuf_bench.skel.h"11#include "perfbuf_bench.skel.h"1213static struct {14bool back2back;15int batch_cnt;16bool sampled;17int sample_rate;18int ringbuf_sz; /* per-ringbuf, in bytes */19bool ringbuf_use_output; /* use slower output API */20int perfbuf_sz; /* per-CPU size, in pages */21bool overwrite;22bool bench_producer;23} args = {24.back2back = false,25.batch_cnt = 500,26.sampled = false,27.sample_rate = 500,28.ringbuf_sz = 512 * 1024,29.ringbuf_use_output = false,30.perfbuf_sz = 128,31.overwrite = false,32.bench_producer = false,33};3435enum {36ARG_RB_BACK2BACK = 2000,37ARG_RB_USE_OUTPUT = 2001,38ARG_RB_BATCH_CNT = 2002,39ARG_RB_SAMPLED = 2003,40ARG_RB_SAMPLE_RATE = 2004,41ARG_RB_OVERWRITE = 2005,42ARG_RB_BENCH_PRODUCER = 2006,43};4445static const struct argp_option opts[] = {46{ "rb-b2b", ARG_RB_BACK2BACK, NULL, 0, "Back-to-back mode"},47{ "rb-use-output", ARG_RB_USE_OUTPUT, NULL, 0, "Use bpf_ringbuf_output() instead of bpf_ringbuf_reserve()"},48{ "rb-batch-cnt", ARG_RB_BATCH_CNT, "CNT", 0, "Set BPF-side record batch count"},49{ "rb-sampled", ARG_RB_SAMPLED, NULL, 0, "Notification sampling"},50{ "rb-sample-rate", ARG_RB_SAMPLE_RATE, "RATE", 0, "Notification sample rate"},51{ "rb-overwrite", ARG_RB_OVERWRITE, NULL, 0, "Overwrite mode"},52{ "rb-bench-producer", ARG_RB_BENCH_PRODUCER, NULL, 0, "Benchmark producer"},53{},54};5556static error_t parse_arg(int key, char *arg, struct argp_state *state)57{58switch (key) {59case ARG_RB_BACK2BACK:60args.back2back = true;61break;62case ARG_RB_USE_OUTPUT:63args.ringbuf_use_output = true;64break;65case ARG_RB_BATCH_CNT:66args.batch_cnt = strtol(arg, NULL, 10);67if (args.batch_cnt < 0) {68fprintf(stderr, "Invalid batch count.");69argp_usage(state);70}71break;72case ARG_RB_SAMPLED:73args.sampled = true;74break;75case ARG_RB_SAMPLE_RATE:76args.sample_rate = strtol(arg, NULL, 10);77if (args.sample_rate < 0) {78fprintf(stderr, "Invalid perfbuf sample rate.");79argp_usage(state);80}81break;82case ARG_RB_OVERWRITE:83args.overwrite = true;84break;85case ARG_RB_BENCH_PRODUCER:86args.bench_producer = true;87break;88default:89return ARGP_ERR_UNKNOWN;90}91return 0;92}9394/* exported into benchmark runner */95const struct argp bench_ringbufs_argp = {96.options = opts,97.parser = parse_arg,98};99100/* RINGBUF-LIBBPF benchmark */101102static struct counter buf_hits;103104static inline void bufs_trigger_batch(void)105{106(void)syscall(__NR_getpgid);107}108109static void bufs_validate(void)110{111if (args.bench_producer && strcmp(env.bench_name, "rb-libbpf")) {112fprintf(stderr, "--rb-bench-producer only works with rb-libbpf!\n");113exit(1);114}115116if (args.overwrite && !args.bench_producer) {117fprintf(stderr, "overwrite mode only works with --rb-bench-producer for now!\n");118exit(1);119}120121if (args.bench_producer && env.consumer_cnt != 0) {122fprintf(stderr, "no consumer is needed for --rb-bench-producer!\n");123exit(1);124}125126if (args.bench_producer && args.back2back) {127fprintf(stderr, "back-to-back mode makes no sense for --rb-bench-producer!\n");128exit(1);129}130131if (args.bench_producer && args.sampled) {132fprintf(stderr, "sampling mode makes no sense for --rb-bench-producer!\n");133exit(1);134}135136if (!args.bench_producer && env.consumer_cnt != 1) {137fprintf(stderr, "benchmarks without --rb-bench-producer require exactly one consumer!\n");138exit(1);139}140141if (args.back2back && env.producer_cnt > 1) {142fprintf(stderr, "back-to-back mode makes sense only for single-producer case!\n");143exit(1);144}145}146147static void *bufs_sample_producer(void *input)148{149if (args.back2back) {150/* initial batch to get everything started */151bufs_trigger_batch();152return NULL;153}154155while (true)156bufs_trigger_batch();157return NULL;158}159160static struct ringbuf_libbpf_ctx {161struct ringbuf_bench *skel;162struct ring_buffer *ringbuf;163} ringbuf_libbpf_ctx;164165static void ringbuf_libbpf_measure(struct bench_res *res)166{167struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;168169if (args.bench_producer)170res->hits = atomic_swap(&ctx->skel->bss->hits, 0);171else172res->hits = atomic_swap(&buf_hits.value, 0);173res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);174}175176static struct ringbuf_bench *ringbuf_setup_skeleton(void)177{178__u32 flags;179struct bpf_map *ringbuf;180struct ringbuf_bench *skel;181182setup_libbpf();183184skel = ringbuf_bench__open();185if (!skel) {186fprintf(stderr, "failed to open skeleton\n");187exit(1);188}189190skel->rodata->batch_cnt = args.batch_cnt;191skel->rodata->use_output = args.ringbuf_use_output ? 1 : 0;192skel->rodata->bench_producer = args.bench_producer;193194if (args.sampled)195/* record data + header take 16 bytes */196skel->rodata->wakeup_data_size = args.sample_rate * 16;197198ringbuf = skel->maps.ringbuf;199if (args.overwrite) {200flags = bpf_map__map_flags(ringbuf) | BPF_F_RB_OVERWRITE;201bpf_map__set_map_flags(ringbuf, flags);202}203204bpf_map__set_max_entries(ringbuf, args.ringbuf_sz);205206if (ringbuf_bench__load(skel)) {207fprintf(stderr, "failed to load skeleton\n");208exit(1);209}210211return skel;212}213214static int buf_process_sample(void *ctx, void *data, size_t len)215{216atomic_inc(&buf_hits.value);217return 0;218}219220static void ringbuf_libbpf_setup(void)221{222struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;223struct bpf_link *link;224int map_fd;225226ctx->skel = ringbuf_setup_skeleton();227228map_fd = bpf_map__fd(ctx->skel->maps.ringbuf);229ctx->ringbuf = ring_buffer__new(map_fd, buf_process_sample, NULL, NULL);230if (!ctx->ringbuf) {231fprintf(stderr, "failed to create ringbuf\n");232exit(1);233}234235link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);236if (!link) {237fprintf(stderr, "failed to attach program!\n");238exit(1);239}240}241242static void *ringbuf_libbpf_consumer(void *input)243{244struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;245246while (ring_buffer__poll(ctx->ringbuf, -1) >= 0) {247if (args.back2back)248bufs_trigger_batch();249}250fprintf(stderr, "ringbuf polling failed!\n");251return NULL;252}253254/* RINGBUF-CUSTOM benchmark */255struct ringbuf_custom {256__u64 *consumer_pos;257__u64 *producer_pos;258__u64 mask;259void *data;260int map_fd;261};262263static struct ringbuf_custom_ctx {264struct ringbuf_bench *skel;265struct ringbuf_custom ringbuf;266int epoll_fd;267struct epoll_event event;268} ringbuf_custom_ctx;269270static void ringbuf_custom_measure(struct bench_res *res)271{272struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;273274res->hits = atomic_swap(&buf_hits.value, 0);275res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);276}277278static void ringbuf_custom_setup(void)279{280struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;281const size_t page_size = getpagesize();282struct bpf_link *link;283struct ringbuf_custom *r;284void *tmp;285int err;286287ctx->skel = ringbuf_setup_skeleton();288289ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC);290if (ctx->epoll_fd < 0) {291fprintf(stderr, "failed to create epoll fd: %d\n", -errno);292exit(1);293}294295r = &ctx->ringbuf;296r->map_fd = bpf_map__fd(ctx->skel->maps.ringbuf);297r->mask = args.ringbuf_sz - 1;298299/* Map writable consumer page */300tmp = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,301r->map_fd, 0);302if (tmp == MAP_FAILED) {303fprintf(stderr, "failed to mmap consumer page: %d\n", -errno);304exit(1);305}306r->consumer_pos = tmp;307308/* Map read-only producer page and data pages. */309tmp = mmap(NULL, page_size + 2 * args.ringbuf_sz, PROT_READ, MAP_SHARED,310r->map_fd, page_size);311if (tmp == MAP_FAILED) {312fprintf(stderr, "failed to mmap data pages: %d\n", -errno);313exit(1);314}315r->producer_pos = tmp;316r->data = tmp + page_size;317318ctx->event.events = EPOLLIN;319err = epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, r->map_fd, &ctx->event);320if (err < 0) {321fprintf(stderr, "failed to epoll add ringbuf: %d\n", -errno);322exit(1);323}324325link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);326if (!link) {327fprintf(stderr, "failed to attach program\n");328exit(1);329}330}331332#define RINGBUF_BUSY_BIT (1 << 31)333#define RINGBUF_DISCARD_BIT (1 << 30)334#define RINGBUF_META_LEN 8335336static inline int roundup_len(__u32 len)337{338/* clear out top 2 bits */339len <<= 2;340len >>= 2;341/* add length prefix */342len += RINGBUF_META_LEN;343/* round up to 8 byte alignment */344return (len + 7) / 8 * 8;345}346347static void ringbuf_custom_process_ring(struct ringbuf_custom *r)348{349unsigned long cons_pos, prod_pos;350int *len_ptr, len;351bool got_new_data;352353cons_pos = smp_load_acquire(r->consumer_pos);354while (true) {355got_new_data = false;356prod_pos = smp_load_acquire(r->producer_pos);357while (cons_pos < prod_pos) {358len_ptr = r->data + (cons_pos & r->mask);359len = smp_load_acquire(len_ptr);360361/* sample not committed yet, bail out for now */362if (len & RINGBUF_BUSY_BIT)363return;364365got_new_data = true;366cons_pos += roundup_len(len);367368atomic_inc(&buf_hits.value);369}370if (got_new_data)371smp_store_release(r->consumer_pos, cons_pos);372else373break;374}375}376377static void *ringbuf_custom_consumer(void *input)378{379struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;380int cnt;381382do {383if (args.back2back)384bufs_trigger_batch();385cnt = epoll_wait(ctx->epoll_fd, &ctx->event, 1, -1);386if (cnt > 0)387ringbuf_custom_process_ring(&ctx->ringbuf);388} while (cnt >= 0);389fprintf(stderr, "ringbuf polling failed!\n");390return 0;391}392393/* PERFBUF-LIBBPF benchmark */394static struct perfbuf_libbpf_ctx {395struct perfbuf_bench *skel;396struct perf_buffer *perfbuf;397} perfbuf_libbpf_ctx;398399static void perfbuf_measure(struct bench_res *res)400{401struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;402403res->hits = atomic_swap(&buf_hits.value, 0);404res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);405}406407static struct perfbuf_bench *perfbuf_setup_skeleton(void)408{409struct perfbuf_bench *skel;410411setup_libbpf();412413skel = perfbuf_bench__open();414if (!skel) {415fprintf(stderr, "failed to open skeleton\n");416exit(1);417}418419skel->rodata->batch_cnt = args.batch_cnt;420421if (perfbuf_bench__load(skel)) {422fprintf(stderr, "failed to load skeleton\n");423exit(1);424}425426return skel;427}428429static enum bpf_perf_event_ret430perfbuf_process_sample_raw(void *input_ctx, int cpu,431struct perf_event_header *e)432{433switch (e->type) {434case PERF_RECORD_SAMPLE:435atomic_inc(&buf_hits.value);436break;437case PERF_RECORD_LOST:438break;439default:440return LIBBPF_PERF_EVENT_ERROR;441}442return LIBBPF_PERF_EVENT_CONT;443}444445static void perfbuf_libbpf_setup(void)446{447struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;448struct perf_event_attr attr;449struct bpf_link *link;450451ctx->skel = perfbuf_setup_skeleton();452453memset(&attr, 0, sizeof(attr));454attr.config = PERF_COUNT_SW_BPF_OUTPUT;455attr.type = PERF_TYPE_SOFTWARE;456attr.sample_type = PERF_SAMPLE_RAW;457/* notify only every Nth sample */458if (args.sampled) {459attr.sample_period = args.sample_rate;460attr.wakeup_events = args.sample_rate;461} else {462attr.sample_period = 1;463attr.wakeup_events = 1;464}465466if (args.sample_rate > args.batch_cnt) {467fprintf(stderr, "sample rate %d is too high for given batch count %d\n",468args.sample_rate, args.batch_cnt);469exit(1);470}471472ctx->perfbuf = perf_buffer__new_raw(bpf_map__fd(ctx->skel->maps.perfbuf),473args.perfbuf_sz, &attr,474perfbuf_process_sample_raw, NULL, NULL);475if (!ctx->perfbuf) {476fprintf(stderr, "failed to create perfbuf\n");477exit(1);478}479480link = bpf_program__attach(ctx->skel->progs.bench_perfbuf);481if (!link) {482fprintf(stderr, "failed to attach program\n");483exit(1);484}485}486487static void *perfbuf_libbpf_consumer(void *input)488{489struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;490491while (perf_buffer__poll(ctx->perfbuf, -1) >= 0) {492if (args.back2back)493bufs_trigger_batch();494}495fprintf(stderr, "perfbuf polling failed!\n");496return NULL;497}498499/* PERFBUF-CUSTOM benchmark */500501/* copies of internal libbpf definitions */502struct perf_cpu_buf {503struct perf_buffer *pb;504void *base; /* mmap()'ed memory */505void *buf; /* for reconstructing segmented data */506size_t buf_size;507int fd;508int cpu;509int map_key;510};511512struct perf_buffer {513perf_buffer_event_fn event_cb;514perf_buffer_sample_fn sample_cb;515perf_buffer_lost_fn lost_cb;516void *ctx; /* passed into callbacks */517518size_t page_size;519size_t mmap_size;520struct perf_cpu_buf **cpu_bufs;521struct epoll_event *events;522int cpu_cnt; /* number of allocated CPU buffers */523int epoll_fd; /* perf event FD */524int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */525};526527static void *perfbuf_custom_consumer(void *input)528{529struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;530struct perf_buffer *pb = ctx->perfbuf;531struct perf_cpu_buf *cpu_buf;532struct perf_event_mmap_page *header;533size_t mmap_mask = pb->mmap_size - 1;534struct perf_event_header *ehdr;535__u64 data_head, data_tail;536size_t ehdr_size;537void *base;538int i, cnt;539540while (true) {541if (args.back2back)542bufs_trigger_batch();543cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, -1);544if (cnt <= 0) {545fprintf(stderr, "perf epoll failed: %d\n", -errno);546exit(1);547}548549for (i = 0; i < cnt; ++i) {550cpu_buf = pb->events[i].data.ptr;551header = cpu_buf->base;552base = ((void *)header) + pb->page_size;553554data_head = ring_buffer_read_head(header);555data_tail = header->data_tail;556while (data_head != data_tail) {557ehdr = base + (data_tail & mmap_mask);558ehdr_size = ehdr->size;559560if (ehdr->type == PERF_RECORD_SAMPLE)561atomic_inc(&buf_hits.value);562563data_tail += ehdr_size;564}565ring_buffer_write_tail(header, data_tail);566}567}568return NULL;569}570571const struct bench bench_rb_libbpf = {572.name = "rb-libbpf",573.argp = &bench_ringbufs_argp,574.validate = bufs_validate,575.setup = ringbuf_libbpf_setup,576.producer_thread = bufs_sample_producer,577.consumer_thread = ringbuf_libbpf_consumer,578.measure = ringbuf_libbpf_measure,579.report_progress = hits_drops_report_progress,580.report_final = hits_drops_report_final,581};582583const struct bench bench_rb_custom = {584.name = "rb-custom",585.argp = &bench_ringbufs_argp,586.validate = bufs_validate,587.setup = ringbuf_custom_setup,588.producer_thread = bufs_sample_producer,589.consumer_thread = ringbuf_custom_consumer,590.measure = ringbuf_custom_measure,591.report_progress = hits_drops_report_progress,592.report_final = hits_drops_report_final,593};594595const struct bench bench_pb_libbpf = {596.name = "pb-libbpf",597.argp = &bench_ringbufs_argp,598.validate = bufs_validate,599.setup = perfbuf_libbpf_setup,600.producer_thread = bufs_sample_producer,601.consumer_thread = perfbuf_libbpf_consumer,602.measure = perfbuf_measure,603.report_progress = hits_drops_report_progress,604.report_final = hits_drops_report_final,605};606607const struct bench bench_pb_custom = {608.name = "pb-custom",609.argp = &bench_ringbufs_argp,610.validate = bufs_validate,611.setup = perfbuf_libbpf_setup,612.producer_thread = bufs_sample_producer,613.consumer_thread = perfbuf_custom_consumer,614.measure = perfbuf_measure,615.report_progress = hits_drops_report_progress,616.report_final = hits_drops_report_final,617};618619620621