int (*func)(struct kdbus_test_env *env);
unsigned int flags;
unsigned timeout;
+ bool manual_only;
};
struct kdbus_test_args {
},
{
.name = "benchmark",
- .desc = "benchmark",
+ .desc = "latency benchmark",
.func = kdbus_test_benchmark,
.flags = TEST_CREATE_BUS,
.timeout = 10,
},
{
.name = "benchmark-nomemfds",
- .desc = "benchmark without using memfds",
+ .desc = "latency benchmark without using memfds",
.func = kdbus_test_benchmark_nomemfds,
.flags = TEST_CREATE_BUS,
.timeout = 10,
},
{
.name = "benchmark-uds",
- .desc = "benchmark comparison to UDS",
+ .desc = "latency benchmark comparison to UDS",
.func = kdbus_test_benchmark_uds,
.flags = TEST_CREATE_BUS,
.timeout = 10,
},
+ {
+ .name = "benchmark-bandwidth",
+ .desc = "bandwidth benchmark",
+ .func = kdbus_test_benchmark_bandwidth,
+ .flags = TEST_CREATE_BUS,
+ .timeout = 10,
+ .manual_only = true,
+ },
};
#define N_TESTS ((int) (sizeof(tests) / sizeof(tests[0])))
print(" ");
}
- ret = test_run_forked(t, kdbus_args, 0);
+ if (t->manual_only)
+ ret = TEST_SKIP;
+ else
+ ret = test_run_forked(t, kdbus_args, 0);
+
switch (ret) {
case TEST_OK:
ok_cnt++;
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/socket.h>
+#include <sys/sysinfo.h>
#include <math.h>
#include "kdbus-api.h"
static bool use_memfd = true; /* transmit memfd? */
static bool compare_uds = false; /* unix-socket comparison? */
static bool attach_none = false; /* clear attach-flags? */
+static bool is_bandwidth = false; /* is this a bandwidth test? */
static char *stress_payload;
struct stats {
- uint64_t count;
+ _Atomic uint64_t count;
+
+ /* These are for latency measurement, which is
+ * single-threaded so doesn't need to be atomic */
uint64_t latency_acc;
uint64_t latency_low;
uint64_t latency_high;
cmd.msg_address = (uintptr_t)kdbus_msg;
ret = kdbus_cmd_send(conn->fd, &cmd);
- ASSERT_RETURN_VAL(ret,==,0, ret);
+ if (!is_bandwidth || ret != -ENOBUFS) // bandwidth test often exhausts buffer space, it's fine
+ ASSERT_RETURN_VAL(ret,==,0, ret);
if (-1 != memfd)
CLOSE(memfd);
return (stats.count > 1) ? TEST_OK : TEST_ERR;
}
+struct bandwidth_worker_data {
+ int i;
+ struct kdbus_test_env *env;
+};
+
+void *bandwidth_receiver_thread(void *conn)
+{
+ while (1) {
+ if (handle_echo_reply((struct kdbus_conn *) conn, 0)) {
+ // no can do
+ }
+ }
+
+ return NULL;
+}
+
+int bandwidth_sender_thread(struct bandwidth_worker_data *data)
+{
+ struct kdbus_msg *kdbus_msg;
+ struct kdbus_conn *conn_a, *conn_b;
+ char name[sizeof SERVICE_NAME + 20];
+
+ /* I don't know if the order of these matters, I copied it
+ * from the latency benchmark. It would probably be cleaner
+ * to let the receiver set itself up otherwise. */
+ conn_a = kdbus_hello(data->env->buspath, 0, NULL, 0);
+ ASSERT_NONZERO(conn_a);
+ conn_b = kdbus_hello(data->env->buspath, 0, NULL, 0);
+ ASSERT_NONZERO(conn_b);
+ ASSERT_ZERO(kdbus_add_match_empty(conn_a));
+ ASSERT_ZERO(kdbus_add_match_empty(conn_b));
+ snprintf(name, sizeof name, SERVICE_NAME "%d", data->i);
+ ASSERT_ZERO(kdbus_name_acquire(conn_a, name, NULL));
+ ASSERT_ZERO(setup_simple_kdbus_msg(conn_b, conn_a->id, data->env->payload, &kdbus_msg));
+ ASSERT_NO_PENDING(conn_a);
+
+ pthread_t p;
+ pthread_create(&p, NULL, bandwidth_receiver_thread, conn_a);
+
+ while (1) {
+ if (send_echo_request(conn_b, kdbus_msg, 0)) {
+ // no can do
+ }
+ }
+
+ return TEST_OK;
+}
+
+void *bandwidth_sender_thread_wrap(void *data)
+{
+ // ASSERT_BLA macros expect the func to return int
+ bandwidth_sender_thread(data);
+ return NULL;
+}
+
+static wur int benchmark_bandwidth(struct kdbus_test_env *env)
+{
+ setlocale(LC_ALL, "");
+
+ stress_payload = malloc(env->payload);
+ ASSERT_NONZERO(stress_payload);
+ for (int i = 0; i < env->payload; i++)
+ stress_payload[i] = i;
+
+ // A pair consists of a reader and a sender
+ int proc_pairs = get_nprocs() / 2;
+ if (proc_pairs <= 1)
+ proc_pairs = 1;
+
+ while (proc_pairs--) {
+ struct bandwidth_worker_data* data = malloc(sizeof *data);
+ data->i = proc_pairs;
+ data->env = env;
+
+ pthread_t p;
+ pthread_create(&p, NULL, bandwidth_sender_thread_wrap, data); // sender spawns the reader
+ }
+
+ sleep(2); // give threads a moment to initialize
+ reset_stats();
+
+ while (1) {
+ const int PERIOD_S = 5;
+ sleep(PERIOD_S);
+ uint64_t kilobytes_received = (stats.count * env->payload) / 1024;
+ kdbus_printf("%llu kB/s\n", kilobytes_received / PERIOD_S);
+ reset_stats();
+ }
+
+ return TEST_OK;
+}
+
wur int kdbus_test_benchmark(struct kdbus_test_env *env)
{
use_memfd = true;
attach_none = false;
compare_uds = false;
+ is_bandwidth = false;
return benchmark(env);
}
+wur int kdbus_test_benchmark_bandwidth(struct kdbus_test_env *env)
+{
+ use_memfd = false;
+ attach_none = false;
+ compare_uds = false;
+ is_bandwidth = true;
+ return benchmark_bandwidth(env);
+}
+
wur int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env)
{
use_memfd = false;
attach_none = false;
compare_uds = false;
+ is_bandwidth = false;
return benchmark(env);
}
use_memfd = false;
attach_none = true;
compare_uds = true;
+ is_bandwidth = false;
return benchmark(env);
}