Path: blob/master/tools/testing/selftests/bpf/benchs/bench_ringbufs.c
29270 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 */21} args = {22.back2back = false,23.batch_cnt = 500,24.sampled = false,25.sample_rate = 500,26.ringbuf_sz = 512 * 1024,27.ringbuf_use_output = false,28.perfbuf_sz = 128,29};3031enum {32ARG_RB_BACK2BACK = 2000,33ARG_RB_USE_OUTPUT = 2001,34ARG_RB_BATCH_CNT = 2002,35ARG_RB_SAMPLED = 2003,36ARG_RB_SAMPLE_RATE = 2004,37};3839static const struct argp_option opts[] = {40{ "rb-b2b", ARG_RB_BACK2BACK, NULL, 0, "Back-to-back mode"},41{ "rb-use-output", ARG_RB_USE_OUTPUT, NULL, 0, "Use bpf_ringbuf_output() instead of bpf_ringbuf_reserve()"},42{ "rb-batch-cnt", ARG_RB_BATCH_CNT, "CNT", 0, "Set BPF-side record batch count"},43{ "rb-sampled", ARG_RB_SAMPLED, NULL, 0, "Notification sampling"},44{ "rb-sample-rate", ARG_RB_SAMPLE_RATE, "RATE", 0, "Notification sample rate"},45{},46};4748static error_t parse_arg(int key, char *arg, struct argp_state *state)49{50switch (key) {51case ARG_RB_BACK2BACK:52args.back2back = true;53break;54case ARG_RB_USE_OUTPUT:55args.ringbuf_use_output = true;56break;57case ARG_RB_BATCH_CNT:58args.batch_cnt = strtol(arg, NULL, 10);59if (args.batch_cnt < 0) {60fprintf(stderr, "Invalid batch count.");61argp_usage(state);62}63break;64case ARG_RB_SAMPLED:65args.sampled = true;66break;67case ARG_RB_SAMPLE_RATE:68args.sample_rate = strtol(arg, NULL, 10);69if (args.sample_rate < 0) {70fprintf(stderr, "Invalid perfbuf sample rate.");71argp_usage(state);72}73break;74default:75return ARGP_ERR_UNKNOWN;76}77return 0;78}7980/* exported into benchmark runner */81const struct argp bench_ringbufs_argp = {82.options = opts,83.parser = parse_arg,84};8586/* RINGBUF-LIBBPF benchmark */8788static struct counter buf_hits;8990static inline void bufs_trigger_batch(void)91{92(void)syscall(__NR_getpgid);93}9495static void bufs_validate(void)96{97if (env.consumer_cnt != 1) {98fprintf(stderr, "rb-libbpf benchmark needs one consumer!\n");99exit(1);100}101102if (args.back2back && env.producer_cnt > 1) {103fprintf(stderr, "back-to-back mode makes sense only for single-producer case!\n");104exit(1);105}106}107108static void *bufs_sample_producer(void *input)109{110if (args.back2back) {111/* initial batch to get everything started */112bufs_trigger_batch();113return NULL;114}115116while (true)117bufs_trigger_batch();118return NULL;119}120121static struct ringbuf_libbpf_ctx {122struct ringbuf_bench *skel;123struct ring_buffer *ringbuf;124} ringbuf_libbpf_ctx;125126static void ringbuf_libbpf_measure(struct bench_res *res)127{128struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;129130res->hits = atomic_swap(&buf_hits.value, 0);131res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);132}133134static struct ringbuf_bench *ringbuf_setup_skeleton(void)135{136struct ringbuf_bench *skel;137138setup_libbpf();139140skel = ringbuf_bench__open();141if (!skel) {142fprintf(stderr, "failed to open skeleton\n");143exit(1);144}145146skel->rodata->batch_cnt = args.batch_cnt;147skel->rodata->use_output = args.ringbuf_use_output ? 1 : 0;148149if (args.sampled)150/* record data + header take 16 bytes */151skel->rodata->wakeup_data_size = args.sample_rate * 16;152153bpf_map__set_max_entries(skel->maps.ringbuf, args.ringbuf_sz);154155if (ringbuf_bench__load(skel)) {156fprintf(stderr, "failed to load skeleton\n");157exit(1);158}159160return skel;161}162163static int buf_process_sample(void *ctx, void *data, size_t len)164{165atomic_inc(&buf_hits.value);166return 0;167}168169static void ringbuf_libbpf_setup(void)170{171struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;172struct bpf_link *link;173174ctx->skel = ringbuf_setup_skeleton();175ctx->ringbuf = ring_buffer__new(bpf_map__fd(ctx->skel->maps.ringbuf),176buf_process_sample, NULL, NULL);177if (!ctx->ringbuf) {178fprintf(stderr, "failed to create ringbuf\n");179exit(1);180}181182link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);183if (!link) {184fprintf(stderr, "failed to attach program!\n");185exit(1);186}187}188189static void *ringbuf_libbpf_consumer(void *input)190{191struct ringbuf_libbpf_ctx *ctx = &ringbuf_libbpf_ctx;192193while (ring_buffer__poll(ctx->ringbuf, -1) >= 0) {194if (args.back2back)195bufs_trigger_batch();196}197fprintf(stderr, "ringbuf polling failed!\n");198return NULL;199}200201/* RINGBUF-CUSTOM benchmark */202struct ringbuf_custom {203__u64 *consumer_pos;204__u64 *producer_pos;205__u64 mask;206void *data;207int map_fd;208};209210static struct ringbuf_custom_ctx {211struct ringbuf_bench *skel;212struct ringbuf_custom ringbuf;213int epoll_fd;214struct epoll_event event;215} ringbuf_custom_ctx;216217static void ringbuf_custom_measure(struct bench_res *res)218{219struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;220221res->hits = atomic_swap(&buf_hits.value, 0);222res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);223}224225static void ringbuf_custom_setup(void)226{227struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;228const size_t page_size = getpagesize();229struct bpf_link *link;230struct ringbuf_custom *r;231void *tmp;232int err;233234ctx->skel = ringbuf_setup_skeleton();235236ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC);237if (ctx->epoll_fd < 0) {238fprintf(stderr, "failed to create epoll fd: %d\n", -errno);239exit(1);240}241242r = &ctx->ringbuf;243r->map_fd = bpf_map__fd(ctx->skel->maps.ringbuf);244r->mask = args.ringbuf_sz - 1;245246/* Map writable consumer page */247tmp = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,248r->map_fd, 0);249if (tmp == MAP_FAILED) {250fprintf(stderr, "failed to mmap consumer page: %d\n", -errno);251exit(1);252}253r->consumer_pos = tmp;254255/* Map read-only producer page and data pages. */256tmp = mmap(NULL, page_size + 2 * args.ringbuf_sz, PROT_READ, MAP_SHARED,257r->map_fd, page_size);258if (tmp == MAP_FAILED) {259fprintf(stderr, "failed to mmap data pages: %d\n", -errno);260exit(1);261}262r->producer_pos = tmp;263r->data = tmp + page_size;264265ctx->event.events = EPOLLIN;266err = epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, r->map_fd, &ctx->event);267if (err < 0) {268fprintf(stderr, "failed to epoll add ringbuf: %d\n", -errno);269exit(1);270}271272link = bpf_program__attach(ctx->skel->progs.bench_ringbuf);273if (!link) {274fprintf(stderr, "failed to attach program\n");275exit(1);276}277}278279#define RINGBUF_BUSY_BIT (1 << 31)280#define RINGBUF_DISCARD_BIT (1 << 30)281#define RINGBUF_META_LEN 8282283static inline int roundup_len(__u32 len)284{285/* clear out top 2 bits */286len <<= 2;287len >>= 2;288/* add length prefix */289len += RINGBUF_META_LEN;290/* round up to 8 byte alignment */291return (len + 7) / 8 * 8;292}293294static void ringbuf_custom_process_ring(struct ringbuf_custom *r)295{296unsigned long cons_pos, prod_pos;297int *len_ptr, len;298bool got_new_data;299300cons_pos = smp_load_acquire(r->consumer_pos);301while (true) {302got_new_data = false;303prod_pos = smp_load_acquire(r->producer_pos);304while (cons_pos < prod_pos) {305len_ptr = r->data + (cons_pos & r->mask);306len = smp_load_acquire(len_ptr);307308/* sample not committed yet, bail out for now */309if (len & RINGBUF_BUSY_BIT)310return;311312got_new_data = true;313cons_pos += roundup_len(len);314315atomic_inc(&buf_hits.value);316}317if (got_new_data)318smp_store_release(r->consumer_pos, cons_pos);319else320break;321}322}323324static void *ringbuf_custom_consumer(void *input)325{326struct ringbuf_custom_ctx *ctx = &ringbuf_custom_ctx;327int cnt;328329do {330if (args.back2back)331bufs_trigger_batch();332cnt = epoll_wait(ctx->epoll_fd, &ctx->event, 1, -1);333if (cnt > 0)334ringbuf_custom_process_ring(&ctx->ringbuf);335} while (cnt >= 0);336fprintf(stderr, "ringbuf polling failed!\n");337return 0;338}339340/* PERFBUF-LIBBPF benchmark */341static struct perfbuf_libbpf_ctx {342struct perfbuf_bench *skel;343struct perf_buffer *perfbuf;344} perfbuf_libbpf_ctx;345346static void perfbuf_measure(struct bench_res *res)347{348struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;349350res->hits = atomic_swap(&buf_hits.value, 0);351res->drops = atomic_swap(&ctx->skel->bss->dropped, 0);352}353354static struct perfbuf_bench *perfbuf_setup_skeleton(void)355{356struct perfbuf_bench *skel;357358setup_libbpf();359360skel = perfbuf_bench__open();361if (!skel) {362fprintf(stderr, "failed to open skeleton\n");363exit(1);364}365366skel->rodata->batch_cnt = args.batch_cnt;367368if (perfbuf_bench__load(skel)) {369fprintf(stderr, "failed to load skeleton\n");370exit(1);371}372373return skel;374}375376static enum bpf_perf_event_ret377perfbuf_process_sample_raw(void *input_ctx, int cpu,378struct perf_event_header *e)379{380switch (e->type) {381case PERF_RECORD_SAMPLE:382atomic_inc(&buf_hits.value);383break;384case PERF_RECORD_LOST:385break;386default:387return LIBBPF_PERF_EVENT_ERROR;388}389return LIBBPF_PERF_EVENT_CONT;390}391392static void perfbuf_libbpf_setup(void)393{394struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;395struct perf_event_attr attr;396struct bpf_link *link;397398ctx->skel = perfbuf_setup_skeleton();399400memset(&attr, 0, sizeof(attr));401attr.config = PERF_COUNT_SW_BPF_OUTPUT;402attr.type = PERF_TYPE_SOFTWARE;403attr.sample_type = PERF_SAMPLE_RAW;404/* notify only every Nth sample */405if (args.sampled) {406attr.sample_period = args.sample_rate;407attr.wakeup_events = args.sample_rate;408} else {409attr.sample_period = 1;410attr.wakeup_events = 1;411}412413if (args.sample_rate > args.batch_cnt) {414fprintf(stderr, "sample rate %d is too high for given batch count %d\n",415args.sample_rate, args.batch_cnt);416exit(1);417}418419ctx->perfbuf = perf_buffer__new_raw(bpf_map__fd(ctx->skel->maps.perfbuf),420args.perfbuf_sz, &attr,421perfbuf_process_sample_raw, NULL, NULL);422if (!ctx->perfbuf) {423fprintf(stderr, "failed to create perfbuf\n");424exit(1);425}426427link = bpf_program__attach(ctx->skel->progs.bench_perfbuf);428if (!link) {429fprintf(stderr, "failed to attach program\n");430exit(1);431}432}433434static void *perfbuf_libbpf_consumer(void *input)435{436struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;437438while (perf_buffer__poll(ctx->perfbuf, -1) >= 0) {439if (args.back2back)440bufs_trigger_batch();441}442fprintf(stderr, "perfbuf polling failed!\n");443return NULL;444}445446/* PERFBUF-CUSTOM benchmark */447448/* copies of internal libbpf definitions */449struct perf_cpu_buf {450struct perf_buffer *pb;451void *base; /* mmap()'ed memory */452void *buf; /* for reconstructing segmented data */453size_t buf_size;454int fd;455int cpu;456int map_key;457};458459struct perf_buffer {460perf_buffer_event_fn event_cb;461perf_buffer_sample_fn sample_cb;462perf_buffer_lost_fn lost_cb;463void *ctx; /* passed into callbacks */464465size_t page_size;466size_t mmap_size;467struct perf_cpu_buf **cpu_bufs;468struct epoll_event *events;469int cpu_cnt; /* number of allocated CPU buffers */470int epoll_fd; /* perf event FD */471int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */472};473474static void *perfbuf_custom_consumer(void *input)475{476struct perfbuf_libbpf_ctx *ctx = &perfbuf_libbpf_ctx;477struct perf_buffer *pb = ctx->perfbuf;478struct perf_cpu_buf *cpu_buf;479struct perf_event_mmap_page *header;480size_t mmap_mask = pb->mmap_size - 1;481struct perf_event_header *ehdr;482__u64 data_head, data_tail;483size_t ehdr_size;484void *base;485int i, cnt;486487while (true) {488if (args.back2back)489bufs_trigger_batch();490cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, -1);491if (cnt <= 0) {492fprintf(stderr, "perf epoll failed: %d\n", -errno);493exit(1);494}495496for (i = 0; i < cnt; ++i) {497cpu_buf = pb->events[i].data.ptr;498header = cpu_buf->base;499base = ((void *)header) + pb->page_size;500501data_head = ring_buffer_read_head(header);502data_tail = header->data_tail;503while (data_head != data_tail) {504ehdr = base + (data_tail & mmap_mask);505ehdr_size = ehdr->size;506507if (ehdr->type == PERF_RECORD_SAMPLE)508atomic_inc(&buf_hits.value);509510data_tail += ehdr_size;511}512ring_buffer_write_tail(header, data_tail);513}514}515return NULL;516}517518const struct bench bench_rb_libbpf = {519.name = "rb-libbpf",520.argp = &bench_ringbufs_argp,521.validate = bufs_validate,522.setup = ringbuf_libbpf_setup,523.producer_thread = bufs_sample_producer,524.consumer_thread = ringbuf_libbpf_consumer,525.measure = ringbuf_libbpf_measure,526.report_progress = hits_drops_report_progress,527.report_final = hits_drops_report_final,528};529530const struct bench bench_rb_custom = {531.name = "rb-custom",532.argp = &bench_ringbufs_argp,533.validate = bufs_validate,534.setup = ringbuf_custom_setup,535.producer_thread = bufs_sample_producer,536.consumer_thread = ringbuf_custom_consumer,537.measure = ringbuf_custom_measure,538.report_progress = hits_drops_report_progress,539.report_final = hits_drops_report_final,540};541542const struct bench bench_pb_libbpf = {543.name = "pb-libbpf",544.argp = &bench_ringbufs_argp,545.validate = bufs_validate,546.setup = perfbuf_libbpf_setup,547.producer_thread = bufs_sample_producer,548.consumer_thread = perfbuf_libbpf_consumer,549.measure = perfbuf_measure,550.report_progress = hits_drops_report_progress,551.report_final = hits_drops_report_final,552};553554const struct bench bench_pb_custom = {555.name = "pb-custom",556.argp = &bench_ringbufs_argp,557.validate = bufs_validate,558.setup = perfbuf_libbpf_setup,559.producer_thread = bufs_sample_producer,560.consumer_thread = perfbuf_custom_consumer,561.measure = perfbuf_measure,562.report_progress = hits_drops_report_progress,563.report_final = hits_drops_report_final,564};565566567568