[PATCH] rpmsg_ns: add synchronization based on component mechanism

From: Arnaud Pouliquen
Date: Tue Nov 10 2020 - 12:39:29 EST


Implement the component bind mechanism to ensure that the rpmsg virtio bus
driver are probed before treating the first RPMsg.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>
---
drivers/rpmsg/rpmsg_ns.c | 26 ++++++++++++-
drivers/rpmsg/virtio_rpmsg_bus.c | 65 ++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 2 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_ns.c b/drivers/rpmsg/rpmsg_ns.c
index 5bda7cb44618..057e5d1d29a0 100644
--- a/drivers/rpmsg/rpmsg_ns.c
+++ b/drivers/rpmsg/rpmsg_ns.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) STMicroelectronics 2020 - All Rights Reserved
*/
+#include <linux/component.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -55,6 +56,24 @@ static int rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
return 0;
}

+static int rpmsg_ns_bind(struct device *dev, struct device *master, void *data)
+{
+ dev_info(dev, "rpmsg ns bound\n");
+
+ return 0;
+}
+
+static void rpmsg_ns_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ dev_info(dev, "rpmsg ns unbound\n");
+}
+
+static const struct component_ops rpmsg_ns_ops = {
+ .bind = rpmsg_ns_bind,
+ .unbind = rpmsg_ns_unbind,
+};
+
static int rpmsg_ns_probe(struct rpmsg_device *rpdev)
{
struct rpmsg_endpoint *ns_ept;
@@ -63,6 +82,7 @@ static int rpmsg_ns_probe(struct rpmsg_device *rpdev)
.dst = RPMSG_NS_ADDR,
.name = "name_service",
};
+ int ret;

/*
* Create the NS announcement service endpoint associated to the RPMsg
@@ -76,7 +96,9 @@ static int rpmsg_ns_probe(struct rpmsg_device *rpdev)
}
rpdev->ept = ns_ept;

- return 0;
+ ret = component_add(&rpdev->dev, &rpmsg_ns_ops);
+
+ return ret;
}

static struct rpmsg_driver rpmsg_ns_driver = {
@@ -104,5 +126,5 @@ module_exit(rpmsg_ns_exit);

MODULE_DESCRIPTION("Name service announcement rpmsg Driver");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>");
-MODULE_ALIAS("rpmsg_ns");
+MODULE_ALIAS("rpmsg:rpmsg_ns");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 30ef4a5de4ed..c28aac1295fa 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -11,6 +11,7 @@

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

+#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/jiffies.h>
@@ -67,11 +68,16 @@ struct virtproc_info {
struct mutex endpoints_lock;
wait_queue_head_t sendq;
atomic_t sleepers;
+ struct component_match *match;
+ struct completion completed;
+ int bind_status;
};

/* The feature bitmap for virtio rpmsg */
#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */

+#define BIND_TIMEOUT_MS 1000
+
/**
* struct rpmsg_hdr - common header for all rpmsg messages
* @src: source address
@@ -768,6 +774,17 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
unsigned int len, msgs_received = 0;
int err;

+ /* Wait for all children to be bound */
+ if (vrp->bind_status) {
+ dev_dbg(dev, "cwait bind\n");
+ if (!wait_for_completion_timeout(&vrp->completed,
+ msecs_to_jiffies(BIND_TIMEOUT_MS)))
+ dev_err(dev, "child device(s) binding timeout\n");
+
+ if (vrp->bind_status)
+ dev_err(dev, "failed to bind RPMsg sub device(s)\n");
+ }
+
msg = virtqueue_get_buf(rvq, &len);
if (!msg) {
dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
@@ -808,6 +825,39 @@ static void rpmsg_xmit_done(struct virtqueue *svq)
wake_up_interruptible(&vrp->sendq);
}

+static int virtio_rpmsg_compare(struct device *dev, void *data)
+{
+ return dev == data;
+}
+
+static void virtio_rpmsg_unbind(struct device *dev)
+{
+ /* undbind all child components */
+ component_unbind_all(dev, NULL);
+}
+
+static int virtio_rpmsg_bind(struct device *dev)
+{
+ struct virtio_device *vdev = dev_to_virtio(dev);
+ struct virtproc_info *vrp = vdev->priv;
+
+ dev_dbg(dev, "Bind virtio rpmsg sub devices\n");
+
+ vdev = container_of(dev, struct virtio_device, dev);
+ vrp->bind_status = component_bind_all(dev, NULL);
+ if (vrp->bind_status)
+ dev_err(dev, "bind virtio rpmsg failed\n");
+
+ complete(&vrp->completed);
+
+ return vrp->bind_status;
+}
+
+static const struct component_master_ops virtio_rpmsg_cmp_ops = {
+ .bind = virtio_rpmsg_bind,
+ .unbind = virtio_rpmsg_unbind,
+};
+
static int rpmsg_probe(struct virtio_device *vdev)
{
vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };
@@ -892,6 +942,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
/* if supported by the remote processor, enable the name service */
if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {
vch = kzalloc(sizeof(*vch), GFP_KERNEL);
+
if (!vch) {
err = -ENOMEM;
goto free_coherent;
@@ -911,6 +962,20 @@ static int rpmsg_probe(struct virtio_device *vdev)
err = rpmsg_ns_register_device(rpdev_ns);
if (err)
goto free_coherent;
+ /* register a component associated to the virtio platform */
+ component_match_add_release(&vdev->dev, &vrp->match,
+ NULL, virtio_rpmsg_compare,
+ &rpdev_ns->dev);
+
+ vrp->bind_status = -ENXIO;
+ init_completion(&vrp->completed);
+ err = component_master_add_with_match(&vdev->dev,
+ &virtio_rpmsg_cmp_ops,
+ vrp->match);
+ if (err) {
+ dev_err(&vdev->dev, "failed to bind virtio rpmsg\n");
+ goto free_coherent;
+ }
}

/*
--
2.17.1