Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) 2019 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/wait_q.h>
#include <ksched.h>

#include "app_threads.h"
#include "user.h"

#define MAIN_PRIO 8
#define THREADS_PRIO 9

enum {
	MEAS_START,
	MEAS_END,
	NUM_STAMP_STATES
};

uint32_t stamps[NUM_STAMP_STATES];

static inline int stamp(int state)
{
	uint32_t t;

	/* In theory the TSC has much lower overhead and higher
	 * precision.  In practice it's VERY jittery in recent qemu
	 * versions and frankly too noisy to trust.
	 */
#ifdef CONFIG_X86
	__asm__ volatile("rdtsc" : "=a"(t) : : "edx");
#else
	t = k_cycle_get_32();
#endif

	stamps[state] = t;
	return t;
}

static int yielder_status;

void yielder_entry(void *_thread, void *_tid, void *_nb_threads)
{
	struct k_app_thread *thread = (struct k_app_thread *) _thread;
	int ret;

	struct k_mem_partition *parts[] = {
		thread->partition,
	};

	ret = k_mem_domain_init(&thread->domain, ARRAY_SIZE(parts), parts);
	if (ret != 0) {
		printk("k_mem_domain_init failed %d\n", ret);
		yielder_status = 1;
		return;
	}

	k_mem_domain_add_thread(&thread->domain, k_current_get());

	k_thread_user_mode_enter(context_switch_yield, _nb_threads, NULL, NULL);
}


static k_tid_t threads[MAX_NB_THREADS];

static int exec_test(uint8_t nb_threads)
{
	if (nb_threads > MAX_NB_THREADS) {
		printk("Too many threads\n");
		return 1;
	}

	yielder_status = 0;

	for (size_t tid = 0; tid < nb_threads; tid++) {
		app_threads[tid].partition = app_partitions[tid];
		app_threads[tid].stack = &app_thread_stacks[tid];

		void *_tid = (void *)(uintptr_t)tid;

		threads[tid] = k_thread_create(&app_threads[tid].thread,
					app_thread_stacks[tid],
					APP_STACKSIZE, yielder_entry,
					&app_threads[tid], _tid, (void *)(uintptr_t)nb_threads,
					THREADS_PRIO, 0, K_FOREVER);
	}

	/* make sure the main thread has a higher priority
	 * this way, user threads all start together
	 * (lower number --> higher prio)
	 */
	k_thread_priority_set(k_current_get(), MAIN_PRIO);

	stamp(MEAS_START);
	for (size_t tid = 0; tid < nb_threads; tid++) {
		k_thread_start(threads[tid]);
	}
	for (size_t tid = 0; tid < nb_threads; tid++) {
		k_thread_join(threads[tid], K_FOREVER);
	}
	stamp(MEAS_END);

	uint32_t full_time = stamps[MEAS_END] - stamps[MEAS_START];
	uint64_t time_ms = k_cyc_to_ns_near64(full_time)/NB_YIELDS;

	printk("Swapping %2u threads: %8" PRIu32 " cyc & %6" PRIu32 " rounds -> %6"
				PRIu64 " ns per ctx\n", nb_threads, full_time,
				NB_YIELDS, time_ms);

	return yielder_status;
}


int main(void)
{
	int ret;

	printk("Userspace scheduling benchmark started on board %s\n", CONFIG_BOARD);

	size_t nb_threads_list[] = {2, 8, 16, 32, 0};

	printk("============================\n");
	printk("user/user^n swapping (yield)\n");

	for (size_t i = 0; nb_threads_list[i] > 0; i++) {
		ret = exec_test(nb_threads_list[i]);
		if (ret != 0) {
			printk("FAIL\n");
			return 0;
		}
	}

	printk("SUCCESS\n");
	return 0;
}