From 748a6a788cea06d8c11117501bc5bb2e08a0cbf4 Mon Sep 17 00:00:00 2001 From: Bartosz Kokoszko Date: Tue, 12 Mar 2019 08:51:23 +0100 Subject: [PATCH] scheduler: add new generic scheduler api This commit: 1. Renames specific schedulers: - schedule (.c/.h) -> edf_schedule (.c/.h) - work (.c/.h) -> ll_schedule (.c/.h) 2. Implements new generic scheduler api based on generic struct with scheduler ops. Every specific scheduler exports struct with pointers to its specific functions. 3. Makes edf and ll using generic api (exports structure with pointers to its own specific functions) 4. Updates host and pipeline tests due to scheduler changes. Signed-off-by: Bartosz Kokoszko --- src/arch/xtensa/CMakeLists.txt | 2 - src/arch/xtensa/smp/cpu.c | 4 +- src/arch/xtensa/smp/idc.c | 10 +- src/arch/xtensa/smp/init.c | 5 +- src/arch/xtensa/smp/schedule.c | 4 +- src/arch/xtensa/smp/xtos/xtos-structs.h | 4 +- src/arch/xtensa/task.c | 24 +- src/arch/xtensa/up/schedule.c | 4 +- src/audio/eq_iir.c | 2 +- src/audio/kpb.c | 10 +- src/audio/pipeline.c | 25 +- src/audio/pipeline_static.c | 6 +- src/audio/selector.c | 1 - src/audio/src.c | 2 +- src/audio/tone.c | 2 +- src/audio/volume.c | 12 +- src/audio/volume.h | 2 +- src/drivers/dw/dma.c | 3 +- src/drivers/dw/ssi-spi.c | 13 +- src/drivers/intel/baytrail/ipc.c | 4 +- src/drivers/intel/cavs/dmic.c | 9 +- src/drivers/intel/cavs/hda-dma.c | 2 +- src/drivers/intel/cavs/ipc.c | 4 +- src/drivers/intel/cavs/sue-ipc.c | 4 +- src/drivers/intel/haswell/ipc.c | 4 +- src/host/CMakeLists.txt | 2 + src/host/common_test.c | 4 +- src/host/edf_schedule.c | 141 +++++ src/host/file.c | 1 - src/host/ll_schedule.c | 49 ++ src/host/schedule.c | 101 ++-- src/include/host/common_test.h | 2 +- .../up/work.c => include/host/edf_schedule.h} | 22 +- .../smp/work.c => include/host/ll_schedule.h} | 23 +- src/include/sof/agent.h | 4 +- src/include/sof/dma-trace.h | 4 +- src/include/sof/dmic.h | 2 +- src/include/sof/edf_schedule.h | 70 +++ src/include/sof/init.h | 4 - src/include/sof/ipc.h | 2 +- src/include/sof/{work.h => ll_schedule.h} | 68 +-- src/include/sof/schedule.h | 173 +++--- src/include/sof/ssp.h | 2 +- src/include/sof/task.h | 2 +- src/include/sof/timer.h | 9 + src/include/sof/trace.h | 4 +- src/include/sof/wait.h | 10 +- src/include/uapi/user/trace.h | 3 +- src/init/init.c | 6 +- src/ipc/handler.c | 5 +- src/lib/CMakeLists.txt | 3 +- src/lib/agent.c | 9 +- src/lib/dma-trace.c | 11 +- src/lib/edf_schedule.c | 549 ++++++++++++++++++ src/lib/{work.c => ll_schedule.c} | 386 +++++++----- src/lib/schedule.c | 481 +++------------ src/lib/wait.c | 6 +- src/platform/baytrail/platform.c | 9 +- src/platform/haswell/platform.c | 9 +- src/platform/intel/cavs/platform.c | 7 +- src/tasks/audio.c | 2 +- test/cmocka/src/audio/kpb/kpb_mock.c | 10 +- test/cmocka/src/audio/mixer/mock.c | 2 +- .../pipeline/pipeline_connect_upstream.c | 2 +- .../pipeline/pipeline_connection_mocks.h | 2 +- .../cmocka/src/audio/pipeline/pipeline_free.c | 4 +- .../src/audio/pipeline/pipeline_mocks.c | 30 +- .../src/audio/pipeline/pipeline_mocks.h | 2 +- .../audio/pipeline/pipeline_mocks_rzalloc.c | 2 +- test/cmocka/src/audio/pipeline/pipeline_new.c | 32 - .../audio/pipeline/pipeline_new_allocation.c | 2 +- .../pipeline/pipeline_new_allocation_mocks.c | 2 +- tools/logger/convert.c | 3 +- 73 files changed, 1479 insertions(+), 961 deletions(-) create mode 100644 src/host/edf_schedule.c create mode 100644 src/host/ll_schedule.c rename src/{arch/xtensa/up/work.c => include/host/edf_schedule.h} (79%) rename src/{arch/xtensa/smp/work.c => include/host/ll_schedule.h} (77%) create mode 100644 src/include/sof/edf_schedule.h rename src/include/sof/{work.h => ll_schedule.h} (51%) create mode 100644 src/lib/edf_schedule.c rename src/lib/{work.c => ll_schedule.c} (52%) diff --git a/src/arch/xtensa/CMakeLists.txt b/src/arch/xtensa/CMakeLists.txt index 6393c11337da..68c09e76004e 100644 --- a/src/arch/xtensa/CMakeLists.txt +++ b/src/arch/xtensa/CMakeLists.txt @@ -161,7 +161,6 @@ if(CONFIG_SMP) smp/notifier.c smp/schedule.c smp/task.c - smp/work.c ) else() add_subdirectory(up) @@ -173,7 +172,6 @@ else() up/notifier.c up/schedule.c up/task.c - up/work.c ) endif() diff --git a/src/arch/xtensa/smp/cpu.c b/src/arch/xtensa/smp/cpu.c index c5890b75c607..56716fc1252c 100644 --- a/src/arch/xtensa/smp/cpu.c +++ b/src/arch/xtensa/smp/cpu.c @@ -105,9 +105,7 @@ void cpu_power_down_core(void) idc_free(); - scheduler_free(); - - free_system_workq(); + schedule_free(); free_system_notify(); diff --git a/src/arch/xtensa/smp/idc.c b/src/arch/xtensa/smp/idc.c index b87117ff2a33..aca26acb498e 100644 --- a/src/arch/xtensa/smp/idc.c +++ b/src/arch/xtensa/smp/idc.c @@ -98,7 +98,7 @@ static void idc_irq_handler(void *arg) idc->received_msg.extension = idctefc & IPC_IDCTEFC_MSG_MASK; - schedule_task(&idc->idc_task, 0, IDC_DEADLINE); + schedule_task(&idc->idc_task, 0, IDC_DEADLINE, 0); break; } @@ -263,7 +263,7 @@ static void idc_cmd(struct idc_msg *msg) * \brief Handles received IDC message. * \param[in,out] data Pointer to IDC data. */ -static void idc_do_cmd(void *data) +static uint64_t idc_do_cmd(void *data) { struct idc *idc = data; int core = arch_cpu_get_id(); @@ -279,6 +279,8 @@ static void idc_do_cmd(void *data) /* enable BUSY interrupt */ idc_write(IPC_IDCCTL, core, idc->busy_bit_mask | idc->done_bit_mask); + + return 0; } /** @@ -343,8 +345,8 @@ int arch_idc_init(void) (*idc)->done_bit_mask = idc_get_done_bit_mask(core); /* process task */ - schedule_task_init(&(*idc)->idc_task, idc_do_cmd, *idc); - schedule_task_config(&(*idc)->idc_task, TASK_PRI_IDC, core); + schedule_task_init(&(*idc)->idc_task, SOF_SCHEDULE_EDF, + SOF_TASK_PRI_IDC, idc_do_cmd, *idc, core, 0); /* configure interrupt */ ret = interrupt_register(PLATFORM_IDC_INTERRUPT(core), IRQ_AUTO_UNMASK, diff --git a/src/arch/xtensa/smp/init.c b/src/arch/xtensa/smp/init.c index b5a0cee1f2f6..c4ad45a8c90d 100644 --- a/src/arch/xtensa/smp/init.c +++ b/src/arch/xtensa/smp/init.c @@ -123,13 +123,10 @@ int slave_core_init(struct sof *sof) init_system_notify(sof); trace_point(TRACE_BOOT_SYS_SCHED); - scheduler_init(sof); + scheduler_init(); platform_interrupt_init(); - trace_point(TRACE_BOOT_SYS_WORK); - init_system_workq(&platform_generic_queue[cpu_get_id()]); - /* initialize IDC mechanism */ trace_point(TRACE_BOOT_PLATFORM_IDC); err = idc_init(); diff --git a/src/arch/xtensa/smp/schedule.c b/src/arch/xtensa/smp/schedule.c index a759eb7815a4..5333871d468a 100644 --- a/src/arch/xtensa/smp/schedule.c +++ b/src/arch/xtensa/smp/schedule.c @@ -39,9 +39,9 @@ #include #include -struct schedule_data **arch_schedule_get(void) +struct schedule_data **arch_schedule_get_data(void) { struct core_context *ctx = (struct core_context *)cpu_read_threadptr(); - return &ctx->sch; + return &ctx->sch_data; } diff --git a/src/arch/xtensa/smp/xtos/xtos-structs.h b/src/arch/xtensa/smp/xtos/xtos-structs.h index a17a7d6ed4bf..bb6257f675c1 100644 --- a/src/arch/xtensa/smp/xtos/xtos-structs.h +++ b/src/arch/xtensa/smp/xtos/xtos-structs.h @@ -37,7 +37,6 @@ struct idc; struct irq_task; struct notify; struct schedule_data; -struct work_queue; struct thread_data { xtos_structures_pointers xtos_ptrs; @@ -58,8 +57,7 @@ struct core_context { struct irq_task *irq_low_task; struct irq_task *irq_med_task; struct irq_task *irq_high_task; - struct schedule_data *sch; - struct work_queue *queue; + struct schedule_data *sch_data; struct notify *notify; struct idc *idc; }; diff --git a/src/arch/xtensa/task.c b/src/arch/xtensa/task.c index 1aba6bed773f..ede2af698375 100644 --- a/src/arch/xtensa/task.c +++ b/src/arch/xtensa/task.c @@ -38,9 +38,7 @@ #include #include #include - #include - #include /** @@ -53,13 +51,13 @@ static uint32_t task_get_irq(struct task *task) uint32_t irq; switch (task->priority) { - case TASK_PRI_MED + 1 ... TASK_PRI_LOW: + case SOF_TASK_PRI_LOW ... SOF_TASK_PRI_MED - 1: irq = PLATFORM_IRQ_TASK_LOW; break; - case TASK_PRI_HIGH ... TASK_PRI_MED - 1: + case SOF_TASK_PRI_MED + 1 ... SOF_TASK_PRI_HIGH: irq = PLATFORM_IRQ_TASK_HIGH; break; - case TASK_PRI_MED: + case SOF_TASK_PRI_MED: default: irq = PLATFORM_IRQ_TASK_MED; break; @@ -80,24 +78,24 @@ static int task_set_data(struct task *task) switch (task->priority) { #ifdef CONFIG_TASK_HAVE_PRIORITY_MEDIUM - case TASK_PRI_MED + 1 ... TASK_PRI_LOW: + case SOF_TASK_PRI_LOW ... SOF_TASK_PRI_MED - 1: irq_task = *task_irq_low_get(); break; - case TASK_PRI_HIGH ... TASK_PRI_MED - 1: + case SOF_TASK_PRI_MED + 1 ... SOF_TASK_PRI_HIGH: irq_task = *task_irq_high_get(); break; - case TASK_PRI_MED: + case SOF_TASK_PRI_MED: irq_task = *task_irq_med_get(); break; #elif CONFIG_TASK_HAVE_PRIORITY_LOW - case TASK_PRI_MED ... TASK_PRI_LOW: + case SOF_TASK_PRI_LOW ... SOF_TASK_PRI_MED: irq_task = *task_irq_low_get(); break; - case TASK_PRI_HIGH ... TASK_PRI_MED - 1: + case SOF_TASK_PRI_MED + 1 ... SOF_TASK_PRI_HIGH: irq_task = *task_irq_high_get(); break; #else - case TASK_PRI_HIGH ... TASK_PRI_LOW: + case SOF_TASK_PRI_LOW ... SOF_TASK_PRI_HIGH: irq_task = *task_irq_high_get(); break; #endif @@ -134,11 +132,11 @@ static void _irq_task(void *arg) interrupt_clear(irq_task->irq); list_for_item_safe(clist, tlist, &irq_task->list) { - task = container_of(clist, struct task, irq_list); list_item_del(clist); - if (task->func && task->state == TASK_STATE_PENDING) { + if (task->func && + task->state == SOF_TASK_STATE_PENDING) { schedule_task_running(task); run_task = 1; } else { diff --git a/src/arch/xtensa/up/schedule.c b/src/arch/xtensa/up/schedule.c index c5f8c862dc80..7372d57d6836 100644 --- a/src/arch/xtensa/up/schedule.c +++ b/src/arch/xtensa/up/schedule.c @@ -37,10 +37,10 @@ #include -/** \brief Schedule data pointer. */ +/** \brief Schedule data pointer */ static struct schedule_data *sch; -struct schedule_data **arch_schedule_get(void) +struct schedule_data **arch_schedule_get_data(void) { return &sch; } diff --git a/src/audio/eq_iir.c b/src/audio/eq_iir.c index ebcaa194c6a9..ee595e6211f9 100644 --- a/src/audio/eq_iir.c +++ b/src/audio/eq_iir.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/audio/kpb.c b/src/audio/kpb.c index e27ba1087059..f5fc06072844 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -47,7 +47,7 @@ static void kpb_event_handler(int message, void *cb_data, void *event_data); static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli); static void kpb_init_draining(struct comp_data *kpb, struct kpb_client *cli); -static void kpb_draining_task(void *arg); +static uint64_t kpb_draining_task(void *arg); static void kpb_buffer_data(struct comp_data *kpb, struct comp_buffer *source); /** @@ -225,7 +225,8 @@ static int kpb_prepare(struct comp_dev *dev) notifier_register(&cd->kpb_events); /* initialie draining task */ - schedule_task_init(&cd->draining_task, kpb_draining_task, cd); + schedule_task_init(&cd->draining_task, SOF_SCHEDULE_EDF, 0, + kpb_draining_task, cd, 0, 0); /* search for the channel selector sink. * NOTE! We assume here that channel selector component device @@ -517,15 +518,16 @@ static void kpb_init_draining(struct comp_data *kpb, struct kpb_client *cli) "sink not ready for draining"); } else { /* add one-time draining task into the scheduler */ - schedule_task(&kpb->draining_task, 0, 0); + schedule_task(&kpb->draining_task, 0, 0, 0); } } -static void kpb_draining_task(void *arg) +static uint64_t kpb_draining_task(void *arg) { /* TODO: while loop drainning history buffer accoriding to * clients request */ + return 0; } struct comp_driver comp_kpb = { diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index 08d72742a871..f454ed7e48b5 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -48,6 +48,7 @@ #include #include #include +#include /* generic pipeline data used by pipeline_comp_* functions */ struct pipeline_data { @@ -58,13 +59,14 @@ struct pipeline_data { int cmd; }; -static void pipeline_task(void *arg); +static uint64_t pipeline_task(void *arg); /* create new pipeline - returns pipeline id or negative error */ struct pipeline *pipeline_new(struct sof_ipc_pipe_new *pipe_desc, struct comp_dev *cd) { struct pipeline *p; + uint32_t type; trace_pipe("pipeline_new()"); @@ -78,12 +80,16 @@ struct pipeline *pipeline_new(struct sof_ipc_pipe_new *pipe_desc, /* init pipeline */ p->sched_comp = cd; p->status = COMP_STATE_INIT; - schedule_task_init(&p->pipe_task, pipeline_task, p); - schedule_task_config(&p->pipe_task, pipe_desc->priority, - pipe_desc->core); + spinlock_init(&p->lock); memcpy(&p->ipc_pipe, pipe_desc, sizeof(*pipe_desc)); + /* get pipeline task type */ + type = pipeline_is_timer_driven(p) ? SOF_SCHEDULE_LL : + SOF_SCHEDULE_EDF; + schedule_task_init(&p->pipe_task, type, pipe_desc->priority, + pipeline_task, p, pipe_desc->core, 0); + return p; } @@ -797,8 +803,7 @@ static int pipeline_xrun_recover(struct pipeline *p) void pipeline_schedule_copy(struct pipeline *p, uint64_t start) { if (p->sched_comp->state == COMP_STATE_ACTIVE) - schedule_task(&p->pipe_task, start, p->ipc_pipe.deadline); - + schedule_task(&p->pipe_task, start, p->ipc_pipe.deadline, 0); } /* notify pipeline that this component requires buffers emptied/filled @@ -807,7 +812,8 @@ void pipeline_schedule_copy(struct pipeline *p, uint64_t start) */ void pipeline_schedule_copy_idle(struct pipeline *p) { - schedule_task_idle(&p->pipe_task, p->ipc_pipe.deadline); + schedule_task(&p->pipe_task, 0, p->ipc_pipe.deadline, + SOF_SCHEDULE_FLAG_IDLE); } void pipeline_schedule_cancel(struct pipeline *p) @@ -822,7 +828,7 @@ void pipeline_schedule_cancel(struct pipeline *p) "failed, err = %d", err); } -static void pipeline_task(void *arg) +static uint64_t pipeline_task(void *arg) { struct pipeline *p = arg; int err; @@ -843,10 +849,11 @@ static void pipeline_task(void *arg) err = pipeline_xrun_recover(p); if (err < 0) { trace_pipe_error_with_ids(p, "pipeline_task(): xrun recover failed! pipeline will be stopped!"); - return; /* failed - host will stop this pipeline */ + goto sched; /* failed - host will stop this pipeline */ } } sched: tracev_pipe("pipeline_task() sched"); + return p->ipc_pipe.timer_delay; } diff --git a/src/audio/pipeline_static.c b/src/audio/pipeline_static.c index 1399490fd0ee..adbbee56082f 100644 --- a/src/audio/pipeline_static.c +++ b/src/audio/pipeline_static.c @@ -345,9 +345,9 @@ static struct spipe spipe[] = { /* pipelines */ struct sof_ipc_pipe_new pipeline[] = { - SPIPE_PIPE(0, 0, 1000, TASK_PRI_HIGH), /* high pri - 1ms deadline */ -// SPIPE_PIPE(1, 0, 4000, TASK_PRI_MED), /* med pri - 4ms deadline */ -// SPIPE_PIPE(2, 0, 5000, TASK_PRI_LOW), /* low pri - 5ms deadline */ + SPIPE_PIPE(0, 0, 1000, SOF_TASK_PRI_HIGH),/* high pri - 1ms deadline */ +// SPIPE_PIPE(1, 0, 4000, SOF_TASK_PRI_MED),/* med pri - 4ms deadline */ +// SPIPE_PIPE(2, 0, 5000, SOF_TASK_PRI_LOW),/* low pri - 5ms deadline */ }; int init_static_pipeline(struct ipc *ipc) diff --git a/src/audio/selector.c b/src/audio/selector.c index 1867cfee078c..7139e99dbb69 100644 --- a/src/audio/selector.c +++ b/src/audio/selector.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include "selector.h" diff --git a/src/audio/src.c b/src/audio/src.c index 5e72121784a1..d0ec2582c706 100644 --- a/src/audio/src.c +++ b/src/audio/src.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/audio/tone.c b/src/audio/tone.c index 0b4131476ccf..59d32bb4a078 100644 --- a/src/audio/tone.c +++ b/src/audio/tone.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/audio/volume.c b/src/audio/volume.c index 3019624dbce8..2651b727e815 100644 --- a/src/audio/volume.c +++ b/src/audio/volume.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include #include "volume.h" @@ -86,7 +86,7 @@ static void vol_update(struct comp_data *cd, uint32_t chan) * \param[in] delay Update time. * \return Time until next work. */ -static uint64_t vol_work(void *data, uint64_t delay) +static uint64_t vol_work(void *data) { struct comp_dev *dev = (struct comp_dev *)data; struct comp_data *cd = comp_get_drvdata(dev); @@ -197,8 +197,8 @@ static struct comp_dev *volume_new(struct sof_ipc_comp *comp) } comp_set_drvdata(dev, cd); - work_init(&cd->volwork, vol_work, dev, WORK_MED_PRI, WORK_ASYNC); - + schedule_task_init(&cd->volwork, SOF_SCHEDULE_LL, SOF_TASK_PRI_MED, + vol_work, dev, 0, 0); /* set volume min/max levels */ vol_set_min_max_levels(cd, ipc_vol->min_value, ipc_vol->max_value); @@ -339,7 +339,7 @@ static int volume_ctrl_set_cmd(struct comp_dev *dev, "invalid i = %u", i); } } - work_schedule_default(&cd->volwork, VOL_RAMP_US); + schedule_task(&cd->volwork, VOL_RAMP_US, 0, 0); break; case SOF_CTRL_CMD_SWITCH: @@ -364,7 +364,7 @@ static int volume_ctrl_set_cmd(struct comp_dev *dev, "invalid i = %u", i); } } - work_schedule_default(&cd->volwork, VOL_RAMP_US); + schedule_task(&cd->volwork, VOL_RAMP_US, 0, 0); break; default: diff --git a/src/audio/volume.h b/src/audio/volume.h index d83972926d2f..bce5535d7586 100644 --- a/src/audio/volume.h +++ b/src/audio/volume.h @@ -115,7 +115,7 @@ struct comp_data { /**< volume processing function */ void (*scale_vol)(struct comp_dev *dev, struct comp_buffer *sink, struct comp_buffer *source, uint32_t frames); - struct work volwork; /**< volume scheduled work function */ + struct task volwork; /**< volume scheduled work function */ struct sof_ipc_ctrl_value_chan *hvol; /**< host volume readback */ }; diff --git a/src/drivers/dw/dma.c b/src/drivers/dw/dma.c index d435ae3693e9..8f1e0e15c571 100644 --- a/src/drivers/dw/dma.c +++ b/src/drivers/dw/dma.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include #include @@ -459,7 +459,6 @@ static void dw_dma_channel_put_unlocked(struct dma *dma, int channel) chan->ptr_data.start_ptr = 0; chan->ptr_data.end_ptr = 0; chan->ptr_data.buffer_bytes = 0; - atomic_sub(&dma->num_channels_busy, 1); } diff --git a/src/drivers/dw/ssi-spi.c b/src/drivers/dw/ssi-spi.c index f628b6fcfd40..d20699b9ffbf 100644 --- a/src/drivers/dw/ssi-spi.c +++ b/src/drivers/dw/ssi-spi.c @@ -38,10 +38,9 @@ #include #include #include -#include #include #include -#include +#include #include #include @@ -309,7 +308,7 @@ static int spi_set_config(struct spi *spi, return spi_slave_dma_set_config(spi, spi_cfg); } -static void spi_completion_work(void *data) +static uint64_t spi_completion_work(void *data) { struct spi *spi = data; struct sof_ipc_hdr *hdr; @@ -343,6 +342,8 @@ static void spi_completion_work(void *data) break; } + + return 0; } static void spi_dma_complete(void *data, uint32_t type, @@ -352,7 +353,7 @@ static void spi_dma_complete(void *data, uint32_t type, next->size = DMA_RELOAD_END; - schedule_task(&spi->completion, 0, 100); + schedule_task(&spi->completion, 0, 100, 0); } int spi_push(struct spi *spi, const void *data, size_t size) @@ -427,8 +428,8 @@ static int spi_slave_init(struct spi *spi) config->dir = SPI_DIR_TX; config->src_buf = spi->tx_buffer; - schedule_task_init(&spi->completion, spi_completion_work, spi); - schedule_task_config(&spi->completion, TASK_PRI_MED, 0); + schedule_task_init(&spi->completion, SOF_SCHEDULE_EDF, SOF_TASK_PRI_MED, + spi_completion_work, spi, 0, 0); return 0; } diff --git a/src/drivers/intel/baytrail/ipc.c b/src/drivers/intel/baytrail/ipc.c index deb6b45f109f..c69fffd03717 100644 --- a/src/drivers/intel/baytrail/ipc.c +++ b/src/drivers/intel/baytrail/ipc.c @@ -218,8 +218,8 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(_ipc, iipc); /* schedule */ - schedule_task_init(&_ipc->ipc_task, ipc_process_task, _ipc); - schedule_task_config(&_ipc->ipc_task, TASK_PRI_IPC, 0); + schedule_task_init(&_ipc->ipc_task, SOF_SCHEDULE_EDF, SOF_TASK_PRI_IPC, + ipc_process_task, _ipc, 0, 0); #ifdef CONFIG_HOST_PTABLE /* allocate page table buffer */ diff --git a/src/drivers/intel/cavs/dmic.c b/src/drivers/intel/cavs/dmic.c index ba2ce85c9325..f153729097ab 100644 --- a/src/drivers/intel/cavs/dmic.c +++ b/src/drivers/intel/cavs/dmic.c @@ -203,7 +203,7 @@ static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t mask, #endif /* this ramps volume changes over time */ -static uint64_t dmic_work(void *data, uint64_t delay) +static uint64_t dmic_work(void *data) { struct dai *dai = (struct dai *)data; struct dmic_pdata *dmic = dai_get_drvdata(dai); @@ -1124,7 +1124,10 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) trace_dmic("dmic_set_config(), dai->index = %d", di); /* Initialize start sequence handler */ - work_init(&dmic->dmicwork, dmic_work, dai, WORK_MED_PRI, WORK_ASYNC); + schedule_task_init(&dmic->dmicwork, SOF_SCHEDULE_LL, + SOF_TASK_PRI_MED, dmic_work, dai, 0, + SOF_SCHEDULE_FLAG_ASYNC); + if (config->dmic.driver_ipc_version != DMIC_IPC_VERSION) { trace_dmic_error("dmic_set_config() error: wrong ipc version"); @@ -1360,7 +1363,7 @@ static void dmic_start(struct dai *dai) * is not suppressed by gain ramp somewhere in the capture pipe. */ - work_schedule_default(&dmic->dmicwork, DMIC_UNMUTE_RAMP_US); + schedule_task(&dmic->dmicwork, DMIC_UNMUTE_RAMP_US, 0, 0); trace_dmic("dmic_start(), done"); } diff --git a/src/drivers/intel/cavs/hda-dma.c b/src/drivers/intel/cavs/hda-dma.c index 691d659ac402..fe9c59422ea6 100644 --- a/src/drivers/intel/cavs/hda-dma.c +++ b/src/drivers/intel/cavs/hda-dma.c @@ -53,6 +53,7 @@ #include #include #include +#include #define trace_hddma(__e, ...) \ trace_event(TRACE_CLASS_DMA, __e, ##__VA_ARGS__) @@ -565,7 +566,6 @@ static int hda_dma_start(struct dma *dma, int channel) hda_dma_enable_unlock(dma, channel); p->chan[channel].status = COMP_STATE_ACTIVE; - out: spin_unlock_irq(&dma->lock, flags); return ret; diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c index 21085e32054b..1a8d9cc85546 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -242,8 +242,8 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(_ipc, iipc); /* schedule */ - schedule_task_init(&_ipc->ipc_task, ipc_process_task, _ipc); - schedule_task_config(&_ipc->ipc_task, TASK_PRI_IPC, 0); + schedule_task_init(&_ipc->ipc_task, SOF_SCHEDULE_EDF, SOF_TASK_PRI_IPC, + ipc_process_task, _ipc, 0, 0); /* PM */ iipc->pm_prepare_D3 = 0; diff --git a/src/drivers/intel/cavs/sue-ipc.c b/src/drivers/intel/cavs/sue-ipc.c index ae9b8b61aa0b..c2555b894749 100644 --- a/src/drivers/intel/cavs/sue-ipc.c +++ b/src/drivers/intel/cavs/sue-ipc.c @@ -132,8 +132,8 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(_ipc, iipc); /* schedule */ - schedule_task_init(&_ipc->ipc_task, ipc_process_task, _ipc); - schedule_task_config(&_ipc->ipc_task, TASK_PRI_MED, 0); + schedule_task_init(&_ipc->ipc_task, SOF_SCHEDULE_EDF, SOF_TASK_PRI_MED, + ipc_process_task, _ipc, 0, 0); /* PM */ iipc->pm_prepare_D3 = 0; diff --git a/src/drivers/intel/haswell/ipc.c b/src/drivers/intel/haswell/ipc.c index 492469dee12b..253bc8aec585 100644 --- a/src/drivers/intel/haswell/ipc.c +++ b/src/drivers/intel/haswell/ipc.c @@ -208,8 +208,8 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(_ipc, iipc); /* schedule */ - schedule_task_init(&_ipc->ipc_task, ipc_process_task, _ipc); - schedule_task_config(&_ipc->ipc_task, TASK_PRI_IPC, 0); + schedule_task_init(&_ipc->ipc_task, SOF_SCHEDULE_EDF, SOF_TASK_PRI_IPC, + ipc_process_task, _ipc, 0, 0); #ifdef CONFIG_HOST_PTABLE /* allocate page table buffer */ diff --git a/src/host/CMakeLists.txt b/src/host/CMakeLists.txt index fea64c347a2b..ed540a8246ba 100644 --- a/src/host/CMakeLists.txt +++ b/src/host/CMakeLists.txt @@ -16,6 +16,8 @@ add_local_sources(tb_common file.c ipc.c schedule.c + edf_schedule.c + ll_schedule.c topology.c trace.c ) diff --git a/src/host/common_test.c b/src/host/common_test.c index 25dd39a20f5e..abd5578bd95b 100644 --- a/src/host/common_test.c +++ b/src/host/common_test.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include @@ -65,7 +65,7 @@ int tb_pipeline_setup(struct sof *sof) } /* init scheduler */ - if (scheduler_init(sof) < 0) { + if (scheduler_init() < 0) { fprintf(stderr, "error: scheduler init\n"); return -EINVAL; } diff --git a/src/host/edf_schedule.c b/src/host/edf_schedule.c new file mode 100644 index 000000000000..5a517481a3a5 --- /dev/null +++ b/src/host/edf_schedule.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Bartosz Kokoszko + */ + +#include +#include +#include +#include +#include + + /* scheduler testbench definition */ + +struct edf_schedule_data { + spinlock_t lock; /* schedule lock */ + struct list_item list; /* list of tasks in priority queue */ + uint32_t clock; +}; + +static struct edf_schedule_data *sch; + +static void schedule_edf_task_complete(struct task *task); +static void schedule_edf_task(struct task *task, uint64_t start, + uint64_t deadline, uint32_t flags); +static int schedule_edf_task_init(struct task *task, uint32_t xflags); +static int edf_scheduler_init(void); +static void edf_scheduler_free(void); +static void schedule_edf(void); +static int schedule_edf_task_cancel(struct task *task); +static void schedule_edf_task_free(struct task *task); + +static void schedule_edf_task_complete(struct task *task) +{ + list_item_del(&task->list); + task->state = SOF_TASK_STATE_COMPLETED; +} + +/* schedule task */ +static void schedule_edf_task(struct task *task, uint64_t start, + uint64_t deadline, uint32_t flags) +{ + (void)deadline; + list_item_prepend(&task->list, &sch->list); + task->state = SOF_TASK_STATE_QUEUED; + + if (task->func) + task->func(task->data); + + schedule_edf_task_complete(task); +} + +static int schedule_edf_task_init(struct task *task, uint32_t xflags) +{ + struct edf_task_pdata *edf_pdata; + (void)xflags; + + edf_pdata = malloc(sizeof(*edf_pdata)); + edf_sch_set_pdata(task, edf_pdata); + + return 0; +} + +/* initialize scheduler */ +static int edf_scheduler_init(void) +{ + trace_edf_sch("edf_scheduler_init()"); + sch = malloc(sizeof(*sch)); + list_init(&sch->list); + spinlock_init(&sch->lock); + + return 0; +} + +static void edf_scheduler_free(void) +{ + free(sch); +} + +/* The following definitions are to satisfy libsof linker errors */ +static void schedule_edf(void) +{ +} + +static int schedule_edf_task_cancel(struct task *task) +{ + if (task->state == SOF_TASK_STATE_QUEUED) { + /* delete task */ + task->state = SOF_TASK_STATE_CANCEL; + list_item_del(&task->list); + } + + return 0; +} + +static void schedule_edf_task_free(struct task *task) +{ + task->state = SOF_TASK_STATE_FREE; + task->func = NULL; + task->data = NULL; + + free(edf_sch_get_pdata(task)); + edf_sch_set_pdata(task, NULL); +} + +struct scheduler_ops schedule_edf_ops = { + .schedule_task = schedule_edf_task, + .schedule_task_init = schedule_edf_task_init, + .schedule_task_running = NULL, + .schedule_task_complete = NULL, + .reschedule_task = NULL, + .schedule_task_cancel = schedule_edf_task_cancel, + .schedule_task_free = schedule_edf_task_free, + .scheduler_init = edf_scheduler_init, + .scheduler_free = edf_scheduler_free, + .scheduler_run = schedule_edf +}; diff --git a/src/host/file.c b/src/host/file.c index 3b218847daca..87af18ba7aa0 100644 --- a/src/host/file.c +++ b/src/host/file.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/src/host/ll_schedule.c b/src/host/ll_schedule.c new file mode 100644 index 000000000000..48235acc74e8 --- /dev/null +++ b/src/host/ll_schedule.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Bartosz Kokoszko + */ + +#include +#include +#include +#include + +/* TODO: Make host ll ops implementation */ +struct scheduler_ops schedule_ll_ops = { + .schedule_task = NULL, + .schedule_task_init = NULL, + .schedule_task_running = NULL, + .schedule_task_complete = NULL, + .reschedule_task = NULL, + .schedule_task_cancel = NULL, + .schedule_task_free = NULL, + .scheduler_init = NULL, + .scheduler_free = NULL, + .scheduler_run = NULL +}; + diff --git a/src/host/schedule.c b/src/host/schedule.c index 994c743407a5..920938204290 100644 --- a/src/host/schedule.c +++ b/src/host/schedule.c @@ -35,68 +35,91 @@ #include #include #include +#include "host/edf_schedule.h" +#include "host/ll_schedule.h" -/* scheduler testbench definition */ - -struct schedule_data { - spinlock_t lock; /* schedule lock */ - struct list_item list; /* list of tasks in priority queue */ - uint32_t clock; +static const struct scheduler_ops *schedulers[SOF_SCHEDULE_COUNT] = { + &schedule_edf_ops, /* SOF_SCHEDULE_EDF */ + &schedule_ll_ops /* SOF_LL_TASK */ }; -static struct schedule_data *sch; - -void schedule_task_complete(struct task *task) +/* testbench work definition */ +int schedule_task_init(struct task *task, uint16_t type, uint16_t priority, + uint64_t (*func)(void *data), void *data, uint16_t core, + uint32_t xflags) { - list_item_del(&task->list); - task->state = TASK_STATE_COMPLETED; + task->type = type; + task->priority = priority; + task->core = core; + task->state = SOF_TASK_STATE_INIT; + task->func = func; + task->data = data; + + return schedulers[task->type]->schedule_task_init(task, xflags); } -/* schedule task */ -void schedule_task(struct task *task, uint64_t start, uint64_t deadline) +void schedule_task(struct task *task, uint64_t start, uint64_t deadline, + uint32_t flags) { - task->deadline = deadline; - list_item_prepend(&task->list, &sch->list); - task->state = TASK_STATE_QUEUED; - - if (task->func) - task->func(task->data); - - schedule_task_complete(task); + if (schedulers[task->type]->schedule_task) + schedulers[task->type]->schedule_task(task, start, deadline, + flags); } -/* initialize scheduler */ -int scheduler_init(struct sof *sof) +void schedule_task_free(struct task *task) { - trace_schedule("scheduler_init()"); - sch = malloc(sizeof(*sch)); - list_init(&sch->list); - spinlock_init(&sch->lock); - - return 0; + if (schedulers[task->type]->schedule_task_free) + schedulers[task->type]->schedule_task_free(task); } -/* The following definitions are to satisfy libsof linker errors */ - -void schedule(void) +void reschedule_task(struct task *task, uint64_t start) { + if (schedulers[task->type]->reschedule_task) + schedulers[task->type]->reschedule_task(task, start); } -void schedule_task_idle(struct task *task, uint64_t deadline) +int schedule_task_cancel(struct task *task) { + int ret = 0; + + if (schedulers[task->type]->schedule_task_cancel) + ret = schedulers[task->type]->schedule_task_cancel(task); + + return ret; } -int schedule_task_cancel(struct task *task) +int scheduler_init(void) { - return 0; + int i = 0; + int ret = 0; + + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_init) { + ret = schedulers[i]->scheduler_init(); + if (ret < 0) + goto out; + } + } +out: + return ret; } -/* testbench work definition */ - -void work_schedule_default(struct work *w, uint64_t timeout) +void schedule_free(void) { + int i; + + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_free) + schedulers[i]->scheduler_free(); + } } -void work_cancel_default(struct work *work) +void schedule(void) { + int i = 0; + + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_run) + schedulers[i]->scheduler_run(); + } } diff --git a/src/include/host/common_test.h b/src/include/host/common_test.h index a19ee5d571e5..d8f9fe7697a2 100644 --- a/src/include/host/common_test.h +++ b/src/include/host/common_test.h @@ -73,7 +73,7 @@ struct shared_lib_table { extern int debug; -int scheduler_init(struct sof *sof); +int edf_scheduler_init(void); void sys_comp_file_init(void); diff --git a/src/arch/xtensa/up/work.c b/src/include/host/edf_schedule.h similarity index 79% rename from src/arch/xtensa/up/work.c rename to src/include/host/edf_schedule.h index f5d4e90d0322..b1153fd57d93 100644 --- a/src/arch/xtensa/up/work.c +++ b/src/include/host/edf_schedule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Intel Corporation + * Copyright (c) 2019, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,22 +25,12 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * Author: Tomasz Lauda - * - */ - -/** - * \file arch/xtensa/up/work.c - * \brief Xtensa UP work queue implementation file - * \authors Tomasz Lauda + * Author: Bartosz Kokoszko */ -#include +#ifndef _INCLUDE_HOST_EDF_SCHEDULE_H_ +#define _INCLUDE_HOST_EDF_SCHEDULE_H_ -/** \brief Generic system work queue. */ -static struct work_queue *queue; +extern struct scheduler_ops schedule_edf_ops; -struct work_queue **arch_work_queue_get(void) -{ - return &queue; -} +#endif /* _INCLUDE_HOST_EDF_SCHEDULE_H_ */ diff --git a/src/arch/xtensa/smp/work.c b/src/include/host/ll_schedule.h similarity index 77% rename from src/arch/xtensa/smp/work.c rename to src/include/host/ll_schedule.h index 41e2f2eac642..9eb07f60f2e7 100644 --- a/src/arch/xtensa/smp/work.c +++ b/src/include/host/ll_schedule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Intel Corporation + * Copyright (c) 2019, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,23 +25,12 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * Author: Tomasz Lauda - * - */ - -/** - * \file arch/xtensa/smp/work.c - * \brief Xtensa SMP work queue implementation file - * \authors Tomasz Lauda + * Author: Bartosz Kokoszko */ -#include -#include -#include +#ifndef _INCLUDE_HOST_LL_SCHEDULE_H_ +#define _INCLUDE_HOST_LL_SCHEDULE_H_ -struct work_queue **arch_work_queue_get(void) -{ - struct core_context *ctx = (struct core_context *)cpu_read_threadptr(); +extern struct scheduler_ops schedule_ll_ops; - return &ctx->queue; -} +#endif /* _INCLUDE_HOST_LL_SCHEDULE_H_ */ diff --git a/src/include/sof/agent.h b/src/include/sof/agent.h index 3d94c353286f..b7dbe68a63a1 100644 --- a/src/include/sof/agent.h +++ b/src/include/sof/agent.h @@ -33,7 +33,7 @@ #include #include -#include +#include struct sof; @@ -41,7 +41,7 @@ struct sof; struct sa { uint64_t last_idle; /* time of last idle */ uint64_t ticks; - struct work work; + struct task work; }; void sa_enter_idle(struct sof *sof); diff --git a/src/include/sof/dma-trace.h b/src/include/sof/dma-trace.h index 9e0bbeffadbc..9e929a86cbf0 100644 --- a/src/include/sof/dma-trace.h +++ b/src/include/sof/dma-trace.h @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -61,7 +61,7 @@ struct dma_trace_data { uint32_t overflow; uint32_t messages; uint32_t host_size; - struct work dmat_work; + struct task dmat_work; uint32_t enabled; uint32_t copy_in_progress; uint32_t stream_tag; diff --git a/src/include/sof/dmic.h b/src/include/sof/dmic.h index 295eae7542b9..8fe6db0ec936 100644 --- a/src/include/sof/dmic.h +++ b/src/include/sof/dmic.h @@ -330,7 +330,7 @@ struct dmic_pdata { uint16_t enable[DMIC_HW_CONTROLLERS]; uint32_t state; completion_t drain_complete; - struct work dmicwork; + struct task dmicwork; int32_t startcount; int32_t gain; }; diff --git a/src/include/sof/edf_schedule.h b/src/include/sof/edf_schedule.h new file mode 100644 index 000000000000..9eee9001216c --- /dev/null +++ b/src/include/sof/edf_schedule.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Liam Girdwood + */ + +#ifndef __INCLUDE_SOF_EDF_SCHEDULE_H__ +#define __INCLUDE_SOF_EDF_SCHEDULE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* schedule tracing */ +#define trace_edf_sch(format, ...) \ + trace_event(TRACE_CLASS_EDF, format, ##__VA_ARGS__) + +#define trace_edf_sch_error(format, ...) \ + trace_error(TRACE_CLASS_EDF, format, ##__VA_ARGS__) + +#define tracev_edf_sch(format, ...) \ + tracev_event(TRACE_CLASS_EDF, format, ##__VA_ARGS__) + +struct sof; + +/* maximun task time slice in microseconds */ +#define SCHEDULE_TASK_MAX_TIME_SLICE 5000 + +#define edf_sch_set_pdata(task, data) \ + task->private = data + +#define edf_sch_get_pdata(task) task->private + +struct edf_task_pdata { + uint64_t deadline; +}; + +extern struct scheduler_ops schedule_edf_ops; + +#endif /* __INCLUDE_SOF_EDF_SCHEDULE_H__ */ diff --git a/src/include/sof/init.h b/src/include/sof/init.h index cc494a9356b0..9331e8912cda 100644 --- a/src/include/sof/init.h +++ b/src/include/sof/init.h @@ -32,13 +32,9 @@ #define __INCLUDE_INIT_H__ #include -#include struct sof; -extern struct work_queue_timesource - platform_generic_queue[PLATFORM_CORE_COUNT]; - /* main firmware entry point - argc and argv not currently used */ int main(int argc, char *argv[]); diff --git a/src/include/sof/ipc.h b/src/include/sof/ipc.h index 1faf4a230db6..b63ea0f4e18b 100644 --- a/src/include/sof/ipc.h +++ b/src/include/sof/ipc.h @@ -139,7 +139,7 @@ int platform_ipc_init(struct ipc *ipc); void ipc_free(struct ipc *ipc); int ipc_process_msg_queue(void); -void ipc_process_task(void *data); +uint64_t ipc_process_task(void *data); void ipc_schedule_process(struct ipc *ipc); int ipc_stream_send_position(struct comp_dev *cdev, diff --git a/src/include/sof/work.h b/src/include/sof/ll_schedule.h similarity index 51% rename from src/include/sof/work.h rename to src/include/sof/ll_schedule.h index 06e472b42cf4..d8d3b478b6f5 100644 --- a/src/include/sof/work.h +++ b/src/include/sof/ll_schedule.h @@ -31,72 +31,32 @@ * interrupt source. It should execute quickly and must not sleep or wait. */ -#ifndef __INCLUDE_WORK__ -#define __INCLUDE_WORK__ +#ifndef __INCLUDE_SOF_LOW_LATENCY_SCHEDULE_H__ +#define __INCLUDE_SOF_LOW_LATENCY_SCHEDULE_H__ #include #include #include +#include +#include +#include struct work_queue; -/* flags */ -#define WORK_ASYNC (0 << 0) /* default - work is scheduled asynchronously */ -#define WORK_SYNC (1 << 0) /* work is scheduled synchronously */ +/* range of priorities (0 - SOF_TASK_PRI_COUNT) */ +#define LL_PRIORITIES SOF_TASK_PRI_COUNT -/* priority levels */ -#define WORK_LOW_PRI 9 /* work lowest priority */ -#define WORK_MED_PRI 4 /* work medium priority */ -#define WORK_HIGH_PRI 0 /* work highest priority */ +#define ll_sch_set_pdata(task, data) \ + task->private = data -#define WORK_PRIORITIES 10 /* range of priorities (0 - 9) */ +#define ll_sch_get_pdata(task) task->private -struct work { - /* returns reschedule timeout in usecs */ - uint64_t (*cb)(void *data, uint64_t udelay); - void *cb_data; - struct list_item list; - uint64_t timeout; - uint32_t pending; - uint32_t priority; +struct ll_task_pdata { uint32_t flags; }; -struct work_queue_timesource { - struct timer timer; - int clk; - int notifier; - int (*timer_set)(struct timer *, uint64_t ticks); - void (*timer_clear)(struct timer *); - uint64_t (*timer_get)(struct timer *); -}; - -/* initialise our work */ -#define work_init(w, x, xd, xpriority, xflags) \ - do { \ - (w)->cb = x; \ - (w)->cb_data = xd; \ - (w)->priority = xpriority; \ - (w)->flags = xflags; \ - } while (0) - -struct work_queue **arch_work_queue_get(void); - -/* schedule/cancel work on work queue */ -void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout); -void work_reschedule(struct work_queue *queue, struct work *w, uint64_t timeout); -void work_cancel(struct work_queue *queue, struct work *work); - -/* schedule/cancel work on default system work queue */ -void work_schedule_default(struct work *work, uint64_t timeout); -void work_reschedule_default(struct work *work, uint64_t timeout); -void work_reschedule_default_at(struct work *w, uint64_t time); -void work_cancel_default(struct work *work); - -/* init system workq */ -void init_system_workq(struct work_queue_timesource *ts); +extern struct timesource_data platform_generic_queue[]; -/* free system workq */ -void free_system_workq(void); +extern struct scheduler_ops schedule_ll_ops; -#endif +#endif /* __INCLUDE_SOF_LOW_LATENCY_SCHEDULE_H__ */ diff --git a/src/include/sof/schedule.h b/src/include/sof/schedule.h index a5609ab1bb55..8a40508633d1 100644 --- a/src/include/sof/schedule.h +++ b/src/include/sof/schedule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Intel Corporation + * Copyright (c) 2019, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,113 +25,120 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * Author: Liam Girdwood + * Author: Bartosz Kokoszko */ -#ifndef __INCLUDE_SOF_SCHEDULE_H__ -#define __INCLUDE_SOF_SCHEDULE_H__ +/* Generic schedule api header */ +#ifndef __INCLUDE_SOF_SCHEDULER_H__ +#define __INCLUDE_SOF_SCHEDULER_H__ #include -#include -#include -#include -#include #include -#include -#include +#include /* schedule tracing */ #define trace_schedule(format, ...) \ - trace_event(TRACE_CLASS_SCHEDULE, format, ##__VA_ARGS__) + trace_event(TRACE_CLASS_EDF, format, ##__VA_ARGS__) #define trace_schedule_error(format, ...) \ - trace_error(TRACE_CLASS_SCHEDULE, format, ##__VA_ARGS__) + trace_error(TRACE_CLASS_EDF, format, ##__VA_ARGS__) #define tracev_schedule(format, ...) \ - tracev_event(TRACE_CLASS_SCHEDULE, format, ##__VA_ARGS__) + tracev_event(TRACE_CLASS_EDF, format, ##__VA_ARGS__) -struct schedule_data; -struct sof; +/* SOF_SCHEDULE_ type comes from topology */ +enum { + SOF_SCHEDULE_EDF = 0, /* EDF scheduler */ + SOF_SCHEDULE_LL, /* Low latency scheduler */ + SOF_SCHEDULE_COUNT +}; + +#define SOF_TASK_PRI_LOW 0 /* priority level 0 - low */ +#define SOF_TASK_PRI_MED 4 /* priority level 4 - medium */ +#define SOF_TASK_PRI_HIGH 9 /* priority level 9 - high */ + +#define SOF_TASK_PRI_COUNT 10 /* range of priorities (0-9) */ + +#define SOF_TASK_PRI_IPC 5 +#define SOF_TASK_PRI_IDC 5 /* task states */ -#define TASK_STATE_INIT 0 -#define TASK_STATE_QUEUED 1 -#define TASK_STATE_PENDING 2 -#define TASK_STATE_RUNNING 3 -#define TASK_STATE_PREEMPTED 4 -#define TASK_STATE_COMPLETED 5 -#define TASK_STATE_FREE 6 -#define TASK_STATE_CANCEL 7 - -/* task priorities - values same as Linux processes, gives scope for future.*/ -#define TASK_PRI_LOW 19 -#define TASK_PRI_MED 0 -#define TASK_PRI_HIGH -20 - -#define TASK_PRI_IPC 1 -#define TASK_PRI_IDC 1 - -/* maximun task time slice in microseconds */ -#define SCHEDULE_TASK_MAX_TIME_SLICE 5000 - -/* task descriptor */ +#define SOF_TASK_STATE_INIT 0 +#define SOF_TASK_STATE_QUEUED 1 +#define SOF_TASK_STATE_PENDING 2 +#define SOF_TASK_STATE_RUNNING 3 +#define SOF_TASK_STATE_PREEMPTED 4 +#define SOF_TASK_STATE_COMPLETED 5 +#define SOF_TASK_STATE_FREE 6 +#define SOF_TASK_STATE_CANCEL 7 + +/* Scheduler flags */ +/* Sync/Async only supported by ll scheduler atm */ +#define SOF_SCHEDULE_FLAG_ASYNC (0 << 0) /* task scheduled asynchronously */ +#define SOF_SCHEDULE_FLAG_SYNC (1 << 0) /* task scheduled synchronously */ +#define SOF_SCHEDULE_FLAG_IDLE (2 << 0) + +struct task; + +struct scheduler_ops { + void (*schedule_task)(struct task *w, uint64_t start, + uint64_t deadline, uint32_t flags); + int (*schedule_task_init)(struct task *task, uint32_t xflags); + void (*schedule_task_running)(struct task *task); + void (*schedule_task_complete)(struct task *task); + void (*reschedule_task)(struct task *task, uint64_t start); + int (*schedule_task_cancel)(struct task *task); + void (*schedule_task_free)(struct task *task); + int (*scheduler_init)(void); + void (*scheduler_free)(void); + void (*scheduler_run)(void); +}; + struct task { - uint16_t core; /* core id to run on */ - int16_t priority; /* scheduling priority TASK_PRI_ */ - uint64_t start; /* scheduling earliest start time */ - uint64_t deadline; /* scheduling deadline */ - uint32_t state; /* TASK_STATE_ */ - struct list_item list; /* list in scheduler */ + uint16_t type; + uint64_t start; + uint16_t priority; + uint16_t state; + uint16_t core; + void *data; + uint64_t (*func)(void *data); + struct list_item list; struct list_item irq_list; /* list for assigned irq level */ + const struct scheduler_ops *ops; + void *private; +}; - /* task function and private data */ - void *data; - void (*func)(void *arg); +struct edf_schedule_data; +struct ll_schedule_data; - /* runtime duration in scheduling clock base */ - uint64_t max_rtime; /* max time taken to run */ - completion_t complete; +struct schedule_data { + struct ll_schedule_data *ll_sch_data; + struct edf_schedule_data *edf_sch_data; }; -struct schedule_data **arch_schedule_get(void); +struct schedule_data **arch_schedule_get_data(void); -void schedule(void); +int schedule_task_init(struct task *task, uint16_t type, uint16_t priority, + uint64_t (*func)(void *data), void *data, uint16_t core, + uint32_t xflags); -void schedule_task(struct task *task, uint64_t start, uint64_t deadline); +void schedule_task_running(struct task *task); -void schedule_task_idle(struct task *task, uint64_t deadline); +void schedule_task_complete(struct task *task); -int schedule_task_cancel(struct task *task); +void schedule_task(struct task *task, uint64_t start, uint64_t deadline, + uint32_t flags); -void schedule_task_complete(struct task *task); +void reschedule_task(struct task *task, uint64_t start); -void schedule_task_running(struct task *task); +void schedule_free(void); + +void schedule(void); + +int scheduler_init(void); + +int schedule_task_cancel(struct task *task); + +void schedule_task_free(struct task *task); -static inline void schedule_task_init(struct task *task, void (*func)(void *), - void *data) -{ - task->core = 0; - task->state = TASK_STATE_INIT; - task->func = func; - task->data = data; -} - -static inline void schedule_task_free(struct task *task) -{ - task->state = TASK_STATE_FREE; - task->func = NULL; - task->data = NULL; -} - -static inline void schedule_task_config(struct task *task, uint16_t priority, - uint16_t core) -{ - task->priority = priority; - task->core = core; -} - -int scheduler_init(struct sof *sof); - -void scheduler_free(void); - -#endif +#endif /* __INCLUDE_SOF_SCHEDULER_H__ */ diff --git a/src/include/sof/ssp.h b/src/include/sof/ssp.h index af405554c4cf..64d9b58dd17e 100644 --- a/src/include/sof/ssp.h +++ b/src/include/sof/ssp.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/include/sof/task.h b/src/include/sof/task.h index f4fec2d56bdf..5c9c3331b573 100644 --- a/src/include/sof/task.h +++ b/src/include/sof/task.h @@ -34,7 +34,7 @@ #include struct sof; -struct task; +struct edf_task; int do_task_master_core(struct sof *sof); diff --git a/src/include/sof/timer.h b/src/include/sof/timer.h index 50d0825de677..981a349afd55 100644 --- a/src/include/sof/timer.h +++ b/src/include/sof/timer.h @@ -34,6 +34,15 @@ #include #include +struct timesource_data { + struct timer timer; + int clk; + int notifier; + int (*timer_set)(struct timer *t, uint64_t ticks); + void (*timer_clear)(struct timer *t); + uint64_t (*timer_get)(struct timer *t); +}; + int timer_register(struct timer *timer, void(*handler)(void *arg), void *arg); void timer_unregister(struct timer *timer); diff --git a/src/include/sof/trace.h b/src/include/sof/trace.h index 21fa86f15530..a8643c94e9b4 100644 --- a/src/include/sof/trace.h +++ b/src/include/sof/trace.h @@ -61,7 +61,6 @@ #define TRACE_BOOT_PLATFORM 0x4000 /* system specific codes */ -#define TRACE_BOOT_SYS_WORK (TRACE_BOOT_SYS + 0x100) #define TRACE_BOOT_SYS_CPU_FREQ (TRACE_BOOT_SYS + 0x200) #define TRACE_BOOT_SYS_HEAP (TRACE_BOOT_SYS + 0x300) #define TRACE_BOOT_SYS_NOTE (TRACE_BOOT_SYS + 0x400) @@ -109,9 +108,10 @@ #define TRACE_CLASS_IDC (24 << 24) #define TRACE_CLASS_CPU (25 << 24) #define TRACE_CLASS_CLK (26 << 24) -#define TRACE_CLASS_SCHEDULE (27 << 24) +#define TRACE_CLASS_EDF (27 << 24) #define TRACE_CLASS_KPB (28 << 24) #define TRACE_CLASS_SELECTOR (29 << 24) +#define TRACE_CLASS_SCHEDULE (30 << 24) /* move to config.h */ #define TRACE 1 diff --git a/src/include/sof/wait.h b/src/include/sof/wait.h index 7cfe746eeb7a..adb3c03b79a3 100644 --- a/src/include/sof/wait.h +++ b/src/include/sof/wait.h @@ -39,12 +39,12 @@ #include #include -#include +#include #include typedef struct { uint32_t complete; - struct work work; + struct task work; uint64_t timeout; } completion_t; @@ -59,7 +59,7 @@ static inline void wait_for_interrupt(int level) tracev_event(TRACE_CLASS_WAIT, "WFX"); } -static uint64_t _wait_cb(void *data, uint64_t delay) +static uint64_t _wait_cb(void *data) { volatile completion_t *wc = (volatile completion_t*)data; @@ -86,7 +86,9 @@ static inline void wait_init(completion_t *comp) volatile completion_t *c = (volatile completion_t *)comp; c->complete = 0; - work_init(&comp->work, _wait_cb, comp, WORK_MED_PRI, WORK_ASYNC); + + schedule_task_init(&comp->work, SOF_SCHEDULE_LL, SOF_TASK_PRI_MED, + _wait_cb, comp, 0, 0); } static inline void wait_clear(completion_t *comp) diff --git a/src/include/uapi/user/trace.h b/src/include/uapi/user/trace.h index 8fd5c0b388e1..c6d3fe8c2cf2 100644 --- a/src/include/uapi/user/trace.h +++ b/src/include/uapi/user/trace.h @@ -75,9 +75,10 @@ struct system_time { #define TRACE_CLASS_POWER (23 << 24) #define TRACE_CLASS_IDC (24 << 24) #define TRACE_CLASS_CPU (25 << 24) -#define TRACE_CLASS_SCHEDULE (27 << 24) +#define TRACE_CLASS_EDF (27 << 24) #define TRACE_CLASS_KPB (28 << 24) #define TRACE_CLASS_SELECTOR (29 << 24) +#define TRACE_CLASS_SCHEDULE (30 << 24) #define LOG_ENABLE 1 /* Enable logging */ #define LOG_DISABLE 0 /* Disable logging */ diff --git a/src/init/init.c b/src/init/init.c index 3020f7b9289d..0080e50da005 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -38,9 +38,8 @@ #include #include #include -#include -#include #include +#include #include #include #include @@ -71,9 +70,6 @@ int master_core_init(struct sof *sof) trace_point(TRACE_BOOT_SYS_NOTE); init_system_notify(sof); - trace_point(TRACE_BOOT_SYS_SCHED); - scheduler_init(sof); - trace_point(TRACE_BOOT_SYS_POWER); pm_runtime_init(); diff --git a/src/ipc/handler.c b/src/ipc/handler.c index cd63d24bfdbc..bb10f43d882a 100644 --- a/src/ipc/handler.c +++ b/src/ipc/handler.c @@ -1259,13 +1259,14 @@ int ipc_process_msg_queue(void) return 0; } -void ipc_process_task(void *data) +uint64_t ipc_process_task(void *data) { if (_ipc->host_pending) ipc_platform_do_cmd(_ipc); + return 0; } void ipc_schedule_process(struct ipc *ipc) { - schedule_task(&ipc->ipc_task, 0, 100); + schedule_task(&ipc->ipc_task, 0, 100, 0); } diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index effcfb63d437..2018e84791e9 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -6,9 +6,10 @@ endif() add_local_sources(sof lib.c alloc.c - work.c + ll_schedule.c notifier.c trace.c + edf_schedule.c schedule.c agent.c interrupt.c diff --git a/src/lib/agent.c b/src/lib/agent.c index 3f6ca894ff6b..6b8914f09974 100644 --- a/src/lib/agent.c +++ b/src/lib/agent.c @@ -59,7 +59,7 @@ void sa_enter_idle(struct sof *sof) sa->last_idle = platform_timer_get(platform_timer); } -static uint64_t validate(void *data, uint64_t delay) +static uint64_t validate(void *data) { struct sa *sa = data; uint64_t current; @@ -94,6 +94,9 @@ void sa_init(struct sof *sof) /* set lst idle time to now to give time for boot completion */ sa->last_idle = platform_timer_get(platform_timer) + sa->ticks; - work_init(&sa->work, validate, sa, WORK_MED_PRI, WORK_ASYNC); - work_schedule_default(&sa->work, PLATFORM_IDLE_TIME); + + schedule_task_init(&sa->work, SOF_SCHEDULE_LL, SOF_TASK_PRI_MED, + validate, sa, 0, 0); + + schedule_task(&sa->work, PLATFORM_IDLE_TIME, 0, 0); } diff --git a/src/lib/dma-trace.c b/src/lib/dma-trace.c index 74a17531bf0d..6bf8df056a98 100644 --- a/src/lib/dma-trace.c +++ b/src/lib/dma-trace.c @@ -50,7 +50,7 @@ static int dma_trace_get_avail_data(struct dma_trace_data *d, struct dma_trace_buf *buffer, int avail); -static uint64_t trace_work(void *data, uint64_t delay) +static uint64_t trace_work(void *data) { struct dma_trace_data *d = (struct dma_trace_data *)data; struct dma_trace_buf *buffer = &d->dmatb; @@ -147,7 +147,8 @@ int dma_trace_init_complete(struct dma_trace_data *d) return ret; } - work_init(&d->dmat_work, trace_work, d, WORK_MED_PRI, WORK_ASYNC); + schedule_task_init(&d->dmat_work, SOF_SCHEDULE_LL, + SOF_TASK_PRI_MED, trace_work, d, 0, 0); return 0; } @@ -327,7 +328,7 @@ int dma_trace_enable(struct dma_trace_data *d) } d->enabled = 1; - work_schedule_default(&d->dmat_work, DMA_TRACE_PERIOD); + schedule_task(&d->dmat_work, DMA_TRACE_PERIOD, 0, 0); return 0; } @@ -484,8 +485,8 @@ void dtrace_event(const char *e, uint32_t length) /* schedule copy now if buffer > 50% full */ if (trace_data->enabled && buffer->avail >= (DMA_TRACE_LOCAL_SIZE / 2)) { - work_reschedule_default(&trace_data->dmat_work, - DMA_TRACE_RESCHEDULE_TIME); + reschedule_task(&trace_data->dmat_work, + DMA_TRACE_RESCHEDULE_TIME); /* reschedule should not be interrupted * just like we are in copy progress */ diff --git a/src/lib/edf_schedule.c b/src/lib/edf_schedule.c new file mode 100644 index 000000000000..3e16388c91b3 --- /dev/null +++ b/src/lib/edf_schedule.c @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct edf_schedule_data { + spinlock_t lock; + struct list_item list; /* list of tasks in priority queue */ + uint32_t clock; +}; + +#define SLOT_ALIGN_TRIES 10 + +static void schedule_edf(void); +static void schedule_edf_task(struct task *task, uint64_t start, + uint64_t deadline, uint32_t flags); +static int schedule_edf_task_cancel(struct task *task); +static void schedule_edf_task_complete(struct task *task); +static void schedule_edf_task_running(struct task *task); +static int schedule_edf_task_init(struct task *task, uint32_t xflags); +static void schedule_edf_task_free(struct task *task); +static int edf_scheduler_init(void); +static void edf_scheduler_free(void); + +/* + * Simple rescheduler to calculate tasks new start time and deadline if + * prevoius deadline was missed. Tries to align at first with current task + * timing, but will just add onto current if too far behind current. + * XRUNs will be propagated upto the host if we have to reschedule. + */ +static inline void edf_reschedule(struct task *task, uint64_t current) +{ + int i; + struct edf_task_pdata *edf_pdata; + uint64_t delta; + + edf_pdata = edf_sch_get_pdata(task); + delta = (edf_pdata->deadline - task->start) << 1; + + /* try and align task with current scheduling slots */ + for (i = 0; i < SLOT_ALIGN_TRIES; i++) { + task->start += delta; + + if (task->start > current + delta) { + edf_pdata->deadline = task->start + delta; + return; + } + } + + /* task has slipped a lot, so just add delay to current */ + task->start = current + delta; + edf_pdata->deadline = task->start + delta; +} + +/* + * Find the first non running task with the earliest deadline. + * TODO: Reduce cache invalidations by checking if the currently + * running task AND the earliest queued task will both complete before their + * deadlines. If so, then schedule the earlier queued task after the currently + * running task has completed. + */ +static inline struct task *edf_get_next(uint64_t current, struct task *ignore) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + struct list_item *clist; + struct list_item *tlist; + uint64_t next_delta = UINT64_MAX; + int next_priority = SOF_TASK_PRI_LOW; + uint64_t delta; + uint64_t deadline; + int reschedule = 0; + struct task *edf_task_next = NULL; + struct task *edf_task; + struct edf_task_pdata *edf_pdata; + + /* any tasks in the scheduler ? */ + if (list_is_empty(&sch->list)) { + return NULL; + } + + /* check every queued or running task in list */ + list_for_item_safe(clist, tlist, &sch->list) { + edf_task = container_of(clist, struct task, list); + + /* only check queued tasks */ + if (edf_task->state != SOF_TASK_STATE_QUEUED) + continue; + + /* ignore the ignored tasks */ + if (edf_task == ignore) + continue; + + edf_pdata = edf_sch_get_pdata(edf_task); + + deadline = edf_pdata->deadline; + + if (current < deadline) { + delta = deadline - current; + + /* get highest priority */ + if (edf_task->priority > next_priority) { + next_priority = edf_task->priority; + next_delta = delta; + edf_task_next = edf_task; + } else if (edf_task->priority == next_priority) { + /* get earliest deadline */ + if (delta < next_delta) { + next_delta = delta; + edf_task_next = edf_task; + } + } + } else { + /* missed scheduling - will be rescheduled */ + trace_edf_sch("edf_get_next(), " + "missed scheduling - will be rescheduled"); + + /* have we already tried to rescheule ? */ + if (!reschedule) { + reschedule++; + trace_edf_sch("edf_get_next(), " + "didn't try to reschedule yet"); + edf_reschedule(edf_task, current); + } else { + /* reschedule failed */ + list_item_del(&edf_task->list); + edf_task->state = SOF_TASK_STATE_CANCEL; + } + } + } + + return edf_task_next; +} + +/* + * EDF Scheduler - Earliest Deadline First Scheduler. + * + * Schedule task with the earliest deadline from task list. + * Can run in IRQ context. + */ +static struct task *sch_edf(void) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint64_t current; + uint32_t flags; + + struct task *task; + struct task *future_task = NULL; + + tracev_edf_sch("sch_edf()"); + + interrupt_clear(PLATFORM_SCHEDULE_IRQ); + + while (!list_is_empty(&sch->list)) { + spin_lock_irq(&sch->lock, flags); + + /* get the current time */ + current = platform_timer_get(platform_timer); + + /* get next task to be scheduled */ + task = edf_get_next(current, NULL); + spin_unlock_irq(&sch->lock, flags); + + /* any tasks ? */ + if (!task) + return NULL; + + /* can task be started now ? */ + if (task->start <= current) { + /* yes, run current task */ + task->start = current; + + /* init task for running */ + spin_lock_irq(&sch->lock, flags); + task->state = SOF_TASK_STATE_PENDING; + list_item_del(&task->list); + spin_unlock_irq(&sch->lock, flags); + + /* now run task at correct run level */ + if (run_task(task) < 0) { + trace_error(TRACE_CLASS_EDF, + "sch_edf() error"); + break; + } + } else { + /* no, then schedule wake up */ + future_task = task; + break; + } + } + + /* tell caller about future task */ + return future_task; +} + +/* cancel and delete task from scheduler - won't stop it if already running */ +static int schedule_edf_task_cancel(struct task *task) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint32_t flags; + int ret = 0; + + tracev_edf_sch("schedule_edf_task_cancel()"); + + spin_lock_irq(&sch->lock, flags); + + /* check current task state, delete it if it is queued + * if it is already running, nothing we can do about it atm + */ + if (task->state == SOF_TASK_STATE_QUEUED) { + /* delete task */ + task->state = SOF_TASK_STATE_CANCEL; + list_item_del(&task->list); + } + + spin_unlock_irq(&sch->lock, flags); + + return ret; +} + +static int _sch_edf_task(struct task *task, uint64_t start, + uint64_t deadline) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint32_t flags; + uint64_t current; + uint64_t ticks_per_ms; + struct edf_task_pdata *edf_pdata; + + edf_pdata = edf_sch_get_pdata(task); + + tracev_edf_sch("_schedule_edf_task()"); + + spin_lock_irq(&sch->lock, flags); + + /* is task already pending ? - not enough MIPS to complete ? */ + if (task->state == SOF_TASK_STATE_PENDING) { + trace_edf_sch("_schedule_edf_task(), task already pending"); + spin_unlock_irq(&sch->lock, flags); + return 0; + } + + /* is task already queued ? - not enough MIPS to complete ? */ + if (task->state == SOF_TASK_STATE_QUEUED) { + trace_edf_sch("_schedule_edf_task(), task already queued"); + spin_unlock_irq(&sch->lock, flags); + return 0; + } + + /* get the current time */ + current = platform_timer_get(platform_timer); + + ticks_per_ms = clock_ms_to_ticks(sch->clock, 1); + + /* calculate start time - TODO: include MIPS */ + if (start == 0) + task->start = current; + else + task->start = task->start + ticks_per_ms * start / 1000 - + PLATFORM_SCHEDULE_COST; + + /* calculate deadline - TODO: include MIPS */ + edf_pdata->deadline = task->start + ticks_per_ms * deadline / 1000; + + /* add task to list */ + list_item_append(&task->list, &sch->list); + task->state = SOF_TASK_STATE_QUEUED; + spin_unlock_irq(&sch->lock, flags); + + return 1; +} + +/* + * Add a new task to the scheduler to be run and define a scheduling + * deadline in time for the task to be ran. Do not invoke the scheduler + * immediately to run task, but wait intil schedule is next called. + * + * deadline is in microseconds relative to start. + */ +static void schedule_edf_task_idle(struct task *task, uint64_t deadline) +{ + _sch_edf_task(task, 0, deadline); +} + +/* + * Add a new task to the scheduler to be run and define a scheduling + * window in time for the task to be ran. i.e. task will run between start and + * deadline times. + * + * start is in microseconds relative to last task start time. + * deadline is in microseconds relative to start. + */ +static void schedule_edf_task_normal(struct task *task, uint64_t start, + uint64_t deadline) +{ + int need_sched; + + need_sched = _sch_edf_task(task, start, deadline); + + /* need to run scheduler if task not already running */ + if (need_sched) { + /* rerun scheduler */ + schedule(); + } +} + +static void schedule_edf_task(struct task *task, uint64_t start, + uint64_t deadline, uint32_t flags) +{ + if (flags & SOF_SCHEDULE_FLAG_IDLE) + schedule_edf_task_idle(task, deadline); + else + schedule_edf_task_normal(task, start, deadline); +} + +/* Remove a task from the scheduler when complete */ +static void schedule_edf_task_complete(struct task *task) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint32_t flags; + + tracev_edf_sch("schedule_edf_task_complete()"); + + spin_lock_irq(&sch->lock, flags); + + /* Some high priority HW based IRQ handlers can reschedule tasks + * immediately. i.e. before the task context can change task state + * back to COMPLETED. Check here to make sure we dont clobber + * task->state for regular non IRQ users. + */ + switch (task->state) { + case SOF_TASK_STATE_RUNNING: + task->state = SOF_TASK_STATE_COMPLETED; + break; + case SOF_TASK_STATE_QUEUED: + case SOF_TASK_STATE_PENDING: + /* nothing to do here, high priority IRQ has scheduled us */ + break; + default: + trace_error(TRACE_CLASS_EDF, "unexpected task state %d " + "at edf_task completion", + task->state); + task->state = SOF_TASK_STATE_COMPLETED; + break; + } + spin_unlock_irq(&sch->lock, flags); +} + +/* Update task state to running */ +static void schedule_edf_task_running(struct task *task) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint32_t flags; + + tracev_edf_sch("schedule_edf_task_running()"); + + spin_lock_irq(&sch->lock, flags); + task->state = SOF_TASK_STATE_RUNNING; + spin_unlock_irq(&sch->lock, flags); +} + +static void edf_scheduler_run(void *unused) +{ + tracev_edf_sch("edf_scheduler_run()"); + + sch_edf(); +} + +/* run the scheduler */ +static void schedule_edf(void) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + struct list_item *tlist; + struct task *edf_task; + + uint32_t flags; + + tracev_edf_sch("schedule_edf()"); + + spin_lock_irq(&sch->lock, flags); + + /* make sure we have a queued task in the list first before we + start scheduling as contexts switches are not free. */ + list_for_item(tlist, &sch->list) { + edf_task = container_of(tlist, struct task, list); + + /* schedule if we find any queued tasks */ + if (edf_task->state == SOF_TASK_STATE_QUEUED) { + spin_unlock_irq(&sch->lock, flags); + goto schedule; + } + } + + /* no task to schedule */ + spin_unlock_irq(&sch->lock, flags); + return; + +schedule: + /* TODO: detect current IRQ context and call edf_scheduler_run if both + * current context matches scheduler context. saves a DSP context + * switch. + */ + + /* the scheduler is run in IRQ context */ + interrupt_set(PLATFORM_SCHEDULE_IRQ); +} + +/* Initialise the scheduler */ +static int edf_scheduler_init(void) +{ + trace_edf_sch("edf_scheduler_init()"); + + struct schedule_data *sch_data = *arch_schedule_get_data(); + struct edf_schedule_data *sch; + + sch_data->edf_sch_data = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, + sizeof(*sch_data->edf_sch_data)); + + sch = sch_data->edf_sch_data; + + list_init(&sch->list); + spinlock_init(&sch->lock); + sch->clock = PLATFORM_SCHED_CLOCK; + + /* configure scheduler interrupt */ + interrupt_register(PLATFORM_SCHEDULE_IRQ, IRQ_AUTO_UNMASK, + edf_scheduler_run, NULL); + interrupt_enable(PLATFORM_SCHEDULE_IRQ); + + /* allocate arch tasks */ + int tasks_result = allocate_tasks(); + + return tasks_result; +} + +/* Frees scheduler */ +static void edf_scheduler_free(void) +{ + struct edf_schedule_data *sch = + (*arch_schedule_get_data())->edf_sch_data; + uint32_t flags; + + spin_lock_irq(&sch->lock, flags); + + /* disable and unregister scheduler interrupt */ + interrupt_disable(PLATFORM_SCHEDULE_IRQ); + interrupt_unregister(PLATFORM_SCHEDULE_IRQ); + + /* free arch tasks */ + arch_free_tasks(); + + list_item_del(&sch->list); + + spin_unlock_irq(&sch->lock, flags); +} + +static int schedule_edf_task_init(struct task *task, uint32_t xflags) +{ + struct edf_task_pdata *edf_pdata; + (void)xflags; + + if (edf_sch_get_pdata(task)) + return -EEXIST; + + edf_pdata = rzalloc(RZONE_SYS_RUNTIME | RZONE_FLAG_UNCACHED, + SOF_MEM_CAPS_RAM, sizeof(*edf_pdata)); + + if (!edf_pdata) { + trace_edf_sch_error("schedule_edf_task_init() error:" + "alloc failed"); + return -ENOMEM; + } + + edf_sch_set_pdata(task, edf_pdata); + + return 0; +} + +static void schedule_edf_task_free(struct task *task) +{ + task->state = SOF_TASK_STATE_FREE; + task->func = NULL; + task->data = NULL; + + rfree(edf_sch_get_pdata(task)); + edf_sch_set_pdata(task, NULL); +} + +struct scheduler_ops schedule_edf_ops = { + .schedule_task = schedule_edf_task, + .schedule_task_init = schedule_edf_task_init, + .schedule_task_running = schedule_edf_task_running, + .schedule_task_complete = schedule_edf_task_complete, + .reschedule_task = NULL, + .schedule_task_cancel = schedule_edf_task_cancel, + .schedule_task_free = schedule_edf_task_free, + .scheduler_init = edf_scheduler_init, + .scheduler_free = edf_scheduler_free, + .scheduler_run = schedule_edf +}; diff --git a/src/lib/work.c b/src/lib/ll_schedule.c similarity index 52% rename from src/lib/work.c rename to src/lib/ll_schedule.c index 49555030544a..18e53c987386 100644 --- a/src/lib/work.c +++ b/src/lib/ll_schedule.c @@ -29,7 +29,8 @@ * Keyon Jie */ -#include +#include +#include #include #include #include @@ -61,19 +62,19 @@ * frequency changes. */ -struct work_queue { - struct list_item work[WORK_PRIORITIES]; /* list of work per priority */ - uint64_t timeout; /* timeout for next queue run */ - uint32_t window_size; /* window size for pending work */ +struct ll_schedule_data { + struct list_item work[LL_PRIORITIES]; /* list of ll per priority */ + uint64_t timeout; /* timeout for next queue run */ + uint32_t window_size; /* window size for pending ll */ spinlock_t lock; - struct notifier notifier; /* notify CPU freq changes */ - struct work_queue_timesource *ts; /* time source for work queue */ - uint32_t ticks_per_msec; /* ticks per msec */ - atomic_t num_work; /* number of queued work items */ + struct notifier notifier; /* notify CPU freq changes */ + struct timesource_data *ts; /* time source for work queue */ + uint32_t ticks_per_msec; /* ticks per msec */ + atomic_t num_ll; /* number of queued ll items */ }; -struct work_queue_shared_context { - atomic_t total_num_work; /* number of total queued work items */ +struct ll_queue_shared_context { + atomic_t total_num_work; /* number of total queued ll items */ atomic_t timer_clients; /* number of timer clients */ uint64_t last_tick; /* time of last tick */ @@ -81,87 +82,98 @@ struct work_queue_shared_context { struct timer *timers[PLATFORM_CORE_COUNT]; }; -static struct work_queue_shared_context *work_shared_ctx; +static struct ll_queue_shared_context *ll_shared_ctx; + +static void reschedule_ll_task(struct task *w, uint64_t start); +static void schedule_ll_task(struct task *w, uint64_t start, uint64_t deadline, + uint32_t flags); +static int schedule_ll_task_cancel(struct task *w); +static void schedule_ll_task_free(struct task *w); +static int ll_scheduler_init(void); +static int schedule_ll_task_init(struct task *w, uint32_t xflags); +static void ll_scheduler_free(void); /* calculate next timeout */ -static inline uint64_t queue_calc_next_timeout(struct work_queue *queue, +static inline uint64_t queue_calc_next_timeout(struct ll_schedule_data *queue, uint64_t start) { return queue->ticks_per_msec * queue->timeout / 1000 + start; } -static inline uint64_t work_get_timer(struct work_queue *queue) +static inline uint64_t ll_get_timer(struct ll_schedule_data *queue) { return queue->ts->timer_get(&queue->ts->timer); } -static inline void work_set_timer(struct work_queue *queue) +static inline void ll_set_timer(struct ll_schedule_data *queue) { uint64_t ticks; - if (atomic_add(&queue->num_work, 1) == 1) - work_shared_ctx->timers[cpu_get_id()] = &queue->ts->timer; + if (atomic_add(&queue->num_ll, 1) == 1) + ll_shared_ctx->timers[cpu_get_id()] = &queue->ts->timer; - if (atomic_add(&work_shared_ctx->total_num_work, 1) == 1) { - ticks = queue_calc_next_timeout(queue, work_get_timer(queue)); - work_shared_ctx->last_tick = ticks; + if (atomic_add(&ll_shared_ctx->total_num_work, 1) == 1) { + ticks = queue_calc_next_timeout(queue, ll_get_timer(queue)); + ll_shared_ctx->last_tick = ticks; queue->ts->timer_set(&queue->ts->timer, ticks); - atomic_add(&work_shared_ctx->timer_clients, 1); + atomic_add(&ll_shared_ctx->timer_clients, 1); timer_enable(&queue->ts->timer); } } -static inline void work_clear_timer(struct work_queue *queue) +static inline void ll_clear_timer(struct ll_schedule_data *queue) { - if (!atomic_sub(&work_shared_ctx->total_num_work, 1)) + if (!atomic_sub(&ll_shared_ctx->total_num_work, 1)) queue->ts->timer_clear(&queue->ts->timer); - if (!atomic_sub(&queue->num_work, 1)) { + if (!atomic_sub(&queue->num_ll, 1)) { timer_disable(&queue->ts->timer); - atomic_sub(&work_shared_ctx->timer_clients, 1); - work_shared_ctx->timers[cpu_get_id()] = NULL; + atomic_sub(&ll_shared_ctx->timer_clients, 1); + ll_shared_ctx->timers[cpu_get_id()] = NULL; } } /* is there any work pending in the current time window ? */ -static int is_work_pending(struct work_queue *queue) +static int is_ll_pending(struct ll_schedule_data *queue) { struct list_item *wlist; - struct work *work; + struct task *ll_task; uint64_t win_end; uint64_t win_start; int pending_count = 0; - uint32_t i; + int i; /* get the current valid window of work */ - win_end = work_get_timer(queue); + win_end = ll_get_timer(queue); win_start = win_end - queue->window_size; /* check every priority level */ - for (i = 0; i < WORK_PRIORITIES; ++i) { + for (i = (LL_PRIORITIES - 1); i >= 0; i--) { /* mark each valid work item in this time period as pending */ list_for_item(wlist, &queue->work[i]) { - work = container_of(wlist, struct work, list); + ll_task = container_of(wlist, struct task, list); /* correct the pending flag window for overflow */ if (win_end > win_start) { /* mark pending work */ - if (work->timeout >= win_start && - work->timeout <= win_end) { - work->pending = 1; + if (ll_task->start >= win_start && + ll_task->start <= win_end) { + ll_task->state = + SOF_TASK_STATE_PENDING; pending_count++; } else { - work->pending = 0; + ll_task->state = 0; } } else { /* mark pending work */ - if ((work->timeout <= win_end || - (work->timeout >= win_start && - work->timeout < ULONG_LONG_MAX))) { - work->pending = 1; + if ((ll_task->start <= win_end || + (ll_task->start >= win_start && + ll_task->start < ULONG_LONG_MAX))) { + ll_task->state = + SOF_TASK_STATE_PENDING; pending_count++; } else { - work->pending = 0; + ll_task->state = 0; } } } @@ -170,60 +182,59 @@ static int is_work_pending(struct work_queue *queue) return pending_count; } -static inline void work_next_timeout(struct work_queue *queue, - struct work *work, uint64_t reschedule_usecs) +static inline void ll_next_timeout(struct ll_schedule_data *queue, + struct task *work, + uint64_t reschedule_usecs) { /* reschedule work */ uint64_t next_d = 0; + struct ll_task_pdata *ll_task_pdata; + + ll_task_pdata = ll_sch_get_pdata(work); next_d = queue->ticks_per_msec * reschedule_usecs / 1000; - if (work->flags & WORK_SYNC) { - work->timeout += next_d; + if (ll_task_pdata->flags & SOF_SCHEDULE_FLAG_SYNC) { + work->start += next_d; } else { /* calc next run based on work request */ - work->timeout = next_d + work_shared_ctx->last_tick; + work->start = next_d + ll_shared_ctx->last_tick; } } /* run all pending work */ -static void run_work(struct work_queue *queue, uint32_t *flags, - uint32_t priority) +static void run_ll(struct ll_schedule_data *queue, uint32_t *flags, + uint32_t priority) { struct list_item *wlist; struct list_item *tlist; - struct work *work; + struct task *ll_task; uint64_t reschedule_usecs; - uint64_t udelay; int cpu = cpu_get_id(); /* check each work item in queue for pending */ list_for_item_safe(wlist, tlist, &queue->work[priority]) { - work = container_of(wlist, struct work, list); + ll_task = container_of(wlist, struct task, list); /* run work if its pending and remove from the queue */ - if (work->pending) { - udelay = ((work_get_timer(queue) - work->timeout) / - queue->ticks_per_msec) * 1000; - + if (ll_task->state == SOF_TASK_STATE_PENDING) { /* work can run in non atomic context */ spin_unlock_irq(&queue->lock, *flags); - reschedule_usecs = work->cb(work->cb_data, udelay); + reschedule_usecs = ll_task->func(ll_task->data); spin_lock_irq(&queue->lock, *flags); /* do we need reschedule this work ? */ if (reschedule_usecs == 0) { - list_item_del(&work->list); - atomic_sub(&work_shared_ctx->total_num_work, - 1); + list_item_del(&ll_task->list); + atomic_sub(&ll_shared_ctx->total_num_work, 1); /* don't enable irq, if no more work to do */ - if (!atomic_sub(&queue->num_work, 1)) - work_shared_ctx->timers[cpu] = NULL; + if (!atomic_sub(&queue->num_ll, 1)) + ll_shared_ctx->timers[cpu] = NULL; } else { /* get next work timeout */ - work_next_timeout(queue, work, - reschedule_usecs); + ll_next_timeout(queue, ll_task, + reschedule_usecs); } } } @@ -238,40 +249,40 @@ static inline uint64_t calc_delta_ticks(uint64_t current, uint64_t work) max -= current; max += work; return max; - } else + } else { return work - current; + } } /* re calculate timers for queue after CPU frequency change */ -static void queue_recalc_timers(struct work_queue *queue, +static void queue_recalc_timers(struct ll_schedule_data *queue, struct clock_notify_data *clk_data) { struct list_item *wlist; - struct work *work; + struct task *ll_task; uint64_t delta_ticks; uint64_t delta_msecs; uint64_t current; - uint32_t i; + int i; /* get current time */ - current = work_get_timer(queue); + current = ll_get_timer(queue); /* recalculate for each priority level */ - for (i = 0; i < WORK_PRIORITIES; ++i) { + for (i = (LL_PRIORITIES - 1); i >= 0; i--) { /* recalculate timers for each work item */ list_for_item(wlist, &queue->work[i]) { - work = container_of(wlist, struct work, list); - - delta_ticks = calc_delta_ticks(current, work->timeout); + ll_task = container_of(wlist, struct task, list); + delta_ticks = calc_delta_ticks(current, ll_task->start); delta_msecs = delta_ticks / clk_data->old_ticks_per_msec; /* is work within next msec, then schedule it now */ if (delta_msecs > 0) - work->timeout = current + + ll_task->start = current + queue->ticks_per_msec * delta_msecs; else - work->timeout = current + + ll_task->start = current + (queue->ticks_per_msec >> 3); } } @@ -283,30 +294,30 @@ static void queue_enable_registered_timers(void) int i; for (i = 0; i < PLATFORM_CORE_COUNT; i++) { - if (work_shared_ctx->timers[i]) { - atomic_add(&work_shared_ctx->timer_clients, 1); - timer_enable(work_shared_ctx->timers[i]); + if (ll_shared_ctx->timers[i]) { + atomic_add(&ll_shared_ctx->timer_clients, 1); + timer_enable(ll_shared_ctx->timers[i]); } } } /* reschedule queue */ -static void queue_reschedule(struct work_queue *queue) +static void queue_reschedule(struct ll_schedule_data *queue) { uint64_t ticks; /* clear only if all timer clients are done */ - if (!atomic_sub(&work_shared_ctx->timer_clients, 1)) { + if (!atomic_sub(&ll_shared_ctx->timer_clients, 1)) { /* clear timer */ queue->ts->timer_clear(&queue->ts->timer); /* re-arm only if there is work to do */ - if (atomic_read(&work_shared_ctx->total_num_work)) { + if (atomic_read(&ll_shared_ctx->total_num_work)) { /* re-arm timer */ ticks = queue_calc_next_timeout (queue, - work_shared_ctx->last_tick); - work_shared_ctx->last_tick = ticks; + ll_shared_ctx->last_tick); + ll_shared_ctx->last_tick = ticks; queue->ts->timer_set(&queue->ts->timer, ticks); queue_enable_registered_timers(); @@ -317,19 +328,19 @@ static void queue_reschedule(struct work_queue *queue) /* run the work queue */ static void queue_run(void *data) { - struct work_queue *queue = (struct work_queue *)data; + struct ll_schedule_data *queue = (struct ll_schedule_data *)data; uint32_t flags; - uint32_t i; + int i; timer_disable(&queue->ts->timer); spin_lock_irq(&queue->lock, flags); /* run work if there is any pending */ - if (is_work_pending(queue)) { + if (is_ll_pending(queue)) { /* run for each priority level */ - for (i = 0; i < WORK_PRIORITIES; ++i) - run_work(queue, &flags, i); + for (i = (LL_PRIORITIES - 1); i >= 0; i--) + run_ll(queue, &flags, i); } /* re-calc timer and re-arm */ @@ -339,9 +350,9 @@ static void queue_run(void *data) } /* notification of CPU frequency changes - atomic PRE and POST sequence */ -static void work_notify(int message, void *data, void *event_data) +static void ll_notify(int message, void *data, void *event_data) { - struct work_queue *queue = (struct work_queue *)data; + struct ll_schedule_data *queue = (struct ll_schedule_data *)data; struct clock_notify_data *clk_data = (struct clock_notify_data *)event_data; uint32_t flags; @@ -350,7 +361,6 @@ static void work_notify(int message, void *data, void *event_data) /* we need to re-calculate timer when CPU frequency changes */ if (message == CLOCK_NOTIFY_POST) { - /* CPU frequency update complete */ /* scale the window size to clock speed */ queue->ticks_per_msec = clock_ms_to_ticks(queue->ts->clk, 1); @@ -364,9 +374,11 @@ static void work_notify(int message, void *data, void *event_data) spin_unlock_irq(&queue->lock, flags); } -void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout) +static void ll_schedule(struct ll_schedule_data *queue, struct task *w, + uint64_t start) { - struct work *work; + struct ll_task_pdata *ll_pdata; + struct task *ll_task; struct list_item *wlist; uint32_t flags; @@ -374,38 +386,43 @@ void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout) /* check to see if we are already scheduled ? */ list_for_item(wlist, &queue->work[w->priority]) { - work = container_of(wlist, struct work, list); + ll_task = container_of(wlist, struct task, list); - /* keep original timeout */ - if (work == w) + /* keep original start */ + if (ll_task == w) goto out; } - w->timeout = queue->ticks_per_msec * timeout / 1000; + w->start = queue->ticks_per_msec * start / 1000; + ll_pdata = ll_sch_get_pdata(w); - /* convert timeout micro seconds to CPU clock ticks */ - if (w->flags & WORK_SYNC) - w->timeout += work_get_timer(queue); + /* convert start micro seconds to CPU clock ticks */ + if (ll_pdata->flags & SOF_SCHEDULE_FLAG_SYNC) + w->start += ll_get_timer(queue); else - w->timeout += work_shared_ctx->last_tick; + w->start += ll_shared_ctx->last_tick; /* insert work into list */ list_item_prepend(&w->list, &queue->work[w->priority]); - work_set_timer(queue); + ll_set_timer(queue); out: spin_unlock_irq(&queue->lock, flags); } -void work_schedule_default(struct work *w, uint64_t timeout) +static void schedule_ll_task(struct task *w, uint64_t start, uint64_t deadline, + uint32_t flags) { - work_schedule(*arch_work_queue_get(), w, timeout); + (void)deadline; + (void)flags; + ll_schedule((*arch_schedule_get_data())->ll_sch_data, w, start); } -static void reschedule(struct work_queue *queue, struct work *w, uint64_t time) +static void reschedule(struct ll_schedule_data *queue, struct task *w, + uint64_t time) { - struct work *work; + struct task *ll_task; struct list_item *wlist; uint32_t flags; @@ -413,93 +430,107 @@ static void reschedule(struct work_queue *queue, struct work *w, uint64_t time) /* check to see if we are already scheduled */ list_for_item(wlist, &queue->work[w->priority]) { - work = container_of(wlist, struct work, list); + ll_task = container_of(wlist, struct task, list); /* found it */ - if (work == w) + if (ll_task == w) goto found; } /* not found insert work into list */ list_item_prepend(&w->list, &queue->work[w->priority]); - work_set_timer(queue); + ll_set_timer(queue); found: /* re-calc timer and re-arm */ - w->timeout = time; + w->start = time; spin_unlock_irq(&queue->lock, flags); } -void work_reschedule(struct work_queue *queue, struct work *w, uint64_t timeout) +static void reschedule_ll_task(struct task *w, uint64_t start) { + struct ll_schedule_data *queue = + (*arch_schedule_get_data())->ll_sch_data; uint64_t time; + struct ll_task_pdata *ll_pdata; + + ll_pdata = ll_sch_get_pdata(w); - time = queue->ticks_per_msec * timeout / 1000; + time = queue->ticks_per_msec * start / 1000; - /* convert timeout micro seconds to CPU clock ticks */ - if (w->flags & WORK_SYNC) - time += work_get_timer(queue); + /* convert start micro seconds to CPU clock ticks */ + if (ll_pdata->flags & SOF_SCHEDULE_FLAG_SYNC) + time += ll_get_timer(queue); else - time += work_shared_ctx->last_tick; + time += ll_shared_ctx->last_tick; reschedule(queue, w, time); } -void work_reschedule_default(struct work *w, uint64_t timeout) +static int schedule_ll_task_cancel(struct task *w) { - work_reschedule(*arch_work_queue_get(), w, timeout); -} - -void work_reschedule_default_at(struct work *w, uint64_t time) -{ - reschedule(*arch_work_queue_get(), w, time); -} - -void work_cancel(struct work_queue *queue, struct work *w) -{ - struct work *work; + struct ll_schedule_data *queue = + (*arch_schedule_get_data())->ll_sch_data; + struct task *ll_task; struct list_item *wlist; uint32_t flags; + int ret = 0; spin_lock_irq(&queue->lock, flags); /* check to see if we are scheduled */ list_for_item(wlist, &queue->work[w->priority]) { - work = container_of(wlist, struct work, list); + ll_task = container_of(wlist, struct task, list); /* found it */ - if (work == w) { - work_clear_timer(queue); + if (ll_task == w) { + ll_clear_timer(queue); break; } } /* remove work from list */ + w->state = SOF_TASK_STATE_CANCEL; list_item_del(&w->list); spin_unlock_irq(&queue->lock, flags); + + return ret; } -void work_cancel_default(struct work *w) +static void schedule_ll_task_free(struct task *w) { - work_cancel(*arch_work_queue_get(), w); + struct ll_schedule_data *queue = + (*arch_schedule_get_data())->ll_sch_data; + uint32_t flags; + struct ll_task_pdata *ll_pdata; + + spin_lock_irq(&queue->lock, flags); + + /* release the resources */ + w->state = SOF_TASK_STATE_FREE; + ll_pdata = ll_sch_get_pdata(w); + rfree(ll_pdata); + ll_sch_set_pdata(w, NULL); + + spin_unlock_irq(&queue->lock, flags); } -static struct work_queue *work_new_queue(struct work_queue_timesource *ts) +static struct ll_schedule_data *work_new_queue(struct timesource_data *ts) { - struct work_queue *queue; - uint32_t i; + struct ll_schedule_data *queue; + int i; /* init work queue */ queue = rmalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*queue)); - for (i = 0; i < WORK_PRIORITIES; ++i) + for (i = 0; i < LL_PRIORITIES; ++i) list_init(&queue->work[i]); spinlock_init(&queue->lock); - atomic_init(&queue->num_work, 0); + atomic_init(&queue->num_ll, 0); queue->ts = ts; queue->ticks_per_msec = clock_ms_to_ticks(queue->ts->clk, 1); queue->window_size = queue->ticks_per_msec * @@ -509,7 +540,7 @@ static struct work_queue *work_new_queue(struct work_queue_timesource *ts) queue->timeout = PLATFORM_WORKQ_DEFAULT_TIMEOUT; /* notification of clk changes */ - queue->notifier.cb = work_notify; + queue->notifier.cb = ll_notify; queue->notifier.cb_data = queue; queue->notifier.id = ts->notifier; notifier_register(&queue->notifier); @@ -520,35 +551,80 @@ static struct work_queue *work_new_queue(struct work_queue_timesource *ts) return queue; } -void init_system_workq(struct work_queue_timesource *ts) +static int ll_scheduler_init(void) { - struct work_queue **queue = arch_work_queue_get(); + int ret = 0; + int cpu; + struct schedule_data *sch_data = *arch_schedule_get_data(); + struct timesource_data *ts; + + cpu = cpu_get_id(); + ts = &platform_generic_queue[cpu]; - *queue = work_new_queue(ts); + sch_data->ll_sch_data = work_new_queue(ts); if (cpu_get_id() == PLATFORM_MASTER_CORE_ID) { - work_shared_ctx = rzalloc(RZONE_SYS | RZONE_FLAG_UNCACHED, - SOF_MEM_CAPS_RAM, - sizeof(*work_shared_ctx)); - atomic_init(&work_shared_ctx->total_num_work, 0); - atomic_init(&work_shared_ctx->timer_clients, 0); + ll_shared_ctx = rzalloc(RZONE_SYS | RZONE_FLAG_UNCACHED, + SOF_MEM_CAPS_RAM, + sizeof(*ll_shared_ctx)); + atomic_init(&ll_shared_ctx->total_num_work, 0); + atomic_init(&ll_shared_ctx->timer_clients, 0); } + + return ret; } -void free_system_workq(void) +/* initialise our work */ +static int schedule_ll_task_init(struct task *w, uint32_t xflags) { - struct work_queue **queue = arch_work_queue_get(); + struct ll_task_pdata *ll_pdata; + + if (ll_sch_get_pdata(w)) + return -EEXIST; + + ll_pdata = rzalloc(RZONE_SYS_RUNTIME | RZONE_FLAG_UNCACHED, + SOF_MEM_CAPS_RAM, sizeof(*ll_pdata)); + + if (!ll_pdata) { + trace_error(0, "schedule_ll_task_init() error: alloc failed"); + return -ENOMEM; + } + + ll_sch_set_pdata(w, ll_pdata); + + ll_pdata->flags = xflags; + + return 0; +} + +static void ll_scheduler_free(void) +{ + struct ll_schedule_data *queue = + (*arch_schedule_get_data())->ll_sch_data; uint32_t flags; - uint32_t i; + int i; - spin_lock_irq(&(*queue)->lock, flags); + spin_lock_irq(&queue->lock, flags); - timer_unregister(&(*queue)->ts->timer); + timer_unregister(&queue->ts->timer); - notifier_unregister(&(*queue)->notifier); + notifier_unregister(&queue->notifier); - for (i = 0; i < WORK_PRIORITIES; ++i) - list_item_del(&(*queue)->work[i]); + for (i = 0; i < LL_PRIORITIES; ++i) + list_item_del(&queue->work[i]); - spin_unlock_irq(&(*queue)->lock, flags); + spin_unlock_irq(&queue->lock, flags); } + +struct scheduler_ops schedule_ll_ops = { + .schedule_task = schedule_ll_task, + .schedule_task_init = schedule_ll_task_init, + .schedule_task_running = NULL, + .schedule_task_complete = NULL, + .reschedule_task = reschedule_ll_task, + .schedule_task_cancel = schedule_ll_task_cancel, + .schedule_task_free = schedule_ll_task_free, + .scheduler_init = ll_scheduler_init, + .scheduler_free = ll_scheduler_free, + .scheduler_run = NULL +}; diff --git a/src/lib/schedule.c b/src/lib/schedule.c index 58ebaad3e59c..5d84394076dc 100644 --- a/src/lib/schedule.c +++ b/src/lib/schedule.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Intel Corporation + * Copyright (c) 2019, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,459 +25,120 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * Author: Liam Girdwood + * Author: Bartosz Kokoszko */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/* Generic scheduler */ #include -#include -#include -#include -#include -#include -#include +#include +#include -struct schedule_data { - spinlock_t lock; - struct list_item list; /* list of tasks in priority queue */ - uint32_t clock; - struct work work; +static const struct scheduler_ops *schedulers[SOF_SCHEDULE_COUNT] = { + &schedule_edf_ops, /* SOF_SCHEDULE_EDF */ + &schedule_ll_ops /* SOF_SCHEDULE_LL */ }; -#define SLOT_ALIGN_TRIES 10 - -/* - * Simple rescheduler to calculate tasks new start time and deadline if - * prevoius deadline was missed. Tries to align at first with current task - * timing, but will just add onto current if too far behind current. - * XRUNs will be propagated upto the host if we have to reschedule. - */ -static inline void edf_reschedule(struct task *task, uint64_t current) -{ - uint64_t delta = (task->deadline - task->start) << 1; - int i; - - /* try and align task with current scheduling slots */ - for (i = 0; i < SLOT_ALIGN_TRIES; i++) { - - task->start += delta; - - if (task->start > current + delta) { - task->deadline = task->start + delta; - return; - } - } - - /* task has slipped a lot, so just add delay to current */ - task->start = current + delta; - task->deadline = task->start + delta; -} - -/* - * Find the first non running task with the earliest deadline. - * TODO: Reduce cache invalidations by checking if the currently - * running task AND the earliest queued task will both complete before their - * deadlines. If so, then schedule the earlier queued task after the currently - * running task has completed. - */ -static inline struct task *edf_get_next(uint64_t current, - struct task *ignore) -{ - struct schedule_data *sch = *arch_schedule_get(); - struct task *task; - struct task *next_task = NULL; - struct list_item *clist; - struct list_item *tlist; - uint64_t next_delta = UINT64_MAX; - int next_priority = TASK_PRI_LOW; - uint64_t delta; - uint64_t deadline; - int reschedule = 0; - - /* any tasks in the scheduler ? */ - if (list_is_empty(&sch->list)) { - return NULL; - } - - /* check every queued or running task in list */ - list_for_item_safe(clist, tlist, &sch->list) { - task = container_of(clist, struct task, list); - - /* only check queued tasks */ - if (task->state != TASK_STATE_QUEUED) - continue; - - /* ignore the ignored tasks */ - if (task == ignore) - continue; - - /* include the length of task in deadline calc */ - deadline = task->deadline - task->max_rtime; - - if (current < deadline) { - delta = deadline - current; - - /* get highest priority */ - if (task->priority < next_priority) { - next_priority = task->priority; - next_delta = delta; - next_task = task; - } else if (task->priority == next_priority) { - /* get earliest deadline */ - if (delta < next_delta) { - next_delta = delta; - next_task = task; - } - } - } else { - /* missed scheduling - will be rescheduled */ - trace_schedule("edf_get_next(), " - "missed scheduling - will be rescheduled"); - - /* have we already tried to rescheule ? */ - if (!reschedule) { - reschedule++; - trace_schedule("edf_get_next(), " - "didn't try to reschedule yet"); - edf_reschedule(task, current); - } else { - /* reschedule failed */ - list_item_del(&task->list); - task->state = TASK_STATE_CANCEL; - } - } - } - - return next_task; -} - -/* work set in the future when next task can be scheduled */ -static uint64_t sch_work(void *data, uint64_t delay) -{ - tracev_schedule("sch_work()"); - schedule(); - return 0; -} - -/* - * EDF Scheduler - Earliest Deadline First Scheduler. - * - * Schedule task with the earliest deadline from task list. - * Can run in IRQ context. - */ -static struct task *schedule_edf(void) -{ - struct schedule_data *sch = *arch_schedule_get(); - struct task *task; - struct task *future_task = NULL; - uint64_t current; - uint32_t flags; - - tracev_schedule("schedule_edf()"); - - interrupt_clear(PLATFORM_SCHEDULE_IRQ); - - while (!list_is_empty(&sch->list)) { - spin_lock_irq(&sch->lock, flags); - - /* get the current time */ - current = platform_timer_get(platform_timer); - - /* get next task to be scheduled */ - task = edf_get_next(current, NULL); - spin_unlock_irq(&sch->lock, flags); - - /* any tasks ? */ - if (!task) - return NULL; - - /* can task be started now ? */ - if (task->start <= current) { - /* yes, run current task */ - task->start = current; - - /* init task for running */ - spin_lock_irq(&sch->lock, flags); - task->state = TASK_STATE_PENDING; - list_item_del(&task->list); - spin_unlock_irq(&sch->lock, flags); - - /* now run task at correct run level */ - if (run_task(task) < 0) { - trace_error(TRACE_CLASS_SCHEDULE, - "schedule_edf() error"); - break; - } - } else { - /* no, then schedule wake up */ - future_task = task; - break; - } - } - - /* tell caller about future task */ - return future_task; -} - -/* cancel and delete task from scheduler - won't stop it if already running */ -int schedule_task_cancel(struct task *task) +int schedule_task_init(struct task *task, uint16_t type, uint16_t priority, + uint64_t (*func)(void *data), void *data, uint16_t core, + uint32_t xflags) { - struct schedule_data *sch = *arch_schedule_get(); - uint32_t flags; int ret = 0; - tracev_schedule("schedule_task_cancel()"); - - spin_lock_irq(&sch->lock, flags); - - /* check current task state, delete it if it is queued - * if it is already running, nothing we can do about it atm - */ - if (task->state == TASK_STATE_QUEUED) { - /* delete task */ - task->state = TASK_STATE_CANCEL; - list_item_del(&task->list); + if (type >= SOF_SCHEDULE_COUNT) { + trace_schedule_error("schedule_task_init() error: " + "invalid task type"); + ret = -EINVAL; + goto out; } - spin_unlock_irq(&sch->lock, flags); - + task->type = type; + task->priority = priority; + task->core = core; + task->state = SOF_TASK_STATE_INIT; + task->func = func; + task->data = data; + task->ops = schedulers[task->type]; + + if (task->ops->schedule_task_init) + ret = task->ops->schedule_task_init(task, xflags); +out: return ret; } -static int _schedule_task(struct task *task, uint64_t start, uint64_t deadline) +void schedule_task_free(struct task *task) { - struct schedule_data *sch = *arch_schedule_get(); - uint32_t flags; - uint64_t current; - uint64_t ticks_per_ms; - - tracev_schedule("_schedule_task()"); - - spin_lock_irq(&sch->lock, flags); - - /* is task already pending ? - not enough MIPS to complete ? */ - if (task->state == TASK_STATE_PENDING) { - trace_schedule("_schedule_task(), task already pending"); - spin_unlock_irq(&sch->lock, flags); - return 0; - } - - /* is task already queued ? - not enough MIPS to complete ? */ - if (task->state == TASK_STATE_QUEUED) { - trace_schedule("_schedule_task(), task already queued"); - spin_unlock_irq(&sch->lock, flags); - return 0; - } - - /* get the current time */ - current = platform_timer_get(platform_timer); - - ticks_per_ms = clock_ms_to_ticks(sch->clock, 1); - - /* calculate start time - TODO: include MIPS */ - if (start == 0) - task->start = current; - else - task->start = task->start + ticks_per_ms * start / 1000 - - PLATFORM_SCHEDULE_COST; - - /* calculate deadline - TODO: include MIPS */ - task->deadline = task->start + ticks_per_ms * deadline / 1000; - - /* add task to list */ - list_item_append(&task->list, &sch->list); - task->state = TASK_STATE_QUEUED; - spin_unlock_irq(&sch->lock, flags); - - return 1; + if (task->ops->schedule_task_free) + task->ops->schedule_task_free(task); } -/* - * Add a new task to the scheduler to be run and define a scheduling - * deadline in time for the task to be ran. Do not invoke the scheduler - * immediately to run task, but wait intil schedule is next called. - * - * deadline is in microseconds relative to start. - */ -void schedule_task_idle(struct task *task, uint64_t deadline) +void schedule_task(struct task *task, uint64_t start, uint64_t deadline, + uint32_t flags) { - _schedule_task(task, 0, deadline); + if (task->ops->schedule_task) + task->ops->schedule_task(task, start, deadline, flags); } -/* - * Add a new task to the scheduler to be run and define a scheduling - * window in time for the task to be ran. i.e. task will run between start and - * deadline times. - * - * start is in microseconds relative to last task start time. - * deadline is in microseconds relative to start. - */ -void schedule_task(struct task *task, uint64_t start, uint64_t deadline) +void reschedule_task(struct task *task, uint64_t start) { - int need_sched; - - need_sched = _schedule_task(task, start, deadline); - - /* need to run scheduler if task not already running */ - if (need_sched) { - /* rerun scheduler */ - schedule(); - } + if (task->ops->reschedule_task) + task->ops->reschedule_task(task, start); } -/* Remove a task from the scheduler when complete */ -void schedule_task_complete(struct task *task) +int schedule_task_cancel(struct task *task) { - struct schedule_data *sch = *arch_schedule_get(); - uint32_t flags; - - tracev_schedule("schedule_task_complete()"); - - spin_lock_irq(&sch->lock, flags); - - /* Some high priority HW based IRQ handlers can reschedule tasks - * immediately. i.e. before the task context can change task state - * back to COMPLETED. Check here to make sure we dont clobber - * task->state for regular non IRQ users. - */ - switch (task->state) { - case TASK_STATE_RUNNING: - task->state = TASK_STATE_COMPLETED; - break; - case TASK_STATE_QUEUED: - case TASK_STATE_PENDING: - /* nothing to do here, high priority IRQ has scheduled us */ - break; - default: - trace_error(TRACE_CLASS_SCHEDULE, "unexpected task state %d " - "at task completion", - task->state); - task->state = TASK_STATE_COMPLETED; - break; - } - spin_unlock_irq(&sch->lock, flags); - - /* tell any waiter that task has completed */ - wait_completed(&task->complete); + if (task->ops->schedule_task_cancel) + return task->ops->schedule_task_cancel(task); + return 0; } -/* Update task state to running */ void schedule_task_running(struct task *task) { - struct schedule_data *sch = *arch_schedule_get(); - uint32_t flags; - - tracev_schedule("schedule_task_running()"); - - spin_lock_irq(&sch->lock, flags); - task->state = TASK_STATE_RUNNING; - spin_unlock_irq(&sch->lock, flags); + if (task->ops->schedule_task_running) + task->ops->schedule_task_running(task); } -static void scheduler_run(void *unused) +void schedule_task_complete(struct task *task) { - struct task *future_task; - - tracev_schedule("scheduler_run()"); - - /* EDF is only scheduler supported atm */ - future_task = schedule_edf(); - if (future_task) - work_reschedule_default_at(&((*arch_schedule_get())->work), - future_task->start); + if (task->ops->schedule_task_complete) + task->ops->schedule_task_complete(task); } -/* run the scheduler */ -void schedule(void) +int scheduler_init(void) { - struct schedule_data *sch = *arch_schedule_get(); - struct list_item *tlist; - struct task *task; - uint32_t flags; - - tracev_schedule("schedule()"); - - spin_lock_irq(&sch->lock, flags); + struct schedule_data **sch = arch_schedule_get_data(); + int i = 0; + int ret = 0; - /* make sure we have a queued task in the list first before we - start scheduling as contexts switches are not free. */ - list_for_item(tlist, &sch->list) { - task = container_of(tlist, struct task, list); + /* init scheduler_data */ + *sch = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(**sch)); - /* schedule if we find any queued tasks */ - if (task->state == TASK_STATE_QUEUED) { - spin_unlock_irq(&sch->lock, flags); - goto schedule; + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_init) { + ret = schedulers[i]->scheduler_init(); + if (ret < 0) + goto out; } } - - /* no task to schedule */ - spin_unlock_irq(&sch->lock, flags); - return; - -schedule: - /* TODO: detect current IRQ context and call scheduler_run if both - * current context matches scheduler context. saves a DSP context - * switch. - */ - - /* the scheduler is run in IRQ context */ - interrupt_set(PLATFORM_SCHEDULE_IRQ); +out: + return ret; } -/* Initialise the scheduler */ -int scheduler_init(struct sof *sof) +void schedule_free(void) { - trace_schedule("scheduler_init()"); - - struct schedule_data **sch = arch_schedule_get(); - *sch = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(**sch)); - - if (!*sch) - return -ENOMEM; - - list_init(&((*sch)->list)); - spinlock_init(&((*sch)->lock)); - (*sch)->clock = PLATFORM_SCHED_CLOCK; - work_init(&((*sch)->work), sch_work, *sch, WORK_MED_PRI, WORK_ASYNC); - - /* configure scheduler interrupt */ - interrupt_register(PLATFORM_SCHEDULE_IRQ, IRQ_AUTO_UNMASK, - scheduler_run, NULL); - interrupt_enable(PLATFORM_SCHEDULE_IRQ); - - /* allocate arch tasks */ - int tasks_result = allocate_tasks(); + int i; - return tasks_result; + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_free) + schedulers[i]->scheduler_free(); + } } -/* Frees scheduler */ -void scheduler_free(void) +void schedule(void) { - struct schedule_data **sch = arch_schedule_get(); - uint32_t flags; - - spin_lock_irq(&(*sch)->lock, flags); - - /* disable and unregister scheduler interrupt */ - interrupt_disable(PLATFORM_SCHEDULE_IRQ); - interrupt_unregister(PLATFORM_SCHEDULE_IRQ); + int i = 0; - /* free arch tasks */ - arch_free_tasks(); - - work_cancel_default(&(*sch)->work); - list_item_del(&(*sch)->list); - - spin_unlock_irq(&(*sch)->lock, flags); + for (i = 0; i < SOF_SCHEDULE_COUNT; i++) { + if (schedulers[i]->scheduler_run) + schedulers[i]->scheduler_run(); + } } diff --git a/src/lib/wait.c b/src/lib/wait.c index 141bbcd49c67..612271d22f38 100644 --- a/src/lib/wait.c +++ b/src/lib/wait.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,7 +54,7 @@ int wait_for_completion_timeout(completion_t *comp) { volatile completion_t *c = (volatile completion_t *)comp; - work_schedule_default(&comp->work, comp->timeout); + schedule_task(&comp->work, comp->timeout, 0, 0); comp->timeout = 0; /* check for completion after every wake from IRQ */ @@ -64,7 +64,7 @@ int wait_for_completion_timeout(completion_t *comp) /* did we complete */ if (c->complete) { /* no timeout so cancel work and return 0 */ - work_cancel_default(&comp->work); + schedule_task_cancel(&comp->work); return 0; } diff --git a/src/platform/baytrail/platform.c b/src/platform/baytrail/platform.c index e5b7283f05a7..9e5639e2e2a9 100644 --- a/src/platform/baytrail/platform.c +++ b/src/platform/baytrail/platform.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -137,7 +136,7 @@ static const struct sof_ipc_window sram_window = { }, }; -struct work_queue_timesource platform_generic_queue[] = { +struct timesource_data platform_generic_queue[] = { { .timer = { .id = TIMER3, /* external timer */ @@ -215,9 +214,9 @@ int platform_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM_CLOCK); clock_init(); - /* init work queues and clocks */ - trace_point(TRACE_BOOT_SYS_WORK); - init_system_workq(&platform_generic_queue[PLATFORM_MASTER_CORE_ID]); + /* init scheduler and clocks */ + trace_point(TRACE_BOOT_SYS_SCHED); + scheduler_init(); trace_point(TRACE_BOOT_PLATFORM_TIMER); platform_timer_start(platform_timer); diff --git a/src/platform/haswell/platform.c b/src/platform/haswell/platform.c index 4ddf54e87c13..b819d00afa2d 100644 --- a/src/platform/haswell/platform.c +++ b/src/platform/haswell/platform.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -136,7 +135,7 @@ static const struct sof_ipc_window sram_window = { }, }; -struct work_queue_timesource platform_generic_queue[] = { +struct timesource_data platform_generic_queue[] = { { .timer = { .id = TIMER1, /* internal timer */ @@ -204,9 +203,9 @@ int platform_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM_CLOCK); clock_init(); - /* init work queues and clocks */ - trace_point(TRACE_BOOT_SYS_WORK); - init_system_workq(&platform_generic_queue[PLATFORM_MASTER_CORE_ID]); + /* init scheduler and clocks */ + trace_point(TRACE_BOOT_SYS_SCHED); + scheduler_init(); trace_point(TRACE_BOOT_PLATFORM_TIMER); platform_timer_start(platform_timer); diff --git a/src/platform/intel/cavs/platform.c b/src/platform/intel/cavs/platform.c index c262601c6f55..abe65ccfe401 100644 --- a/src/platform/intel/cavs/platform.c +++ b/src/platform/intel/cavs/platform.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -151,7 +150,7 @@ static const struct sof_ipc_window sram_window = { }; #endif -struct work_queue_timesource platform_generic_queue[] = { +struct timesource_data platform_generic_queue[] = { { .timer = { .id = TIMER3, /* external timer */ @@ -429,8 +428,8 @@ int platform_init(struct sof *sof) trace_point(TRACE_BOOT_PLATFORM_CLOCK); clock_init(); - trace_point(TRACE_BOOT_SYS_WORK); - init_system_workq(&platform_generic_queue[PLATFORM_MASTER_CORE_ID]); + trace_point(TRACE_BOOT_SYS_SCHED); + scheduler_init(); /* init the system agent */ sa_init(sof); diff --git a/src/tasks/audio.c b/src/tasks/audio.c index ab888250b9d9..e2d1e4a60965 100644 --- a/src/tasks/audio.c +++ b/src/tasks/audio.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/cmocka/src/audio/kpb/kpb_mock.c b/test/cmocka/src/audio/kpb/kpb_mock.c index ea66bfcec6a9..6a1e1deb9a32 100644 --- a/test/cmocka/src/audio/kpb/kpb_mock.c +++ b/test/cmocka/src/audio/kpb/kpb_mock.c @@ -39,14 +39,18 @@ int comp_set_state(struct comp_dev *dev, int cmd) return 0; } -void work_schedule_default(struct work *w, uint64_t timeout) +void notifier_register(struct notifier *notifier) { } -void notifier_register(struct notifier *notifier) +int schedule_task_init(struct task *task, uint16_t type, uint16_t priority, + uint64_t (*func)(void *data), void *data, uint16_t core, + uint32_t xflags) { + return 0; } -void schedule_task(struct task *task, uint64_t start, uint64_t deadline) +void schedule_task(struct task *task, uint64_t start, uint64_t deadline, + uint32_t flags) { } diff --git a/test/cmocka/src/audio/mixer/mock.c b/test/cmocka/src/audio/mixer/mock.c index b66b69168c6c..1746e987ba91 100644 --- a/test/cmocka/src/audio/mixer/mock.c +++ b/test/cmocka/src/audio/mixer/mock.c @@ -72,6 +72,6 @@ int comp_set_state(struct comp_dev *dev, int cmd) return 0; } -void work_schedule_default(struct work *w, uint64_t timeout) +void ll_schedule_default(struct task *w, uint64_t timeout) { } diff --git a/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c b/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c index 876605f82640..363e906024c9 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c +++ b/test/cmocka/src/audio/pipeline/pipeline_connect_upstream.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "pipeline_mocks.h" #include "pipeline_connection_mocks.h" #include diff --git a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h index 4d177d75d59c..80bd630ebff0 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h +++ b/test/cmocka/src/audio/pipeline/pipeline_connection_mocks.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/cmocka/src/audio/pipeline/pipeline_free.c b/test/cmocka/src/audio/pipeline/pipeline_free.c index 9132e3ea73bd..3d348d969de6 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_free.c +++ b/test/cmocka/src/audio/pipeline/pipeline_free.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "pipeline_mocks.h" #include "pipeline_connection_mocks.h" #include @@ -95,7 +95,7 @@ static void test_audio_pipeline_free_sheduler_task_free(void **state) /*Testing component*/ pipeline_free(&result); - assert_int_equal(result.pipe_task.state, TASK_STATE_FREE); + assert_int_equal(result.pipe_task.state, SOF_TASK_STATE_FREE); assert_ptr_equal(NULL, result.pipe_task.data); assert_ptr_equal(NULL, result.pipe_task.func); } diff --git a/test/cmocka/src/audio/pipeline/pipeline_mocks.c b/test/cmocka/src/audio/pipeline/pipeline_mocks.c index c6014d6644be..2e246fa4a8c7 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_mocks.c +++ b/test/cmocka/src/audio/pipeline/pipeline_mocks.c @@ -43,22 +43,42 @@ void platform_dai_timestamp(struct comp_dev *dai, (void)posn; } -void schedule_task(struct task *task, uint64_t start, uint64_t deadline) +void schedule_task_free(struct task *task) { - (void)deadline; - (void)start; (void)task; + task->state = SOF_TASK_STATE_FREE; + task->func = NULL; + task->data = NULL; } -void schedule_task_complete(struct task *task) +void schedule_edf_task_idle(struct task *task, uint64_t deadline) { + (void)deadline; (void)task; } -void schedule_task_idle(struct task *task, uint64_t deadline) +void schedule_task(struct task *task, uint64_t start, uint64_t deadline, + uint32_t flags) { + (void)task; + (void)start; (void)deadline; + (void)flags; +} + +int schedule_task_init(struct task *task, uint16_t type, uint16_t priority, + uint64_t (*func)(void *data), void *data, uint16_t core, + uint32_t xflags) +{ (void)task; + (void)type; + (void)priority; + (void)func; + (void)data; + (void)core; + (void)xflags; + + return 0; } int schedule_task_cancel(struct task *task) diff --git a/test/cmocka/src/audio/pipeline/pipeline_mocks.h b/test/cmocka/src/audio/pipeline/pipeline_mocks.h index 17021e229ca7..360db7ab5a01 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_mocks.h +++ b/test/cmocka/src/audio/pipeline/pipeline_mocks.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/cmocka/src/audio/pipeline/pipeline_mocks_rzalloc.c b/test/cmocka/src/audio/pipeline/pipeline_mocks_rzalloc.c index fc6372d87c44..76b683053b38 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_mocks_rzalloc.c +++ b/test/cmocka/src/audio/pipeline/pipeline_mocks_rzalloc.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/cmocka/src/audio/pipeline/pipeline_new.c b/test/cmocka/src/audio/pipeline/pipeline_new.c index 8e033ed3e76f..9b7873530af9 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_new.c +++ b/test/cmocka/src/audio/pipeline/pipeline_new.c @@ -72,35 +72,6 @@ static void test_audio_pipeline_pipeline_new_creation(void **state) assert_non_null(result); } -static void test_audio_pipeline_new_sheduler_init(void **state) -{ - struct pipeline_new_setup_data *test_data = *state; - - /*Testing component*/ - struct pipeline *result = pipeline_new(&test_data->ipc_data, - test_data->comp_data); - - /*Check if all parameters are passed to sheduler - *(initialization) - */ - assert_int_equal(result->pipe_task.state, TASK_STATE_INIT); - assert_int_equal(result->pipe_task.core, test_data->ipc_data.core); -} - -static void test_audio_pipeline_new_sheduler_config(void **state) -{ - struct pipeline_new_setup_data *test_data = *state; - - /*Testing component*/ - struct pipeline *result = pipeline_new(&test_data->ipc_data, - test_data->comp_data); - - /*Pipeline should end in pre init state untli sheduler runs - *task that initializes it - */ - assert_int_equal(COMP_STATE_INIT, result->status); -} - static void test_audio_pipeline_new_ipc_data_coppy(void **state) { struct pipeline_new_setup_data *test_data = *state; @@ -115,11 +86,8 @@ static void test_audio_pipeline_new_ipc_data_coppy(void **state) int main(void) { - const struct CMUnitTest tests[] = { cmocka_unit_test(test_audio_pipeline_pipeline_new_creation), - cmocka_unit_test(test_audio_pipeline_new_sheduler_init), - cmocka_unit_test(test_audio_pipeline_new_sheduler_config), cmocka_unit_test(test_audio_pipeline_new_ipc_data_coppy), }; diff --git a/test/cmocka/src/audio/pipeline/pipeline_new_allocation.c b/test/cmocka/src/audio/pipeline/pipeline_new_allocation.c index 35b929be99f1..476fbf4f7fb2 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_new_allocation.c +++ b/test/cmocka/src/audio/pipeline/pipeline_new_allocation.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "pipeline_mocks.h" #include #include diff --git a/test/cmocka/src/audio/pipeline/pipeline_new_allocation_mocks.c b/test/cmocka/src/audio/pipeline/pipeline_new_allocation_mocks.c index fefe55cc710c..e0151bbfa359 100644 --- a/test/cmocka/src/audio/pipeline/pipeline_new_allocation_mocks.c +++ b/test/cmocka/src/audio/pipeline/pipeline_new_allocation_mocks.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/tools/logger/convert.c b/tools/logger/convert.c index b3b7652d6b2a..cd16e3f56d36 100644 --- a/tools/logger/convert.c +++ b/tools/logger/convert.c @@ -96,9 +96,10 @@ static const char * get_component_name(uint32_t component_id) { CASE(SA); CASE(DMIC); CASE(POWER); - CASE(SCHEDULE); + CASE(EDF); CASE(KPB); CASE(SELECTOR); + CASE(SCHEDULE); default: return "unknown"; } }