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"; } }