All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andy Lutomirski <luto@amacapital.net>,
	Jiri Kosina <jikos@kernel.org>, Greg KH <greg@kroah.com>,
	Hannes Reinecke <hare@suse.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Arnd Bergmann <arnd@arndb.de>, Tom Gundersen <teg@jklm.no>,
	David Herrmann <dh.herrmann@gmail.com>,
	Josh Triplett <josh@joshtriplett.org>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Andrew Morton <akpm@linux-foundation.org>
Subject: [RFC v1 14/14] bus1: basic user-space kselftests
Date: Wed, 26 Oct 2016 21:18:10 +0200	[thread overview]
Message-ID: <20161026191810.12275-15-dh.herrmann@gmail.com> (raw)
In-Reply-To: <20161026191810.12275-1-dh.herrmann@gmail.com>

From: Tom Gundersen <teg@jklm.no>

This adds kselftests integration and provides some basic API tests for
bus1.

Signed-off-by: Tom Gundersen <teg@jklm.no>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 tools/testing/selftests/bus1/.gitignore   |   2 +
 tools/testing/selftests/bus1/Makefile     |  19 ++
 tools/testing/selftests/bus1/bus1-ioctl.h | 111 +++++++
 tools/testing/selftests/bus1/test-api.c   | 532 ++++++++++++++++++++++++++++++
 tools/testing/selftests/bus1/test-io.c    | 198 +++++++++++
 tools/testing/selftests/bus1/test.h       | 114 +++++++
 6 files changed, 976 insertions(+)
 create mode 100644 tools/testing/selftests/bus1/.gitignore
 create mode 100644 tools/testing/selftests/bus1/Makefile
 create mode 100644 tools/testing/selftests/bus1/bus1-ioctl.h
 create mode 100644 tools/testing/selftests/bus1/test-api.c
 create mode 100644 tools/testing/selftests/bus1/test-io.c
 create mode 100644 tools/testing/selftests/bus1/test.h

diff --git a/tools/testing/selftests/bus1/.gitignore b/tools/testing/selftests/bus1/.gitignore
new file mode 100644
index 0000000..76ecb9c
--- /dev/null
+++ b/tools/testing/selftests/bus1/.gitignore
@@ -0,0 +1,2 @@
+test-api
+test-io
diff --git a/tools/testing/selftests/bus1/Makefile b/tools/testing/selftests/bus1/Makefile
new file mode 100644
index 0000000..cbcf689
--- /dev/null
+++ b/tools/testing/selftests/bus1/Makefile
@@ -0,0 +1,19 @@
+# Makefile for bus1 selftests
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS += -D_FILE_OFFSET_BITS=64 -Wall -g -O2
+CFLAGS += -I../../../../include/uapi/
+CFLAGS += -I../../../../include/
+CFLAGS += -I../../../../usr/include/
+
+TEST_PROGS := test-api test-io
+
+all: $(TEST_PROGS)
+
+%: %.c bus1-ioctl.h test.h ../../../../usr/include/linux/bus1.h
+	$(CC) $(CFLAGS) $< -o $@
+
+include ../lib.mk
+
+clean:
+	$(RM) $(TEST_PROGS)
diff --git a/tools/testing/selftests/bus1/bus1-ioctl.h b/tools/testing/selftests/bus1/bus1-ioctl.h
new file mode 100644
index 0000000..552bd5d
--- /dev/null
+++ b/tools/testing/selftests/bus1/bus1-ioctl.h
@@ -0,0 +1,111 @@
+#pragma once
+
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <linux/bus1.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline int
+bus1_ioctl(int fd, unsigned int cmd, void *arg)
+{
+	return (ioctl(fd, cmd, arg) >= 0) ? 0: -errno;
+}
+
+static inline int
+bus1_ioctl_peer_disconnect(int fd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_PEER_DISCONNECT) == sizeof(uint64_t),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_PEER_DISCONNECT, NULL);
+}
+
+static inline int
+bus1_ioctl_peer_query(int fd, struct bus1_cmd_peer_reset *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_PEER_QUERY) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_PEER_QUERY, cmd);
+}
+
+static inline int
+bus1_ioctl_peer_reset(int fd, struct bus1_cmd_peer_reset *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_PEER_RESET) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_PEER_RESET, cmd);
+}
+
+static inline int
+bus1_ioctl_handle_release(int fd, uint64_t *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_HANDLE_RELEASE) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_HANDLE_RELEASE, cmd);
+}
+
+static inline int
+bus1_ioctl_handle_transfer(int fd, struct bus1_cmd_handle_transfer *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_HANDLE_TRANSFER) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_HANDLE_TRANSFER, cmd);
+}
+
+static inline int
+bus1_ioctl_nodes_destroy(int fd, struct bus1_cmd_nodes_destroy *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_NODES_DESTROY) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_NODES_DESTROY, cmd);
+}
+
+static inline int
+bus1_ioctl_slice_release(int fd, uint64_t *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_SLICE_RELEASE) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_SLICE_RELEASE, cmd);
+}
+
+static inline int
+bus1_ioctl_send(int fd, struct bus1_cmd_send *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_SEND) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_SEND, cmd);
+}
+
+static inline int
+bus1_ioctl_recv(int fd, struct bus1_cmd_recv *cmd)
+{
+	static_assert(_IOC_SIZE(BUS1_CMD_RECV) == sizeof(*cmd),
+		      "ioctl is called with invalid argument size");
+
+	return bus1_ioctl(fd, BUS1_CMD_RECV, cmd);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tools/testing/selftests/bus1/test-api.c b/tools/testing/selftests/bus1/test-api.c
new file mode 100644
index 0000000..a289197
--- /dev/null
+++ b/tools/testing/selftests/bus1/test-api.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include "test.h"
+
+/* make sure /dev/busX exists, is a cdev and accessible */
+static void test_api_cdev(void)
+{
+	const uint8_t *map;
+	struct stat st;
+	size_t n_map;
+	int r, fd;
+
+	r = access(test_path, F_OK);
+	assert(r >= 0);
+
+	r = stat(test_path, &st);
+	assert(r >= 0);
+	assert((st.st_mode & S_IFMT) == S_IFCHR);
+
+	r = open(test_path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+	assert(r >= 0);
+	close(r);
+
+	fd = test_open(&map, &n_map);
+	test_close(fd, map, n_map);
+}
+
+/* make sure basic connect works */
+static void test_api_connect(void)
+{
+	struct bus1_cmd_peer_reset cmd_reset = {
+		.flags			= 0,
+		.peer_flags		= -1,
+		.max_slices		= -1,
+		.max_handles		= -1,
+		.max_inflight_bytes	= -1,
+		.max_inflight_fds	= -1,
+	};
+	const uint8_t *map1;
+	size_t n_map1;
+	int r, fd1;
+
+	/* create @fd1 */
+
+	fd1 = test_open(&map1, &n_map1);
+
+	/* test empty RESET */
+
+	r = bus1_ioctl_peer_reset(fd1, &cmd_reset);
+	assert(r >= 0);
+
+	/* test DISCONNECT and verify ESHUTDOWN afterwards */
+
+	r = bus1_ioctl_peer_disconnect(fd1);
+	assert(r >= 0);
+
+	r = bus1_ioctl_peer_disconnect(fd1);
+	assert(r < 0);
+	assert(r == -ESHUTDOWN);
+
+	r = bus1_ioctl_peer_reset(fd1, &cmd_reset);
+	assert(r < 0);
+	assert(r == -ESHUTDOWN);
+
+	/* cleanup */
+
+	test_close(fd1, map1, n_map1);
+}
+
+/* make sure basic transfer works */
+static void test_api_transfer(void)
+{
+	struct bus1_cmd_handle_transfer cmd_transfer;
+	const uint8_t *map1, *map2;
+	size_t n_map1, n_map2;
+	int r, fd1, fd2;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+	fd2 = test_open(&map2, &n_map2);
+
+	/* import a handle from @fd1 into @fd2 */
+
+	cmd_transfer = (struct bus1_cmd_handle_transfer){
+		.flags			= 0,
+		.src_handle		= 0x100,
+		.dst_fd			= fd2,
+		.dst_handle		= BUS1_HANDLE_INVALID,
+	};
+	r = bus1_ioctl_handle_transfer(fd1, &cmd_transfer);
+	assert(r >= 0);
+	assert(cmd_transfer.dst_handle != BUS1_HANDLE_INVALID);
+	assert(cmd_transfer.dst_handle & BUS1_HANDLE_FLAG_MANAGED);
+	assert(cmd_transfer.dst_handle & BUS1_HANDLE_FLAG_REMOTE);
+
+	/* cleanup */
+
+	test_close(fd2, map2, n_map2);
+	test_close(fd1, map1, n_map1);
+}
+
+/* test release notification */
+static void test_api_notify_release(void)
+{
+	struct bus1_cmd_handle_transfer cmd_transfer;
+	struct bus1_cmd_recv cmd_recv;
+	const uint8_t *map1;
+	uint64_t id = 0x100;
+	size_t n_map1;
+	int r, fd1;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+
+	/* import a handle from @fd1 into @fd2 */
+
+	cmd_transfer = (struct bus1_cmd_handle_transfer){
+		.flags			= 0,
+		.src_handle		= id,
+		.dst_fd			= -1,
+		.dst_handle		= BUS1_HANDLE_INVALID,
+	};
+	r = bus1_ioctl_handle_transfer(fd1, &cmd_transfer);
+	assert(r >= 0);
+	assert(cmd_transfer.dst_handle == id);
+
+	/* no message can be queued */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* release handle to trigger release notification */
+
+	r = bus1_ioctl_handle_release(fd1, &id);
+	assert(r == 0);
+
+	/* dequeue release notification */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_NODE_RELEASE);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == id);
+
+	/* no more messages */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/*
+	 * Trigger the same thing again.
+	 */
+
+	cmd_transfer = (struct bus1_cmd_handle_transfer){
+		.flags			= 0,
+		.src_handle		= id,
+		.dst_fd			= -1,
+		.dst_handle		= BUS1_HANDLE_INVALID,
+	};
+	r = bus1_ioctl_handle_transfer(fd1, &cmd_transfer);
+	assert(r >= 0);
+	assert(cmd_transfer.dst_handle == id);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	r = bus1_ioctl_handle_release(fd1, &id);
+	assert(r == 0);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_NODE_RELEASE);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == id);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* cleanup */
+
+	test_close(fd1, map1, n_map1);
+}
+
+/* test destroy notification */
+static void test_api_notify_destroy(void)
+{
+	struct bus1_cmd_handle_transfer cmd_transfer;
+	struct bus1_cmd_nodes_destroy cmd_destroy;
+	struct bus1_cmd_recv cmd_recv;
+	uint64_t node = 0x100, handle;
+	const uint8_t *map1, *map2;
+	size_t n_map1, n_map2;
+	int r, fd1, fd2;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+	fd2 = test_open(&map2, &n_map2);
+
+	/* import a handle from @fd1 into @fd2 */
+
+	cmd_transfer = (struct bus1_cmd_handle_transfer){
+		.flags			= 0,
+		.src_handle		= node,
+		.dst_fd			= fd2,
+		.dst_handle		= BUS1_HANDLE_INVALID,
+	};
+	r = bus1_ioctl_handle_transfer(fd1, &cmd_transfer);
+	assert(r >= 0);
+	handle = cmd_transfer.dst_handle;
+
+	/* both queues must be empty */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map2,
+	};
+	r = bus1_ioctl_recv(fd2, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* destroy node and trigger destruction notification */
+
+	cmd_destroy = (struct bus1_cmd_nodes_destroy){
+		.flags			= 0,
+		.ptr_nodes		= (unsigned long)&node,
+		.n_nodes		= 1,
+	};
+	r = bus1_ioctl_nodes_destroy(fd1, &cmd_destroy);
+	assert(r >= 0);
+
+	/* dequeue destruction notification */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_NODE_DESTROY);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == node);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd2, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_NODE_DESTROY);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == handle);
+
+	/* cleanup */
+
+	test_close(fd2, map2, n_map2);
+	test_close(fd1, map1, n_map1);
+}
+
+/* make sure basic unicasts works */
+static void test_api_unicast(void)
+{
+	struct bus1_cmd_send cmd_send;
+	struct bus1_cmd_recv cmd_recv;
+	const uint8_t *map1;
+	uint64_t id = 0x100;
+	size_t n_map1;
+	int r, fd1;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+
+	/* send empty message */
+
+	cmd_send = (struct bus1_cmd_send){
+		.flags			= 0,
+		.ptr_destinations	= (unsigned long)&id,
+		.ptr_errors		= 0,
+		.n_destinations		= 1,
+		.ptr_vecs		= 0,
+		.n_vecs			= 0,
+		.ptr_handles		= 0,
+		.n_handles		= 0,
+		.ptr_fds		= 0,
+		.n_fds			= 0,
+	};
+	r = bus1_ioctl_send(fd1, &cmd_send);
+	assert(r >= 0);
+
+	/* retrieve empty message */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_DATA);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == id);
+
+	/* queue must be empty now */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* cleanup */
+
+	test_close(fd1, map1, n_map1);
+}
+
+/* make sure basic multicasts works */
+static void test_api_multicast(void)
+{
+	struct bus1_cmd_send cmd_send;
+	struct bus1_cmd_recv cmd_recv;
+	uint64_t ids[] = { 0x100, 0x200 };
+	uint64_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+	struct iovec vec = { data, sizeof(data) };
+	const uint8_t *map1;
+	size_t n_map1;
+	int r, fd1;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+
+	/* send multicast */
+
+	cmd_send = (struct bus1_cmd_send){
+		.flags			= 0,
+		.ptr_destinations	= (unsigned long)ids,
+		.ptr_errors		= 0,
+		.n_destinations		= sizeof(ids) / sizeof(*ids),
+		.ptr_vecs		= (unsigned long)&vec,
+		.n_vecs			= 1,
+		.ptr_handles		= 0,
+		.n_handles		= 0,
+		.ptr_fds		= 0,
+		.n_fds			= 0,
+	};
+	r = bus1_ioctl_send(fd1, &cmd_send);
+	assert(r >= 0);
+
+	/* retrieve messages */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_DATA);
+	assert(cmd_recv.msg.flags == BUS1_MSG_FLAG_CONTINUE);
+	assert(cmd_recv.msg.destination == ids[0] ||
+	       cmd_recv.msg.destination == ids[1]);
+	assert(cmd_recv.msg.n_bytes == sizeof(data));
+	assert(!memcmp(map1 + cmd_recv.msg.offset, data, sizeof(data)));
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_DATA);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == ids[0] ||
+	       cmd_recv.msg.destination == ids[1]);
+	assert(cmd_recv.msg.n_bytes == sizeof(data));
+	assert(!memcmp(map1 + cmd_recv.msg.offset, data, sizeof(data)));
+
+	/* queue must be empty now */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* cleanup */
+
+	test_close(fd1, map1, n_map1);
+}
+
+/* make sure basic payload-handles work */
+static void test_api_handle(void)
+{
+	struct bus1_cmd_send cmd_send;
+	struct bus1_cmd_recv cmd_recv;
+	uint64_t id = 0x100;
+	const uint8_t *map1;
+	size_t n_map1;
+	int r, fd1;
+
+	/* setup */
+
+	fd1 = test_open(&map1, &n_map1);
+
+	/* send message */
+
+	cmd_send = (struct bus1_cmd_send){
+		.flags			= 0,
+		.ptr_destinations	= (unsigned long)&id,
+		.ptr_errors		= 0,
+		.n_destinations		= 1,
+		.ptr_vecs		= 0,
+		.n_vecs			= 0,
+		.ptr_handles		= (unsigned long)&id,
+		.n_handles		= 1,
+		.ptr_fds		= 0,
+		.n_fds			= 0,
+	};
+	r = bus1_ioctl_send(fd1, &cmd_send);
+	assert(r >= 0);
+
+	/* retrieve messages */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_DATA);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == id);
+	assert(cmd_recv.msg.n_handles == 1);
+
+	/* queue must be empty now */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* releasing one reference must trigger a release notification */
+
+	r = bus1_ioctl_handle_release(fd1, &id);
+	assert(r >= 0);
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r >= 0);
+	assert(cmd_recv.msg.type == BUS1_MSG_NODE_RELEASE);
+	assert(cmd_recv.msg.flags == 0);
+	assert(cmd_recv.msg.destination == id);
+
+	/* queue must be empty again */
+
+	cmd_recv = (struct bus1_cmd_recv){
+		.flags = 0,
+		.max_offset = n_map1,
+	};
+	r = bus1_ioctl_recv(fd1, &cmd_recv);
+	assert(r == -EAGAIN);
+
+	/* cleanup */
+
+	test_close(fd1, map1, n_map1);
+}
+
+int main(int argc, char **argv)
+{
+	int r;
+
+	r = test_parse_argv(argc, argv);
+	if (r > 0) {
+		test_api_cdev();
+		test_api_connect();
+		test_api_transfer();
+		test_api_notify_release();
+		test_api_notify_destroy();
+		test_api_unicast();
+		test_api_multicast();
+		test_api_handle();
+	}
+
+	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tools/testing/selftests/bus1/test-io.c b/tools/testing/selftests/bus1/test-io.c
new file mode 100644
index 0000000..6cb48e7
--- /dev/null
+++ b/tools/testing/selftests/bus1/test-io.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+#include "test.h"
+
+#define MAX_DESTINATIONS (256)
+
+static inline uint64_t nsec_from_clock(clockid_t clock)
+{
+	struct timespec ts;
+	int r;
+
+	r = clock_gettime(clock, &ts);
+	assert(r >= 0);
+	return ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
+}
+
+static void test_one_uds(int uds[2], void *payload, size_t n_bytes)
+{
+	int r;
+
+	/* send */
+	r = write(uds[0], payload, n_bytes);
+	assert(r == n_bytes);
+
+	/* receive */
+	r = recv(uds[1], payload, n_bytes, 0);
+	assert(r == n_bytes);
+}
+
+static uint64_t test_iterate_uds(unsigned int iterations, size_t n_bytes)
+{
+	int uds[2];
+	char payload[n_bytes];
+	unsigned int i;
+	uint64_t time_start, time_end;
+	int r;
+
+	/* create socket pair */
+	r = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, uds);
+	assert(r >= 0);
+
+	/* caches */
+	test_one_uds(uds, payload, n_bytes);
+
+	time_start = nsec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+	for (i = 0; i < iterations; i++)
+		test_one_uds(uds, payload, n_bytes);
+	time_end = nsec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+
+	/* cleanup */
+	close(uds[0]);
+	close(uds[1]);
+
+	return (time_end - time_start) / iterations;
+}
+
+static void test_one(int fd1,
+		     int *fds,
+		     uint64_t *handles,
+		     unsigned int n_destinations,
+		     char *payload,
+		     size_t n_bytes)
+{
+	struct bus1_cmd_send cmd_send;
+	struct bus1_cmd_recv cmd_recv;
+	struct iovec vec = { payload, n_bytes };
+	size_t i;
+	int r;
+
+	cmd_send = (struct bus1_cmd_send){
+		.flags			= 0,
+		.ptr_destinations	= (unsigned long)handles,
+		.ptr_errors		= 0,
+		.n_destinations		= n_destinations,
+		.ptr_vecs		= (unsigned long)&vec,
+		.n_vecs			= 1,
+		.ptr_handles		= 0,
+		.n_handles		= 0,
+		.ptr_fds		= 0,
+		.n_fds			= 0,
+	};
+	r = bus1_ioctl_send(fd1, &cmd_send);
+	assert(r >= 0);
+
+	for (i = 0; i < n_destinations; ++i) {
+		cmd_recv = (struct bus1_cmd_recv){
+			.flags = 0,
+			.max_offset = -1,
+		};
+		r = bus1_ioctl_recv(fds[i], &cmd_recv);
+		assert(r >= 0);
+		assert(cmd_recv.msg.type == BUS1_MSG_DATA);
+		assert(cmd_recv.msg.n_bytes == n_bytes);
+
+		r = bus1_ioctl_slice_release(fds[i],
+					     (uint64_t *)&cmd_recv.msg.offset);
+		assert(r >= 0);
+	}
+}
+
+static uint64_t test_iterate(unsigned int iterations,
+			     unsigned int n_destinations,
+			     size_t n_bytes)
+{
+	struct bus1_cmd_handle_transfer cmd_transfer;
+	const uint8_t *maps[MAX_DESTINATIONS + 1];
+	size_t n_maps[MAX_DESTINATIONS + 1];
+	uint64_t handles[MAX_DESTINATIONS + 1];
+	int r, fds[MAX_DESTINATIONS + 1];
+	uint64_t time_start, time_end;
+	char payload[n_bytes];
+	size_t i;
+
+	assert(n_destinations <= MAX_DESTINATIONS);
+
+	/* setup */
+	fds[0] = test_open(&maps[0], &n_maps[0]);
+
+	for (i = 1; i < sizeof(fds) / sizeof(*fds); ++i) {
+		fds[i] = test_open(&maps[i], &n_maps[i]);
+
+		cmd_transfer = (struct bus1_cmd_handle_transfer){
+			.flags			= 0,
+			.src_handle		= 0x100,
+			.dst_fd			= fds[0],
+			.dst_handle		= BUS1_HANDLE_INVALID,
+		};
+		r = bus1_ioctl_handle_transfer(fds[i], &cmd_transfer);
+		assert(r >= 0);
+		handles[i] = cmd_transfer.dst_handle;
+	}
+
+	/* caches */
+	test_one(fds[0], fds + 1, handles + 1, n_destinations, payload,
+		 n_bytes);
+
+	time_start = nsec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+	for (i = 0; i < iterations; i++)
+		test_one(fds[0], fds + 1, handles + 1, n_destinations, payload,
+			 n_bytes);
+	time_end = nsec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+
+	for (i = 0; i < sizeof(fds) / sizeof(*fds); ++i)
+		test_close(fds[i], maps[i], n_maps[i]);
+
+	return (time_end - time_start) / iterations;
+}
+
+static void test_io(void)
+{
+	unsigned long base;
+	unsigned int i;
+
+	fprintf(stderr, "UDS took %lu ns without payload\n",
+		test_iterate_uds(100000, 0));
+	fprintf(stderr, "UDS took %lu ns\n",
+		test_iterate_uds(100000, 1024));
+
+	base = test_iterate(1000000, 0, 1024);
+
+	fprintf(stderr, "it took %lu ns for no destinations\n", base);
+	fprintf(stderr,
+		"it took %lu ns + %lu ns for one destination without payload\n",
+		base, test_iterate(100000, 1, 0) - base);
+	fprintf(stderr, "it took %lu ns + %lu ns for one destination\n", base,
+		test_iterate(100000, 1, 1024) - base);
+
+	for (i = 1; i < 9; ++i) {
+		unsigned int dests = 1UL << i;
+
+		fprintf(stderr, "it took %lu ns + %lu ns per destination for %u destinations\n",
+			base, (test_iterate(100000 >> i, dests, 1024) - base) / dests, dests);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int r;
+
+	r = test_parse_argv(argc, argv);
+	if (r > 0) {
+		test_io();
+	}
+
+	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tools/testing/selftests/bus1/test.h b/tools/testing/selftests/bus1/test.h
new file mode 100644
index 0000000..fee815e
--- /dev/null
+++ b/tools/testing/selftests/bus1/test.h
@@ -0,0 +1,114 @@
+#ifndef __TEST_H
+#define __TEST_H
+
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* include standard environment for all tests */
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/bus1.h>
+#include <linux/sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "bus1-ioctl.h"
+
+static char *test_path;
+static char *test_arg_module = "bus1";
+
+#define c_align_to(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1))
+
+static inline int test_parse_argv(int argc, char **argv)
+{
+	enum {
+		ARG_MODULE = 0x100,
+	};
+	static const struct option options[] = {
+		{ "help",	no_argument,		NULL, 'h' },
+		{ "module",	required_argument,	NULL, ARG_MODULE },
+		{}
+	};
+	char *t;
+	int c;
+
+	t = getenv("BUS1EXT");
+	if (t) {
+		test_arg_module = malloc(strlen(t) + 4);
+		assert(test_arg_module);
+		strcpy(test_arg_module, "bus");
+		strcpy(test_arg_module + 3, t);
+	}
+
+	while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+		switch (c) {
+		case 'h':
+			fprintf(stderr,
+				"Usage: %s [OPTIONS...] ...\n\n"
+				"Run bus1 test.\n\n"
+				"\t-h, --help         Print this help\n"
+				"\t    --module=bus1  Module to use\n"
+				, program_invocation_short_name);
+
+			return 0;
+
+		case ARG_MODULE:
+			test_arg_module = optarg;
+			break;
+
+		case '?':
+			/* fallthrough */
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* store cdev-path for tests to access ("/dev/<module>") */
+	free(test_path);
+	test_path = malloc(strlen(test_arg_module) + 6);
+	assert(test_path);
+	strcpy(test_path, "/dev/");
+	strcpy(test_path + 5, test_arg_module);
+
+	return 1;
+}
+
+static inline int test_open(const uint8_t **mapp, size_t *n_mapp)
+{
+	const size_t size = 16UL * 1024UL * 1024UL;
+	int fd;
+
+	fd = open(test_path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+	assert(fd >= 0);
+
+	*mapp = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+	assert(*mapp != MAP_FAILED);
+
+	*n_mapp = size;
+	return fd;
+}
+
+static inline void test_close(int fd, const uint8_t *map, size_t n_map)
+{
+	munmap((void *)map, n_map);
+	close(fd);
+}
+
+#endif /* __TEST_H */
-- 
2.10.1

  parent reply	other threads:[~2016-10-26 19:22 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-26 19:17 [RFC v1 00/14] Bus1 Kernel Message Bus David Herrmann
2016-10-26 19:17 ` [RFC v1 01/14] bus1: add bus1(7) man-page David Herrmann
2016-10-27 23:12   ` Kirill A. Shutemov
2016-10-26 19:17 ` [RFC v1 02/14] bus1: provide stub cdev /dev/bus1 David Herrmann
2016-10-26 23:19   ` Andy Lutomirski
2016-10-26 23:54     ` Tom Gundersen
2016-10-27  9:11       ` Arnd Bergmann
2016-10-27 15:25         ` Tom Gundersen
2016-10-27 16:37           ` Linus Torvalds
2016-10-27 16:39             ` Tom Gundersen
2016-10-29 22:13           ` Arnd Bergmann
2016-10-26 19:17 ` [RFC v1 03/14] bus1: util - active reference utility library David Herrmann
2016-10-26 19:18 ` [RFC v1 04/14] bus1: util - fixed list " David Herrmann
2016-10-27 12:37   ` Peter Zijlstra
2016-10-27 12:48     ` David Herrmann
2016-10-27 12:56       ` Arnd Bergmann
2016-10-27 13:31         ` David Herrmann
2016-10-26 19:18 ` [RFC v1 05/14] bus1: util - pool " David Herrmann
2016-10-27 12:54   ` Peter Zijlstra
2016-10-27 12:59   ` Peter Zijlstra
2016-10-27 15:00     ` Peter Zijlstra
2016-10-27 15:14   ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 06/14] bus1: util - queue " David Herrmann
2016-10-27 15:27   ` Peter Zijlstra
2016-10-27 16:43   ` Peter Zijlstra
2016-10-28 11:33     ` Tom Gundersen
2016-10-28 13:33       ` Peter Zijlstra
2016-10-28 13:47         ` Tom Gundersen
2016-10-28 13:58           ` Peter Zijlstra
2016-10-28 14:33             ` Tom Gundersen
2016-10-28 16:49               ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 07/14] bus1: tracking user contexts David Herrmann
2016-10-26 19:18 ` [RFC v1 08/14] bus1: implement peer management context David Herrmann
2016-10-28 12:06   ` Richard Weinberger
2016-10-28 13:18     ` Tom Gundersen
2016-10-28 13:21       ` Richard Weinberger
2016-10-28 13:05   ` Richard Weinberger
2016-10-28 13:23     ` Tom Gundersen
2016-10-28 13:54       ` Richard Weinberger
2016-10-26 19:18 ` [RFC v1 09/14] bus1: provide transaction context for multicasts David Herrmann
2016-10-28 14:37   ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 10/14] bus1: add handle management David Herrmann
2016-10-26 19:18 ` [RFC v1 11/14] bus1: implement message transmission David Herrmann
2016-10-26 19:18 ` [RFC v1 12/14] bus1: hook up file-operations David Herrmann
2016-10-26 19:18 ` [RFC v1 13/14] bus1: limit and protect resources David Herrmann
2016-10-26 19:18 ` David Herrmann [this message]
2016-10-26 19:39 ` [RFC v1 00/14] Bus1 Kernel Message Bus Linus Torvalds
2016-10-26 20:34   ` David Herrmann
2016-10-27  0:45     ` Kirill A. Shutemov
2016-10-29 21:04       ` Josh Triplett
2016-11-02 14:45       ` David Herrmann
2017-01-30 22:11     ` Pavel Machek
2016-10-27 11:10 ` Michael Kerrisk
2016-10-28 13:11 ` Richard Weinberger
2016-10-28 13:37   ` Tom Gundersen

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=20161026191810.12275-15-dh.herrmann@gmail.com \
    --to=dh.herrmann@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=arnd@arndb.de \
    --cc=greg@kroah.com \
    --cc=hare@suse.com \
    --cc=jikos@kernel.org \
    --cc=josh@joshtriplett.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=rostedt@goodmis.org \
    --cc=teg@jklm.no \
    --cc=torvalds@linux-foundation.org \
    /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.