2021-07-01 09:05:24

by Arnaud Pouliquen

[permalink] [raw]
Subject: [PATCH v3 0/4] rpmsg: char: introduce the rpmsg-raw channel

Purpose:
Allow the remote processor to instantiate a /dev/rpmsgX interface relying on the NS announcement
of the "rpmsg-raw" service.
This patchet is extracted from the series [1] with rework to add rpmsg_create_default_ept helper.


Aim:
There is no generic sysfs interface based on RPMsg that allows a user application to communicate
with a remote processor in a simple way.
The rpmsg_char dev solves a part of this problem by allowing an endpoint to be created on the
local side. But it does not take advantage of the NS announcement mechanism implemented for some
backends such as the virtio backend. So it is not possible to probe it from a remote initiative.
Extending the char rpmsg device to support NS announcement makes the rpmsg_char more generic.
By announcing a "rpmg-raw" service, the firmware of a remote processor will be able to
instantiate a /dev/rpmsgX interface providing to the user application a basic link to communicate
with it without any knowledge of the rpmsg protocol.

Implementation details:
- Register a rpmsg driver for the rpmsg_char driver, associated to the "rpmsg-raw" channel service.
- In case of rpmsg char device instantiated by the rpmsg bus (on NS announcement) manage the
channel default endpoint to ensure a stable default endpoint address, for communication with
the remote processor.

delta vs V2 [2]:
- Fix typos.
- Suppress useless check on eptdev->rpdev, in rpmsg_eptdev_ioctl.
- Add the Reviewed-by: Mathieu Poirier <[email protected]>.

How to test it:
- This series can be applied on git/andersson/remoteproc.git for-next branch (7486f29e5e60)
+ the "Restructure the rpmsg char to decorrelate the control part" series[3]

[1] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=475217
[2] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=505873
[3] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=483793



Arnaud Pouliquen (4):
rpmsg: Introduce rpmsg_create_default_ept function
rpmsg: char: Introduce __rpmsg_chrdev_create_eptdev function
rpmsg: char: Add possibility to use default endpoint of the rpmsg
device.
rpmsg: char: Introduce the "rpmsg-raw" channel

drivers/rpmsg/rpmsg_char.c | 120 ++++++++++++++++++++++++++++++++++---
drivers/rpmsg/rpmsg_core.c | 51 ++++++++++++++++
include/linux/rpmsg.h | 13 ++++
3 files changed, 175 insertions(+), 9 deletions(-)

--
2.17.1


2021-07-01 09:05:30

by Arnaud Pouliquen

[permalink] [raw]
Subject: [PATCH v3 1/4] rpmsg: Introduce rpmsg_create_default_ept function

By providing a callback in the rpmsg_driver structure, the rpmsg devices
can be probed with a default endpoint created.

In this case, it is not possible to associated to this endpoint private data
that could allow the driver to retrieve the context.

This helper function allows rpmsg drivers to create a default endpoint
on runtime with an associated private context.

For example, a driver might create a context structure on the probe and
want to provide that context as private data for the default rpmsg
callback.

Signed-off-by: Arnaud Pouliquen <[email protected]>
Reviewed-by: Mathieu Poirier <[email protected]>
---

update from V2:
- fix typos.
---

drivers/rpmsg/rpmsg_core.c | 51 ++++++++++++++++++++++++++++++++++++++
include/linux/rpmsg.h | 13 ++++++++++
2 files changed, 64 insertions(+)

diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index e5daee4f9373..196f922be3e1 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -115,6 +115,57 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
}
EXPORT_SYMBOL(rpmsg_create_ept);

+/**
+ * rpmsg_create_default_ept() - create a default rpmsg_endpoint for a rpmsg device
+ * @rpdev: rpmsg channel device
+ * @cb: rx callback handler
+ * @priv: private data for the driver's use
+ * @chinfo: channel_info with the local rpmsg address to bind with @cb
+ *
+ * On register_rpmsg_driver if no callback is provided in the rpmsg_driver structure,
+ * no endpoint is created when the device is probed by the rpmsg bus.
+ *
+ * This function returns a pointer to the default endpoint if already created or creates
+ * an endpoint and assign it as the default endpoint of the rpmsg device.
+ *
+ * Drivers should provide their @rpdev channel (so the new endpoint would belong
+ * to the same remote processor their channel belongs to), an rx callback
+ * function, an optional private data (which is provided back when the
+ * rx callback is invoked), and an address they want to bind with the
+ * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
+ * dynamically assign them an available rpmsg address (drivers should have
+ * a very good reason why not to always use RPMSG_ADDR_ANY here).
+ *
+ * Returns a pointer to the endpoint on success, or NULL on error.
+ */
+struct rpmsg_endpoint *rpmsg_create_default_ept(struct rpmsg_device *rpdev,
+ rpmsg_rx_cb_t cb, void *priv,
+ struct rpmsg_channel_info chinfo)
+{
+ struct rpmsg_endpoint *ept;
+
+ if (WARN_ON(!rpdev))
+ return NULL;
+
+ /* It does not make sense to create a default endpoint without a callback. */
+ if (!cb)
+ return NULL;
+
+ if (rpdev->ept)
+ return rpdev->ept;
+
+ ept = rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
+ if (!ept)
+ return NULL;
+
+ /* Assign the new endpoint as default endpoint */
+ rpdev->ept = ept;
+ rpdev->src = ept->addr;
+
+ return ept;
+}
+EXPORT_SYMBOL(rpmsg_create_default_ept);
+
/**
* rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
* @ept: endpoing to destroy
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index d97dcd049f18..11f473834e86 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -172,6 +172,9 @@ void rpmsg_destroy_ept(struct rpmsg_endpoint *);
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *,
rpmsg_rx_cb_t cb, void *priv,
struct rpmsg_channel_info chinfo);
+struct rpmsg_endpoint *rpmsg_create_default_ept(struct rpmsg_device *rpdev,
+ rpmsg_rx_cb_t cb, void *priv,
+ struct rpmsg_channel_info chinfo);

int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len);
int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
@@ -234,6 +237,16 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev
return ERR_PTR(-ENXIO);
}

+static inline struct rpmsg_endpoint *rpmsg_create_default_ept(struct rpmsg_device *rpdev,
+ rpmsg_rx_cb_t cb, void *priv,
+ struct rpmsg_channel_info chinfo)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return NULL;
+}
+
static inline int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
{
/* This shouldn't be possible */
--
2.17.1

2021-07-01 09:05:33

by Arnaud Pouliquen

[permalink] [raw]
Subject: [PATCH v3 4/4] rpmsg: char: Introduce the "rpmsg-raw" channel

Allows to probe the endpoint device on a remote name service announcement,
by registering a rpmsg_driverfor the "rpmsg-raw" channel.

With this patch the /dev/rpmsgX interface can be instantiated by the remote
firmware.

Signed-off-by: Arnaud Pouliquen <[email protected]>
Reviewed-by: Mathieu Poirier <[email protected]>
---
drivers/rpmsg/rpmsg_char.c | 75 +++++++++++++++++++++++++++++++++++++-
1 file changed, 73 insertions(+), 2 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c
index bd728d90ba4c..1b7b610e113d 100644
--- a/drivers/rpmsg/rpmsg_char.c
+++ b/drivers/rpmsg/rpmsg_char.c
@@ -25,6 +25,8 @@

#include "rpmsg_char.h"

+#define RPMSG_CHAR_DEVNAME "rpmsg-raw"
+
static dev_t rpmsg_major;
static struct class *rpmsg_class;

@@ -421,6 +423,61 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent
}
EXPORT_SYMBOL(rpmsg_chrdev_eptdev_create);

+static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
+{
+ struct rpmsg_channel_info chinfo;
+ struct rpmsg_eptdev *eptdev;
+ struct rpmsg_endpoint *ept;
+
+ memcpy(chinfo.name, RPMSG_CHAR_DEVNAME, sizeof(RPMSG_CHAR_DEVNAME));
+ chinfo.src = rpdev->src;
+ chinfo.dst = rpdev->dst;
+
+ eptdev = __rpmsg_chrdev_eptdev_create(rpdev, &rpdev->dev, chinfo);
+ if (IS_ERR(eptdev))
+ return PTR_ERR(eptdev);
+
+ /*
+ * Create the default endpoint associated to the rpmsg device and provide rpmsg_eptdev
+ * structure as callback private data.
+ */
+ ept = rpmsg_create_default_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
+ if (!ept) {
+ dev_err(&rpdev->dev, "failed to create %s\n", eptdev->chinfo.name);
+ put_device(&eptdev->dev);
+ return -EINVAL;
+ }
+
+ /*
+ * Do not allow the creation and release of an endpoint on /dev/rpmsgX open and close,
+ * reuse the default endpoint instead
+ */
+ eptdev->static_ept = true;
+
+ return 0;
+}
+
+static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)
+{
+ int ret;
+
+ ret = device_for_each_child(&rpdev->dev, NULL, rpmsg_chrdev_eptdev_destroy);
+ if (ret)
+ dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret);
+}
+
+static struct rpmsg_device_id rpmsg_chrdev_id_table[] = {
+ { .name = RPMSG_CHAR_DEVNAME },
+ { },
+};
+
+static struct rpmsg_driver rpmsg_chrdev_driver = {
+ .probe = rpmsg_chrdev_probe,
+ .remove = rpmsg_chrdev_remove,
+ .id_table = rpmsg_chrdev_id_table,
+ .drv.name = "rpmsg_chrdev",
+};
+
static int rpmsg_chrdev_init(void)
{
int ret;
@@ -434,16 +491,30 @@ static int rpmsg_chrdev_init(void)
rpmsg_class = class_create(THIS_MODULE, "rpmsg");
if (IS_ERR(rpmsg_class)) {
pr_err("failed to create rpmsg class\n");
- unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
- return PTR_ERR(rpmsg_class);
+ ret = PTR_ERR(rpmsg_class);
+ goto free_region;
+ }
+
+ ret = register_rpmsg_driver(&rpmsg_chrdev_driver);
+ if (ret < 0) {
+ pr_err("rpmsg: failed to register rpmsg raw driver\n");
+ goto free_class;
}

return 0;
+
+free_class:
+ class_destroy(rpmsg_class);
+free_region:
+ unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
+
+ return ret;
}
postcore_initcall(rpmsg_chrdev_init);

static void rpmsg_chrdev_exit(void)
{
+ unregister_rpmsg_driver(&rpmsg_chrdev_driver);
class_destroy(rpmsg_class);
unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
}
--
2.17.1

2021-07-01 09:06:15

by Arnaud Pouliquen

[permalink] [raw]
Subject: [PATCH v3 3/4] rpmsg: char: Add possibility to use default endpoint of the rpmsg device.

Current implementation create/destroy a new endpoint on each
rpmsg_eptdev_open/rpmsg_eptdev_release calls.

For a rpmsg device created by the NS announcement mechanism we need to
use a unique static endpoint that is the default rpmsg device endpoint
associated to the channel.

This patch prepares the introduction of a rpmsg channel device for the
char device. The rpmsg channel device will require a default endpoint to
communicate to the remote processor.

Add the static_ept field in rpmsg_eptdev structure. This boolean
determines the behavior on rpmsg_eptdev_open and rpmsg_eptdev_release call.

- If static_ept == false:
Use the legacy behavior by creating a new endpoint each time
rpmsg_eptdev_open is called and release it when rpmsg_eptdev_release
is called on /dev/rpmsgX device open/close.

- If static_ept == true:
use the rpmsg device default endpoint for the communication.
- Address the update of _rpmsg_chrdev_eptdev_create in e separate patch for readability.

Add protection in rpmsg_eptdev_ioctl to prevent to destroy a default endpoint.

Signed-off-by: Arnaud Pouliquen <[email protected]>
Reviewed-by: Mathieu Poirier <[email protected]>
---

update vs V2:
- remove null pointer check for eptdev->rpdev in rpmsg_eptdev_ioctl. The pointer is set in
__rpmsg_chrdev_eptdev_create and cannot be null.
---
drivers/rpmsg/rpmsg_char.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c
index 50b7d4b00175..bd728d90ba4c 100644
--- a/drivers/rpmsg/rpmsg_char.c
+++ b/drivers/rpmsg/rpmsg_char.c
@@ -45,6 +45,8 @@ static DEFINE_IDA(rpmsg_minor_ida);
* @queue_lock: synchronization of @queue operations
* @queue: incoming message queue
* @readq: wait object for incoming queue
+ * @static_ept: specify if the endpoint has to be created at each device opening or
+ * if the default endpoint should be used.
*/
struct rpmsg_eptdev {
struct device dev;
@@ -59,6 +61,8 @@ struct rpmsg_eptdev {
spinlock_t queue_lock;
struct sk_buff_head queue;
wait_queue_head_t readq;
+
+ bool static_ept;
};

int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data)
@@ -116,7 +120,15 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)

get_device(dev);

- ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
+ /*
+ * If the static_ept is set to true, the rpmsg device default endpoint is used.
+ * Else a new endpoint is created on open that will be destroyed on release.
+ */
+ if (eptdev->static_ept)
+ ept = rpdev->ept;
+ else
+ ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
+
if (!ept) {
dev_err(dev, "failed to open %s\n", eptdev->chinfo.name);
put_device(dev);
@@ -137,7 +149,8 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)
/* Close the endpoint, if it's not already destroyed by the parent */
mutex_lock(&eptdev->ept_lock);
if (eptdev->ept) {
- rpmsg_destroy_ept(eptdev->ept);
+ if (!eptdev->static_ept)
+ rpmsg_destroy_ept(eptdev->ept);
eptdev->ept = NULL;
}
mutex_unlock(&eptdev->ept_lock);
@@ -264,6 +277,10 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,
if (cmd != RPMSG_DESTROY_EPT_IOCTL)
return -EINVAL;

+ /* Don't allow to destroy a default endpoint. */
+ if (eptdev->ept == eptdev->rpdev->ept)
+ return -EPERM;
+
return rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL);
}

--
2.17.1

2021-07-01 09:06:51

by Arnaud Pouliquen

[permalink] [raw]
Subject: [PATCH v3 2/4] rpmsg: char: Introduce __rpmsg_chrdev_create_eptdev function

Introduce the __rpmsg_chrdev_create_eptdev internal function that returns
the rpmsg_eptdev context structure.

This patch prepares the introduction of a rpmsg channel device for the
char device. The rpmsg device will need a reference to the context.

Signed-off-by: Arnaud Pouliquen <[email protected]>
Reviewed-by: Mathieu Poirier <[email protected]>
---
drivers/rpmsg/rpmsg_char.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c
index fbe10d527c5c..50b7d4b00175 100644
--- a/drivers/rpmsg/rpmsg_char.c
+++ b/drivers/rpmsg/rpmsg_char.c
@@ -323,8 +323,9 @@ static void rpmsg_eptdev_release_device(struct device *dev)
kfree(eptdev);
}

-int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent,
- struct rpmsg_channel_info chinfo)
+static struct rpmsg_eptdev *__rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev,
+ struct device *parent,
+ struct rpmsg_channel_info chinfo)
{
struct rpmsg_eptdev *eptdev;
struct device *dev;
@@ -332,7 +333,7 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent

eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
if (!eptdev)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);

dev = &eptdev->dev;
eptdev->rpdev = rpdev;
@@ -374,9 +375,10 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent
if (ret) {
dev_err(dev, "device_add failed: %d\n", ret);
put_device(dev);
+ return ERR_PTR(ret);
}

- return ret;
+ return eptdev;

free_ept_ida:
ida_simple_remove(&rpmsg_ept_ida, dev->id);
@@ -386,7 +388,19 @@ int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent
put_device(dev);
kfree(eptdev);

- return ret;
+ return ERR_PTR(ret);
+}
+
+int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent,
+ struct rpmsg_channel_info chinfo)
+{
+ struct rpmsg_eptdev *eptdev;
+
+ eptdev = __rpmsg_chrdev_eptdev_create(rpdev, parent, chinfo);
+ if (IS_ERR(eptdev))
+ return PTR_ERR(eptdev);
+
+ return 0;
}
EXPORT_SYMBOL(rpmsg_chrdev_eptdev_create);

--
2.17.1

2021-07-02 11:12:34

by Julien Massot

[permalink] [raw]
Subject: Re: [PATCH v3 0/4] rpmsg: char: introduce the rpmsg-raw channel

Hi,

>
> How to test it:
> - This series can be applied on git/andersson/remoteproc.git for-next branch (7486f29e5e60)
> + the "Restructure the rpmsg char to decorrelate the control part" series[3]
>
> [1] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=475217
> [2] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=505873
> [3] https://patchwork.kernel.org/project/linux-remoteproc/list/?series=483793Just did test this patchset v3 again. With the same protocol

Works for me, /dev/rpmsg0 is created on NS announcement, and removed when stopping the remote processor.
I can do a repeated series of open /read/write/close, and got EBUSY if I try to open it more than once
at a time.

Firmware used for testing is derived from:
https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/subsys/ipc/openamp_rsc_table
with:
#define RPMSG_CHAN_NAME "rpmsg-raw"

Thanks Arnaud for your work.

Tested-by: Julien Massot <[email protected]>
--
Julien Massot [IoT.bzh]