From 3d7d797a574b4294a6f36e630ab881ed782ec7a2 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Fri, 4 Aug 2017 14:21:38 +0200 Subject: [PATCH 1/3] core/sched: add sched_change_priority() function Co-authored-by: Marian Buschsieweke --- core/include/sched.h | 16 +++++- core/sched.c | 119 ++++++++++++++++++++++++++++++------------- 2 files changed, 100 insertions(+), 35 deletions(-) diff --git a/core/include/sched.h b/core/include/sched.h index db9fad5463..7a423a48a9 100644 --- a/core/include/sched.h +++ b/core/include/sched.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014-2017 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -75,6 +75,7 @@ * @brief Scheduler API definition * * @author Kaspar Schleiser + * @author Hauke Petersen */ #ifndef SCHED_H @@ -243,6 +244,19 @@ extern clist_node_t sched_runqueues[SCHED_PRIO_LEVELS]; */ NORETURN void sched_task_exit(void); +/** + * @brief Change the priority of the given thread + * + * @note This functions expects interrupts to be disabled when called! + * + * @pre (thread != NULL) + * @pre (priority < SCHED_PRIO_LEVELS) + * + * @param[in,out] thread target thread + * @param[in] priority new priority to assign to @p thread + */ +void sched_change_priority(thread_t *thread, uint8_t priority); + /** * @brief Set CPU to idle mode (CPU dependent) * diff --git a/core/sched.c b/core/sched.c index b6eb15c28f..72f269f2b0 100644 --- a/core/sched.c +++ b/core/sched.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014-2017 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -15,6 +15,7 @@ * * @author Kaspar Schleiser * @author René Kijewski + * @author Hauke Petersen * * @} */ @@ -86,21 +87,21 @@ static void (*sched_cb)(kernel_pid_t active_thread, * and readout away, switching between the two orders depending on the CLZ * instruction availability */ -static inline void _set_runqueue_bit(thread_t *process) +static inline void _set_runqueue_bit(uint8_t priority) { #if defined(BITARITHM_HAS_CLZ) - runqueue_bitcache |= BIT31 >> process->priority; + runqueue_bitcache |= BIT31 >> priority; #else - runqueue_bitcache |= 1 << process->priority; + runqueue_bitcache |= 1 << priority; #endif } -static inline void _clear_runqueue_bit(thread_t *process) +static inline void _clear_runqueue_bit(uint8_t priority) { #if defined(BITARITHM_HAS_CLZ) - runqueue_bitcache &= ~(BIT31 >> process->priority); + runqueue_bitcache &= ~(BIT31 >> priority); #else - runqueue_bitcache &= ~(1 << process->priority); + runqueue_bitcache &= ~(1 << priority); #endif } @@ -214,41 +215,54 @@ thread_t *__attribute__((used)) sched_run(void) return next_thread; } +/* Note: Forcing the compiler to inline this function will reduce .text for applications + * not linking in sched_change_priority(), which benefits the vast majority of apps. + */ +static inline __attribute__((always_inline)) void _runqueue_push(thread_t *thread, uint8_t priority) +{ + DEBUG("sched_set_status: adding thread %" PRIkernel_pid " to runqueue %" PRIu8 ".\n", + thread->pid, priority); + clist_rpush(&sched_runqueues[priority], &(thread->rq_entry)); + _set_runqueue_bit(priority); + + /* some thread entered a runqueue + * if it is the active runqueue + * inform the runqueue_change callback */ +#if (IS_USED(MODULE_SCHED_RUNQ_CALLBACK)) + thread_t *active_thread = thread_get_active(); + if (active_thread && active_thread->priority == priority) { + sched_runq_callback(priority); + } +#endif +} + +/* Note: Forcing the compiler to inline this function will reduce .text for applications + * not linking in sched_change_priority(), which benefits the vast majority of apps. + */ +static inline __attribute__((always_inline)) void _runqueue_pop(thread_t *thread) +{ + DEBUG("sched_set_status: removing thread %" PRIkernel_pid " from runqueue %" PRIu8 ".\n", + thread->pid, thread->priority); + clist_lpop(&sched_runqueues[thread->priority]); + + if (!sched_runqueues[thread->priority].next) { + _clear_runqueue_bit(thread->priority); +#if (IS_USED(MODULE_SCHED_RUNQ_CALLBACK)) + sched_runq_callback(thread->priority); +#endif + } +} + void sched_set_status(thread_t *process, thread_status_t status) { if (status >= STATUS_ON_RUNQUEUE) { if (!(process->status >= STATUS_ON_RUNQUEUE)) { - DEBUG( - "sched_set_status: adding thread %" PRIkernel_pid " to runqueue %" PRIu8 ".\n", - process->pid, process->priority); - clist_rpush(&sched_runqueues[process->priority], - &(process->rq_entry)); - _set_runqueue_bit(process); - - /* some thread entered a runqueue - * if it is the active runqueue - * inform the runqueue_change callback */ -#if (IS_USED(MODULE_SCHED_RUNQ_CALLBACK)) - thread_t *active_thread = thread_get_active(); - if (active_thread && active_thread->priority == process->priority) { - sched_runq_callback(process->priority); - } -#endif + _runqueue_push(process, process->priority); } } else { if (process->status >= STATUS_ON_RUNQUEUE) { - DEBUG( - "sched_set_status: removing thread %" PRIkernel_pid " from runqueue %" PRIu8 ".\n", - process->pid, process->priority); - clist_lpop(&sched_runqueues[process->priority]); - - if (!sched_runqueues[process->priority].next) { - _clear_runqueue_bit(process); -#if (IS_USED(MODULE_SCHED_RUNQ_CALLBACK)) - sched_runq_callback(process->priority); -#endif - } + _runqueue_pop(process); } } @@ -302,3 +316,40 @@ void sched_register_cb(void (*callback)(kernel_pid_t, kernel_pid_t)) sched_cb = callback; } #endif + +void sched_change_priority(thread_t *thread, uint8_t priority) +{ + assert(thread && (priority < SCHED_PRIO_LEVELS)); + + if (thread->priority == priority) { + return; + } + + unsigned irq_state = irq_disable(); + + if (thread_is_active(thread)) { + _runqueue_pop(thread); + _runqueue_push(thread, priority); + } + thread->priority = priority; + + irq_restore(irq_state); + + thread_t *active = thread_get_active(); + + if ((active == thread) + || ((active != NULL) && (active->priority > priority) && thread_is_active(thread)) + ) { + /* If the change in priority would result in a different decision of + * the scheduler, we need to yield to make sure the change in priority + * takes effect immediately. This can be due to one of the following: + * + * 1) The priority of the thread currently running has been reduced + * (higher numeric value), so that other threads now have priority + * over the currently running. + * 2) The priority of a pending thread has been increased (lower numeric value) so that it + * now has priority over the running thread. + */ + thread_yield_higher(); + } +} From ed9bf358c565892bbd67712ac8f35e4dc0b11e51 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 1 Nov 2021 12:41:38 +0100 Subject: [PATCH 2/3] sys/shell: Add nice shell command via module nice --- makefiles/pseudomodules.inc.mk | 1 + sys/shell/commands/Makefile | 3 ++ sys/shell/commands/sc_nice.c | 53 +++++++++++++++++++++++++++++ sys/shell/commands/shell_commands.c | 7 ++++ 4 files changed, 64 insertions(+) create mode 100644 sys/shell/commands/sc_nice.c diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index e3607b0c24..830d1ae611 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -108,6 +108,7 @@ PSEUDOMODULES += nimble_autoconn_% PSEUDOMODULES += newlib PSEUDOMODULES += newlib_gnu_source PSEUDOMODULES += newlib_nano +PSEUDOMODULES += nice PSEUDOMODULES += nrf24l01p_ng_diagnostics PSEUDOMODULES += openthread PSEUDOMODULES += picolibc diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 1ceaf16de0..17ff4c5959 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -14,6 +14,9 @@ endif ifneq (,$(filter mci,$(USEMODULE))) SRC += sc_disk.c endif +ifneq (,$(filter nice,$(USEMODULE))) + SRC += sc_nice.c +endif ifneq (,$(filter periph_pm,$(USEMODULE))) SRC += sc_pm.c endif diff --git a/sys/shell/commands/sc_nice.c b/sys/shell/commands/sc_nice.c new file mode 100644 index 0000000000..2905f9c389 --- /dev/null +++ b/sys/shell/commands/sc_nice.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief A shell command to change the niceness (inverse priority) of a thread + * + * @note Enable this by using the modules shell_commands and nice + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include + +#include "sched.h" +#include "thread.h" + +int _sc_nice(int argc, char **argv) +{ + if (argc != 3) { + printf("Usage: %s \n" + "Note: Lower number means higher priority (niceness)\n", + argv[0]); + return EXIT_FAILURE; + } + + /* Note: thread_get() does bounds checking and returns NULL on out of bounds PID */ + thread_t *thread = thread_get(atoi(argv[1])); + if (!thread) { + printf("No active thread found for ID \"%s\"\n", argv[1]); + return EXIT_FAILURE; + } + + uint8_t prio = atoi(argv[2]); + if (prio >= SCHED_PRIO_LEVELS) { + printf("Priority \"%s\" is invalid (try 0..%u)\n", + argv[2], (unsigned)SCHED_PRIO_LEVELS - 1); + } + + sched_change_priority(thread, prio); + return EXIT_SUCCESS; +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index d3d8b97790..a4b0b502da 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -187,6 +187,10 @@ extern int _i2c_scan(int argc, char **argv); extern int _loramac_handler(int argc, char **argv); #endif +#ifdef MODULE_NICE +extern int _sc_nice(int argc, char **argv); +#endif + #ifdef MODULE_NIMBLE_NETIF extern int _nimble_netif_handler(int argc, char **argv); #endif @@ -263,6 +267,9 @@ const shell_command_t _shell_command_list[] = { #ifdef MODULE_GNRC_IPV6_NIB {"nib", "Configure neighbor information base", _gnrc_ipv6_nib}, #endif +#ifdef MODULE_NICE + {"nice", "Change priority of an active thread", _sc_nice}, +#endif #ifdef MODULE_NETSTATS_NEIGHBOR {"neigh", "Show neighbor statistics", _netstats_nb}, #endif From 755652b6cf83b387618345a6198cf3af3ee614b1 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 1 Nov 2021 12:43:02 +0100 Subject: [PATCH 3/3] tests/sched_change_priority: add test app --- tests/sched_change_priority/Makefile | 13 +++ tests/sched_change_priority/Makefile.ci | 9 ++ tests/sched_change_priority/main.c | 102 ++++++++++++++++++++ tests/sched_change_priority/tests/01-run.py | 17 ++++ 4 files changed, 141 insertions(+) create mode 100644 tests/sched_change_priority/Makefile create mode 100644 tests/sched_change_priority/Makefile.ci create mode 100644 tests/sched_change_priority/main.c create mode 100755 tests/sched_change_priority/tests/01-run.py diff --git a/tests/sched_change_priority/Makefile b/tests/sched_change_priority/Makefile new file mode 100644 index 0000000000..a39f492c4a --- /dev/null +++ b/tests/sched_change_priority/Makefile @@ -0,0 +1,13 @@ +include ../Makefile.tests_common + +USEMODULE += nice +USEMODULE += ps +USEMODULE += shell +USEMODULE += shell_commands + +# Use a terminal that does not introduce extra characters into the stream. +RIOT_TERMINAL ?= socat + +APP_SHELL_FMT ?= NONE + +include $(RIOTBASE)/Makefile.include diff --git a/tests/sched_change_priority/Makefile.ci b/tests/sched_change_priority/Makefile.ci new file mode 100644 index 0000000000..b5de876337 --- /dev/null +++ b/tests/sched_change_priority/Makefile.ci @@ -0,0 +1,9 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + atmega328p-xplained-mini \ + nucleo-l011k4 \ + # diff --git a/tests/sched_change_priority/main.c b/tests/sched_change_priority/main.c new file mode 100644 index 0000000000..68a9dd7a6e --- /dev/null +++ b/tests/sched_change_priority/main.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for sched_change_priority / nice + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include + +#include "architecture.h" +#include "sched.h" +#include "shell.h" +#include "shell_commands.h" +#include "thread.h" + +static int sc_hint(int argc, char **argv); + +static WORD_ALIGNED char t2_stack[THREAD_STACKSIZE_TINY]; +static WORD_ALIGNED char t3_stack[THREAD_STACKSIZE_DEFAULT]; +static kernel_pid_t t3_pid; + +static const shell_command_t cmds[] = { + { "hint", "Display the correct invocation of nice for this teset", sc_hint }, + { NULL, NULL, NULL } +}; + +/* + * Note: An extra shell command just for displaying this hint is very much of + * an overkill for a human being, especially since the ps command already + * provides all the details. However, for automatic testing this makes it + * easy to extract the pid of t3 and the numeric value of + * THREAD_PRIORITY_MAIN - 1, resulting in more robust automatic testing. + * A shell command also has the convenient side effect of synchronizing + * with the shell. + */ +static int sc_hint(int argc, char **argv) +{ + (void)argc; + (void)argv; + + printf("Run \"nice %" PRIkernel_pid " %u\"\n", + t3_pid, (unsigned)THREAD_PRIORITY_MAIN - 1); + + return EXIT_SUCCESS; +} + +static void *t2_func(void *unused) +{ + (void)unused; + + while (1) { + /* blocking t3 from running with busy loop while t3 has lower prio than me */ + } + + return NULL; +} + +static void *t3_func(void *unused) +{ + (void)unused; + + while (1) { + uint8_t prio = THREAD_PRIORITY_MAIN + 2; + printf("[t3] Setting my priority to THREAD_PRIORITY_MAIN + 2 = %u\n", + (unsigned)prio); + sched_change_priority(thread_get_active(), prio); + puts("[t3] Running again."); + } + + return NULL; +} + +int main(void) +{ + thread_create(t2_stack, sizeof(t2_stack), THREAD_PRIORITY_MAIN + 1, + THREAD_CREATE_STACKTEST, t2_func, NULL, "t2"); + + t3_pid = thread_create(t3_stack, sizeof(t3_stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, t3_func, NULL, "t3"); + + puts("[main] Use shell command \"nice\" to increase prio of t3.\n" + "[main] If it works, it will run again. The \"hint\" cmd can be useful."); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(cmds, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/sched_change_priority/tests/01-run.py b/tests/sched_change_priority/tests/01-run.py new file mode 100755 index 0000000000..c6ebaf4aee --- /dev/null +++ b/tests/sched_change_priority/tests/01-run.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import sys +from testrunner import run + + +def testfunc(child): + child.sendline("hint") + child.expect(r"Run \"nice (\d+) (\d+)\"\r\n") + pid = int(child.match.group(1)) + prio = int(child.match.group(2)) + child.sendline("nice {} {}".format(pid, prio)) + child.expect_exact('[t3] Running again.') + + +if __name__ == "__main__": + sys.exit(run(testfunc))