[RFC 06/18] remoteproc: Add component in core for child devices synchronization

From: Arnaud Pouliquen
Date: Thu Apr 16 2020 - 12:14:38 EST


As virtio is now a platform device. We need to ensure that the virtio
device is initialized before subdev ops are called.
Add a rproc master component to synchronize child devices bind/unbind
before the resource allocation and the firmware start.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>
---
drivers/remoteproc/remoteproc_core.c | 96 +++++++++++++++++++++++++++-
include/linux/remoteproc.h | 4 ++
2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 5dcef62d8d1d..cb40aae12b98 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -16,6 +16,7 @@

#define pr_fmt(fmt) "%s: " fmt, __func__

+#include <linux/component.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -42,6 +43,8 @@

#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL

+#define BIND_TIMEOUT_MS 1000
+
static DEFINE_MUTEX(rproc_list_mutex);
static LIST_HEAD(rproc_list);

@@ -410,6 +413,45 @@ void rproc_free_vring(struct rproc_vring *rvring)
rsc->vring[idx].notifyid = -1;
}

+static int rproc_compare_of(struct device *dev, void *data)
+{
+ if (dev->of_node)
+ return dev->of_node == data;
+ else
+ return dev == data;
+}
+
+static void rproc_release_of(struct device *dev, void *data)
+{
+ if (dev->of_node)
+ of_node_put(data);
+}
+
+static void rproc_unbind(struct device *dev)
+{
+ struct rproc *rproc = container_of(dev, struct rproc, dev);
+
+ /* undbind all child components */
+ component_unbind_all(dev, NULL);
+ complete(&rproc->completed);
+}
+
+static int rproc_bind(struct device *dev)
+{
+ struct rproc *rproc = container_of(dev, struct rproc, dev);
+
+ /* bind all child components */
+ rproc->bind_status = component_bind_all(dev, NULL);
+ complete(&rproc->completed);
+
+ return rproc->bind_status;
+}
+
+static const struct component_master_ops rproc_cmp_ops = {
+ .bind = rproc_bind,
+ .unbind = rproc_unbind,
+};
+
/**
* rproc_handle_vdev() - handle a vdev fw resource
* @rproc: the remote processor
@@ -475,6 +517,10 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
"failed to create rproc-virtio device\n");
return ret;
}
+ /* register a component associated to the virtio platform */
+ component_match_add_release(&pdev->dev, &rproc->match,
+ rproc_release_of, rproc_compare_of,
+ &pdev->dev);
rproc->nb_vdev++;

return 0;
@@ -1318,20 +1364,51 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up_resources;
}

+ /*
+ * bind all the children associated to the resources before starting
+ * the remote processor. This synchro point ensures that everything is
+ * ready to run.
+ */
+ init_completion(&rproc->completed);
+ if (rproc->match) {
+ ret = component_master_add_with_match(dev, &rproc_cmp_ops,
+ rproc->match);
+ if (ret) {
+ dev_err(dev, "failed to bind rproc\n");
+ goto clean_up_resources;
+ }
+ }
+ /* Wait for all children to be bound */
+ if (!wait_for_completion_timeout(&rproc->completed,
+ msecs_to_jiffies(BIND_TIMEOUT_MS))) {
+ dev_err(dev, "failed to bind child device(s)\n");
+ ret = -ETIMEDOUT;
+ goto clean_up_resources;
+ }
+
+ ret = rproc->bind_status;
+ if (ret) {
+ dev_err(dev, "failed to bind\n");
+ goto clean_up_resources;
+ }
+
/* Allocate carveout resources associated to rproc */
ret = rproc_alloc_registered_carveouts(rproc);
if (ret) {
dev_err(dev, "Failed to allocate associated carveouts: %d\n",
ret);
- goto clean_up_resources;
+ goto unbind_comp;
}

ret = rproc_start(rproc, fw);
if (ret)
- goto clean_up_resources;
+ goto unbind_comp;

return 0;

+unbind_comp:
+ component_master_del(dev, &rproc_cmp_ops);
+ rproc->match = NULL;
clean_up_resources:
rproc_resource_cleanup(rproc);
kfree(rproc->cached_table);
@@ -1733,6 +1810,21 @@ void rproc_shutdown(struct rproc *rproc)
goto out;
}

+ /*
+ * Unbind all the children before cleaning resources. This synchro
+ * point ensures that everything has been released before resources are
+ * freed.
+ */
+ init_completion(&rproc->completed);
+
+ component_master_del(dev, &rproc_cmp_ops);
+ rproc->match = NULL;
+
+ if (!wait_for_completion_timeout(&rproc->completed,
+ msecs_to_jiffies(BIND_TIMEOUT_MS))) {
+ dev_err(dev, "failed to unbind child device(s)\n");
+ }
+
/* clean up all acquired resources */
rproc_resource_cleanup(rproc);

diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index eb5bd568f11e..d7235f7356e2 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -514,6 +514,10 @@ struct rproc {
bool auto_boot;
struct list_head dump_segments;
int nb_vdev;
+ struct component_match *match;
+ struct completion completed;
+ int bind_status;
+
};

/**
--
2.17.1