Skip to content

kernel: cbs: adding support for the Constant Bandwidth Server (CBS) #83601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/introduction/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Zephyr offers a large and ever growing number of features including:

* Cooperative and Preemptive Scheduling
* Earliest Deadline First (EDF)
* Constant Bandwidth Server (CBS)
* Meta IRQ scheduling implementing "interrupt bottom half" or "tasklet"
behavior
* Timeslicing: Enables time slicing between preemptible threads of equal
Expand Down
1 change: 1 addition & 0 deletions doc/kernel/services/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ synchronization.

threads/index.rst
scheduling/index.rst
sched_server/cbs.rst
threads/system_threads.rst
threads/workqueue.rst
threads/nothread.rst
Expand Down
374 changes: 374 additions & 0 deletions doc/kernel/services/sched_server/cbs.rst

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions doc/kernel/services/sched_server/example-1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions doc/kernel/services/sched_server/example-2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions doc/kernel/services/sched_server/example-3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion doc/zephyr.doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -2493,7 +2493,7 @@ PREDEFINED = __DOXYGEN__ \
CONFIG_NET_UDP \
CONFIG_SCHED_CPU_MASK \
CONFIG_SCHED_DEADLINE \
CONFIG_SCHED_DEADLINE \
CONFIG_CBS \
CONFIG_SETTINGS_RUNTIME \
CONFIG_SMP \
CONFIG_SYS_CLOCK_EXISTS \
Expand Down
5 changes: 5 additions & 0 deletions include/zephyr/kernel/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ struct k_thread {
_wait_q_t halt_queue;
#endif /* CONFIG_SMP */

#ifdef CONFIG_CBS
/** Constant Bandwidth Server (CBS) struct */
void *cbs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be preferable to forward declare a struct and specifically type this field rather than using a void pointer.

#endif

/** arch-specifics: must always be at the end */
struct _thread_arch arch;
};
Expand Down
169 changes: 169 additions & 0 deletions include/zephyr/sched_server/cbs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (c) 2024 Instituto Superior de Engenharia do Porto (ISEP)
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
*
* @brief Constant Bandwidth Server (CBS) public API
*/

#ifndef ZEPHYR_CBS
#define ZEPHYR_CBS
Comment on lines +12 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use the full path of the function in header guards like in other header files? I realize it's a bit verbose, but it's conventional and unique.


#include <zephyr/kernel.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef CONFIG_CBS

/**
* @defgroup cbs_apis Constant Bandwidth Server (CBS) APIs
* @ingroup kernel_apis
* @{
*/

/**
* @brief CBS job format.
*
* A job pushed to a CBS is a regular function that must
* have a void pointer as an argument (can be NULL if not
* needed) and return nothing. jobs are pushed to the CBS by
* invoking k_cbs_push_job().
*/
typedef void (*cbs_callback_t)(void *arg);

/**
* @cond INTERNAL_HIDDEN
*/

#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER
typedef uint64_t cbs_cycle_t;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little surprised we don't already have a cycle counter type like this.

can you prefix this with a k_ please?

#else
typedef uint32_t cbs_cycle_t;
#endif

struct cbs_job {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefix all of these structs with k_ as well

cbs_callback_t function;
void *arg;
};

struct cbs_budget {
cbs_cycle_t current;
cbs_cycle_t max;
};

struct cbs_arg {
k_timeout_t budget;
k_timeout_t period;
};

struct k_cbs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be a special thread structure for CBS?

The main reason I ask is because Zephyr already has some Kconfig options or defines for specifying maximum thread name length and whether or not there is a field for thread name.

I guess the other reason is to see if there is a convention for naming used by other schedulers for similar types.

The name of the struct leaves me wondering and could maybe be expanded a bit to be more descriptive

struct k_timer timer;
struct k_msgq *queue;
struct k_thread *thread;
struct cbs_budget budget;
cbs_cycle_t period;
cbs_cycle_t abs_deadline;
cbs_cycle_t start_cycle;
cbs_cycle_t bandwidth;
bool is_active;
unsigned int left_shift;
#ifdef CONFIG_CBS_LOG
char name[CONFIG_CBS_THREAD_MAX_NAME_LEN];
#endif
};

extern void cbs_thread(void *job_queue, void *cbs_struct, void *unused);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should be prefixed with k_.

Is it possible for the job queue use named struct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do the prefix thing, sure. But I don't know if I understood the second request, though. Do you want type void * to be changed to an explicit k_msgq * struct? Aint't thread entry points supposed to be formatted as having all arguments as void *?


/** @endcond */

/**
* @brief pushes a job to a CBS queue.
*
* This routine inserts a job (i.e. a regular C function) in a CBS queue,
* which will eventually execute it when the CBS deadline becomes the
* earliest of the taskset. Inserted jobs are always served in a FIFO
* manner. The job queue can store up to @kconfig{CONFIG_CBS_QUEUE_LENGTH}
* jobs at once.
*
* @param cbs Name of the CBS.
* @param job_function Function of the job.
* @param job_arg Argument to be passed to the job function.
* @param timeout Waiting period to push the job, or one of the special
* values K_NO_WAIT and K_FOREVER.
*
* @retval 0 the job was pushed to the CBS queue.
* @retval -ENOMSG if CBS is not defined, returned without waiting or CBS queue purged.
* @retval -EAGAIN if waiting period timed out.
*/
int k_cbs_push_job(struct k_cbs *cbs, cbs_callback_t job_function, void *job_arg,
k_timeout_t timeout);

/**
* @brief Statically define and initialize a Constant Bandwidth Server (CBS).
*
* A CBS is an extension of the Earliest Deadline First (EDF) scheduler
* that allows tasks to be executed virtually isolated from each other,
* in a way that if a task executes for longer than expected it doesn’t
* interfere on the execution of the others. In other words, the CBS
* prevents that a task misbehavior causes other tasks to miss their
* own deadlines.
*
* In a nutshell, the CBS is a work-conserving wrapper for the tasks
* that automatically recalculates their deadlines when they exceed their
* allowed execution time slice. This time slice is known as the CBS "budget".
* The value used to recalculate the deadline is known as the CBS "period".
*
* When a task instance (i.e. job) runs within a CBS, it consumes the "budget".
* When the "budget" runs out, the deadline is postponed by "period" time units
* and the "budget" is replenished to its maximum capacity. when there are no jobs
* left for a CBS to execute, it remains idle and takes no CPU time.
*
* Finally, whenever a new job is pushed to an idle server, the kernel verifies
* if the current pair of (budget, deadline) are proportionally compatible with
* the configured values. If not, the deadline is also recalculated here.
* These two procedures ensure the CBS will never use the CPU more than
* what was configured, and that it will not endanger other thread's deadlines.
*
* Once created, the CBS can be referenced through its name:
*
* @code extern const struct k_cbs <cbs_name>; @endcode
*
* @param cbs_name Name of the CBS.
* @param cbs_budget Budget of the CBS thread, in system ticks.
* Used for triggering deadline recalculations.
* @param cbs_period Period of the CBS thread. in system ticks.
* Used for recalculating the absolute deadline.
* @param cbs_static_priority Static priority of the CBS thread.
*
* @note The CBS is meant to be used alongside the EDF policy, which in Zephyr
* is effectively used as a "tie-breaker" when two threads of equal static priorities
* are ready for execution. Therefore it is recommended that all user threads feature
* the same preemptive static priority (e.g. 5) in order to ensure the scheduling
* to work as expected, and that this same value is passed as @a cbs_static_priority.
*
* @note you should have @kconfig{CONFIG_CBS} enabled in your project to use the CBS.
*/
#define K_CBS_DEFINE(cbs_name, cbs_budget, cbs_period, cbs_static_priority) \
K_MSGQ_DEFINE(queue_##cbs_name, sizeof(struct cbs_job), CONFIG_CBS_QUEUE_LENGTH, 1); \
static struct k_cbs cbs_name = { \
.queue = &queue_##cbs_name, \
.is_active = false, \
}; \
static struct cbs_arg args_##cbs_name = {.budget = cbs_budget, .period = cbs_period}; \
K_THREAD_DEFINE(thread_##cbs_name, CONFIG_CBS_THREAD_STACK_SIZE, cbs_thread, \
(void *)STRINGIFY(cbs_name), (void *)&cbs_name, (void *)&args_##cbs_name, \
cbs_static_priority, 0, CONFIG_CBS_INITIAL_DELAY)
/** @} */ /* end of Constant Bandwidth Server (CBS) APIs */

#endif /* CONFIG_CBS */

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_CBS */
10 changes: 10 additions & 0 deletions kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ list(APPEND kernel_files
)
endif()

if(CONFIG_CBS)
list(APPEND kernel_files
sched_server/cbs.c
)
if(CONFIG_CBS_LOG)
list(APPEND kernel_files
sched_server/cbs_log.c
)
endif() # CONFIG_CBS_LOG
endif()

if(CONFIG_THREAD_MONITOR)
list(APPEND kernel_files
Expand Down
1 change: 1 addition & 0 deletions kernel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1083,3 +1083,4 @@ endmenu
rsource "Kconfig.device"
rsource "Kconfig.vm"
rsource "Kconfig.init"
rsource "Kconfig.sched_server"
130 changes: 130 additions & 0 deletions kernel/Kconfig.sched_server
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# config options for Scheduling Servers
#
# Copyright (c) 2024 Instituto Superior de Engenharia do Porto (ISEP)
# SPDX-License-Identifier: Apache-2.0

menu "Constant Bandwidth Server (CBS)"

config CBS
bool "Constant Bandwidth Server (CBS)"
depends on USE_SWITCH
select SCHED_DEADLINE
default n
help
Enables the Constant Bandwidth Server (CBS), an extension
of the Earliest Deadline First (EDF) scheduler that allows
tasks to execute with guaranteed bandwidth. Selecting
this option automatically enables the EDF scheduler
(CONFIG_SCHED_DEADLINE=y). The CBS is also currently
supported only in targets that use the _arch_switch
context switching primitive (CONFIG_USE_SWITCH=y).

config CBS_LOG
bool "CBS event logging"
depends on CBS
select LOG
default n
help
Enables logging of Constant Bandwidth Server (CBS) events,
such as jobs being pushed to the server, budget consumpion
and CBS thread switching in/out of the CPU. This option
requires CONFIG_CBS to be included within the project.

if CBS_LOG
menu "CBS events to be logged"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The events being recorded here really seem like they would fit better with Zephyr's tracing subsystem. Can they use that mechanism instead?


config CBS_LOG_JOB_PUSH
bool "J_PUSH log"
default y
help
Enables logging of when
a job is pushed to a CBS.

config CBS_LOG_JOB_COMPLETE
bool "J_COMP log"
default y
help
Enables logging of when
a job is completed.

config CBS_LOG_BUDGET_CONDITION
bool "B_COND log"
default y
help
Enables logging of when
the CBS condition gets met,
causing the CBS deadline to
be updated and the budget to
be replenished.

config CBS_LOG_BUDGET_RAN_OUT
bool "B_ROUT log"
default y
help
Enables logging of when
the CBS budget runs out,
causing the CBS deadline to
be postponed and the budget to
be replenished.

config CBS_LOG_SWITCHED_IN
bool "SWT_TO log"
default y
help
Enables logging of when
the CBS thread enters the
CPU for execution of jobs.

config CBS_LOG_SWITCHED_OUT
bool "SWT_AY log"
default y
help
Enables logging of when
the CBS thread leaves the
CPU after completing a job
or being preempted by
another thread.

endmenu
endif

if CBS
menu "CBS constants"

config CBS_THREAD_STACK_SIZE
int "CBS thread memory"
default 2048
help
The amount of memory to be
allocated for the CBS thread.

config CBS_QUEUE_LENGTH
int "CBS job queue length"
default 16
help
The highest number of jobs that can
be pushed to a CBS job queue at once.

config CBS_INITIAL_DELAY
int "CBS thread initial delay"
default 0
help
Scheduling delay (in milliseconds) for the CBS thread.

if CBS_LOG
config CBS_THREAD_MAX_NAME_LEN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This likely duplicates something that already exists in Zephyr, so I would suggest using the existing convention.

int "Max length of the CBS thread name"
default 20
help
CBS thread names are the same as the variables created with
K_CBS_DEFINE and get stored in the k_cbs struct. This option
indicates the maximum name length of the CBS server to be
displayed on log events, including the terminating NULL byte.
Reducing this value wil help conserving memory, but as a tradeoff
it might chop a portion the server name on the aforementioned logs.
endif

endmenu
endif

endmenu # Constant Bandwidth Server (CBS)
16 changes: 16 additions & 0 deletions kernel/include/kswap.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#include <zephyr/sys/barrier.h>
#include <kernel_arch_func.h>

#ifdef CONFIG_CBS
#include <sched_server/cbs_internal.h>
#endif

#ifdef CONFIG_STACK_SENTINEL
extern void z_check_stack_sentinel(void);
#else
Expand Down Expand Up @@ -130,9 +134,21 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
z_smp_release_global_lock(new_thread);
}
#endif /* CONFIG_SMP */
#ifdef CONFIG_CBS
/* if old_thread belongs to an active CBS, stop its budget timer */
if (old_thread->cbs) {
cbs_switched_out(old_thread->cbs);
}
#endif
z_thread_mark_switched_out();
z_sched_switch_spin(new_thread);
z_current_thread_set(new_thread);
#ifdef CONFIG_CBS
/* if new_thread belongs to an active CBS, start its budget timer */
if (new_thread->cbs) {
cbs_switched_in(new_thread->cbs);
}
#endif

#ifdef CONFIG_TIMESLICING
z_reset_time_slice(new_thread);
Expand Down
Loading
Loading