From 225c74bbdfae09984d31233299dabcbcca625406 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 27 Jun 2018 11:20:50 -0700 Subject: [PATCH] kernel/Kconfig: Reorgnize wait_q and sched algorithm choices Make these "choice" items instead of a single boolean that implies the element unset. Also renames WAITQ_FAST to WAITQ_SCALABLE, as the rbtree is really only "fast" for large queue sizes (it's constant factor overhead is bigger than a list's!) Signed-off-by: Andy Ross --- boards/x86/qemu_x86/qemu_x86_defconfig | 4 +- include/kernel.h | 4 +- kernel/Kconfig | 67 ++++++++++++++++++++------ kernel/include/wait_q.h | 6 +-- kernel/sched.c | 14 +++--- 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/boards/x86/qemu_x86/qemu_x86_defconfig b/boards/x86/qemu_x86/qemu_x86_defconfig index ff3845a7e24..f4e3a53d115 100644 --- a/boards/x86/qemu_x86/qemu_x86_defconfig +++ b/boards/x86/qemu_x86/qemu_x86_defconfig @@ -22,5 +22,5 @@ CONFIG_USERSPACE=y CONFIG_APPLICATION_MEMORY=y CONFIG_X86_PAE_MODE=y CONFIG_DEBUG_INFO=y -CONFIG_SCHED_DUMB=n -CONFIG_WAITQ_FAST=y +CONFIG_SCHED_SCALABLE=y +CONFIG_WAITQ_SCALABLE=y diff --git a/include/kernel.h b/include/kernel.h index 3027c96f2d9..02a46405c3d 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -73,7 +73,7 @@ extern "C" { #define K_HIGHEST_APPLICATION_THREAD_PRIO (K_HIGHEST_THREAD_PRIO) #define K_LOWEST_APPLICATION_THREAD_PRIO (K_LOWEST_THREAD_PRIO - 1) -#ifdef CONFIG_WAITQ_FAST +#ifdef CONFIG_WAITQ_SCALABLE typedef struct { struct _priq_rb waitq; @@ -406,7 +406,7 @@ struct _thread_base { struct rbnode qnode_rb; }; -#ifdef CONFIG_WAITQ_FAST +#ifdef CONFIG_WAITQ_SCALABLE /* wait queue on which the thread is pended (needed only for * trees, not dumb lists) */ diff --git a/kernel/Kconfig b/kernel/Kconfig index 9d756e447a5..03439759880 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -195,23 +195,17 @@ config APPLICATION_MEMORY will have the project-level application objects and any libraries including the C library in it. -config WAITQ_FAST - bool - prompt "Use scalable wait_q implementation" +choice SCHED_ALGORITHM + prompt "Scheduler priority queue algorithm" + default SCHED_DUMB help - When selected, the wait_q abstraction used in IPC primitives - to pend threads for wakeup later will be implemented with a - balanced tree instead of a linear list. Choose this if you - expect to have many threads waiting on individual - primitives, or if you have already included the red/black - tree code in the build for some other purpose (in which case - this results in less code size increase than the default - implementation). + The kernel can be built with with several choices for the + ready queue implementation, offering different choices between + code size, constant factor runtime overhead and performance + scaling when many threads are added. config SCHED_DUMB - bool - prompt "Use a simple linked list scheduler" - default y + bool "Simple linked-list ready queue" help When selected, the scheduler ready queue will be implemented as a simple unordered list, with very fast constant time @@ -222,6 +216,51 @@ config SCHED_DUMB (that are not otherwise using the red/black tree) this results in a savings of ~2k of code size. +config SCHED_SCALABLE + bool "Red/black tree ready queue" + help + When selected, the scheduler ready queue will be implemented + as a red/black tree. This has rather slower constant-time + insertion and removal overhead, and on most platforms (that + are not otherwise using the rbtree somehwere) requires an + extra ~2kb of code. But the resulting behavior will scale + cleanly and quickly into the many thousands of threads. Use + this on platforms where you may have MANY threads marked as + runnable at a given time. Most applications don't want this. + +endchoice # SCHED_ALGORITHM + +choice WAITQ_ALGORITHM + prompt "Wait queue priority algorithm" + default WAITQ_DUMB + help + The wait_q abstraction used in IPC primitives to pend threads + for later wakeup shares the same backend data structure + choices as the scheduler, and can use the same options. + +config WAITQ_SCALABLE + bool + prompt "Use scalable wait_q implementation" + help + When selected, the wait_q will be implemented with a + balanced tree. Choose this if you expect to have many + threads waiting on individual primitives. There is a ~2kb + code size increase over WAITQ_DUMB (which may be shared with + SCHED_SCALABLE) if the rbtree is not used elsewhere in the + application, and pend/unpend operations on "small" queues + will be somewhat slower (though this is not generally a + performance path). + +config WAITQ_DUMB + bool + prompt "Simple linked-list wait_q" + help + When selected, the wait_q will be implemented with a + doubly-linked list. Choose this if you expect to have only + a few threads blocked on any single IPC primitive. + +endchoice # WAITQ_ALGORITHM + menu "Kernel Debugging and Metrics" config INIT_STACKS diff --git a/kernel/include/wait_q.h b/kernel/include/wait_q.h index e6bf4043bf3..6aea2d30fc6 100644 --- a/kernel/include/wait_q.h +++ b/kernel/include/wait_q.h @@ -44,7 +44,7 @@ static ALWAYS_INLINE int _abort_thread_timeout(struct k_thread *thread) #define _get_next_timeout_expiry() (K_FOREVER) #endif -#ifdef CONFIG_WAITQ_FAST +#ifdef CONFIG_WAITQ_SCALABLE #define _WAIT_Q_FOR_EACH(wq, thread_ptr) \ RB_FOR_EACH_CONTAINER(&(wq)->waitq.tree, thread_ptr, base.qnode_rb) @@ -63,7 +63,7 @@ static inline struct k_thread *_waitq_head(_wait_q_t *w) return (void *)rb_get_min(&w->waitq.tree); } -#else /* !CONFIG_WAITQ_FAST: */ +#else /* !CONFIG_WAITQ_SCALABLE: */ #define _WAIT_Q_FOR_EACH(wq, thread_ptr) \ SYS_DLIST_FOR_EACH_CONTAINER(&((wq)->waitq), thread_ptr, \ @@ -79,7 +79,7 @@ static inline struct k_thread *_waitq_head(_wait_q_t *w) return (void *)sys_dlist_peek_head(&w->waitq); } -#endif /* !CONFIG_WAITQ_FAST */ +#endif /* !CONFIG_WAITQ_SCALABLE */ #ifdef __cplusplus } diff --git a/kernel/sched.c b/kernel/sched.c index c0f0925e91a..bc9a9d4cfed 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -12,21 +12,21 @@ #include #include -#ifdef CONFIG_SCHED_DUMB +#if defined(CONFIG_SCHED_DUMB) #define _priq_run_add _priq_dumb_add #define _priq_run_remove _priq_dumb_remove #define _priq_run_best _priq_dumb_best -#else +#elif defined(CONFIG_SCHED_SCALABLE) #define _priq_run_add _priq_rb_add #define _priq_run_remove _priq_rb_remove #define _priq_run_best _priq_rb_best #endif -#ifdef CONFIG_WAITQ_FAST +#if defined(CONFIG_WAITQ_SCALABLE) #define _priq_wait_add _priq_rb_add #define _priq_wait_remove _priq_rb_remove #define _priq_wait_best _priq_rb_best -#else +#elif defined(CONFIG_WAITQ_DUMB) #define _priq_wait_add _priq_dumb_add #define _priq_wait_remove _priq_dumb_remove #define _priq_wait_best _priq_dumb_best @@ -275,7 +275,7 @@ static void pend(struct k_thread *thread, _wait_q_t *wait_q, s32_t timeout) } if (wait_q) { -#ifdef CONFIG_WAITQ_FAST +#ifdef CONFIG_WAITQ_SCALABLE thread->base.pended_on = wait_q; #endif _priq_wait_add(&wait_q->waitq, thread); @@ -294,7 +294,7 @@ void _pend_thread(struct k_thread *thread, _wait_q_t *wait_q, s32_t timeout) static _wait_q_t *pended_on(struct k_thread *thread) { -#ifdef CONFIG_WAITQ_FAST +#ifdef CONFIG_WAITQ_SCALABLE __ASSERT_NO_MSG(thread->base.pended_on); return thread->base.pended_on; @@ -325,7 +325,7 @@ void _unpend_thread_no_timeout(struct k_thread *thread) _mark_thread_as_not_pending(thread); } -#if defined(CONFIG_ASSERT) && defined(CONFIG_WAITQ_FAST) +#if defined(CONFIG_ASSERT) && defined(CONFIG_WAITQ_SCALABLE) thread->base.pended_on = NULL; #endif }