Path: blob/main/tools/test/buf_ring/buf_ring_test.c
39563 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2024 Arm Ltd.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*26*/2728#include <sys/types.h>29#include <sys/errno.h>3031#include <machine/atomic.h>32#include <machine/cpu.h>33#include <machine/cpufunc.h>3435#include <assert.h>36#include <err.h>37#include <getopt.h>38#include <pthread.h>39#include <stdarg.h>40#include <stdatomic.h>41#include <stdbool.h>42#include <stdio.h>43#include <stdlib.h>44#include <string.h>45#include <unistd.h>4647/* #define DEBUG_BUFRING */4849#ifdef DEBUG_BUFRING50static void51panic(const char *fmt, ...)52{53va_list ap;5455va_start(ap, fmt);56vfprintf(stderr, fmt, ap);57va_end(ap);58fprintf(stderr, "\n");59exit(1);60}61#endif6263static void64critical_enter(void)65{66}6768static void69critical_exit(void)70{71}7273#include "../../../sys/sys/buf_ring.h"7475#define PROD_ITERATIONS 1000000007677static enum {78CT_UNKNOWN,79CT_MC,80CT_MC_MT,81CT_SC,82CT_PEEK,83CT_PEEK_CLEAR,84} cons_type = CT_UNKNOWN;8586static unsigned int prod_count;8788static struct buf_ring *br;89static _Atomic bool prod_done = false;90static _Atomic int prod_done_count = 0;91static _Atomic size_t total_cons_count = 0;9293static uint64_t *mt_seen;9495static void *96producer(void *arg)97{98int id, rv;99100id = (int)(uintptr_t)arg;101102for (size_t i = 0; i < PROD_ITERATIONS;) {103rv = buf_ring_enqueue(br, (void *)(i * prod_count + 1 + id));104if (rv == 0) {105i++;106}107}108if ((unsigned int)atomic_fetch_add(&prod_done_count, 1) ==109(prod_count - 1))110atomic_store(&prod_done, true);111112return (NULL);113}114115static void *116consumer(void *arg)117{118void *val;119size_t *max_vals;120size_t consume_count, curr;121int id;122123(void)arg;124125max_vals = calloc(prod_count, sizeof(*max_vals));126assert(max_vals != NULL);127128/* Set the initial value to be the expected value */129for (unsigned int i = 1; i < prod_count; i++) {130max_vals[i] = (int)(i - prod_count);131}132133consume_count = 0;134while (!atomic_load(&prod_done) || !buf_ring_empty(br)) {135switch(cons_type) {136case CT_MC:137case CT_MC_MT:138val = buf_ring_dequeue_mc(br);139break;140case CT_SC:141val = buf_ring_dequeue_sc(br);142break;143case CT_PEEK:144val = buf_ring_peek(br);145if (val != NULL)146buf_ring_advance_sc(br);147break;148case CT_PEEK_CLEAR:149val = buf_ring_peek_clear_sc(br);150if (val != NULL)151buf_ring_advance_sc(br);152break;153case CT_UNKNOWN:154__unreachable();155}156if (val != NULL) {157consume_count++;158curr = (size_t)(uintptr_t)val;159id = curr % prod_count;160if (cons_type != CT_MC_MT) {161if (curr != max_vals[id] + prod_count)162printf("Incorrect val: %zu Expect: %zu "163"Difference: %zd\n", curr,164max_vals[id] + prod_count,165curr - max_vals[id] - prod_count);166} else {167size_t idx, bit;168169idx = ((size_t)(uintptr_t)val - 1) /170(sizeof(*mt_seen) * NBBY);171bit = ((size_t)(uintptr_t)val - 1) %172(sizeof(*mt_seen) * NBBY);173174if (atomic_testandset_64(&mt_seen[idx], bit))175printf("Repeat ID: %zx\n", (size_t)(uintptr_t)val);176}177178max_vals[id] = (uintptr_t)val;179}180}181182atomic_fetch_add(&total_cons_count, consume_count);183184for (unsigned int i = 0; i < prod_count; i++)185printf("max[%d] = %zu\n", i, max_vals[i]);186187return (NULL);188}189190static struct option longopts[] = {191{ "buf-size", required_argument, NULL, 'b' },192{ "cons-type", required_argument, NULL, 'c' },193{ "prod-count", required_argument, NULL, 'p' },194{ "help", no_argument, NULL, 'h' },195{ NULL, 0, NULL, 0 },196};197198static void199usage(void)200{201errx(1, "test --cons-type=<mc|mc-mt|sc|peek|peek-clear> --prod-count=<prod thread count> [--buf-size=<buf_ring size>]");202}203204static uint32_t205next_power_of_2(uint32_t x)206{207x--;208x |= x >> 1;209x |= x >> 2;210x |= x >> 4;211x |= x >> 8;212x |= x >> 16;213x++;214return (x);215}216217int218main(int argc, char *argv[])219{220pthread_t *prod;221pthread_t cons[2];222const char *errstr;223uint32_t size;224int ch, ret;225226size = 0;227while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1) {228switch(ch) {229case 'b':230errstr = NULL;231size = strtonum(optarg, 1, UINT_MAX, &errstr);232if (errstr != NULL) {233errx(1, "--bufsize=%s: %s", optarg, errstr);234}235if (!powerof2(size)) {236errx(1, "--bufsize needs a power of 2 size");237}238break;239case 'c':240if (strcmp(optarg, "mc") == 0) {241cons_type = CT_MC;242} else if (strcmp(optarg, "mc-mt") == 0) {243cons_type = CT_MC_MT;244} else if (strcmp(optarg, "sc") == 0) {245cons_type = CT_SC;246} else if (strcmp(optarg, "peek") == 0) {247cons_type = CT_PEEK;248} else if (strcmp(optarg, "peek-clear") == 0) {249cons_type = CT_PEEK_CLEAR;250} else {251errx(1, "Unknown --cons-type: %s", optarg);252}253break;254case 'p':255errstr = NULL;256prod_count = strtonum(optarg, 1, UINT_MAX, &errstr);257if (errstr != NULL) {258errx(1, "--prod-count=%s: %s", optarg, errstr);259}260break;261case 'h':262default:263usage();264}265}266argc -= optind;267argv += optind;268269if (cons_type == CT_UNKNOWN)270errx(1, "No cons-type set");271272if (prod_count == 0)273errx(1, "prod-count is not set");274275if (size == 0)276size = next_power_of_2(prod_count);277278if (cons_type == CT_MC_MT) {279size_t entries;280281entries = (size_t)PROD_ITERATIONS * prod_count;282entries = roundup2(entries, sizeof(*mt_seen));283mt_seen = calloc(entries / (sizeof(*mt_seen) * NBBY),284sizeof(*mt_seen));285}286287br = buf_ring_alloc(size);288289ret = pthread_create(&cons[0], NULL, consumer, NULL);290assert(ret == 0);291if (cons_type == CT_MC_MT) {292ret = pthread_create(&cons[1], NULL, consumer, NULL);293assert(ret == 0);294}295296prod = calloc(prod_count, sizeof(*prod));297assert(prod != NULL);298for (unsigned i = 0; i < prod_count; i++) {299ret = pthread_create(&prod[i], NULL, producer,300(void *)(uintptr_t)i);301assert(ret == 0);302}303304for (unsigned int i = 0; i < prod_count; i++) {305ret = pthread_join(prod[i], NULL);306assert(ret == 0);307}308ret = pthread_join(cons[0], NULL);309assert(ret == 0);310if (cons_type == CT_MC_MT) {311ret = pthread_join(cons[1], NULL);312assert(ret == 0);313}314315printf("Expected: %zu\n", (size_t)PROD_ITERATIONS * prod_count);316printf("Received: %zu\n", total_cons_count);317318buf_ring_free(br);319320return (0);321}322323324