From 461043b163c1d92370f97faa39851ae1dd0cd341 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 17 Jan 2024 22:51:09 +0200 Subject: [PATCH] idc: zephyr: fix race between IDC p4wq worker and new msg submission A race exists between submitting a new IDC message and completion of a previous message. The p4wq interface mandates k_p4wq_wait() must be called to claim back ownership of the work object. The SOF IDC code does not do this, but relies on the caller not to send multiple IDC messages to the same destination core. Add appropriate calls to k_p4wq_wait() to handle this issue. If caller wants to send an IDC in blocking mode, also call k_p4wq_wait() in blocking mode. If non-blocking, return -EBUSY immediately. The check for CPU status is moved earlier in the function. No pointing in waiting for p4wq semaphore if the target core is already powered down, so do the power state check as the first step. Signed-off-by: Kai Vehmanen --- src/idc/zephyr_idc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index dd2413f74831..2a68487b158a 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -113,6 +113,32 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) int ret; int idc_send_memcpy_err __unused; + if (!cpu_is_core_enabled(target_cpu)) { + tr_err(&zephyr_idc_tr, "Core %u is down, cannot sent IDC message", target_cpu); + return -EACCES; + } + + /* + * Handler is NULL when work object has never been submitted. + * In all other cases, we must use k_p4wq_wait() before reuse + * of the object. + */ + if (work->handler) { + /* + * If new request is in blocking mode, we must call + * k_p4wq in blocking mode. This is workaround for + * k_p4wq_wait() interface. + */ + work->sync = (mode == IDC_BLOCKING); + + ret = k_p4wq_wait(work, K_USEC(IDC_TIMEOUT)); + if (ret < 0) { + tr_err(&zephyr_idc_tr, "idc_send_msg error %d, target core %u", + ret, target_cpu); + return ret; + } + } + idc_send_memcpy_err = memcpy_s(msg_cp, sizeof(*msg_cp), msg, sizeof(*msg)); assert(!idc_send_memcpy_err); /* Same priority as the IPC thread which is an EDF task and under Zephyr */