lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20161026191810.12275-15-dh.herrmann@gmail.com>
Date:   Wed, 26 Oct 2016 21:18:10 +0200
From:   David Herrmann <dh.herrmann@...il.com>
To:     linux-kernel@...r.kernel.org
Cc:     Andy Lutomirski <luto@...capital.net>,
        Jiri Kosina <jikos@...nel.org>, Greg KH <greg@...ah.com>,
        Hannes Reinecke <hare@...e.com>,
        Steven Rostedt <rostedt@...dmis.org>,
        Arnd Bergmann <arnd@...db.de>, Tom Gundersen <teg@...m.no>,
        David Herrmann <dh.herrmann@...il.com>,
        Josh Triplett <josh@...htriplett.org>,
        Linus Torvalds <torvalds@...ux-foundation.org>,
        Andrew Morton <akpm@...ux-foundation.org>
Subject: [RFC v1 14/14] bus1: basic user-space kselftests

From: Tom Gundersen <teg@...m.no>

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

Signed-off-by: Tom Gundersen <teg@...m.no>
Signed-off-by: David Herrmann <dh.herrmann@...il.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ