From: Kent Overstreet <kent.overstreet@linux.dev>
To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Jens Axboe <axboe@kernel.dk>,
Kent Overstreet <kent.overstreet@linux.dev>,
brauner@kernel.org, viro@zeniv.linux.org.uk,
Bernd Schubert <bernd.schubert@fastmail.fm>,
linux-mm@kvack.org, Josef Bacik <josef@toxicpanda.com>
Subject: [PATCH 5/5] ringbuffer: Userspace test helper
Date: Sun, 2 Jun 2024 20:33:02 -0400 [thread overview]
Message-ID: <20240603003306.2030491-6-kent.overstreet@linux.dev> (raw)
In-Reply-To: <20240603003306.2030491-1-kent.overstreet@linux.dev>
This adds a helper for testing the new ringbuffer syscall using
/dev/ringbuffer-test; it can do performance testing of both normal reads
and writes, and reads and writes via the ringbuffer interface.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
---
tools/ringbuffer/Makefile | 3 +
tools/ringbuffer/ringbuffer-test.c | 254 +++++++++++++++++++++++++++++
2 files changed, 257 insertions(+)
create mode 100644 tools/ringbuffer/Makefile
create mode 100644 tools/ringbuffer/ringbuffer-test.c
diff --git a/tools/ringbuffer/Makefile b/tools/ringbuffer/Makefile
new file mode 100644
index 000000000000..2fb27a19b43e
--- /dev/null
+++ b/tools/ringbuffer/Makefile
@@ -0,0 +1,3 @@
+CFLAGS=-g -O2 -Wall -Werror -I../../include
+
+all: ringbuffer-test
diff --git a/tools/ringbuffer/ringbuffer-test.c b/tools/ringbuffer/ringbuffer-test.c
new file mode 100644
index 000000000000..0fba99e40858
--- /dev/null
+++ b/tools/ringbuffer/ringbuffer-test.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#define READ 0
+#define WRITE 1
+
+#define min(a, b) (a < b ? a : b)
+
+#define __EXPORTED_HEADERS__
+#include <uapi/linux/ringbuffer_sys.h>
+
+#define BUF_NR 4
+
+typedef uint32_t u32;
+typedef unsigned long ulong;
+
+static inline struct ringbuffer_ptrs *ringbuffer(int fd, int rw, u32 size)
+{
+ ulong addr = 0;
+ int ret = syscall(463, fd, rw, size, &addr);
+ if (ret < 0)
+ errno = -ret;
+ return (void *) addr;
+}
+
+static inline int ringbuffer_wait(int fd, int rw)
+{
+ return syscall(464, fd, rw);
+}
+
+static inline int ringbuffer_wakeup(int fd, int rw)
+{
+ return syscall(465, fd, rw);
+}
+
+static ssize_t ringbuffer_read(int fd, struct ringbuffer_ptrs *rb,
+ void *buf, size_t len)
+{
+ void *rb_data = (void *) rb + rb->data_offset;
+
+ u32 head, orig_tail = rb->tail, tail = orig_tail;
+
+ while ((head = __atomic_load_n(&rb->head, __ATOMIC_ACQUIRE)) == tail)
+ ringbuffer_wait(fd, READ);
+
+ while (len && head != tail) {
+ u32 tail_masked = tail & rb->mask;
+ unsigned b = min(len,
+ min(head - tail,
+ rb->size - tail_masked));
+
+ memcpy(buf, rb_data + tail_masked, b);
+ buf += b;
+ len -= b;
+ tail += b;
+ }
+
+ __atomic_store_n(&rb->tail, tail, __ATOMIC_RELEASE);
+
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+
+ if (rb->head - orig_tail >= rb->size)
+ ringbuffer_wakeup(fd, READ);
+
+ return tail - orig_tail;
+}
+
+static ssize_t ringbuffer_write(int fd, struct ringbuffer_ptrs *rb,
+ void *buf, size_t len)
+{
+ void *rb_data = (void *) rb + rb->data_offset;
+
+ u32 orig_head = rb->head, head = orig_head, tail;
+
+ while (head - (tail = __atomic_load_n(&rb->tail, __ATOMIC_ACQUIRE)) >= rb->size)
+ ringbuffer_wait(fd, WRITE);
+
+ while (len && head - tail < rb->size) {
+ u32 head_masked = head & rb->mask;
+ unsigned b = min(len,
+ min(tail - head + rb->size,
+ rb->size - head_masked));
+
+ memcpy(rb_data + head_masked, buf, b);
+ buf += b;
+ len -= b;
+ head += b;
+ }
+
+ __atomic_store_n(&rb->head, head, __ATOMIC_RELEASE);
+
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+
+ if ((s32) (rb->tail - orig_head) >= 0)
+ ringbuffer_wakeup(fd, WRITE);
+
+ return head - orig_head;
+}
+
+static void usage(void)
+{
+ puts("ringbuffer-test - test ringbuffer syscall\n"
+ "Usage: ringbuffer-test [OPTION]...\n"
+ "\n"
+ "Options:\n"
+ " --type=(io|ringbuffer)\n"
+ " --rw=(read|write)\n"
+ " -h, --help Display this help and exit\n");
+}
+
+static inline ssize_t rb_test_read(int fd, struct ringbuffer_ptrs *rb,
+ void *buf, size_t len)
+{
+ return rb
+ ? ringbuffer_read(fd, rb, buf, len)
+ : read(fd, buf, len);
+}
+
+static inline ssize_t rb_test_write(int fd, struct ringbuffer_ptrs *rb,
+ void *buf, size_t len)
+{
+ return rb
+ ? ringbuffer_write(fd, rb, buf, len)
+ : write(fd, buf, len);
+}
+
+int main(int argc, char *argv[])
+{
+ const struct option longopts[] = {
+ { "type", required_argument, NULL, 't' },
+ { "rw", required_argument, NULL, 'r' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ int use_ringbuffer = false, rw = false;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "h", longopts, NULL)) != -1) {
+ switch (opt) {
+ case 't':
+ if (!strcmp(optarg, "io"))
+ use_ringbuffer = false;
+ else if (!strcmp(optarg, "ringbuffer") ||
+ !strcmp(optarg, "rb"))
+ use_ringbuffer = true;
+ else {
+ fprintf(stderr, "Invalid type %s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'r':
+ if (!strcmp(optarg, "read"))
+ rw = false;
+ else if (!strcmp(optarg, "write"))
+ rw = true;
+ else {
+ fprintf(stderr, "Invalid rw %s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case '?':
+ fprintf(stderr, "Invalid option %c\n", opt);
+ usage();
+ exit(EXIT_FAILURE);
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ int fd = open("/dev/ringbuffer-test", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening /dev/ringbuffer-test: %m\n");
+ exit(EXIT_FAILURE);
+ }
+
+ struct ringbuffer_ptrs *rb = NULL;
+ if (use_ringbuffer) {
+ rb = ringbuffer(fd, rw, 4096);
+ if (!rb) {
+ fprintf(stderr, "Error from sys_ringbuffer: %m\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "got ringbuffer %p\n", rb);
+ }
+
+ printf("Starting test with ringbuffer=%u, rw=%u\n", use_ringbuffer, rw);
+ static const char * const rw_str[] = { "read", "wrote" };
+
+ struct timeval start;
+ gettimeofday(&start, NULL);
+ size_t nr_prints = 1;
+
+ u32 buf[BUF_NR];
+ u32 idx = 0;
+
+ while (true) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ struct timeval next_print = start;
+ next_print.tv_sec += nr_prints;
+
+ if (timercmp(&now, &next_print, >)) {
+ printf("%s %u u32s, %lu mb/sec\n", rw_str[rw], idx,
+ (idx * sizeof(u32) / (now.tv_sec - start.tv_sec)) / (1UL << 20));
+ nr_prints++;
+ if (nr_prints > 20)
+ break;
+ }
+
+ if (rw == READ) {
+ int r = rb_test_read(fd, rb, buf, sizeof(buf));
+ if (r <= 0) {
+ fprintf(stderr, "Read returned %i (%m)\n", r);
+ exit(EXIT_FAILURE);
+ }
+
+ unsigned nr = r / sizeof(u32);
+ for (unsigned i = 0; i < nr; i++) {
+ if (buf[i] != idx + i) {
+ fprintf(stderr, "Read returned wrong data at idx %u: got %u instead\n",
+ idx + i, buf[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ idx += nr;
+ } else {
+ for (unsigned i = 0; i < BUF_NR; i++)
+ buf[i] = idx + i;
+
+ int r = rb_test_write(fd, rb, buf, sizeof(buf));
+ if (r <= 0) {
+ fprintf(stderr, "Write returned %i (%m)\n", r);
+ exit(EXIT_FAILURE);
+ }
+
+ unsigned nr = r / sizeof(u32);
+ idx += nr;
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
--
2.45.1
next prev parent reply other threads:[~2024-06-03 0:33 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-03 0:32 [PATCH 0/5] sys_ringbuffer Kent Overstreet
2024-06-03 0:32 ` [PATCH 1/5] darray: lift from bcachefs Kent Overstreet
2024-06-03 5:00 ` kernel test robot
2024-06-03 0:32 ` [PATCH 2/5] darray: Fix darray_for_each_reverse() when darray is empty Kent Overstreet
2024-06-03 0:33 ` [PATCH 3/5] fs: sys_ringbuffer Kent Overstreet
2024-06-03 4:16 ` kernel test robot
2024-06-03 4:38 ` kernel test robot
2024-06-23 22:13 ` Thomas Gleixner
2024-06-23 22:21 ` Kent Overstreet
2024-06-23 23:16 ` Thomas Gleixner
2024-06-24 0:27 ` Kent Overstreet
2024-06-03 0:33 ` [PATCH 4/5] ringbuffer: Test device Kent Overstreet
2024-06-03 0:33 ` Kent Overstreet [this message]
2024-06-07 1:49 ` [PATCH 0/5] sys_ringbuffer Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240603003306.2030491-6-kent.overstreet@linux.dev \
--to=kent.overstreet@linux.dev \
--cc=axboe@kernel.dk \
--cc=bernd.schubert@fastmail.fm \
--cc=brauner@kernel.org \
--cc=josef@toxicpanda.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.