#include <string.h>
#include <time.h>
#include <fcntl.h>
+#include <locale.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/socket.h>
#include "kdbus-test.h"
#include "kdbus-util.h"
#define SERVICE_NAME "foo.bar.echo"
+static const bool use_memfd = true; /* transmit memfd? */
+static const bool compare_uds = false; /* unix-socket comparison? */
static char stress_payload[8192];
struct stats {
static struct stats stats;
-static uint64_t
-timeval_diff(const struct timeval *hi, const struct timeval *lo)
+static uint64_t now(void)
{
- struct timeval r;
+ struct timespec spec;
- timersub(hi, lo, &r);
-
- return (uint64_t) (r.tv_sec * 1000000ULL) + (uint64_t) r.tv_usec;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &spec);
+ return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec;
}
static void reset_stats(void)
stats.latency_high = 0;
}
-static void dump_stats(void)
+static void dump_stats(bool is_uds)
{
if (stats.count > 0) {
- kdbus_printf("stats: %llu packets processed, latency (usecs) min/max/avg %llu/%llu/%llu\n",
+ kdbus_printf("stats %s: %'llu packets processed, latency (nsecs) min/max/avg %'7llu // %'7llu // %'7llu\n",
+ is_uds ? " (UNIX)" : "(KDBUS)",
(unsigned long long) stats.count,
(unsigned long long) stats.latency_low,
(unsigned long long) stats.latency_high,
}
}
-static void add_stats(const struct timeval *tv)
+static void add_stats(uint64_t prev)
{
- struct timeval now;
uint64_t diff;
- gettimeofday(&now, NULL);
- diff = timeval_diff(&now, tv);
+ diff = now() - prev;
stats.count++;
stats.latency_acc += diff;
uint64_t size;
int memfd = -1;
int ret;
- struct timeval now;
+ uint64_t now_ns;
- gettimeofday(&now, NULL);
+ now_ns = now();
size = sizeof(struct kdbus_msg);
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
- memfd = sys_memfd_create("memfd-name", 0);
- ASSERT_RETURN_VAL(memfd >= 0, memfd);
+ if (use_memfd) {
+ memfd = sys_memfd_create("memfd-name", 0);
+ ASSERT_RETURN_VAL(memfd >= 0, memfd);
- ret = write(memfd, &now, sizeof(now));
- ASSERT_RETURN_VAL(ret == sizeof(now), -EAGAIN);
+ ret = write(memfd, &now_ns, sizeof(now_ns));
+ ASSERT_RETURN_VAL(ret == sizeof(now_ns), -EAGAIN);
- ret = sys_memfd_seal_set(memfd);
- ASSERT_RETURN_VAL(ret == 0, -errno);
+ ret = sys_memfd_seal_set(memfd);
+ ASSERT_RETURN_VAL(ret == 0, -errno);
- size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+ }
msg = malloc(size);
ASSERT_RETURN_VAL(msg, -ENOMEM);
item->vec.size = sizeof(stress_payload);
item = KDBUS_ITEM_NEXT(item);
- item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
- item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd);
- item->memfd.size = sizeof(struct timeval);
- item->memfd.fd = memfd;
- item = KDBUS_ITEM_NEXT(item);
+ if (use_memfd) {
+ item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd);
+ item->memfd.size = sizeof(now_ns);
+ item->memfd.fd = memfd;
+ item = KDBUS_ITEM_NEXT(item);
+ }
ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg);
ASSERT_RETURN_VAL(ret == 0, -errno);
}
static int
-handle_echo_reply(struct kdbus_conn *conn)
+handle_echo_reply(struct kdbus_conn *conn, uint64_t send_ns)
{
int ret;
struct kdbus_cmd_recv recv = {};
struct kdbus_msg *msg;
const struct kdbus_item *item;
+ bool has_memfd = false;
ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
+ if (ret < 0 && errno == EAGAIN)
+ return -EAGAIN;
+
ASSERT_RETURN_VAL(ret == 0, -errno);
msg = (struct kdbus_msg *)(conn->buf + recv.offset);
buf = mmap(NULL, item->memfd.size, PROT_READ,
MAP_PRIVATE, item->memfd.fd, 0);
ASSERT_RETURN_VAL(buf != MAP_FAILED, -EINVAL);
+ ASSERT_RETURN_VAL(item->memfd.size == sizeof(uint64_t),
+ -EINVAL);
- add_stats((struct timeval *) buf);
+ add_stats(*(uint64_t*)buf);
munmap(buf, item->memfd.size);
close(item->memfd.fd);
+ has_memfd = true;
break;
}
}
}
+ if (!has_memfd)
+ add_stats(send_ns);
+
ret = kdbus_free(conn, recv.offset);
ASSERT_RETURN_VAL(ret == 0, -errno);
int kdbus_test_benchmark(struct kdbus_test_env *env)
{
+ static char buf[sizeof(stress_payload)];
int ret;
struct kdbus_conn *conn_a, *conn_b;
struct pollfd fds[2];
- struct timeval start;
+ uint64_t start, send_ns, now_ns, diff;
unsigned int i;
+ int uds[2];
+
+ setlocale(LC_ALL, "");
for (i = 0; i < sizeof(stress_payload); i++)
stress_payload[i] = i;
+ /* setup kdbus pair */
+
conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
ASSERT_RETURN(conn_a && conn_b);
ret = kdbus_add_match_empty(conn_b);
ASSERT_RETURN(ret == 0);
- fds[0].fd = conn_a->fd;
- fds[1].fd = conn_b->fd;
-
ret = kdbus_name_acquire(conn_a, SERVICE_NAME, NULL);
ASSERT_RETURN(ret == 0);
- gettimeofday(&start, NULL);
- reset_stats();
+ /* setup UDS pair */
- ret = send_echo_request(conn_b, conn_a->id);
+ ret = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, uds);
ASSERT_RETURN(ret == 0);
+ /* start benchmark */
+
kdbus_printf("-- entering poll loop ...\n");
- while (1) {
- struct timeval now;
- unsigned int nfds = sizeof(fds) / sizeof(fds[0]);
- unsigned int i;
+ do {
+ /* run kdbus benchmark */
- for (i = 0; i < nfds; i++) {
- fds[i].events = POLLIN | POLLPRI | POLLHUP;
- fds[i].revents = 0;
- }
+ fds[0].fd = conn_a->fd;
+ fds[1].fd = conn_b->fd;
- ret = poll(fds, nfds, 10);
- if (ret < 0)
- break;
+ /* cancel any prending message */
+ handle_echo_reply(conn_a, 0);
+
+ start = now();
+ reset_stats();
- if (fds[0].revents & POLLIN) {
- ret = handle_echo_reply(conn_a);
- if (ret)
+ send_ns = now();
+ ret = send_echo_request(conn_b, conn_a->id);
+ ASSERT_RETURN(ret == 0);
+
+ while (1) {
+ unsigned int nfds = sizeof(fds) / sizeof(fds[0]);
+ unsigned int i;
+
+ for (i = 0; i < nfds; i++) {
+ fds[i].events = POLLIN | POLLPRI | POLLHUP;
+ fds[i].revents = 0;
+ }
+
+ ret = poll(fds, nfds, 10);
+ if (ret < 0)
break;
- ret = send_echo_request(conn_b, conn_a->id);
- if (ret)
+ if (fds[0].revents & POLLIN) {
+ ret = handle_echo_reply(conn_a, send_ns);
+ if (ret)
+ break;
+
+ send_ns = now();
+ ret = send_echo_request(conn_b, conn_a->id);
+ if (ret)
+ break;
+ }
+
+ now_ns = now();
+ diff = now_ns - start;
+ if (diff > 1000000000ULL) {
+ start = now_ns;
+
+ dump_stats(false);
+ reset_stats();
+
break;
+ }
}
- gettimeofday(&now, NULL);
- if (timeval_diff(&now, &start) / 1000ULL > 1000ULL) {
- start.tv_sec = now.tv_sec;
- start.tv_usec = now.tv_usec;
+ if (!compare_uds)
+ continue;
+
+ /* run unix-socket benchmark as comparison */
+
+ fds[0].fd = uds[0];
+ fds[1].fd = uds[1];
+
+ /* cancel any pendign message */
+ read(uds[1], buf, sizeof(buf));
+
+ start = now();
+ reset_stats();
+
+ send_ns = now();
+ ret = write(uds[0], stress_payload, sizeof(stress_payload));
+ ASSERT_RETURN(ret == sizeof(stress_payload));
+
+ while (1) {
+ unsigned int nfds = sizeof(fds) / sizeof(fds[0]);
+ unsigned int i;
- if (!kdbus_util_verbose)
+ for (i = 0; i < nfds; i++) {
+ fds[i].events = POLLIN | POLLPRI | POLLHUP;
+ fds[i].revents = 0;
+ }
+
+ ret = poll(fds, nfds, 10);
+ if (ret < 0)
break;
- dump_stats();
- reset_stats();
+ if (fds[1].revents & POLLIN) {
+ ret = read(uds[1], buf, sizeof(buf));
+ ASSERT_RETURN(ret == sizeof(buf));
+ add_stats(send_ns);
+
+ send_ns = now();
+ ret = write(uds[0], buf, sizeof(buf));
+ ASSERT_RETURN(ret == sizeof(buf));
+ }
+
+ now_ns = now();
+ diff = now_ns - start;
+ if (diff > 1000000000ULL) {
+ start = now_ns;
+
+ dump_stats(true);
+ reset_stats();
+
+ break;
+ }
}
- }
+
+ } while (kdbus_util_verbose);
kdbus_printf("-- closing bus connections\n");