diff mbox series

[1/2] remoteproc: Add userspace char device driver

Message ID 1585241440-7572-2-git-send-email-rishabhb@codeaurora.org (mailing list archive)
State Superseded
Headers show
Series Add character device interface to remoteproc | expand

Commit Message

Rishabh Bhatnagar March 26, 2020, 4:50 p.m. UTC
Add the driver for creating the character device interface for
userspace applications. The character device interface can be used
in order to boot up and shutdown the remote processor.
This might be helpful for remote processors that are booted by
userspace applications and need to shutdown when the application
crahes/shutsdown.

Change-Id: If23c8986272bb7c943eb76665f127ff706f12394
Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
---
 drivers/remoteproc/Makefile               |  1 +
 drivers/remoteproc/remoteproc_internal.h  |  6 +++
 drivers/remoteproc/remoteproc_userspace.c | 90 +++++++++++++++++++++++++++++++
 include/linux/remoteproc.h                |  2 +
 4 files changed, 99 insertions(+)
 create mode 100644 drivers/remoteproc/remoteproc_userspace.c

Comments

Clément Leger March 26, 2020, 5:37 p.m. UTC | #1
Hi Rishabh,

While being interesting to have a such a userspace interface, I have
some remarks.

----- On 26 Mar, 2020, at 17:50, Rishabh Bhatnagar rishabhb@codeaurora.org wrote:

> Add the driver for creating the character device interface for
> userspace applications. The character device interface can be used
> in order to boot up and shutdown the remote processor.
> This might be helpful for remote processors that are booted by
> userspace applications and need to shutdown when the application
> crahes/shutsdown.
> 
> Change-Id: If23c8986272bb7c943eb76665f127ff706f12394
> Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
> ---
> drivers/remoteproc/Makefile               |  1 +
> drivers/remoteproc/remoteproc_internal.h  |  6 +++
> drivers/remoteproc/remoteproc_userspace.c | 90 +++++++++++++++++++++++++++++++
> include/linux/remoteproc.h                |  2 +
> 4 files changed, 99 insertions(+)
> create mode 100644 drivers/remoteproc/remoteproc_userspace.c
> 
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index e30a1b1..facb3fa 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_REMOTEPROC)		+= remoteproc.o
> remoteproc-y				:= remoteproc_core.o
> remoteproc-y				+= remoteproc_debugfs.o
> remoteproc-y				+= remoteproc_sysfs.o
> +remoteproc-y				+= remoteproc_userspace.o
> remoteproc-y				+= remoteproc_virtio.o
> remoteproc-y				+= remoteproc_elf_loader.o
> obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
> diff --git a/drivers/remoteproc/remoteproc_internal.h
> b/drivers/remoteproc/remoteproc_internal.h
> index 493ef92..97513ba 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -47,6 +47,9 @@ struct dentry *rproc_create_trace_file(const char *name,
> struct rproc *rproc,
> int rproc_init_sysfs(void);
> void rproc_exit_sysfs(void);
> 
> +void rproc_init_cdev(void);
> +void rproc_exit_cdev(void);
> +
> void rproc_free_vring(struct rproc_vring *rvring);
> int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
> 
> @@ -63,6 +66,9 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct
> rproc *rproc,
> struct rproc_mem_entry *
> rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...);
> 
> +/* from remoteproc_userspace.c */
> +int rproc_char_device_add(struct rproc *rproc);
> +
> static inline
> int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
> {
> diff --git a/drivers/remoteproc/remoteproc_userspace.c
> b/drivers/remoteproc/remoteproc_userspace.c
> new file mode 100644
> index 0000000..2ef7679
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc_userspace.c
> @@ -0,0 +1,90 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Character device interface driver for Remoteproc framework.
> + *
> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +#include <linux/mutex.h>
> +#include <linux/remoteproc.h>
> +
> +#include "remoteproc_internal.h"
> +
> +#define NUM_RPROC_DEVICES	64
> +static dev_t rproc_cdev;
> +static DEFINE_IDA(cdev_minor_ida);
> +
> +static int rproc_open(struct inode *inode, struct file *file)
> +{
> +	struct rproc *rproc;
> +
> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> +	if (!rproc)
> +		return -EINVAL;
> +
> +	return rproc_boot(rproc);
> +}

What happens if multiple user open this chardev ? Apparently,
rproc_boot returns 0 if already powered_up, so the next user won't know
what is the state of the rproc.
Exclusive access could probably be a good idea.

> +
> +static int rproc_release(struct inode *inode, struct file *file)
> +{
> +	struct rproc *rproc;
> +
> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> +	if (!rproc)
> +		return -EINVAL;
> +
> +	rproc_shutdown(rproc);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations rproc_fops = {
> +	.open = rproc_open,
> +	.release = rproc_release,
> +};
> +
> +int rproc_char_device_add(struct rproc *rproc)
> +{
> +	int ret, minor;
> +	dev_t cdevt;
> +
> +	minor = ida_simple_get(&cdev_minor_ida, 0, NUM_RPROC_DEVICES,
> +			GFP_KERNEL);
> +	if (minor < 0) {
> +	pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
> +							minor);
> +		return -ENODEV;
> +	}

How can you make the link between the chardev and the device instance ?
In our case, we have several remoteproc instances and thus we will end
up having multiple chardev.

Regards,

Clément

> +
> +	cdev_init(&rproc->char_dev, &rproc_fops);
> +	rproc->char_dev.owner = THIS_MODULE;
> +
> +	cdevt = MKDEV(MAJOR(rproc_cdev), minor);
> +	ret = cdev_add(&rproc->char_dev, cdevt, 1);
> +	if (ret < 0)
> +		ida_simple_remove(&cdev_minor_ida, minor);
> +
> +	rproc->dev.devt = cdevt;
> +
> +	return ret;
> +}
> +
> +void __init rproc_init_cdev(void)
> +{
> +	int ret;
> +
> +	ret = alloc_chrdev_region(&rproc_cdev, 0, NUM_RPROC_DEVICES, "rproc");
> +	if (ret < 0) {
> +		pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
> +		return;
> +	}
> +}
> +
> +void __exit rproc_exit_cdev(void)
> +{
> +	unregister_chrdev_region(MKDEV(MAJOR(rproc_cdev), 0),
> +				NUM_RPROC_DEVICES);
> +}
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 16ad666..c4ca796 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -37,6 +37,7 @@
> 
> #include <linux/types.h>
> #include <linux/mutex.h>
> +#include <linux/cdev.h>
> #include <linux/virtio.h>
> #include <linux/completion.h>
> #include <linux/idr.h>
> @@ -514,6 +515,7 @@ struct rproc {
> 	bool auto_boot;
> 	struct list_head dump_segments;
> 	int nb_vdev;
> +	struct cdev char_dev;
> };
> 
> /**
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
kernel test robot March 27, 2020, 4 a.m. UTC | #2
Hi Rishabh,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on linux/master v5.6-rc7]
[cannot apply to next-20200326]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Rishabh-Bhatnagar/Add-character-device-interface-to-remoteproc/20200327-062958
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 9420e8ade4353a6710908ffafa23ecaf1caa0123
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=9.2.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> drivers/remoteproc/remoteproc_userspace.c:31:12: error: conflicting types for 'rproc_release'
      31 | static int rproc_release(struct inode *inode, struct file *file)
         |            ^~~~~~~~~~~~~
   In file included from drivers/remoteproc/remoteproc_userspace.c:14:
   drivers/remoteproc/remoteproc_internal.h:28:6: note: previous declaration of 'rproc_release' was here
      28 | void rproc_release(struct kref *kref);
         |      ^~~~~~~~~~~~~

vim +/rproc_release +31 drivers/remoteproc/remoteproc_userspace.c

    30	
  > 31	static int rproc_release(struct inode *inode, struct file *file)
    32	{
    33		struct rproc *rproc;
    34	
    35		rproc = container_of(inode->i_cdev, struct rproc, char_dev);
    36		if (!rproc)
    37			return -EINVAL;
    38	
    39		rproc_shutdown(rproc);
    40	
    41		return 0;
    42	}
    43	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Rishabh Bhatnagar March 28, 2020, 12:09 a.m. UTC | #3
On 2020-03-26 10:37, Clément Leger wrote:
> Hi Rishabh,
> 
> While being interesting to have a such a userspace interface, I have
> some remarks.
> 
> ----- On 26 Mar, 2020, at 17:50, Rishabh Bhatnagar
> rishabhb@codeaurora.org wrote:
> 
>> Add the driver for creating the character device interface for
>> userspace applications. The character device interface can be used
>> in order to boot up and shutdown the remote processor.
>> This might be helpful for remote processors that are booted by
>> userspace applications and need to shutdown when the application
>> crahes/shutsdown.
>> 
>> Change-Id: If23c8986272bb7c943eb76665f127ff706f12394
>> Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
>> ---
>> drivers/remoteproc/Makefile               |  1 +
>> drivers/remoteproc/remoteproc_internal.h  |  6 +++
>> drivers/remoteproc/remoteproc_userspace.c | 90 
>> +++++++++++++++++++++++++++++++
>> include/linux/remoteproc.h                |  2 +
>> 4 files changed, 99 insertions(+)
>> create mode 100644 drivers/remoteproc/remoteproc_userspace.c
>> 
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index e30a1b1..facb3fa 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -7,6 +7,7 @@ obj-$(CONFIG_REMOTEPROC)		+= remoteproc.o
>> remoteproc-y				:= remoteproc_core.o
>> remoteproc-y				+= remoteproc_debugfs.o
>> remoteproc-y				+= remoteproc_sysfs.o
>> +remoteproc-y				+= remoteproc_userspace.o
>> remoteproc-y				+= remoteproc_virtio.o
>> remoteproc-y				+= remoteproc_elf_loader.o
>> obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
>> diff --git a/drivers/remoteproc/remoteproc_internal.h
>> b/drivers/remoteproc/remoteproc_internal.h
>> index 493ef92..97513ba 100644
>> --- a/drivers/remoteproc/remoteproc_internal.h
>> +++ b/drivers/remoteproc/remoteproc_internal.h
>> @@ -47,6 +47,9 @@ struct dentry *rproc_create_trace_file(const char 
>> *name,
>> struct rproc *rproc,
>> int rproc_init_sysfs(void);
>> void rproc_exit_sysfs(void);
>> 
>> +void rproc_init_cdev(void);
>> +void rproc_exit_cdev(void);
>> +
>> void rproc_free_vring(struct rproc_vring *rvring);
>> int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
>> 
>> @@ -63,6 +66,9 @@ struct resource_table 
>> *rproc_elf_find_loaded_rsc_table(struct
>> rproc *rproc,
>> struct rproc_mem_entry *
>> rproc_find_carveout_by_name(struct rproc *rproc, const char *name, 
>> ...);
>> 
>> +/* from remoteproc_userspace.c */
>> +int rproc_char_device_add(struct rproc *rproc);
>> +
>> static inline
>> int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware 
>> *fw)
>> {
>> diff --git a/drivers/remoteproc/remoteproc_userspace.c
>> b/drivers/remoteproc/remoteproc_userspace.c
>> new file mode 100644
>> index 0000000..2ef7679
>> --- /dev/null
>> +++ b/drivers/remoteproc/remoteproc_userspace.c
>> @@ -0,0 +1,90 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Character device interface driver for Remoteproc framework.
>> + *
>> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/fs.h>
>> +#include <linux/cdev.h>
>> +#include <linux/mutex.h>
>> +#include <linux/remoteproc.h>
>> +
>> +#include "remoteproc_internal.h"
>> +
>> +#define NUM_RPROC_DEVICES	64
>> +static dev_t rproc_cdev;
>> +static DEFINE_IDA(cdev_minor_ida);
>> +
>> +static int rproc_open(struct inode *inode, struct file *file)
>> +{
>> +	struct rproc *rproc;
>> +
>> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
>> +	if (!rproc)
>> +		return -EINVAL;
>> +
>> +	return rproc_boot(rproc);
>> +}
> 
> What happens if multiple user open this chardev ? Apparently,
> rproc_boot returns 0 if already powered_up, so the next user won't know
> what is the state of the rproc.
> Exclusive access could probably be a good idea.
Since it is synchronized inside rproc_boot multiple users simultaneously
calling open shouldn't be a problem. If it is one after the other then
second caller will get result as 0 and assume that rproc booted 
successfully.
That is the expected flow right?
> 
>> +
>> +static int rproc_release(struct inode *inode, struct file *file)
>> +{
>> +	struct rproc *rproc;
>> +
>> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
>> +	if (!rproc)
>> +		return -EINVAL;
>> +
>> +	rproc_shutdown(rproc);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct file_operations rproc_fops = {
>> +	.open = rproc_open,
>> +	.release = rproc_release,
>> +};
>> +
>> +int rproc_char_device_add(struct rproc *rproc)
>> +{
>> +	int ret, minor;
>> +	dev_t cdevt;
>> +
>> +	minor = ida_simple_get(&cdev_minor_ida, 0, NUM_RPROC_DEVICES,
>> +			GFP_KERNEL);
>> +	if (minor < 0) {
>> +	pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
>> +							minor);
>> +		return -ENODEV;
>> +	}
> 
> How can you make the link between the chardev and the device instance ?
I do this rproc->dev.devt = cdevt. Let me know of there is a better way
to do this?
> In our case, we have several remoteproc instances and thus we will end
> up having multiple chardev.
> 
> Regards,
> 
> Clément
> 
rproc_char_device_add will be called for each remoteproc that is
added. So we will have one char dev per remoteproc.
>> +
>> +	cdev_init(&rproc->char_dev, &rproc_fops);
>> +	rproc->char_dev.owner = THIS_MODULE;
>> +
>> +	cdevt = MKDEV(MAJOR(rproc_cdev), minor);
>> +	ret = cdev_add(&rproc->char_dev, cdevt, 1);
>> +	if (ret < 0)
>> +		ida_simple_remove(&cdev_minor_ida, minor);
>> +
>> +	rproc->dev.devt = cdevt;
>> +
>> +	return ret;
>> +}
>> +
>> +void __init rproc_init_cdev(void)
>> +{
>> +	int ret;
>> +
>> +	ret = alloc_chrdev_region(&rproc_cdev, 0, NUM_RPROC_DEVICES, 
>> "rproc");
>> +	if (ret < 0) {
>> +		pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
>> +		return;
>> +	}
>> +}
>> +
>> +void __exit rproc_exit_cdev(void)
>> +{
>> +	unregister_chrdev_region(MKDEV(MAJOR(rproc_cdev), 0),
>> +				NUM_RPROC_DEVICES);
>> +}
>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>> index 16ad666..c4ca796 100644
>> --- a/include/linux/remoteproc.h
>> +++ b/include/linux/remoteproc.h
>> @@ -37,6 +37,7 @@
>> 
>> #include <linux/types.h>
>> #include <linux/mutex.h>
>> +#include <linux/cdev.h>
>> #include <linux/virtio.h>
>> #include <linux/completion.h>
>> #include <linux/idr.h>
>> @@ -514,6 +515,7 @@ struct rproc {
>> 	bool auto_boot;
>> 	struct list_head dump_segments;
>> 	int nb_vdev;
>> +	struct cdev char_dev;
>> };
>> 
>> /**
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora 
>> Forum,
>> a Linux Foundation Collaborative Project
Clément Leger March 30, 2020, 10:42 a.m. UTC | #4
Hi Rishabh,

----- On 28 Mar, 2020, at 01:09, Rishabh Bhatnagar rishabhb@codeaurora.org wrote:

> On 2020-03-26 10:37, Clément Leger wrote:
>> Hi Rishabh,
>> 
>> While being interesting to have a such a userspace interface, I have
>> some remarks.
>> 
>> ----- On 26 Mar, 2020, at 17:50, Rishabh Bhatnagar
>> rishabhb@codeaurora.org wrote:
>> 
>>> Add the driver for creating the character device interface for
>>> userspace applications. The character device interface can be used
>>> in order to boot up and shutdown the remote processor.
>>> This might be helpful for remote processors that are booted by
>>> userspace applications and need to shutdown when the application
>>> crahes/shutsdown.
>>> 
>>> Change-Id: If23c8986272bb7c943eb76665f127ff706f12394
>>> Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
>>> ---
>>> drivers/remoteproc/Makefile               |  1 +
>>> drivers/remoteproc/remoteproc_internal.h  |  6 +++
>>> drivers/remoteproc/remoteproc_userspace.c | 90
>>> +++++++++++++++++++++++++++++++
>>> include/linux/remoteproc.h                |  2 +
>>> 4 files changed, 99 insertions(+)
>>> create mode 100644 drivers/remoteproc/remoteproc_userspace.c
>>> 
>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>> index e30a1b1..facb3fa 100644
>>> --- a/drivers/remoteproc/Makefile
>>> +++ b/drivers/remoteproc/Makefile
>>> @@ -7,6 +7,7 @@ obj-$(CONFIG_REMOTEPROC)		+= remoteproc.o
>>> remoteproc-y				:= remoteproc_core.o
>>> remoteproc-y				+= remoteproc_debugfs.o
>>> remoteproc-y				+= remoteproc_sysfs.o
>>> +remoteproc-y				+= remoteproc_userspace.o
>>> remoteproc-y				+= remoteproc_virtio.o
>>> remoteproc-y				+= remoteproc_elf_loader.o
>>> obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
>>> diff --git a/drivers/remoteproc/remoteproc_internal.h
>>> b/drivers/remoteproc/remoteproc_internal.h
>>> index 493ef92..97513ba 100644
>>> --- a/drivers/remoteproc/remoteproc_internal.h
>>> +++ b/drivers/remoteproc/remoteproc_internal.h
>>> @@ -47,6 +47,9 @@ struct dentry *rproc_create_trace_file(const char
>>> *name,
>>> struct rproc *rproc,
>>> int rproc_init_sysfs(void);
>>> void rproc_exit_sysfs(void);
>>> 
>>> +void rproc_init_cdev(void);
>>> +void rproc_exit_cdev(void);
>>> +
>>> void rproc_free_vring(struct rproc_vring *rvring);
>>> int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
>>> 
>>> @@ -63,6 +66,9 @@ struct resource_table
>>> *rproc_elf_find_loaded_rsc_table(struct
>>> rproc *rproc,
>>> struct rproc_mem_entry *
>>> rproc_find_carveout_by_name(struct rproc *rproc, const char *name,
>>> ...);
>>> 
>>> +/* from remoteproc_userspace.c */
>>> +int rproc_char_device_add(struct rproc *rproc);
>>> +
>>> static inline
>>> int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware
>>> *fw)
>>> {
>>> diff --git a/drivers/remoteproc/remoteproc_userspace.c
>>> b/drivers/remoteproc/remoteproc_userspace.c
>>> new file mode 100644
>>> index 0000000..2ef7679
>>> --- /dev/null
>>> +++ b/drivers/remoteproc/remoteproc_userspace.c
>>> @@ -0,0 +1,90 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Character device interface driver for Remoteproc framework.
>>> + *
>>> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/fs.h>
>>> +#include <linux/cdev.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/remoteproc.h>
>>> +
>>> +#include "remoteproc_internal.h"
>>> +
>>> +#define NUM_RPROC_DEVICES	64
>>> +static dev_t rproc_cdev;
>>> +static DEFINE_IDA(cdev_minor_ida);
>>> +
>>> +static int rproc_open(struct inode *inode, struct file *file)
>>> +{
>>> +	struct rproc *rproc;
>>> +
>>> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
>>> +	if (!rproc)
>>> +		return -EINVAL;
>>> +
>>> +	return rproc_boot(rproc);
>>> +}
>> 
>> What happens if multiple user open this chardev ? Apparently,
>> rproc_boot returns 0 if already powered_up, so the next user won't know
>> what is the state of the rproc.
>> Exclusive access could probably be a good idea.
> Since it is synchronized inside rproc_boot multiple users simultaneously
> calling open shouldn't be a problem. If it is one after the other then
> second caller will get result as 0 and assume that rproc booted
> successfully.

It will be the same for close, it will assume the rproc has been stopped ?
But in fact it will still be running until the refcount is 0.

> That is the expected flow right?

I would expect only one caller to be successful, others should probably
receive a EBUSY errno IMHO.

>> 
>>> +
>>> +static int rproc_release(struct inode *inode, struct file *file)
>>> +{
>>> +	struct rproc *rproc;
>>> +
>>> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
>>> +	if (!rproc)
>>> +		return -EINVAL;
>>> +
>>> +	rproc_shutdown(rproc);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct file_operations rproc_fops = {
>>> +	.open = rproc_open,
>>> +	.release = rproc_release,
>>> +};
>>> +
>>> +int rproc_char_device_add(struct rproc *rproc)
>>> +{
>>> +	int ret, minor;
>>> +	dev_t cdevt;
>>> +
>>> +	minor = ida_simple_get(&cdev_minor_ida, 0, NUM_RPROC_DEVICES,
>>> +			GFP_KERNEL);
>>> +	if (minor < 0) {
>>> +	pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
>>> +							minor);
>>> +		return -ENODEV;
>>> +	}
>> 
>> How can you make the link between the chardev and the device instance ?
> I do this rproc->dev.devt = cdevt. Let me know of there is a better way
> to do this?

If this is sufficient to create a link in the sysfs, then it's ok but I'm
no expert here.

Clément

>> In our case, we have several remoteproc instances and thus we will end
>> up having multiple chardev.
>> 
>> Regards,
>> 
>> Clément
>> 
> rproc_char_device_add will be called for each remoteproc that is
> added. So we will have one char dev per remoteproc.
>>> +
>>> +	cdev_init(&rproc->char_dev, &rproc_fops);
>>> +	rproc->char_dev.owner = THIS_MODULE;
>>> +
>>> +	cdevt = MKDEV(MAJOR(rproc_cdev), minor);
>>> +	ret = cdev_add(&rproc->char_dev, cdevt, 1);
>>> +	if (ret < 0)
>>> +		ida_simple_remove(&cdev_minor_ida, minor);
>>> +
>>> +	rproc->dev.devt = cdevt;
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +void __init rproc_init_cdev(void)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = alloc_chrdev_region(&rproc_cdev, 0, NUM_RPROC_DEVICES,
>>> "rproc");
>>> +	if (ret < 0) {
>>> +		pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
>>> +		return;
>>> +	}
>>> +}
>>> +
>>> +void __exit rproc_exit_cdev(void)
>>> +{
>>> +	unregister_chrdev_region(MKDEV(MAJOR(rproc_cdev), 0),
>>> +				NUM_RPROC_DEVICES);
>>> +}
>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>>> index 16ad666..c4ca796 100644
>>> --- a/include/linux/remoteproc.h
>>> +++ b/include/linux/remoteproc.h
>>> @@ -37,6 +37,7 @@
>>> 
>>> #include <linux/types.h>
>>> #include <linux/mutex.h>
>>> +#include <linux/cdev.h>
>>> #include <linux/virtio.h>
>>> #include <linux/completion.h>
>>> #include <linux/idr.h>
>>> @@ -514,6 +515,7 @@ struct rproc {
>>> 	bool auto_boot;
>>> 	struct list_head dump_segments;
>>> 	int nb_vdev;
>>> +	struct cdev char_dev;
>>> };
>>> 
>>> /**
>>> --
>>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>>> Forum,
> >> a Linux Foundation Collaborative Project
Mathieu Poirier March 30, 2020, 10:12 p.m. UTC | #5
On Thu, Mar 26, 2020 at 09:50:39AM -0700, Rishabh Bhatnagar wrote:
> Add the driver for creating the character device interface for
> userspace applications. The character device interface can be used
> in order to boot up and shutdown the remote processor.
> This might be helpful for remote processors that are booted by
> userspace applications and need to shutdown when the application
> crahes/shutsdown.
> 
> Change-Id: If23c8986272bb7c943eb76665f127ff706f12394

On my side checkpatch is complaining loudly that Change-Id tags should be
removed... I wonder how you didn't get it on your side.  

> Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
> ---
>  drivers/remoteproc/Makefile               |  1 +
>  drivers/remoteproc/remoteproc_internal.h  |  6 +++
>  drivers/remoteproc/remoteproc_userspace.c | 90 +++++++++++++++++++++++++++++++
>  include/linux/remoteproc.h                |  2 +
>  4 files changed, 99 insertions(+)
>  create mode 100644 drivers/remoteproc/remoteproc_userspace.c
> 
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index e30a1b1..facb3fa 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_REMOTEPROC)		+= remoteproc.o
>  remoteproc-y				:= remoteproc_core.o
>  remoteproc-y				+= remoteproc_debugfs.o
>  remoteproc-y				+= remoteproc_sysfs.o
> +remoteproc-y				+= remoteproc_userspace.o
>  remoteproc-y				+= remoteproc_virtio.o
>  remoteproc-y				+= remoteproc_elf_loader.o
>  obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 493ef92..97513ba 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -47,6 +47,9 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
>  int rproc_init_sysfs(void);
>  void rproc_exit_sysfs(void);
>  
> +void rproc_init_cdev(void);
> +void rproc_exit_cdev(void);
> +
>  void rproc_free_vring(struct rproc_vring *rvring);
>  int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
>  
> @@ -63,6 +66,9 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
>  struct rproc_mem_entry *
>  rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...);
>  
> +/* from remoteproc_userspace.c */
> +int rproc_char_device_add(struct rproc *rproc);
> +
>  static inline
>  int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
>  {
> diff --git a/drivers/remoteproc/remoteproc_userspace.c b/drivers/remoteproc/remoteproc_userspace.c
> new file mode 100644
> index 0000000..2ef7679
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc_userspace.c
> @@ -0,0 +1,90 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Character device interface driver for Remoteproc framework.
> + *
> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +#include <linux/mutex.h>
> +#include <linux/remoteproc.h>

Alphabetical oder.

> +
> +#include "remoteproc_internal.h"
> +
> +#define NUM_RPROC_DEVICES	64
> +static dev_t rproc_cdev;
> +static DEFINE_IDA(cdev_minor_ida);
> +
> +static int rproc_open(struct inode *inode, struct file *file)
> +{
> +	struct rproc *rproc;
> +
> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> +	if (!rproc)
> +		return -EINVAL;
> +
> +	return rproc_boot(rproc);
> +}
> +
> +static int rproc_release(struct inode *inode, struct file *file)
> +{

This function declaration conflicts with the one already defined in
remoteproc_internal.h.  Again, I wonder how you didn't hit that problem when you
compiled this patch.  

> +	struct rproc *rproc;
> +
> +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> +	if (!rproc)
> +		return -EINVAL;
> +
> +	rproc_shutdown(rproc);

The scenario I see here is that a userspace program will call
open(/dev/rproc_xyz, SOME_OPTION) when it is launched.  The file stays open
until either the application shuts down, in which case it calls close() or it
crashes.  In that case the system will automatically close all file descriptors
that were open by the application, which will also call rproc_shutdown().

To me the same functionality can be achieved with the current functionality
provided by sysfs.  

When the application starts it needs to read
"/sys/class/remoteproc/remoteprocX/state".  If the state is "offline" then
"start" should be written to "/sys/.../state".  If the state is "running" the
application just crashed and got restarted.  In which case it needs to stop the
remote processor and start it again.

> +
> +	return 0;
> +}
> +
> +static const struct file_operations rproc_fops = {
> +	.open = rproc_open,
> +	.release = rproc_release,
> +};
> +
> +int rproc_char_device_add(struct rproc *rproc)
> +{
> +	int ret, minor;
> +	dev_t cdevt;
> +
> +	minor = ida_simple_get(&cdev_minor_ida, 0, NUM_RPROC_DEVICES,
> +			GFP_KERNEL);

Indentation issue.

> +	if (minor < 0) {
> +	pr_err("%s: No more minor numbers left! rc:%d\n", __func__,

Here to...

> +							minor);

And here.

> +		return -ENODEV;
> +	}
> +
> +	cdev_init(&rproc->char_dev, &rproc_fops);
> +	rproc->char_dev.owner = THIS_MODULE;
> +
> +	cdevt = MKDEV(MAJOR(rproc_cdev), minor);
> +	ret = cdev_add(&rproc->char_dev, cdevt, 1);
> +	if (ret < 0)
> +		ida_simple_remove(&cdev_minor_ida, minor);

If the module is removed unregister_chrdev_region() is called but I don't see
anywhere in there that cdev_del() is called.  

> +
> +	rproc->dev.devt = cdevt;
> +
> +	return ret;
> +}
> +
> +void __init rproc_init_cdev(void)
> +{
> +	int ret;
> +
> +	ret = alloc_chrdev_region(&rproc_cdev, 0, NUM_RPROC_DEVICES, "rproc");
> +	if (ret < 0) {
> +		pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
> +		return;
> +	}
> +}
> +
> +void __exit rproc_exit_cdev(void)
> +{
> +	unregister_chrdev_region(MKDEV(MAJOR(rproc_cdev), 0),
> +				NUM_RPROC_DEVICES);

Indentation problem.

> +}
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 16ad666..c4ca796 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -37,6 +37,7 @@
>  
>  #include <linux/types.h>
>  #include <linux/mutex.h>
> +#include <linux/cdev.h>
>  #include <linux/virtio.h>
>  #include <linux/completion.h>
>  #include <linux/idr.h>
> @@ -514,6 +515,7 @@ struct rproc {
>  	bool auto_boot;
>  	struct list_head dump_segments;
>  	int nb_vdev;
> +	struct cdev char_dev;

The next time you send a patchset, please compile it and run checkpatch on it.

Thanks,
Mathieu

>  };
>  
>  /**
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
Bjorn Andersson March 30, 2020, 10:45 p.m. UTC | #6
On Mon 30 Mar 15:12 PDT 2020, Mathieu Poirier wrote:
[..]
> > +	struct rproc *rproc;
> > +
> > +	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> > +	if (!rproc)
> > +		return -EINVAL;
> > +
> > +	rproc_shutdown(rproc);
> 
> The scenario I see here is that a userspace program will call
> open(/dev/rproc_xyz, SOME_OPTION) when it is launched.  The file stays open
> until either the application shuts down, in which case it calls close() or it
> crashes.  In that case the system will automatically close all file descriptors
> that were open by the application, which will also call rproc_shutdown().
> 
> To me the same functionality can be achieved with the current functionality
> provided by sysfs.  
> 
> When the application starts it needs to read
> "/sys/class/remoteproc/remoteprocX/state".  If the state is "offline" then
> "start" should be written to "/sys/.../state".  If the state is "running" the
> application just crashed and got restarted.  In which case it needs to stop the
> remote processor and start it again.
> 

A case when this would be useful is the Qualcomm modem, which relies on
disk access through a "remote file system service" [1].

Today we register the service (a few layers ontop of rpmsg/GLINK) then
find the modem remoteproc and write "start" into the state sysfs file.

When we get a signal for termination we write "stop" into state to stop
the remoteproc before exiting.

There is however no way for us to indicate to the modem that rmtfs just
died, e.g. a kill -9 on the process will result in the modem continue
and the next IO request will fail which in most cases will be fatal.

So instead having rmtfs holding /dev/rproc_foo open would upon its
termination cause the modem to be stopped automatically, and as the
system respawns rmtfs the modem would be started anew and the two sides
would be synced up again.

[1] https://github.com/andersson/rmtfs

Regards,
Bjorn
Mathieu Poirier March 31, 2020, 4:47 p.m. UTC | #7
On Mon, 30 Mar 2020 at 16:45, Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Mon 30 Mar 15:12 PDT 2020, Mathieu Poirier wrote:
> [..]
> > > +   struct rproc *rproc;
> > > +
> > > +   rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> > > +   if (!rproc)
> > > +           return -EINVAL;
> > > +
> > > +   rproc_shutdown(rproc);
> >
> > The scenario I see here is that a userspace program will call
> > open(/dev/rproc_xyz, SOME_OPTION) when it is launched.  The file stays open
> > until either the application shuts down, in which case it calls close() or it
> > crashes.  In that case the system will automatically close all file descriptors
> > that were open by the application, which will also call rproc_shutdown().
> >
> > To me the same functionality can be achieved with the current functionality
> > provided by sysfs.
> >
> > When the application starts it needs to read
> > "/sys/class/remoteproc/remoteprocX/state".  If the state is "offline" then
> > "start" should be written to "/sys/.../state".  If the state is "running" the
> > application just crashed and got restarted.  In which case it needs to stop the
> > remote processor and start it again.
> >
>
> A case when this would be useful is the Qualcomm modem, which relies on
> disk access through a "remote file system service" [1].
>
> Today we register the service (a few layers ontop of rpmsg/GLINK) then
> find the modem remoteproc and write "start" into the state sysfs file.
>
> When we get a signal for termination we write "stop" into state to stop
> the remoteproc before exiting.
>
> There is however no way for us to indicate to the modem that rmtfs just
> died, e.g. a kill -9 on the process will result in the modem continue
> and the next IO request will fail which in most cases will be fatal.

The modem will crash when attempting an IO while rmtfs is down?

>
> So instead having rmtfs holding /dev/rproc_foo open would upon its
> termination cause the modem to be stopped automatically, and as the
> system respawns rmtfs the modem would be started anew and the two sides
> would be synced up again.

I have a better idea of what is going on now - thanks for writing this up.

I would make this feature a kernel configurable option as some people
may not want it.  I also think having "/dev/remoteprocX" is fine, so
no need to change anything currently visible in sysfs.

Mathieu

>
> [1] https://github.com/andersson/rmtfs
>
> Regards,
> Bjorn
Rishabh Bhatnagar March 31, 2020, 5:38 p.m. UTC | #8
On 2020-03-31 09:47, Mathieu Poirier wrote:
> On Mon, 30 Mar 2020 at 16:45, Bjorn Andersson
> <bjorn.andersson@linaro.org> wrote:
>> 
>> On Mon 30 Mar 15:12 PDT 2020, Mathieu Poirier wrote:
>> [..]
>> > > +   struct rproc *rproc;
>> > > +
>> > > +   rproc = container_of(inode->i_cdev, struct rproc, char_dev);
>> > > +   if (!rproc)
>> > > +           return -EINVAL;
>> > > +
>> > > +   rproc_shutdown(rproc);
>> >
>> > The scenario I see here is that a userspace program will call
>> > open(/dev/rproc_xyz, SOME_OPTION) when it is launched.  The file stays open
>> > until either the application shuts down, in which case it calls close() or it
>> > crashes.  In that case the system will automatically close all file descriptors
>> > that were open by the application, which will also call rproc_shutdown().
>> >
>> > To me the same functionality can be achieved with the current functionality
>> > provided by sysfs.
>> >
>> > When the application starts it needs to read
>> > "/sys/class/remoteproc/remoteprocX/state".  If the state is "offline" then
>> > "start" should be written to "/sys/.../state".  If the state is "running" the
>> > application just crashed and got restarted.  In which case it needs to stop the
>> > remote processor and start it again.
>> >
>> 
>> A case when this would be useful is the Qualcomm modem, which relies 
>> on
>> disk access through a "remote file system service" [1].
>> 
>> Today we register the service (a few layers ontop of rpmsg/GLINK) then
>> find the modem remoteproc and write "start" into the state sysfs file.
>> 
>> When we get a signal for termination we write "stop" into state to 
>> stop
>> the remoteproc before exiting.
>> 
>> There is however no way for us to indicate to the modem that rmtfs 
>> just
>> died, e.g. a kill -9 on the process will result in the modem continue
>> and the next IO request will fail which in most cases will be fatal.
I have this scenario written down in the cover letter for this patchset
  "[PATCH 0/2] Add character device interface to remoteproc"
I'll add it to the commit text as well.
> 
> The modem will crash when attempting an IO while rmtfs is down?
> 
>> 
>> So instead having rmtfs holding /dev/rproc_foo open would upon its
>> termination cause the modem to be stopped automatically, and as the
>> system respawns rmtfs the modem would be started anew and the two 
>> sides
>> would be synced up again.
> 
> I have a better idea of what is going on now - thanks for writing this 
> up.
> 
> I would make this feature a kernel configurable option as some people
> may not want it.  I also think having "/dev/remoteprocX" is fine, so
> no need to change anything currently visible in sysfs.
Ok. Makes sense.
> 
> Mathieu
> 
>> 
>> [1] https://github.com/andersson/rmtfs
>> 
>> Regards,
>> Bjorn
Bjorn Andersson March 31, 2020, 5:55 p.m. UTC | #9
On Tue 31 Mar 09:47 PDT 2020, Mathieu Poirier wrote:

> On Mon, 30 Mar 2020 at 16:45, Bjorn Andersson
> <bjorn.andersson@linaro.org> wrote:
> >
> > On Mon 30 Mar 15:12 PDT 2020, Mathieu Poirier wrote:
> > [..]
> > > > +   struct rproc *rproc;
> > > > +
> > > > +   rproc = container_of(inode->i_cdev, struct rproc, char_dev);
> > > > +   if (!rproc)
> > > > +           return -EINVAL;
> > > > +
> > > > +   rproc_shutdown(rproc);
> > >
> > > The scenario I see here is that a userspace program will call
> > > open(/dev/rproc_xyz, SOME_OPTION) when it is launched.  The file stays open
> > > until either the application shuts down, in which case it calls close() or it
> > > crashes.  In that case the system will automatically close all file descriptors
> > > that were open by the application, which will also call rproc_shutdown().
> > >
> > > To me the same functionality can be achieved with the current functionality
> > > provided by sysfs.
> > >
> > > When the application starts it needs to read
> > > "/sys/class/remoteproc/remoteprocX/state".  If the state is "offline" then
> > > "start" should be written to "/sys/.../state".  If the state is "running" the
> > > application just crashed and got restarted.  In which case it needs to stop the
> > > remote processor and start it again.
> > >
> >
> > A case when this would be useful is the Qualcomm modem, which relies on
> > disk access through a "remote file system service" [1].
> >
> > Today we register the service (a few layers ontop of rpmsg/GLINK) then
> > find the modem remoteproc and write "start" into the state sysfs file.
> >
> > When we get a signal for termination we write "stop" into state to stop
> > the remoteproc before exiting.
> >
> > There is however no way for us to indicate to the modem that rmtfs just
> > died, e.g. a kill -9 on the process will result in the modem continue
> > and the next IO request will fail which in most cases will be fatal.
> 
> The modem will crash when attempting an IO while rmtfs is down?
> 

In certain cases there's nothing else to do.

> >
> > So instead having rmtfs holding /dev/rproc_foo open would upon its
> > termination cause the modem to be stopped automatically, and as the
> > system respawns rmtfs the modem would be started anew and the two sides
> > would be synced up again.
> 
> I have a better idea of what is going on now - thanks for writing this up.
> 
> I would make this feature a kernel configurable option as some people
> may not want it.

Sounds reasonable.

> I also think having "/dev/remoteprocX" is fine, so
> no need to change anything currently visible in sysfs.
> 

I agree, it sure is annoying that remoteproc%d isn't stable, but it's
what we have and consistency is important.

Regards,
Bjorn
diff mbox series

Patch

diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index e30a1b1..facb3fa 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -7,6 +7,7 @@  obj-$(CONFIG_REMOTEPROC)		+= remoteproc.o
 remoteproc-y				:= remoteproc_core.o
 remoteproc-y				+= remoteproc_debugfs.o
 remoteproc-y				+= remoteproc_sysfs.o
+remoteproc-y				+= remoteproc_userspace.o
 remoteproc-y				+= remoteproc_virtio.o
 remoteproc-y				+= remoteproc_elf_loader.o
 obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 493ef92..97513ba 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -47,6 +47,9 @@  struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
 int rproc_init_sysfs(void);
 void rproc_exit_sysfs(void);
 
+void rproc_init_cdev(void);
+void rproc_exit_cdev(void);
+
 void rproc_free_vring(struct rproc_vring *rvring);
 int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
 
@@ -63,6 +66,9 @@  struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
 struct rproc_mem_entry *
 rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...);
 
+/* from remoteproc_userspace.c */
+int rproc_char_device_add(struct rproc *rproc);
+
 static inline
 int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
 {
diff --git a/drivers/remoteproc/remoteproc_userspace.c b/drivers/remoteproc/remoteproc_userspace.c
new file mode 100644
index 0000000..2ef7679
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_userspace.c
@@ -0,0 +1,90 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Character device interface driver for Remoteproc framework.
+ *
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+#define NUM_RPROC_DEVICES	64
+static dev_t rproc_cdev;
+static DEFINE_IDA(cdev_minor_ida);
+
+static int rproc_open(struct inode *inode, struct file *file)
+{
+	struct rproc *rproc;
+
+	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
+	if (!rproc)
+		return -EINVAL;
+
+	return rproc_boot(rproc);
+}
+
+static int rproc_release(struct inode *inode, struct file *file)
+{
+	struct rproc *rproc;
+
+	rproc = container_of(inode->i_cdev, struct rproc, char_dev);
+	if (!rproc)
+		return -EINVAL;
+
+	rproc_shutdown(rproc);
+
+	return 0;
+}
+
+static const struct file_operations rproc_fops = {
+	.open = rproc_open,
+	.release = rproc_release,
+};
+
+int rproc_char_device_add(struct rproc *rproc)
+{
+	int ret, minor;
+	dev_t cdevt;
+
+	minor = ida_simple_get(&cdev_minor_ida, 0, NUM_RPROC_DEVICES,
+			GFP_KERNEL);
+	if (minor < 0) {
+	pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
+							minor);
+		return -ENODEV;
+	}
+
+	cdev_init(&rproc->char_dev, &rproc_fops);
+	rproc->char_dev.owner = THIS_MODULE;
+
+	cdevt = MKDEV(MAJOR(rproc_cdev), minor);
+	ret = cdev_add(&rproc->char_dev, cdevt, 1);
+	if (ret < 0)
+		ida_simple_remove(&cdev_minor_ida, minor);
+
+	rproc->dev.devt = cdevt;
+
+	return ret;
+}
+
+void __init rproc_init_cdev(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&rproc_cdev, 0, NUM_RPROC_DEVICES, "rproc");
+	if (ret < 0) {
+		pr_err("Failed to alloc rproc_cdev region, err %d\n", ret);
+		return;
+	}
+}
+
+void __exit rproc_exit_cdev(void)
+{
+	unregister_chrdev_region(MKDEV(MAJOR(rproc_cdev), 0),
+				NUM_RPROC_DEVICES);
+}
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 16ad666..c4ca796 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -37,6 +37,7 @@ 
 
 #include <linux/types.h>
 #include <linux/mutex.h>
+#include <linux/cdev.h>
 #include <linux/virtio.h>
 #include <linux/completion.h>
 #include <linux/idr.h>
@@ -514,6 +515,7 @@  struct rproc {
 	bool auto_boot;
 	struct list_head dump_segments;
 	int nb_vdev;
+	struct cdev char_dev;
 };
 
 /**