This header does not expose any public APIs, so move it under kernel/include and change files including it. Signed-off-by: Anas Nashif <anas.nashif@intel.com>
142 lines
3.0 KiB
C
142 lines
3.0 KiB
C
/*
|
|
* Copyright (c) 2019 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/printk.h>
|
|
|
|
/* private kernel APIs */
|
|
#include <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;
|
|
}
|