Skip to content
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
13 changes: 11 additions & 2 deletions src/audio/module_adapter/module/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,8 +1024,12 @@ __cold int module_unbind(struct processing_module *mod, const struct bind_info *

uint32_t module_get_deadline(struct processing_module *mod)
{
/* a DP module can write mod: read the count once and bound it */
size_t num_of_sinks = mod->num_of_sinks;
uint32_t deadline;

num_of_sinks = MIN(num_of_sinks, ARRAY_SIZE(mod->sinks));

/* LL modules have no deadline - it is always "now" */
if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL)
return 0;
Expand All @@ -1036,9 +1040,14 @@ uint32_t module_get_deadline(struct processing_module *mod)

deadline = UINT32_MAX;
/* calculate the shortest LFT for all sinks */
for (size_t i = 0; i < mod->num_of_sinks; i++) {
uint32_t sink_lft = sink_get_last_feeding_time(mod->sinks[i]);
for (size_t i = 0; i < num_of_sinks; i++) {
struct sof_sink *sink = mod->sinks[i];
uint32_t sink_lft;

if (!sink)
continue;

sink_lft = sink_get_last_feeding_time(sink);
deadline = MIN(deadline, sink_lft);
}

Expand Down
53 changes: 51 additions & 2 deletions src/include/sof/schedule/dp_schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,62 @@ __syscall void scheduler_dp_internal_free(struct task *task);
/**
* @brief Tick notification from the Low-Latency (LL) timer domain to the DP scheduler.
*
* Invoked on each core after all LL tasks complete. Evaluates readiness for all
* queued DP tasks, updates their absolute deadlines, and signals worker threads.
* Evaluates readiness for all queued DP tasks, updates their absolute
* deadlines, and signals worker threads. Not available with
* CONFIG_SOF_USERSPACE_APPLICATION and CONFIG_SOF_USERSPACE_LL, see
* scheduler_dp_ll_post_tick().
*/
__syscall void scheduler_dp_ll_tick(void);

/** @brief scheduler_dp_kick flag: signal the DP thread to process data */
#define SCHEDULER_DP_KICK_PROCESS BIT(0)
/** @brief scheduler_dp_kick flag: set the DP thread deadline */
#define SCHEDULER_DP_KICK_DEADLINE BIT(1)

/** @brief Maximum number of entries passed to one scheduler_dp_kick() call */
#define SCHEDULER_DP_KICK_MAX 8

/** @brief Result of the DP readiness and deadline calculation for one task */
struct scheduler_dp_kick {
const struct task *task; /**< DP task, only used to look up its record */
uint32_t flags; /**< SCHEDULER_DP_KICK_* */
uint32_t deadline; /**< deadline in cycles from now */
};

/**
* @brief Apply DP readiness and deadline results to the DP threads.
*
* The DP thread kernel objects are looked up from the kernel-only DP task
* records, never from DP task memory. Entries for unknown tasks and for
* tasks of other cores are skipped. Only the LL user thread may call this,
* or the LL scheduler in kernel mode without CONFIG_SOF_USERSPACE_LL.
*
* @param[in] kicks Array of results.
* @param[in] count Number of entries, at most SCHEDULER_DP_KICK_MAX.
* @return Number of skipped entries.
*/
__syscall int scheduler_dp_kick(const struct scheduler_dp_kick *kicks, uint32_t count);
#include <zephyr/syscalls/dp_schedule.h>
#endif

/**
* @brief Run the DP scheduler after an LL tick.
*
* Called by the LL scheduler on each core after all LL tasks complete. With
* CONFIG_SOF_USERSPACE_APPLICATION and CONFIG_SOF_USERSPACE_LL, the DP task
* memory is writable by the DP threads, so the calculation runs in the calling
* LL user thread, and only its results are passed to the kernel with
* scheduler_dp_kick(). Otherwise this calls scheduler_dp_ll_tick().
*/
void scheduler_dp_ll_post_tick(void);

#if CONFIG_SOF_BOOT_TEST_STANDALONE && CONFIG_SOF_USERSPACE_APPLICATION
/* Test hooks: add and remove kernel-only DP task records without a DP task */
int scheduler_dp_test_record_add(struct task *task, struct k_event *event,
struct k_thread *thread, uint16_t core);
void scheduler_dp_test_record_del(struct task *task);
#endif

/**
* @brief Extract telemetry information about Data Processing scheduler tasks.
*
Expand Down
10 changes: 8 additions & 2 deletions src/include/sof/userspace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ syscall by subsystem:
| CPU | `cpu_get_id` | `sof/lib/cpu.h` | `zephyr/syscall/cpu.c` / `zephyr/lib/cpu.c` |
| Module management | `mod_balloc_align`, `mod_alloc_ext`, `mod_free`, `mod_free_all`, `mod_data_blob_handler_new`, `mod_data_blob_set`, `mod_fast_get` | `sof/audio/module_adapter/module/generic.h` | scattered: `audio/module_adapter/module/generic.c` |
| Library manager | `lib_manager_free_module` | `sof/lib_manager.h` | scattered: `kernel/library_manager/lib_manager.c` |
| Scheduling | `zephyr_ll_task_sem_alloc`, `zephyr_ll_task_sem_free`, `scheduler_dp_internal_free`, `scheduler_dp_ll_tick` | `sof/schedule/ll_schedule_domain.h`, `sof/schedule/dp_schedule.h` | scattered: `schedule/zephyr_ll.c`, `schedule/zephyr_dp_schedule*.c` |
| Scheduling | `zephyr_ll_task_sem_alloc`, `zephyr_ll_task_sem_free`, `scheduler_dp_internal_free`, `scheduler_dp_ll_tick`, `scheduler_dp_kick` | `sof/schedule/ll_schedule_domain.h`, `sof/schedule/dp_schedule.h` | scattered: `schedule/zephyr_ll.c`, `schedule/zephyr_dp_schedule*.c` |
| Debug | `debug_stream_slot_send_record` | `user/debug_stream_slot.h` | scattered: `debug/debug_stream/debug_stream_slot.c` |
| KPB | `kpb_notifier_init` | `sof/audio/kpb.h` | scattered: `audio/kpb.c` |

Expand All @@ -141,9 +141,15 @@ from user-writable data such as `ipc_user->thread[]`.
|-----------------|----------|
| Any user thread, including DP module threads | `cpu_get_id`, all module management syscalls (`mod_*`), `ipc_msg_send`, `ipc_msg_list_remove`, `send_resource_notif` |
| IPC user thread only | `ipc_compound_pre_start`, `ipc_compound_post_start`, `ipc_wait_for_compound_msg`, `lib_manager_free_module` |
| LL user thread only | `scheduler_dp_ll_tick` |
| LL user thread only | `scheduler_dp_ll_tick`, `scheduler_dp_kick` |
| LL or IPC user thread | all other SOF syscalls |

With `CONFIG_SOF_USERSPACE_APPLICATION`, the DP task list and task memory are writable by DP
threads, so the kernel does not walk them. The DP readiness and deadline calculation runs in
the LL user thread (`scheduler_dp_ll_post_tick()`), which passes its results to
`scheduler_dp_kick()`; the kernel takes the DP thread and event objects from its own task
records. `scheduler_dp_ll_tick` is not built when `CONFIG_SOF_USERSPACE_LL` is also set.

DP modules only get the syscalls needed to implement the module interface. That includes
sending notifications: modules queue messages from `mod_ipc_msg_init()` with
`ipc_msg_send()` and free them with `mod_ipc_msg_free()`, and `dp_task_run()` reports
Expand Down
2 changes: 1 addition & 1 deletion src/schedule/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ The DP scheduler (`zephyr_dp_schedule.c`) manages asynchronous, compute-heavy ta
### Architecture

- **Separate Threads**: Unlike LL which multiplexes tasks inside a single thread, **each DP task is assigned its own Zephyr thread**.
- **Wakeup Mechanism**: DP scheduling is evaluated at the end of each LL tick (`scheduler_dp_recalculate()`).
- **Wakeup Mechanism**: DP scheduling is evaluated at the end of each LL tick (`scheduler_dp_recalculate()`). With user-space LL and DP, this runs in the LL user thread and the kernel applies the results with `scheduler_dp_kick()`.
- **Readiness**: It checks if a component has sufficient data across its sinks and sources. If so, it transitions to `RUNNING` and signals the individual DP thread via a Zephyr Event object.
- **Deadlines**: Once ready, the DP thread computes its deadline absolute timestamp (`module_get_deadline()`) and calls `k_thread_absolute_deadline_set()`, submitting to the Zephyr kernel's EDF scheduler.

Expand Down
48 changes: 46 additions & 2 deletions src/schedule/zephyr_dp_schedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,25 @@ static enum task_state scheduler_dp_ll_tick_dummy(void *data)
* needed 1.2ms for processing - but the example would be too complicated)
*/

#if CONFIG_SOF_USERSPACE_APPLICATION && CONFIG_SOF_USERSPACE_LL
/* Runs in the LL user thread, see scheduler_dp_ll_post_tick() documentation */
void scheduler_dp_ll_post_tick(void)
{
struct scheduler_dp_data *dp_sch = scheduler_get_user_data(SOF_SCHEDULE_DP);
unsigned int lock_key;

/*
* The LL thread is granted the DP lock when the first DP task of its
* core is created, so there is nothing to do and no access before.
*/
if (!dp_sch || list_is_empty(&dp_sch->tasks))
return;

lock_key = scheduler_dp_lock(cpu_get_id());
scheduler_dp_recalculate(dp_sch);
scheduler_dp_unlock(lock_key);
}
#else
void z_impl_scheduler_dp_ll_tick(void)
{
unsigned int lock_key;
Expand Down Expand Up @@ -259,6 +278,12 @@ void z_vrfy_scheduler_dp_ll_tick(void)
#include <zephyr/syscalls/scheduler_dp_ll_tick_mrsh.c>
#endif

void scheduler_dp_ll_post_tick(void)
{
scheduler_dp_ll_tick();
}
#endif

#if CONFIG_SOF_USERSPACE_APPLICATION
static int scheduler_dp_task_cancel(void *data, struct task *task)
{
Expand Down Expand Up @@ -392,16 +417,35 @@ __cold int scheduler_dp_init(void)

void scheduler_get_task_info_dp(struct scheduler_props *scheduler_props, uint32_t *data_off_size)
{
unsigned int lock_key;

scheduler_props->processing_domain = COMP_PROCESSING_DOMAIN_DP;
#if CONFIG_SOF_USERSPACE_APPLICATION
/*
* The task list links are in DP task memory, which DP threads can
* write. Count the kernel-only task records instead of walking it.
*/
struct task_props *task_props = (struct task_props *)(scheduler_props + 1);
unsigned int count = scheduler_dp_task_count(cpu_get_id());
unsigned int i;

scheduler_props->core_id = cpu_get_id();
scheduler_props->task_count = count;
*data_off_size += sizeof(*scheduler_props);

for (i = 0; i < count; i++) {
task_props[i].task_id = 0;
task_props[i].module_instance_count = 0;
*data_off_size += sizeof(*task_props);
}
#else
#if CONFIG_SOF_USERSPACE_LL
struct scheduler_dp_data *dp_sch = scheduler_get_user_data(SOF_SCHEDULE_DP);
#else
struct scheduler_dp_data *dp_sch = scheduler_get_data(SOF_SCHEDULE_DP);
#endif
unsigned int lock_key;

lock_key = scheduler_dp_lock(cpu_get_id());
scheduler_get_task_info(scheduler_props, data_off_size, &dp_sch->tasks);
scheduler_dp_unlock(lock_key);
#endif
}
9 changes: 9 additions & 0 deletions src/schedule/zephyr_dp_schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ struct task_dp_pdata {
*/
void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch);

#if CONFIG_SOF_USERSPACE_APPLICATION
/**
* @brief Count the DP tasks of a core from the kernel-only DP task records.
* @param[in] core DSP core.
* @return Number of scheduled DP tasks on @p core. Kernel context only.
*/
unsigned int scheduler_dp_task_count(unsigned int core);
#endif

/**
* @brief Worker thread entry point function for Data Processing tasks.
*
Expand Down
Loading
Loading