Testing on our ZynqMP platform has shown, that some R5 messages might get dropped under high CPU load. This patch creates a new high-prio workqueue which is now used instead of the default system workqueue. With this change we don't experience these message drops any more.
Signed-off-by: Stefan Roese <[email protected]> Cc: Tanmay Shah <[email protected]> Cc: Mathieu Poirier <[email protected]> --- v3: - Call cancel_work_sync() before freeing ipi (suggested by Zhongqiu Han) v2: - Also call destroy_workqueue() in zynqmp_r5_cluster_exit() (suggested by Zhongqiu Han) - Correct call seq to avoid UAF (suggested by Zhongqiu Han) drivers/remoteproc/xlnx_r5_remoteproc.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index feca6de68da28..308328b0b489f 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -16,6 +16,7 @@ #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> +#include <linux/workqueue.h> #include "remoteproc_internal.h" @@ -116,6 +117,7 @@ struct zynqmp_r5_cluster { enum zynqmp_r5_cluster_mode mode; int core_count; struct zynqmp_r5_core **r5_cores; + struct workqueue_struct *workqueue; }; /** @@ -174,10 +176,18 @@ static void handle_event_notified(struct work_struct *work) static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) { struct zynqmp_ipi_message *ipi_msg, *buf_msg; + struct zynqmp_r5_cluster *cluster; struct mbox_info *ipi; + struct device *dev; size_t len; ipi = container_of(cl, struct mbox_info, mbox_cl); + dev = ipi->r5_core->dev; + cluster = dev_get_drvdata(dev->parent); + if (!cluster) { + dev_err(dev->parent, "Invalid driver data\n"); + return; + } /* copy data from ipi buffer to r5_core */ ipi_msg = (struct zynqmp_ipi_message *)msg; @@ -195,7 +205,7 @@ static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) if (mbox_send_message(ipi->rx_chan, NULL) < 0) dev_err(cl->dev, "ack failed to mbox rx_chan\n"); - schedule_work(&ipi->mbox_work); + queue_work(cluster->workqueue, &ipi->mbox_work); } /** @@ -1154,6 +1164,7 @@ static void zynqmp_r5_cluster_exit(void *data) for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; + cancel_work_sync(&r5_core->ipi->mbox_work); zynqmp_r5_free_mbox(r5_core->ipi); of_reserved_mem_device_release(r5_core->dev); put_device(r5_core->dev); @@ -1162,6 +1173,7 @@ static void zynqmp_r5_cluster_exit(void *data) } kfree(cluster->r5_cores); + destroy_workqueue(cluster->workqueue); kfree(cluster); platform_set_drvdata(pdev, NULL); } @@ -1194,11 +1206,20 @@ static int zynqmp_r5_remoteproc_probe(struct platform_device *pdev) return ret; } + cluster->workqueue = alloc_workqueue(dev_name(dev), + WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!cluster->workqueue) { + dev_err_probe(dev, -ENOMEM, "cannot create workqueue\n"); + kfree(cluster); + return -ENOMEM; + } + /* wire in so each core can be cleaned up at driver remove */ platform_set_drvdata(pdev, cluster); ret = zynqmp_r5_cluster_init(cluster); if (ret) { + destroy_workqueue(cluster->workqueue); kfree(cluster); platform_set_drvdata(pdev, NULL); dev_err_probe(dev, ret, "Invalid r5f subsystem device tree\n"); -- 2.52.0
