From b03cb94b43c39ae9b8b693e8f3060ed79f5cded0 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 1 Jul 2022 14:32:10 +0200 Subject: [PATCH] pc: add linux driver timer test The test runs as lx_user task and uses several *delay and wait queue test cases happened to be used in real ported linux drivers. The test shows the time spent with several time sources, e.g. jiffies, rdtsc, lx_time_counter_count etc. Issue #4540 --- repos/pc/run/driver_time.run | 96 +++++++ repos/pc/src/test/driver_time/dummies.c | 71 +++++ .../src/test/driver_time/generated_dummies.c | 234 +++++++++++++++ repos/pc/src/test/driver_time/lx_emul.h | 23 ++ repos/pc/src/test/driver_time/lx_user.c | 270 ++++++++++++++++++ repos/pc/src/test/driver_time/main.cc | 67 +++++ repos/pc/src/test/driver_time/source.list | 77 +++++ repos/pc/src/test/driver_time/target.mk | 28 ++ repos/pc/src/test/driver_time/time.cc | 25 ++ 9 files changed, 891 insertions(+) create mode 100644 repos/pc/run/driver_time.run create mode 100644 repos/pc/src/test/driver_time/dummies.c create mode 100644 repos/pc/src/test/driver_time/generated_dummies.c create mode 100644 repos/pc/src/test/driver_time/lx_emul.h create mode 100644 repos/pc/src/test/driver_time/lx_user.c create mode 100644 repos/pc/src/test/driver_time/main.cc create mode 100644 repos/pc/src/test/driver_time/source.list create mode 100644 repos/pc/src/test/driver_time/target.mk create mode 100644 repos/pc/src/test/driver_time/time.cc diff --git a/repos/pc/run/driver_time.run b/repos/pc/run/driver_time.run new file mode 100644 index 0000000000..edd4ad863b --- /dev/null +++ b/repos/pc/run/driver_time.run @@ -0,0 +1,96 @@ +# +# Build +# + +if {[expr ![have_spec x86_64]]} { + puts "Run script is not supported on this platform." + exit 0 +} + +set use_top 0 + +set build_components { + core init timer + test/driver_time +} + + +append_if $use_top build_components { app/top } + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +# override default platform driver policy +proc platform_drv_policy {} { + return { + + } +} + +# +# Generate config +# + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + } + +append_platform_drv_config + +append_if $use_top config { + + + + } + +append config { +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core ld.lib.so init timer + test-driver_time +} + +append_if $use_top boot_modules { top } + +append_platform_drv_boot_modules + +build_boot_image $boot_modules + +append qemu_args "-nographic " + +run_genode_until forever diff --git a/repos/pc/src/test/driver_time/dummies.c b/repos/pc/src/test/driver_time/dummies.c new file mode 100644 index 0000000000..12522c1b78 --- /dev/null +++ b/repos/pc/src/test/driver_time/dummies.c @@ -0,0 +1,71 @@ +/* + * \brief Dummy definitions of Linux Kernel functions - handled manually + * \author Alexander Boettcher + * \date 2022-07-01 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void calc_load_nohz_start(void) +{ + lx_emul_trace(__func__); +} + + +void calc_load_nohz_stop(void) +{ + lx_emul_trace(__func__); +} + + +void account_idle_ticks(unsigned long ticks) +{ + lx_emul_trace(__func__); +} + + + +bool irq_work_needs_cpu(void) +{ + return false; +} + + +int ___ratelimit(struct ratelimit_state * rs, const char * func) +{ + /* + * from lib/ratelimit.c: + * " 0 means callbacks will be suppressed. + * 1 means go ahead and do it. " + */ + lx_emul_trace(__func__); + return 1; +} + +void register_syscore_ops(struct syscore_ops * ops) +{ + lx_emul_trace(__func__); +} diff --git a/repos/pc/src/test/driver_time/generated_dummies.c b/repos/pc/src/test/driver_time/generated_dummies.c new file mode 100644 index 0000000000..ffd8e13205 --- /dev/null +++ b/repos/pc/src/test/driver_time/generated_dummies.c @@ -0,0 +1,234 @@ +/* + * \brief Dummy definitions of Linux Kernel functions + * \author Automatically generated file - do no edit + * \date 2022-05-06 + */ + +#include + + +#include + +void * PDE_DATA(const struct inode * inode) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +const char * __clk_get_name(const struct clk * clk) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int printk_deferred(const char * fmt,...) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void irq_work_tick(void) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +asmlinkage __visible void dump_stack(void) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int io_schedule_prepare(void) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void io_schedule_finish(int token) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +long io_schedule_timeout(long timeout) +{ + lx_emul_trace_and_stop(__func__); +} + + +extern void ack_bad_irq(unsigned int irq); +void ack_bad_irq(unsigned int irq) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int kobject_synth_uevent(struct kobject * kobj,const char * buf,size_t count) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void synchronize_srcu(struct srcu_struct * ssp) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void __srcu_read_unlock(struct srcu_struct * ssp,int idx) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +bool initcall_debug; + + +#include + +struct irq_chip no_irq_chip; + + +#include + +struct kobject *kernel_kobj; + + +#include + +void kill_anon_super(struct super_block * sb) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +bool is_software_node(const struct fwnode_handle * fwnode) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void fwnode_remove_software_node(struct fwnode_handle * fwnode) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void __put_task_struct(struct task_struct * tsk) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int get_option(char ** str,int * pint) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void wake_q_add_safe(struct wake_q_head * head,struct task_struct * task) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +bool file_ns_capable(const struct file * file,struct user_namespace * ns,int cap) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void seq_printf(struct seq_file * m,const char * f,...) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +struct pseudo_fs_context * init_pseudo(struct fs_context * fc,unsigned long magic) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +int smp_call_function_single(int cpu,void (* func)(void * info),void * info,int wait) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +void srcu_drive_gp(struct work_struct * wp) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +unsigned long _copy_to_user(void __user * to,const void * from,unsigned long n) +{ + lx_emul_trace_and_stop(__func__); +} + + +#include + +bool static_key_initialized; + + +#include + +int string_escape_mem(const char * src,size_t isz,char * dst,size_t osz,unsigned int flags,const char * only) +{ + lx_emul_trace_and_stop(__func__); +} diff --git a/repos/pc/src/test/driver_time/lx_emul.h b/repos/pc/src/test/driver_time/lx_emul.h new file mode 100644 index 0000000000..acee561257 --- /dev/null +++ b/repos/pc/src/test/driver_time/lx_emul.h @@ -0,0 +1,23 @@ +/** + * \brief Dummy definitions of Linux Kernel functions + * \author Alexander Boettcher + * \date 2022-07-01 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +void lx_emul_time_udelay(unsigned long usec); + +#ifdef __cplusplus +} +#endif diff --git a/repos/pc/src/test/driver_time/lx_user.c b/repos/pc/src/test/driver_time/lx_user.c new file mode 100644 index 0000000000..8f69ca0b3a --- /dev/null +++ b/repos/pc/src/test/driver_time/lx_user.c @@ -0,0 +1,270 @@ +/* + * \brief Post kernel activity + * \author Alexander Boettcher + * \date 2022-07-01 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + + +#include + +#include + +#include "i915_drv.h" /* test wait_for() macro */ + +#include + + +extern uint64_t tsc_freq_khz; + + +static int timing_tests(void *); + + +void lx_user_init(void) +{ + kernel_thread(timing_tests, NULL, CLONE_FS | CLONE_FILES); +} + + +struct measure { + uint64_t start; + uint64_t end; + uint64_t diff; +}; + +#define test_timing(fn_test, fn_evaluation) \ +{ \ + struct measure m_lxemul, m_jiffies, m_rdtsc; \ + uint64_t jiffies_in_us; \ +\ + m_lxemul.start = lx_emul_time_counter(); \ + m_jiffies.start = jiffies_64; \ + m_rdtsc.start = rdtsc(); \ +\ + { \ + fn_test \ + } \ +\ + m_rdtsc.end = rdtsc(); \ + m_jiffies.end = jiffies_64; \ + m_lxemul.end = lx_emul_time_counter(); \ +\ + m_rdtsc.diff = m_rdtsc.end - m_rdtsc.start; \ + m_lxemul.diff = m_lxemul.end - m_lxemul.start; \ + m_jiffies.diff = m_jiffies.end - m_jiffies.start; \ +\ + jiffies_in_us = (m_jiffies.diff) * (1000ull * 1000 / CONFIG_HZ); \ +\ + { \ + fn_evaluation \ + } \ +\ +} + +#define test_timing_no_ret(text, fn_test) \ +{ \ + test_timing( \ + fn_test \ + , \ + if (rdtsc_freq_mhz) \ + printk(text \ + " %6llu:%10llu:%10llu:%10llu:%8lld\n", \ + m_jiffies.diff, jiffies_in_us, m_lxemul.diff, \ + m_rdtsc.diff / rdtsc_freq_mhz, \ + jiffies_in_us - m_lxemul.diff); \ + else \ + printk(text \ + " %6llu:%10llu:%10llu:%8lld\n", \ + m_jiffies.diff, jiffies_in_us, m_lxemul.diff, \ + jiffies_in_us - m_lxemul.diff); \ + ); \ + /* trigger to update jiffies to avoid printk time part of next test */ \ + msleep(1); \ +} + + +#define test_timing_with_ret(text, fn_test) \ +{ \ + test_timing( \ + fn_test \ + , \ + if (rdtsc_freq_mhz) \ + printk(text \ + " %6llu:%10llu:%10llu:%10llu:%8lld " \ + "ret=%d%s\n", \ + m_jiffies.diff, jiffies_in_us, m_lxemul.diff, \ + m_rdtsc.diff / rdtsc_freq_mhz, \ + jiffies_in_us - m_lxemul.diff, \ + ret, ret == -ETIMEDOUT ? " (ETIMEDOUT)" : ""); \ + else \ + printk(text \ + " %6llu:%10llu:%10llu:%8lld " \ + "ret=%d%s\n", \ + m_jiffies.diff, jiffies_in_us, m_lxemul.diff, \ + jiffies_in_us - m_lxemul.diff, \ + ret, ret == -ETIMEDOUT ? " (ETIMEDOUT)" : ""); \ + ); \ + /* trigger to update jiffies to avoid printk time part of next test */ \ + msleep(1); \ +} + + +static int timing_tests(void * data) +{ + DEFINE_WAIT(wait); + wait_queue_head_t wq; + int ret; + uint64_t const rdtsc_freq_mhz = tsc_freq_khz / 1000; + + init_waitqueue_head(&wq); + + while (true) { + if (rdtsc_freq_mhz) + printk("test(parameters) -> " + "jiffies:jiff_us:lx_time_us:rdtsc_us:diff_jiff_lx_time " + "tsc=%lluMhz\n", rdtsc_freq_mhz); + else + printk("test(parameters) -> " + "jiffies:jiff_us:lx_time_us:diff_jiff_lx_time\n"); + + test_timing_no_ret ("udelay(40) ->", + udelay(40); + ); + + test_timing_no_ret ("ndelay(4000) ->", + ndelay(4000); + ); + + test_timing_no_ret ("msleep(5000) ->", + msleep(5000); + ); + + test_timing_with_ret("wait_for(cond,10ms) A ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 10); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,5ms) B ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 5); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,2ms) C ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 2); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,10ms) D ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 10); + remove_wait_queue(&wq, &wait); + ); + + /* do some work, so that jiffies becomes a bit outdated */ + { + unsigned long long i = 0; + printk("cause some long running load in task ...\n"); + for (i = 0; i < (1ull << 24); i++) { + asm volatile("pause":::"memory"); + } + } + + /* display driver test case -> waking up too early before irq triggered */ + test_timing_with_ret("wait_for(cond,10ms) E ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 10); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,5ms) F ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 5); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,2ms) G ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 2); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,10ms) H ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 10); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,5000ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 5000); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,4000ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 4000); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,3000ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 3000); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,2000ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 2000); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,500ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 500); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,200ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 200); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,100ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 100); + remove_wait_queue(&wq, &wait); + ); + + test_timing_with_ret("wait_for(cond,50ms) ->", + add_wait_queue(&wq, &wait); + ret = wait_for((0), 50); + remove_wait_queue(&wq, &wait); + ); + + /* audio driver test case -> sleeping too short or long is bad */ + test_timing_no_ret ("usleep_range(20,21) ->", + usleep_range(20, 21); + ); + + test_timing_no_ret ("usleep_range(40,41) ->", + usleep_range(40, 41); + ); + + test_timing_no_ret ("usleep_range(400,410) ->", + usleep_range(400, 410); + ); + } + + return 0; +} diff --git a/repos/pc/src/test/driver_time/main.cc b/repos/pc/src/test/driver_time/main.cc new file mode 100644 index 0000000000..3bcbee4c0b --- /dev/null +++ b/repos/pc/src/test/driver_time/main.cc @@ -0,0 +1,67 @@ +/* + * \brief Linux test driver + * \author Alexander Boettcher + * \date 2022-07-01 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include + +/* emulation includes */ +#include +#include + + +namespace Test { + using namespace Genode; + struct Driver; +} + + +unsigned long long tsc_freq_khz; + + +struct Test::Driver +{ + Env &env; + + Driver(Env &env) : env(env) + { + Lx_kit::initialize(env); + + env.exec_static_constructors(); + + try { + Attached_rom_dataspace info(env, "platform_info"); + tsc_freq_khz = info.xml().sub_node("hardware").sub_node("tsc") + .attribute_value("freq_khz", 0ULL); + } catch (...) { }; + } + + void start() + { + log("--- Test driver started ---"); + + lx_emul_start_kernel(nullptr); + } +}; + + +static Test::Driver &driver(Genode::Env & env) +{ + static Test::Driver driver(env); + return driver; +} + + +void Component::construct(Genode::Env &env) +{ + driver(env).start(); +} diff --git a/repos/pc/src/test/driver_time/source.list b/repos/pc/src/test/driver_time/source.list new file mode 100644 index 0000000000..de86489245 --- /dev/null +++ b/repos/pc/src/test/driver_time/source.list @@ -0,0 +1,77 @@ +drivers/base/bus.c +drivers/base/class.c +drivers/base/component.c +drivers/base/core.c +drivers/base/dd.c +drivers/base/devres.c +drivers/base/driver.c +drivers/base/platform.c +drivers/base/property.c +kernel/async.c +kernel/irq/chip.c +kernel/irq/devres.c +kernel/irq/handle.c +kernel/irq/irqdesc.c +kernel/irq/irqdomain.c +kernel/irq/manage.c +kernel/irq/resend.c +kernel/kthread.c +kernel/locking/mutex.c +kernel/locking/osq_lock.c +kernel/locking/rtmutex.c +kernel/locking/rwsem.c +kernel/notifier.c +kernel/panic.c +kernel/resource.c +kernel/sched/clock.c +kernel/sched/completion.c +kernel/sched/swait.c +kernel/sched/wait.c +kernel/sched/wait_bit.c +kernel/smpboot.c +kernel/time/clockevents.c +kernel/time/clocksource.c +kernel/time/hrtimer.c +kernel/time/jiffies.c +kernel/time/ntp.c +kernel/time/tick-broadcast.c +kernel/time/tick-common.c +kernel/time/tick-oneshot.c +kernel/time/tick-sched.c +kernel/time/time.c +kernel/time/timeconv.c +kernel/time/timecounter.c +kernel/time/timekeeping.c +kernel/time/timer.c +kernel/time/timer_list.c +kernel/workqueue.c +lib/bitmap.c +lib/crc32.c +lib/ctype.c +lib/debug_locks.c +lib/dec_and_lock.c +lib/find_bit.c +lib/hexdump.c +lib/hweight.c +lib/idr.c +lib/iomap.c +lib/irq_regs.c +lib/kasprintf.c +lib/klist.c +lib/kobject.c +lib/kstrtox.c +lib/list_sort.c +lib/llist.c +lib/radix-tree.c +lib/rbtree.c +lib/refcount.c +lib/scatterlist.c +lib/siphash.c +lib/sort.c +lib/string.c +lib/timerqueue.c +lib/uuid.c +lib/vsprintf.c +lib/xarray.c +mm/mempool.c +mm/util.c diff --git a/repos/pc/src/test/driver_time/target.mk b/repos/pc/src/test/driver_time/target.mk new file mode 100644 index 0000000000..9f7387e135 --- /dev/null +++ b/repos/pc/src/test/driver_time/target.mk @@ -0,0 +1,28 @@ +REQUIRES := x86_64 + +TARGET := test-driver_time +LIBS := base pc_lx_emul jitterentropy + +SRC_CC += main.cc time.cc +SRC_C += lx_user.c +SRC_C += dummies.c +SRC_C += generated_dummies.c + +SRC_C += lx_emul/common_dummies.c +SRC_C += lx_emul/shadow/lib/kobject_uevent.c +SRC_C += lx_emul/shadow/drivers/char/random.c +SRC_C += lx_emul/shadow/kernel/softirq.c + +vpath %.c $(REP_DIR)/src/lib/pc +vpath %.cc $(REP_DIR)/src/lib/pc + +LX_SRC_DIR := $(call select_from_ports,linux)/src/linux +ifeq ($(wildcard $(LX_SRC_DIR)),) +LX_SRC_DIR := $(call select_from_repositories,src/linux) +endif +ifeq ($(wildcard $(LX_SRC_DIR)),) +fail +endif + +INC_DIR += $(PRG_DIR) +INC_DIR += $(LX_SRC_DIR)/drivers/gpu/drm/i915 diff --git a/repos/pc/src/test/driver_time/time.cc b/repos/pc/src/test/driver_time/time.cc new file mode 100644 index 0000000000..0f8f40ad69 --- /dev/null +++ b/repos/pc/src/test/driver_time/time.cc @@ -0,0 +1,25 @@ +/* + * \brief Lx_emul udelay function for very short delays + * \author Stefan Kalkowski + * \date 2021-07-10 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include + +extern "C" void lx_emul_time_udelay(unsigned long usec); +extern "C" void lx_emul_time_udelay(unsigned long usec) +{ + if (usec > 100) + Genode::error("Cannot delay that long ", usec, " microseconds"); + + unsigned long long start = Lx_kit::env().timer.curr_time().trunc_to_plain_us().value; + while (Lx_kit::env().timer.curr_time().trunc_to_plain_us().value < (start + usec)) { ; } +}