linux-arm-msm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor
       [not found] <20200412112405.24116-1-wenhu.wang@vivo.com>
@ 2020-04-14  3:59 ` Wang Wenhu
  2020-04-14  3:59   ` [PATCH v3,1/3] driver: " Wang Wenhu
                     ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Wang Wenhu @ 2020-04-14  3:59 UTC (permalink / raw)
  To: gregkh, linux-kernel
  Cc: rdunlap, kernel, agross, bjorn.andersson, ohad, linux-remoteproc,
	linux-arm-msm, Wang Wenhu

RPMON is a driver framework. It supports remote processor monitor
from user level. The basic components are a character device
with sysfs interfaces for user space communication and different
kinds of message drivers introduced modularly, which are used to
communicate with remote processors.

As for user space, one can get notifications of different events
of remote processors, like their registrations, through standard
file read operation of the file descriptors related to the exported
character devices. Actions can also be taken into account via
standard write operations to the devices. Besides, the sysfs class
attributes could be accessed conveniently.

Message drivers act as engines to communicate with remote processors.
Currently RPMON_QMI is available which uses QMI infrastructures
on Qualcomm SoC Platforms.

RPMON_QMI implements a kind of communication routine for RPMON to
communicate with remote processors through QMI infrastructure.
RPMON_QMI itself is designed as a modular framework that would
introduce different kind of message sets which are binding to
different services.

RPMON_QMI creates a device of rpmon_device type for each remote
processor endpoint. All the endpoint devices share an unique set
of QMI suite.

RPMON_QMI_MSG_V01 implements a RPMON_QMI message set for connection check.
RPMON_QMI defines its message types modularly. Each rpmon service
binds to a message set and introduced as a module. This version 1.0
message set could be used for connection checking of remote processors.

RPMON_QMI messages depend on QCOM_QMI_HELPERS and should be updated
together with QMI related modules.

Changes since v1:
 - Addressed review comments from Randy
Changes since v2:
 - Added Cc list
 - Commit log typo fixing
 - Use the ARRAY_SIZE instead of calculations of multiple sizeof()
 - Use micros for qmi message tly_type fields

Wang Wenhu (3):
  driver: rpmon: new driver Remote Processor Monitor
  driver: rpmon: qmi message version 01
  driver: rpmon: add rpmon_qmi driver

 drivers/Kconfig                  |   2 +
 drivers/Makefile                 |   1 +
 drivers/rpmon/Kconfig            |  54 ++++
 drivers/rpmon/Makefile           |   3 +
 drivers/rpmon/rpmon.c            | 506 +++++++++++++++++++++++++++++++
 drivers/rpmon/rpmon_qmi.c        | 431 ++++++++++++++++++++++++++
 drivers/rpmon/rpmon_qmi.h        |  76 +++++
 drivers/rpmon/rpmon_qmi_msg_v1.c | 258 ++++++++++++++++
 include/linux/rpmon.h            |  68 +++++
 9 files changed, 1399 insertions(+)
 create mode 100644 drivers/rpmon/Kconfig
 create mode 100644 drivers/rpmon/Makefile
 create mode 100644 drivers/rpmon/rpmon.c
 create mode 100644 drivers/rpmon/rpmon_qmi.c
 create mode 100644 drivers/rpmon/rpmon_qmi.h
 create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
 create mode 100644 include/linux/rpmon.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3,1/3] driver: rpmon: new driver Remote Processor Monitor
  2020-04-14  3:59 ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Wang Wenhu
@ 2020-04-14  3:59   ` Wang Wenhu
  2020-04-28 12:57     ` Greg KH
  2020-04-14  3:59   ` [PATCH v3,2/3] driver: rpmon: qmi message version 01 Wang Wenhu
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Wang Wenhu @ 2020-04-14  3:59 UTC (permalink / raw)
  To: gregkh, linux-kernel
  Cc: rdunlap, kernel, agross, bjorn.andersson, ohad, linux-remoteproc,
	linux-arm-msm, Wang Wenhu

RPMON is a driver framework. It supports remote processor monitor
from user level. The basic components are a character device
with sysfs interfaces for user space communication and different
kinds of message drivers introduced modularly, which are used to
communicate with remote processors.

As for user space, one can get notifications of different events
of remote processors, like their registrations, through standard
file read operation of the file descriptors related to the exported
character devices. Actions can also be taken into account via
standard write operations to the devices. Besides, the sysfs class
attributes could be accessed conveniently.

Message drivers act as engines to communicate with remote processors.
Currently RPMON_QMI is available which uses QMI infrastructures
on Qualcomm SoC Platforms.

Cc: Andy Gross <agross@kernel.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Ohad Ben-Cohen <ohad@wizery.com>
Cc: linux-remoteproc@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Signed-off-by: Wang Wenhu <wenhu.wang@vivo.com>
---
Changes since v1:
 - Addressed review comments from Randy
Changes since v2:
 - Log message typo
 - Added Cc list
---
 drivers/Kconfig        |   2 +
 drivers/Makefile       |   1 +
 drivers/rpmon/Kconfig  |  26 +++
 drivers/rpmon/Makefile |   1 +
 drivers/rpmon/rpmon.c  | 506 +++++++++++++++++++++++++++++++++++++++++
 include/linux/rpmon.h  |  68 ++++++
 6 files changed, 604 insertions(+)
 create mode 100644 drivers/rpmon/Kconfig
 create mode 100644 drivers/rpmon/Makefile
 create mode 100644 drivers/rpmon/rpmon.c
 create mode 100644 include/linux/rpmon.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index dcecc9f6e33f..40409d8a87b3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -166,6 +166,8 @@ source "drivers/remoteproc/Kconfig"
 
 source "drivers/rpmsg/Kconfig"
 
+source "drivers/rpmon/Kconfig"
+
 source "drivers/soundwire/Kconfig"
 
 source "drivers/soc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index c0cd1b9075e3..75d215cd9136 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -155,6 +155,7 @@ obj-$(CONFIG_MAILBOX)		+= mailbox/
 obj-$(CONFIG_HWSPINLOCK)	+= hwspinlock/
 obj-$(CONFIG_REMOTEPROC)	+= remoteproc/
 obj-$(CONFIG_RPMSG)		+= rpmsg/
+obj-$(CONFIG_RPMON)		+= rpmon/
 obj-$(CONFIG_SOUNDWIRE)		+= soundwire/
 
 # Virtualization drivers
diff --git a/drivers/rpmon/Kconfig b/drivers/rpmon/Kconfig
new file mode 100644
index 000000000000..ad0e6d6561ca
--- /dev/null
+++ b/drivers/rpmon/Kconfig
@@ -0,0 +1,26 @@
+#
+# Remote Processor Monitor Drivers
+#
+menu "Remote Processor Monitor Drivers"
+
+config RPMON
+	tristate "Remote Processor Monitor Core Framework"
+	help
+	  RPMON is a driver framework. It supports remote processor monitor
+	  from user level. The basic components are a character device
+	  with sysfs interfaces for user space communication and different
+	  kinds of message drivers introduced modularly, which are used to
+	  communicate with remote processors.
+
+	  As for user space, one can get notifications of different events
+	  of remote processors, like their registrations, through standard
+	  file read operation of the file descriptors related to the exported
+	  character devices. Actions can also be taken into account via
+	  standard write operations to the devices. Besides, the sysfs class
+	  attributes could be accessed conveniently.
+
+	  Message drivers act as engines to communicate with remote processors.
+	  Currently RPMON_QMI is available which uses QMI infrastructures
+	  on Qualcomm SoC Platforms.
+
+endmenu
diff --git a/drivers/rpmon/Makefile b/drivers/rpmon/Makefile
new file mode 100644
index 000000000000..b0f0ec4ecc30
--- /dev/null
+++ b/drivers/rpmon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RPMON) += rpmon.o
diff --git a/drivers/rpmon/rpmon.c b/drivers/rpmon/rpmon.c
new file mode 100644
index 000000000000..72262b010a68
--- /dev/null
+++ b/drivers/rpmon/rpmon.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <wenhu.wang@vivo.com>
+ * All rights reserved.
+ *
+ * RPMON: An implementation of remote processor monitor framework
+ * for modern SoCs that typically have heterogeneous remote processor
+ * devices in asymmetric multiprocessing configurations. It is
+ * implemented with chardev and sysfs class, which act as interfaces
+ * to communicate with user level. It supports different communication
+ * interfaces added modularly to communicate with remote processors.
+ * Currently QMI implementation, named RPMON_QMI, is available.
+ *
+ * RPMON could be used to detect the stabilities of remote processors,
+ * collect any kinds of information you are interested in, take
+ * actions like connection status check, and so on. Enhancements
+ * can be made upon current implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/rpmon.h>
+
+#define RPMON_MAX_DEVICES	(1U << MINORBITS)
+#define RPMON_NAME			"rpmon"
+
+static int rpmon_major;
+static struct cdev *rpmon_cdev;
+static DEFINE_IDR(rpmon_idr);
+static const struct file_operations rpmon_fops;
+
+/* Protect idr accesses */
+static DEFINE_MUTEX(minor_lock);
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct rpmon_device *rpmondev = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (!rpmondev->info) {
+		ret = -EINVAL;
+		dev_err(dev, "the device has been unregistered\n");
+		goto out;
+	}
+
+	ret = sprintf(buf, "%s\n", rpmondev->info->name);
+
+out:
+	mutex_unlock(&rpmondev->info_lock);
+	return ret;
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t version_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct rpmon_device *rpmondev = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (!rpmondev->info) {
+		ret = -EINVAL;
+		dev_err(dev, "the device has been unregistered\n");
+		goto out;
+	}
+
+	ret = sprintf(buf, "%s\n", rpmondev->info->version);
+
+out:
+	mutex_unlock(&rpmondev->info_lock);
+	return ret;
+}
+static DEVICE_ATTR_RO(version);
+
+static struct attribute *rpmon_attrs[] = {
+	&dev_attr_name.attr,
+	&dev_attr_version.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(rpmon);
+
+/* RPMON class infrastructure */
+static struct class rpmon_class = {
+	.name = RPMON_NAME,
+	.dev_groups = rpmon_groups,
+};
+
+static bool rpmon_class_registered;
+
+static int rpmon_get_minor(struct rpmon_device *rpmondev)
+{
+	int ret = -ENOMEM;
+
+	mutex_lock(&minor_lock);
+	ret = idr_alloc(&rpmon_idr, rpmondev, 0, RPMON_MAX_DEVICES, GFP_KERNEL);
+	if (ret >= 0) {
+		rpmondev->minor = ret;
+		ret = 0;
+	} else if (ret == -ENOSPC) {
+		dev_err(&rpmondev->dev, "too many rpmon devices\n");
+		ret = -EINVAL;
+	}
+	mutex_unlock(&minor_lock);
+	return ret;
+}
+
+static void rpmon_free_minor(struct rpmon_device *rpmondev)
+{
+	mutex_lock(&minor_lock);
+	idr_remove(&rpmon_idr, rpmondev->minor);
+	mutex_unlock(&minor_lock);
+}
+
+/**
+ * rpmon_event_notify - trigger an notify event
+ * @info:  RPMON device capabilities
+ * @event: RPMON event to be triggered
+ */
+void rpmon_event_notify(struct rpmon_info *info, u32 event)
+{
+	struct rpmon_device *rpmondev = info->rpmon_dev;
+
+	if (event >= RPMON_EVENT_MAX) {
+		pr_err("Error unsupported rpmon event %d", event);
+		return;
+	}
+
+	atomic_set(&rpmondev->event, RPMON_EVENT(event));
+	wake_up_interruptible(&rpmondev->wait);
+	kill_fasync(&rpmondev->async_queue, SIGIO, POLL_IN);
+}
+EXPORT_SYMBOL_GPL(rpmon_event_notify);
+
+static int rpmon_open(struct inode *inode, struct file *filep)
+{
+	struct rpmon_device *rpmondev;
+	int ret = 0;
+
+	mutex_lock(&minor_lock);
+	rpmondev = idr_find(&rpmon_idr, iminor(inode));
+	mutex_unlock(&minor_lock);
+	if (!rpmondev) {
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	get_device(&rpmondev->dev);
+
+	if (!try_module_get(rpmondev->owner)) {
+		ret = -ENODEV;
+		goto err_module_get;
+	}
+
+	filep->private_data = rpmondev;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (!rpmondev->info) {
+		mutex_unlock(&rpmondev->info_lock);
+		ret = -EINVAL;
+		goto err_module_get;
+	}
+
+	if (rpmondev->info->open)
+		ret = rpmondev->info->open(rpmondev->info, inode);
+	mutex_unlock(&rpmondev->info_lock);
+	if (ret)
+		goto err_module_get;
+
+	return ret;
+
+err_module_get:
+	put_device(&rpmondev->dev);
+
+err_out:
+	return ret;
+}
+
+static int rpmon_release(struct inode *inode, struct file *filep)
+{
+	struct rpmon_device *rpmondev = filep->private_data;
+	int ret = 0;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (rpmondev->info && rpmondev->info->release)
+		ret = rpmondev->info->release(rpmondev->info, inode);
+	mutex_unlock(&rpmondev->info_lock);
+
+	module_put(rpmondev->owner);
+	put_device(&rpmondev->dev);
+	return ret;
+}
+
+static __poll_t rpmon_poll(struct file *filep, poll_table *wait)
+{
+	struct rpmon_device *rpmondev = filep->private_data;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (!rpmondev->info) {
+		mutex_unlock(&rpmondev->info_lock);
+		return -EIO;
+	}
+	mutex_unlock(&rpmondev->info_lock);
+
+	poll_wait(filep, &rpmondev->wait, wait);
+	if (!atomic_read(&rpmondev->event))
+		return EPOLLIN | EPOLLRDNORM;
+	return 0;
+}
+
+static ssize_t rpmon_read(struct file *filep, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	struct rpmon_device *rpmondev = filep->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	u32 event;
+
+	if (count != sizeof(u32))
+		return -EINVAL;
+
+	add_wait_queue(&rpmondev->wait, &wait);
+
+	do {
+		mutex_lock(&rpmondev->info_lock);
+		if (!rpmondev->info) {
+			ret = -EIO;
+			mutex_unlock(&rpmondev->info_lock);
+			break;
+		}
+		mutex_unlock(&rpmondev->info_lock);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		event = atomic_read(&rpmondev->event);
+		if (event) {
+			__set_current_state(TASK_RUNNING);
+			if (copy_to_user(buf, &event, count))
+				ret = -EFAULT;
+			else {
+				atomic_set(&rpmondev->event, 0);
+				ret = count;
+			}
+			break;
+		}
+
+		if (filep->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	} while (1);
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rpmondev->wait, &wait);
+
+	return ret;
+}
+
+static ssize_t rpmon_write(struct file *filep, const char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct rpmon_device *rpmondev = filep->private_data;
+	ssize_t ret;
+	u32 action;
+
+	if (count != sizeof(u32))
+		return -EINVAL;
+
+	if (copy_from_user(&action, buf, count))
+		return -EFAULT;
+
+	mutex_lock(&rpmondev->info_lock);
+	if (!rpmondev->info) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!rpmondev->info->monitor) {
+		ret = -ENOTSUPP;
+		goto out;
+	}
+
+	if (rpmondev->info->monitor)
+		ret = rpmondev->info->monitor(rpmondev->info, action);
+out:
+	mutex_unlock(&rpmondev->info_lock);
+	return ret ? ret : sizeof(u32);
+}
+
+static const struct file_operations rpmon_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rpmon_open,
+	.read		= rpmon_read,
+	.write		= rpmon_write,
+	.poll		= rpmon_poll,
+	.release	= rpmon_release,
+};
+
+static int rpmon_major_init(void)
+{
+	static const char name[] = RPMON_NAME;
+	struct cdev *cdev = NULL;
+	dev_t rpmon_dev = 0;
+	int ret;
+
+	ret = alloc_chrdev_region(&rpmon_dev, 0, RPMON_MAX_DEVICES, name);
+	if (ret)
+		goto out;
+
+	ret = -ENOMEM;
+	cdev = cdev_alloc();
+	if (!cdev)
+		goto out_unregister;
+
+	cdev->owner = THIS_MODULE;
+	cdev->ops = &rpmon_fops;
+	kobject_set_name(&cdev->kobj, "%s", name);
+
+	ret = cdev_add(cdev, rpmon_dev, RPMON_MAX_DEVICES);
+	if (ret)
+		goto out_put;
+
+	rpmon_major = MAJOR(rpmon_dev);
+	rpmon_cdev = cdev;
+	return 0;
+out_put:
+	kobject_put(&cdev->kobj);
+out_unregister:
+	unregister_chrdev_region(rpmon_dev, RPMON_MAX_DEVICES);
+out:
+	return ret;
+}
+
+
+static void rpmon_major_cleanup(void)
+{
+	unregister_chrdev_region(MKDEV(rpmon_major, 0), RPMON_MAX_DEVICES);
+	cdev_del(rpmon_cdev);
+}
+
+static int init_rpmon_class(void)
+{
+	int ret;
+
+	/* Allocate and add char device to the system. */
+	ret = rpmon_major_init();
+	if (ret)
+		goto exit;
+
+	ret = class_register(&rpmon_class);
+	if (ret) {
+		pr_err("class_register failed for rpmon\n");
+		goto err_class_register;
+	}
+
+	rpmon_class_registered = true;
+
+	return 0;
+
+err_class_register:
+	rpmon_major_cleanup();
+exit:
+	return ret;
+}
+
+static void release_rpmon_class(void)
+{
+	rpmon_class_registered = false;
+	class_unregister(&rpmon_class);
+	rpmon_major_cleanup();
+}
+
+static void rpmon_device_release(struct device *dev)
+{
+	struct rpmon_device *rpmondev = dev_get_drvdata(dev);
+
+	kfree(rpmondev);
+}
+
+/**
+ * rpmon_register_device - register a new rpmon interface device
+ * @owner:	module that creates the new device
+ * @parent:	parent device
+ * @info:	rpmon device capabilities
+ *
+ * return:	zero on success or a negative error code.
+ */
+int __rpmon_register_device(struct module *owner,
+			    struct device *parent,
+			    struct rpmon_info *info)
+{
+	struct rpmon_device *rpmondev;
+	int ret = 0;
+
+	if (!rpmon_class_registered)
+		return -EPROBE_DEFER;
+
+	if (!parent || !info || !info->name || !info->version)
+		return -EINVAL;
+
+	info->rpmon_dev = NULL;
+
+	rpmondev = kzalloc(sizeof(*rpmondev), GFP_KERNEL);
+	if (!rpmondev)
+		return -ENOMEM;
+
+	rpmondev->owner = owner;
+	rpmondev->info = info;
+	mutex_init(&rpmondev->info_lock);
+	init_waitqueue_head(&rpmondev->wait);
+	atomic_set(&rpmondev->event, 0);
+
+	ret = rpmon_get_minor(rpmondev);
+	if (ret) {
+		kfree(rpmondev);
+		return ret;
+	}
+
+	device_initialize(&rpmondev->dev);
+	rpmondev->dev.devt = MKDEV(rpmon_major, rpmondev->minor);
+	rpmondev->dev.class = &rpmon_class;
+	rpmondev->dev.parent = parent;
+	rpmondev->dev.release = rpmon_device_release;
+	dev_set_drvdata(&rpmondev->dev, rpmondev);
+
+	ret = dev_set_name(&rpmondev->dev, RPMON_NAME"%d", rpmondev->minor);
+	if (ret)
+		goto err_device_create;
+
+	ret = device_add(&rpmondev->dev);
+	if (ret)
+		goto err_device_create;
+
+	if (rpmondev->info->rpmon_dev_add_attrs) {
+		ret = rpmondev->info->rpmon_dev_add_attrs(rpmondev);
+		if (ret)
+			goto err_dev_add_attrs;
+	}
+
+	info->rpmon_dev = rpmondev;
+
+	return 0;
+
+err_dev_add_attrs:
+	device_del(&rpmondev->dev);
+err_device_create:
+	rpmon_free_minor(rpmondev);
+	put_device(&rpmondev->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__rpmon_register_device);
+
+/**
+ * rpmon_unregister_device - unregister a rpmon interface device
+ * @info:	rpmon device capabilities
+ */
+void rpmon_unregister_device(struct rpmon_info *info)
+{
+	struct rpmon_device *rpmondev;
+
+	if (!info || !info->rpmon_dev)
+		return;
+
+	rpmondev = info->rpmon_dev;
+
+	rpmon_free_minor(rpmondev);
+
+	mutex_lock(&rpmondev->info_lock);
+
+	if (rpmondev->info->rpmon_dev_add_attrs)
+		rpmondev->info->rpmon_dev_del_attrs(rpmondev);
+
+	rpmondev->info = NULL;
+	mutex_unlock(&rpmondev->info_lock);
+
+	wake_up_interruptible(&rpmondev->wait);
+	kill_fasync(&rpmondev->async_queue, SIGIO, POLL_IN);
+
+	device_unregister(&rpmondev->dev);
+}
+EXPORT_SYMBOL_GPL(rpmon_unregister_device);
+
+static int __init rpmon_init(void)
+{
+	return init_rpmon_class();
+}
+subsys_initcall(rpmon_init);
+
+static void rpmon_exit(void)
+{
+	release_rpmon_class();
+	idr_destroy(&rpmon_idr);
+}
+module_exit(rpmon_exit);
+
+MODULE_AUTHOR("Wang Wenhu <wenhu.wang@vivo.com>");
+MODULE_DESCRIPTION("Remote Processor Monitor Core Framework");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/rpmon.h b/include/linux/rpmon.h
new file mode 100644
index 000000000000..4bbcdeefebee
--- /dev/null
+++ b/include/linux/rpmon.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <wenhu.wang@vivo.com>
+ * All rights reserved.
+ */
+
+#ifndef RPMON_H
+#define RPMON_H
+
+#include <net/sock.h>
+
+/* RPMON action would be taken */
+enum rpmon_exec {
+	RPMON_EXEC_CHECK_CONN = 0,
+	RPMON_EXEC_MAX,
+};
+
+/* RPMON events that may be notified */
+enum rpmon_event {
+	RPMON_EVENT_CHKCONN_FAIL = 0,
+	RPMON_EVENT_REGISTER,
+	RPMON_EVENT_MAX,
+};
+
+#define RPMON_EVENT(x)		(0x1 << (x))
+#define RPMON_ACTION(x)		(0x1 << (x))
+
+struct rpmon_device {
+	struct module		*owner;
+	struct device		dev;
+	int			minor;
+	atomic_t		event;
+	struct fasync_struct	*async_queue;
+	wait_queue_head_t	wait;
+	struct rpmon_info	*info;
+	struct mutex		info_lock;
+	struct kobject		*map_dir;
+};
+
+struct rpmon_info {
+	struct rpmon_device	*rpmon_dev;
+	const char		*name;
+	const char		*version;
+	void			*priv;
+	u32			event;
+
+	int (*open)(struct rpmon_info *info, struct inode *inode);
+	int (*release)(struct rpmon_info *info, struct inode *inode);
+	int (*monitor)(struct rpmon_info *info, u32 event);
+	int (*rpmon_dev_add_attrs)(struct rpmon_device *rpmondev);
+	void (*rpmon_dev_del_attrs)(struct rpmon_device *rpmondev);
+};
+
+extern int __must_check
+	__rpmon_register_device(struct module *owner,
+				struct device *parent,
+				struct rpmon_info *info);
+
+/* Use a define to avoid include chaining to get THIS_MODULE */
+#define rpmon_register_device(parent, info) \
+	__rpmon_register_device(THIS_MODULE, parent, info)
+
+extern void rpmon_unregister_device(struct rpmon_info *info);
+
+extern void rpmon_event_notify(struct rpmon_info *info, u32 event);
+
+#endif /* RPMON_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3,2/3] driver: rpmon: qmi message version 01
  2020-04-14  3:59 ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Wang Wenhu
  2020-04-14  3:59   ` [PATCH v3,1/3] driver: " Wang Wenhu
@ 2020-04-14  3:59   ` Wang Wenhu
  2020-04-14  3:59   ` [PATCH v3,3/3] driver: rpmon: add rpmon_qmi driver Wang Wenhu
  2020-04-14 22:58   ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Bjorn Andersson
  3 siblings, 0 replies; 7+ messages in thread
From: Wang Wenhu @ 2020-04-14  3:59 UTC (permalink / raw)
  To: gregkh, linux-kernel
  Cc: rdunlap, kernel, agross, bjorn.andersson, ohad, linux-remoteproc,
	linux-arm-msm, Wang Wenhu

Implements a RPMON_QMI message set for connection checking service.
RPMON_QMI defines its message types modularly. Each rpmon service
binds to a message set and introduced as a module. This version 1.0
message set could be used for connection checking of remote processors.

RPMON_QMI messages depend on QCOM_QMI_HELPERS and should be updated
together with QMI related modules, and if so, RPMON_QMI_MAG_V2 would
be introduced as a new module, in parallel with RPMON_QMI_MAG_V1.

Cc: Andy Gross <agross@kernel.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Ohad Ben-Cohen <ohad@wizery.com>
Cc: linux-remoteproc@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Signed-off-by: Wang Wenhu <wenhu.wang@vivo.com>
---
Changes since v1:
 - Addressed review comments from Randy
Changes since v2:
 - Use micros for tlv_type fields, and another patch would be helpful:
   * [PATCH] soc: qmi: move tlv-micros to header file
   * Link: https://lore.kernel.org/linux-arm-msm/20200413035758.60238-1-wenhu.wang@vivo.com/
 - Added Cc list
---
 drivers/rpmon/Kconfig            |  13 ++
 drivers/rpmon/Makefile           |   1 +
 drivers/rpmon/rpmon_qmi.h        |  76 +++++++++
 drivers/rpmon/rpmon_qmi_msg_v1.c | 258 +++++++++++++++++++++++++++++++
 4 files changed, 348 insertions(+)
 create mode 100644 drivers/rpmon/rpmon_qmi.h
 create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c

diff --git a/drivers/rpmon/Kconfig b/drivers/rpmon/Kconfig
index ad0e6d6561ca..0b80236ad186 100644
--- a/drivers/rpmon/Kconfig
+++ b/drivers/rpmon/Kconfig
@@ -23,4 +23,17 @@ config RPMON
 	  Currently RPMON_QMI is available which uses QMI infrastructures
 	  on Qualcomm SoC Platforms.
 
+config RPMON_QMI_MSG_V1
+	tristate "RPMON QMI Message Version 1.0"
+	depends on RPMON
+	depends on QCOM_QMI_HELPERS
+	help
+	  Implements a RPMON_QMI message set for a certain rpmon service.
+	  RPMON_QMI defines its message types modularly. Each rpmon service
+	  binds to a message set and introduced as a module. This version 1.0
+	  message set could be used for connection checking of remote processors.
+
+	  RPMON_QMI messages depend on QCOM_QMI_HELPERS and should be updated
+	  together with QMI related modules.
+
 endmenu
diff --git a/drivers/rpmon/Makefile b/drivers/rpmon/Makefile
index b0f0ec4ecc30..25f468a73a20 100644
--- a/drivers/rpmon/Makefile
+++ b/drivers/rpmon/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_RPMON) += rpmon.o
+obj-$(CONFIG_RPMON_QMI_MSG_V1)	+= rpmon_qmi_msg_v1.o
diff --git a/drivers/rpmon/rpmon_qmi.h b/drivers/rpmon/rpmon_qmi.h
new file mode 100644
index 000000000000..191405140a5c
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <wenhu.wang@vivo.com>
+ * All rights reserved.
+ */
+
+#ifndef RPMON_QMI_INTERFACE_H
+#define RPMON_QMI_INTERFACE_H
+
+#define RP_NAME_LEN 255
+#define RPQMI_BUF_SIZE	4096
+
+enum rpmon_exec_result {
+	RPMON_EXEC_SUCCESS = 0,
+	RPMON_EXEC_FAILURE = 1,
+};
+
+struct rpmon_register_req {
+	uint8_t name_valid;
+	char name[RP_NAME_LEN + 1];
+	uint8_t timeout_valid;
+	u32 timeout;
+};
+
+struct rpmon_conn_indication {
+	char placeholder;
+};
+
+struct rpmon_conn_check_resp {
+	uint8_t result_valid;
+	struct qmi_response_type_v01 result;
+};
+
+struct rpmon_response {
+	struct qmi_response_type_v01 resp;
+};
+
+struct rpmon_qmi_device {
+	struct list_head	list;
+	struct sockaddr_qrtr	addr;
+	u32			timeout;
+	u32			flag;
+	struct ratelimit_state	ratelimit;
+
+	atomic_t		checks;
+	atomic_t		reports;
+
+	struct rpmon_info *info;
+	struct rpmon_qmi  *rqmi;
+};
+
+struct rpmon_qmi {
+	struct rpmon_info		*info;
+	struct qmi_handle		qmi;
+	struct qmi_service		*svc;
+	struct qmi_ops			*ops;
+	struct qmi_msg_handler		*handlers;
+	int (*sendmsg)(const struct rpmon_qmi_device *rdev,
+			const void *data,
+			u32 len);
+};
+
+/* rpqmi message types currently supported. */
+enum rpmon_qmi_msg_type {
+	RPQMI_MSG_REGISTER = 0,
+	RPQMI_MSG_CONNCHK_RESP,
+	RPQMI_MSG_MAX,
+};
+
+int rpmon_qmi_handle_init(struct rpmon_qmi *rqmi,
+		void (*cb)(enum rpmon_qmi_msg_type type,
+			struct sockaddr_qrtr *sq,
+			const void *msg));
+
+#endif /* RPMON_QMI_INTERFACE_H */
diff --git a/drivers/rpmon/rpmon_qmi_msg_v1.c b/drivers/rpmon/rpmon_qmi_msg_v1.c
new file mode 100644
index 000000000000..7e786d0c245a
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <wenhu.wang@vivo.com>
+ * All rights reserved.
+ *
+ * RPMON: An implementation of remote processor monitor framework
+ * for modern SoCs that typically have heterogeneous remote processor
+ * devices in asymmetric multiprocessing configurations. It is
+ * implemented with chardev and sysfs class, which act as interfaces
+ * to communicate with user level. It supports different communication
+ * interfaces added modularly to communicate with remote processors.
+ * Currently QMI implementation, named RPMON_QMI, is available.
+ *
+ * RPMON could be used to detect the stabilities of remote processors,
+ * collect any kinds of information you are interested in, take
+ * actions like connection status check, and so on. Enhancements
+ * can be made upon current implementation.
+ *
+ * RPMON_QMI_MSG_V1 is specifically implemented as a set of messages
+ * for RPMON_QMI to support connection checking of remote processors.
+ */
+
+#include <linux/soc/qcom/qmi.h>
+#include <linux/rpmon.h>
+#include "rpmon_qmi.h"
+
+#define RPMON_SVC_ID_V01	0x3c
+#define RPMON_SVC_VER_V01	0x01
+#define RPMON_SVC_INS_V01	0x00
+
+#define RPMON_CONN_REQ_MSG_ID_VO1		0x20
+#define RPMON_CONN_RESP_MSG_ID_VO1		0x20
+#define RPMON_CONN_IND_MSG_ID_V01		0x21
+#define RPMON_EXEC_COMP_REQ_MSG_ID_V01		0x22
+#define RPMON_EXEC_COMP_RESP_MSG_ID_V01		0x22
+
+#define QMI_COMMON_TLV_TYPE_1ST		(QMI_COMMON_TLV_TYPE + 1)
+
+#define QMI_OPTIONAL_TLV_TYPE_START	0x10
+#define QMI_OPTIONAL_TLV_TYPE_1ST	QMI_OPTIONAL_TLV_TYPE_START
+#define QMI_OPTIONAL_TLV_TYPE_2ND	(QMI_OPTIONAL_TLV_TYPE_START + 0x01)
+#define QMI_OPTIONAL_TLV_TYPE_END	QMI_OPTIONAL_TLV_TYPE_2ND
+
+#define TLV_TYPE_REG_REQ_NAME		QMI_OPTIONAL_TLV_TYPE_1ST
+#define TLV_TYPE_REG_REQ_TIMEOUT	QMI_OPTIONAL_TLV_TYPE_2ND
+
+#define TLV_TYPE_CONN_CHK_RESP_RESULT	QMI_OPTIONAL_TLV_TYPE_1ST
+
+#define QMI_TLV_LEN_SIZE sizeof(u16)
+#define QMI_TLV_TYPE_SIZE sizeof(u8)
+#define QMI_TLV_TL_SIZE (QMI_TLV_LEN_SIZE + QMI_TLV_TYPE_SIZE)
+
+static struct qmi_elem_info register_req_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct rpmon_register_req,
+					   name_valid),
+	},
+	{
+		.data_type      = QMI_STRING,
+		.elem_len       = RP_NAME_LEN,
+		.elem_size      = sizeof(char),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = TLV_TYPE_REG_REQ_NAME,
+		.offset         = offsetof(struct rpmon_register_req,
+					   name),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = TLV_TYPE_REG_REQ_TIMEOUT,
+		.offset         = offsetof(struct rpmon_register_req,
+					   timeout_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = TLV_TYPE_REG_REQ_TIMEOUT,
+		.offset         = offsetof(struct rpmon_register_req,
+					   timeout),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type     = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+static struct qmi_elem_info conn_check_resp_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = TLV_TYPE_CONN_CHK_RESP_RESULT,
+		.offset         = offsetof(struct rpmon_conn_check_resp,
+					   result_valid),
+	},
+	{
+		.data_type      = QMI_SIGNED_4_BYTE_ENUM,
+		.elem_len       = 1,
+		.elem_size      = sizeof(enum rpmon_exec_result),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = TLV_TYPE_CONN_CHK_RESP_RESULT,
+		.offset         = offsetof(struct rpmon_conn_check_resp,
+					   result),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type     = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+static struct qmi_elem_info conn_indication_v01_ei[] = {
+	{
+		.data_type      = QMI_EOTI,
+		.array_type     = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+static struct qmi_elem_info response_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.array_type     = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE_1ST,
+		.offset         = offsetof(struct rpmon_response, resp),
+		.ei_array       = qmi_response_type_v01_ei,
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type     = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+static void (*msg_callback)(enum rpmon_qmi_msg_type type,
+			    struct sockaddr_qrtr *sq,
+			    const void *msg);
+
+static int rpmon_qmi_sendmsg_v1(const struct rpmon_qmi_device *rdev,
+				const void *data,
+				u32 len)
+{
+	int ret;
+	struct sockaddr_qrtr sq;
+
+	memcpy(&sq, &rdev->addr, sizeof(sq));
+
+	ret = qmi_send_indication(&rdev->rqmi->qmi, &sq,
+				  RPMON_CONN_IND_MSG_ID_V01,
+				  QMI_TLV_TL_SIZE,
+				  conn_indication_v01_ei, NULL);
+	if (ret < 0)
+		pr_err("Error %d send indication failed", ret);
+
+	return ret;
+}
+
+static void rpmon_qmi_recv_register_req_v1(struct qmi_handle *qmi,
+			struct sockaddr_qrtr *sq,
+			struct qmi_txn *txn,
+			const void *msg)
+{
+	struct rpmon_response resp;
+	int ret;
+
+	resp.resp.result = QMI_RESULT_SUCCESS_V01;
+	resp.resp.error = QMI_ERR_NONE_V01;
+	ret = qmi_send_response(qmi, sq, txn,
+				RPMON_CONN_RESP_MSG_ID_VO1,
+				sizeof(resp.resp) + QMI_TLV_TL_SIZE * 2,
+				response_v01_ei,
+				&resp.resp);
+	if (ret < 0)
+		pr_err("Error %d send response failed", ret);
+
+	if (msg_callback)
+		msg_callback(RPQMI_MSG_REGISTER, sq, msg);
+}
+
+void rpmon_qmi_recv_conn_check_resp_v1(struct qmi_handle *qmi,
+	struct sockaddr_qrtr *sq,
+	struct qmi_txn *txn,
+	const void *msg)
+{
+	struct rpmon_response resp;
+	int ret;
+
+	resp.resp.result = QMI_RESULT_SUCCESS_V01;
+	resp.resp.error = QMI_ERR_NONE_V01;
+	ret = qmi_send_response(qmi, sq, txn,
+				RPMON_EXEC_COMP_REQ_MSG_ID_V01,
+				sizeof(resp.resp) + QMI_TLV_TL_SIZE * 2,
+				response_v01_ei,
+				&resp.resp);
+	if (ret < 0)
+		pr_err("Error %d send response failed", ret);
+
+	if (msg_callback)
+		msg_callback(RPQMI_MSG_CONNCHK_RESP, sq, msg);
+}
+
+static struct qmi_msg_handler rpmon_qmi_msg_handlers_v01[] = {
+	{
+		.type = QMI_REQUEST,
+		.msg_id = RPMON_CONN_REQ_MSG_ID_VO1,
+		.ei = register_req_v01_ei,
+		.decoded_size = sizeof(struct rpmon_register_req),
+		.fn = rpmon_qmi_recv_register_req_v1,
+	},
+	{
+		.type = QMI_REQUEST,
+		.msg_id = RPMON_EXEC_COMP_REQ_MSG_ID_V01,
+		.ei = conn_check_resp_v01_ei,
+		.decoded_size = sizeof(struct rpmon_conn_check_resp),
+		.fn = rpmon_qmi_recv_conn_check_resp_v1,
+	},
+};
+
+static struct qmi_service rpmon_qmi_svc = {
+	.service = RPMON_SVC_ID_V01,
+	.version = RPMON_SVC_VER_V01,
+	.instance = RPMON_SVC_INS_V01,
+};
+
+int rpmon_qmi_handle_init(struct rpmon_qmi *rqmi,
+		void (*cb)(enum rpmon_qmi_msg_type type,
+			   struct sockaddr_qrtr *sq,
+			   const void *msg))
+{
+	rqmi->svc = &rpmon_qmi_svc;
+	rqmi->handlers = rpmon_qmi_msg_handlers_v01;
+	rqmi->sendmsg = rpmon_qmi_sendmsg_v1;
+
+	if (cb)
+		msg_callback = cb;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(rpmon_qmi_handle_init);
+
+MODULE_AUTHOR("Wang Wenhu <wenhu.wang@vivo.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3,3/3] driver: rpmon: add rpmon_qmi driver
  2020-04-14  3:59 ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Wang Wenhu
  2020-04-14  3:59   ` [PATCH v3,1/3] driver: " Wang Wenhu
  2020-04-14  3:59   ` [PATCH v3,2/3] driver: rpmon: qmi message version 01 Wang Wenhu
@ 2020-04-14  3:59   ` Wang Wenhu
  2020-04-14 22:58   ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Bjorn Andersson
  3 siblings, 0 replies; 7+ messages in thread
From: Wang Wenhu @ 2020-04-14  3:59 UTC (permalink / raw)
  To: gregkh, linux-kernel
  Cc: rdunlap, kernel, agross, bjorn.andersson, ohad, linux-remoteproc,
	linux-arm-msm, Wang Wenhu

Implements a kind of communication routine for RPMON to communicate
with remote processors through QMI infrastructure. RPMON_QMI itself
is designed as a modular framework that would introduce different
kind of message sets binding to different services.

RPMON_QMI creates a device of rpmon_device type for each remote
processor endpoint. All the endpoint devices share an unique set
of QMI suite.

Cc: Andy Gross <agross@kernel.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Ohad Ben-Cohen <ohad@wizery.com>
Cc: linux-remoteproc@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Signed-off-by: Wang Wenhu <wenhu.wang@vivo.com>
---
Changes since v1:
 - Addressed review comments from Randy
Changes since v2:
 - Added Cc list
 - Use ARRAY_SIZE instead of multiple sizeof calculation
---
 drivers/rpmon/Kconfig     |  15 ++
 drivers/rpmon/Makefile    |   1 +
 drivers/rpmon/rpmon_qmi.c | 431 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 447 insertions(+)
 create mode 100644 drivers/rpmon/rpmon_qmi.c

diff --git a/drivers/rpmon/Kconfig b/drivers/rpmon/Kconfig
index 0b80236ad186..fc44d3e803c1 100644
--- a/drivers/rpmon/Kconfig
+++ b/drivers/rpmon/Kconfig
@@ -23,6 +23,21 @@ config RPMON
 	  Currently RPMON_QMI is available which uses QMI infrastructures
 	  on Qualcomm SoC Platforms.
 
+config RPMON_QMI
+	tristate "RPMON QMI Driver Engine"
+	select RPMON_QMI_MSG_V1
+	depends on RPMON
+	depends on QCOM_QMI_HELPERS
+	help
+	  RPMON_QMI is used by RPMON to communicate with remote processors
+	  with QMI APIs if enabled. RPMON_QMI itself is designed as a modular
+	  framework that would introduce different kinds of message sets
+	  which may be updated for versions.
+
+	  RPMON_QMI creates a device of rpmon_device type for each remote
+	  processor endpoint. All the endpoint devices shares an unique set
+	  of QMI suite.
+
 config RPMON_QMI_MSG_V1
 	tristate "RPMON QMI Message Version 1.0"
 	depends on RPMON
diff --git a/drivers/rpmon/Makefile b/drivers/rpmon/Makefile
index 25f468a73a20..76d9525339d9 100644
--- a/drivers/rpmon/Makefile
+++ b/drivers/rpmon/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_RPMON) += rpmon.o
+obj-$(CONFIG_RPMON_QMI) += rpmon_qmi.o
 obj-$(CONFIG_RPMON_QMI_MSG_V1)	+= rpmon_qmi_msg_v1.o
diff --git a/drivers/rpmon/rpmon_qmi.c b/drivers/rpmon/rpmon_qmi.c
new file mode 100644
index 000000000000..3c33a86284d5
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <wenhu.wang@vivo.com>
+ * All rights reserved.
+ *
+ * RPMON: An implementation of remote processor monitor framework
+ * for modern SoCs that typically have heterogeneous remote processor
+ * devices in asymmetric multiprocessing configurations. It is
+ * implemented with chardev and sysfs class, which act as interfaces
+ * to communicate with user level. It supports different communication
+ * interfaces added modularly to communicate with remote processors.
+ *
+ * RPMON_QMI: Implements a kind of communication routine for RPMON
+ * to communicate with remote processors through QMI infrastructure.
+ * At least one set of RPMON_QMI_MSG should be available and RPMON_QMI
+ * initiates with the message set(s) to provide certain servicei(s)
+ * like stability checking of remote processors. Currently a set of
+ * messages, implemented by RPMON_QMI_MSG_V1 is available.
+ */
+
+#include <linux/module.h>
+#include <linux/rpmon.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/of_platform.h>
+#include <linux/nospec.h>
+#include "rpmon_qmi.h"
+
+#define DRIVER_NAME "rpmon_qmi_drv"
+
+/* Remote processor registered. */
+#define RP_REGISTERED		0x0001
+
+/* work struct for message processing. */
+struct recv_work {
+	struct work_struct work;
+	struct sockaddr_qrtr sq;
+	void *msg;
+};
+
+/* Delayed work to take a reset action when a failure is detected. */
+struct exec_cb_work {
+	struct delayed_work		dwk;
+	struct rpmon_qmi_device *rdev;
+	u32			checks;
+};
+
+struct rpmon_qmi_exec_fn {
+	int (*exec_call)(struct rpmon_qmi_device *rdev);
+};
+
+struct rpmon_qmi_cb_fn {
+	void (*callback)(struct work_struct *work);
+	u32 msg_len;
+};
+
+static DEFINE_MUTEX(rdev_list_lock);
+static LIST_HEAD(rdev_list);
+static struct rpmon_qmi *rpqmi;
+static struct workqueue_struct *rpqmi_wq;
+
+static void rpmon_qmi_register_req_cb(struct work_struct *work)
+{
+	struct rpmon_register_req *req;
+	struct rpmon_qmi_device *rdev;
+	struct recv_work *rwk = container_of(work, struct recv_work, work);
+
+	req = (struct rpmon_register_req *)rwk->msg;
+
+	mutex_lock(&rdev_list_lock);
+	list_for_each_entry(rdev, &rdev_list, list) {
+		if (strncmp(rdev->info->name, req->name, RP_NAME_LEN))
+			continue;
+
+		rdev->flag |= RP_REGISTERED;
+		memcpy(&rdev->addr, &rwk->sq, sizeof(rwk->sq));
+		if (req->timeout_valid)
+			rdev->timeout = req->timeout;
+		else
+			rdev->timeout = 5000;
+		rpmon_event_notify(rdev->info, RPMON_EVENT_REGISTER);
+		break;
+	}
+	mutex_unlock(&rdev_list_lock);
+
+	kfree(rwk->msg);
+	kfree(rwk);
+}
+
+void rpmon_qmi_conn_check_resp_cb(struct work_struct *work)
+{
+	struct rpmon_conn_check_resp *cc_resp;
+	struct rpmon_qmi_device *rdev;
+	struct sockaddr_qrtr *addr;
+	struct recv_work *rwk =
+			container_of(work, struct recv_work, work);
+
+	cc_resp = (struct rpmon_conn_check_resp *)rwk->msg;
+	mutex_lock(&rdev_list_lock);
+	list_for_each_entry(rdev, &rdev_list, list) {
+		addr = &rdev->addr;
+		if (addr->sq_node != rwk->sq.sq_node ||
+		    addr->sq_port != rwk->sq.sq_port)
+			continue;
+
+		if (!cc_resp->result.error)
+			atomic_inc(&rdev->reports);
+		break;
+	}
+	mutex_unlock(&rdev_list_lock);
+
+	kfree(rwk->msg);
+	kfree(rwk);
+}
+
+/**
+ * rpmon_qmi_exec_cb_worker - callback worker for execution
+ * @work: work to been done
+ *
+ * Called as worker handler by the single worker thread of rpmon_wq.
+ * The worker is scheduled after timeout ms duration since the execution.
+ */
+static void rpmon_qmi_exec_cb_worker(struct work_struct *work)
+{
+	struct delayed_work *dwk = to_delayed_work(work);
+	struct exec_cb_work *ewk =
+			container_of(dwk, struct exec_cb_work, dwk);
+	struct rpmon_qmi_device *rdev = ewk->rdev;
+
+	mutex_lock(&rdev_list_lock);
+	if (ewk->checks <= atomic_read(&rdev->reports)) {
+		pr_debug("%s health check success", rdev->info->name);
+		goto out;
+	}
+
+	pr_err("subsystem %s failed to respond in time", rdev->info->name);
+
+	rpmon_event_notify(rdev->info, RPMON_EVENT_CHKCONN_FAIL);
+
+out:
+	mutex_unlock(&rdev_list_lock);
+	kfree(ewk);
+}
+
+static struct rpmon_qmi_cb_fn rpmon_qmi_event_callbacks[] = {
+	{
+		.callback = rpmon_qmi_register_req_cb,
+		.msg_len = sizeof(struct rpmon_register_req),
+	},
+	{
+		.callback = rpmon_qmi_conn_check_resp_cb,
+		.msg_len = sizeof(struct rpmon_conn_check_resp),
+	},
+};
+
+/**
+ * rpmon_qmi_conn_check - send indication, initiate and queue callback work
+ * @rdev: device interface of specific remote processor to be checked
+ */
+static int rpmon_qmi_conn_check(struct rpmon_qmi_device *rdev)
+{
+	struct exec_cb_work *ewk;
+
+	mutex_lock(&rdev_list_lock);
+	if (!(rdev->flag & RP_REGISTERED)) {
+		pr_err("%s has not registered", rdev->info->name);
+		return -ENONET;
+	}
+
+	if (!__ratelimit(&rdev->ratelimit)) {
+		pr_err("%s rate-limited", rdev->info->name);
+		return 0;
+	}
+	mutex_unlock(&rdev_list_lock);
+
+	rdev->rqmi->sendmsg(rdev, NULL, 0);
+
+	ewk = kzalloc(sizeof(*ewk), GFP_KERNEL);
+	if (!ewk)
+		return -ENOMEM;
+
+	ewk->rdev = rdev;
+	ewk->checks = atomic_inc_return(&rdev->checks);
+	INIT_DELAYED_WORK(&ewk->dwk, rpmon_qmi_exec_cb_worker);
+	queue_delayed_work(rpqmi_wq,
+			   &ewk->dwk, msecs_to_jiffies(rdev->timeout));
+
+	return 0;
+}
+
+static struct rpmon_qmi_exec_fn rpmon_qmi_exec_calls[] = {
+	{.exec_call = rpmon_qmi_conn_check},
+};
+
+static int rpmon_qmi_monitor(struct rpmon_info *info, u32 event)
+{
+	struct rpmon_qmi_device *rdev = (struct rpmon_qmi_device *)info->priv;
+	int i, idx;
+
+	for (i = 0; i < RPMON_EXEC_MAX; i++) {
+		if (event & RPMON_ACTION(i)) {
+			if (i < ARRAY_SIZE(rpmon_qmi_exec_calls)) {
+				idx = array_index_nospec(i, ARRAY_SIZE(rpmon_qmi_exec_calls));
+				if (rpmon_qmi_exec_calls[idx].exec_call)
+					return rpmon_qmi_exec_calls[idx].exec_call(rdev);
+				else
+					return -ENOTSUPP;
+			} else
+				return -ENOPARAM;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int rpmon_qmi_drv_probe(struct platform_device *pdev)
+{
+	struct rpmon_info *info = pdev->dev.platform_data;
+	struct rpmon_qmi_device *rdev;
+	struct device_node *node = pdev->dev.of_node;
+	const char *name;
+	int ret = -ENODEV;
+
+	if (node) {
+		/* Allocate info for one device */
+		info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+		if (!info) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		if (!of_property_read_string(node, "linux,subsys", &name))
+			info->name = devm_kstrdup(&pdev->dev, name, GFP_KERNEL);
+		else
+			info->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+						       "%pOFn", node);
+		info->version = "devicetree";
+	}
+
+	if (!info || !info->name || !info->version) {
+		dev_dbg(&pdev->dev, "%s: err_info\n", __func__);
+		return ret;
+	}
+
+	/* Allocate device for qmi specific reference */
+	rdev = devm_kzalloc(&pdev->dev, sizeof(*rdev), GFP_KERNEL);
+	if (!rdev) {
+		ret = -ENOMEM;
+		goto err_info_free;
+	}
+
+	rdev->rqmi = rpqmi;
+	rdev->info = info;
+	info->priv = rdev;
+	info->monitor = rpmon_qmi_monitor;
+	platform_set_drvdata(pdev, rdev);
+
+	ret = rpmon_register_device(&pdev->dev, info);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register rpmon_qmi_device\n");
+		goto err_rdev_free;
+	}
+
+	mutex_lock(&rdev_list_lock);
+	list_add_tail(&rdev->list, &rdev_list);
+	mutex_unlock(&rdev_list_lock);
+
+	return ret;
+
+err_rdev_free:
+	devm_kfree(&pdev->dev, rdev);
+err_info_free:
+	devm_kfree(&pdev->dev, info);
+out:
+	return ret;
+}
+
+static int rpmon_qmi_drv_remove(struct platform_device *pdev)
+{
+	struct rpmon_qmi_device *rdev = platform_get_drvdata(pdev);
+
+	rpmon_unregister_device(rdev->info);
+
+	return 0;
+}
+
+static void rpmon_qmi_msg_callback(enum rpmon_qmi_msg_type type,
+			struct sockaddr_qrtr *sq,
+			const void *msg)
+{
+	struct recv_work *rwk;
+
+	if (type >= ARRAY_SIZE(rpmon_qmi_event_callbacks)) {
+		pr_err("Error non-supported message type.\n");
+		return;
+	}
+
+	if (rpmon_qmi_event_callbacks[type].callback) {
+		rwk = kzalloc(sizeof(*rwk), GFP_KERNEL);
+		if (!rwk) {
+			pr_err("Error to alloc recv_work");
+			return;
+		}
+
+		INIT_WORK(&rwk->work, rpmon_qmi_event_callbacks[type].callback);
+		memcpy(&rwk->sq, sq, sizeof(*sq));
+
+		rwk->msg = kzalloc(rpmon_qmi_event_callbacks[type].msg_len,
+				   GFP_KERNEL);
+		if (!rwk->msg) {
+			pr_err("Error to alloc message of recv_work");
+			kfree(rwk);
+			return;
+		}
+
+		memcpy(rwk->msg, msg, rpmon_qmi_event_callbacks[type].msg_len);
+		queue_work(rpqmi_wq, &rwk->work);
+	}
+}
+
+static const struct of_device_id rpmon_of_qmi_match[] = {
+	{ .compatible = "rpmon-qmi" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver rpmon_qmi_drv = {
+	.probe = rpmon_qmi_drv_probe,
+	.remove = rpmon_qmi_drv_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rpmon_of_qmi_match),
+	},
+};
+
+static int __init rpmon_qmi_drv_init(void)
+{
+	int ret;
+
+	rpqmi_wq = create_singlethread_workqueue("rpmon_qmi_wq");
+	if (!rpqmi_wq) {
+		pr_err("Error creating workqueue\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	rpqmi = kzalloc(sizeof(*rpqmi), GFP_KERNEL);
+	if (!rpqmi) {
+		ret = -ENOMEM;
+		goto err_wq_free;
+	}
+
+	ret = rpmon_qmi_handle_init(rpqmi, rpmon_qmi_msg_callback);
+	if (ret)
+		goto err_rpqmi_free;
+
+	ret = qmi_handle_init(&rpqmi->qmi,
+			      RPQMI_BUF_SIZE, NULL, rpqmi->handlers);
+	if (ret < 0) {
+		pr_err("Error init qmi handle, %d", ret);
+		goto err_rpqmi_free;
+	}
+
+	ret = qmi_add_server(&rpqmi->qmi,
+			     rpqmi->svc->service,
+			     rpqmi->svc->version,
+			     rpqmi->svc->instance);
+	if (ret < 0) {
+		pr_err("Error add qmi server, %d", ret);
+		goto err_rpqmi_free;
+	}
+	mutex_init(&rdev_list_lock);
+
+	return platform_driver_register(&rpmon_qmi_drv);
+
+err_rpqmi_free:
+	kfree(rpqmi);
+err_wq_free:
+	destroy_workqueue(rpqmi_wq);
+out:
+	return ret;
+}
+late_initcall_sync(rpmon_qmi_drv_init);
+
+static void rpmon_qmi_del_server(void)
+{
+	struct qrtr_ctrl_pkt pkt;
+	struct sockaddr_qrtr sq;
+	struct msghdr msg = { };
+	struct kvec iv = { &pkt, sizeof(pkt) };
+	struct qmi_service *svc = rpqmi->svc;
+	struct qmi_handle *qmi = &rpqmi->qmi;
+	int ret;
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.cmd = cpu_to_le32(QRTR_TYPE_DEL_SERVER);
+	pkt.server.service = cpu_to_le32(svc->service);
+	pkt.server.instance = cpu_to_le32(svc->version | svc->instance << 8);
+	pkt.server.node = cpu_to_le32(qmi->sq.sq_node);
+	pkt.server.port = cpu_to_le32(qmi->sq.sq_port);
+
+	sq.sq_family = qmi->sq.sq_family;
+	sq.sq_node = qmi->sq.sq_node;
+	sq.sq_port = QRTR_PORT_CTRL;
+
+	msg.msg_name = &sq;
+	msg.msg_namelen = sizeof(sq);
+
+	mutex_lock(&qmi->sock_lock);
+	if (qmi->sock) {
+		ret = kernel_sendmsg(qmi->sock, &msg, &iv, 1, sizeof(pkt));
+		if (ret < 0)
+			pr_err("send service delete message failed: %d\n", ret);
+	}
+	mutex_unlock(&qmi->sock_lock);
+}
+
+static void __exit rpmon_qmi_drv_exit(void)
+{
+	rpmon_qmi_del_server();
+
+	qmi_handle_release(&rpqmi->qmi);
+
+	platform_driver_unregister(&rpmon_qmi_drv);
+}
+module_exit(rpmon_qmi_drv_exit);
+
+MODULE_AUTHOR("Wang Wenhu <wenhu.wang@vivo.com>");
+MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor
  2020-04-14  3:59 ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Wang Wenhu
                     ` (2 preceding siblings ...)
  2020-04-14  3:59   ` [PATCH v3,3/3] driver: rpmon: add rpmon_qmi driver Wang Wenhu
@ 2020-04-14 22:58   ` Bjorn Andersson
  2020-04-15 15:16     ` Wang Wenhu
  3 siblings, 1 reply; 7+ messages in thread
From: Bjorn Andersson @ 2020-04-14 22:58 UTC (permalink / raw)
  To: Wang Wenhu
  Cc: gregkh, linux-kernel, rdunlap, kernel, agross, ohad,
	linux-remoteproc, linux-arm-msm

On Mon 13 Apr 20:59 PDT 2020, Wang Wenhu wrote:

> RPMON is a driver framework. It supports remote processor monitor
> from user level. The basic components are a character device
> with sysfs interfaces for user space communication and different
> kinds of message drivers introduced modularly, which are used to
> communicate with remote processors.
> 
> As for user space, one can get notifications of different events
> of remote processors, like their registrations, through standard
> file read operation of the file descriptors related to the exported
> character devices. Actions can also be taken into account via
> standard write operations to the devices. Besides, the sysfs class
> attributes could be accessed conveniently.
> 
> Message drivers act as engines to communicate with remote processors.
> Currently RPMON_QMI is available which uses QMI infrastructures
> on Qualcomm SoC Platforms.
> 
> RPMON_QMI implements a kind of communication routine for RPMON to
> communicate with remote processors through QMI infrastructure.
> RPMON_QMI itself is designed as a modular framework that would
> introduce different kind of message sets which are binding to
> different services.
> 
> RPMON_QMI creates a device of rpmon_device type for each remote
> processor endpoint. All the endpoint devices share an unique set
> of QMI suite.
> 
> RPMON_QMI_MSG_V01 implements a RPMON_QMI message set for connection check.
> RPMON_QMI defines its message types modularly. Each rpmon service
> binds to a message set and introduced as a module. This version 1.0
> message set could be used for connection checking of remote processors.
> 
> RPMON_QMI messages depend on QCOM_QMI_HELPERS and should be updated
> together with QMI related modules.
> 

Hi Wang,

What additional transports do you expect for this to be a framework and
not just a driver? Why not implement the rpmon client directly in
userspace?

Regards,
Bjorn

> Changes since v1:
>  - Addressed review comments from Randy
> Changes since v2:
>  - Added Cc list
>  - Commit log typo fixing
>  - Use the ARRAY_SIZE instead of calculations of multiple sizeof()
>  - Use micros for qmi message tly_type fields
> 
> Wang Wenhu (3):
>   driver: rpmon: new driver Remote Processor Monitor
>   driver: rpmon: qmi message version 01
>   driver: rpmon: add rpmon_qmi driver
> 
>  drivers/Kconfig                  |   2 +
>  drivers/Makefile                 |   1 +
>  drivers/rpmon/Kconfig            |  54 ++++
>  drivers/rpmon/Makefile           |   3 +
>  drivers/rpmon/rpmon.c            | 506 +++++++++++++++++++++++++++++++
>  drivers/rpmon/rpmon_qmi.c        | 431 ++++++++++++++++++++++++++
>  drivers/rpmon/rpmon_qmi.h        |  76 +++++
>  drivers/rpmon/rpmon_qmi_msg_v1.c | 258 ++++++++++++++++
>  include/linux/rpmon.h            |  68 +++++
>  9 files changed, 1399 insertions(+)
>  create mode 100644 drivers/rpmon/Kconfig
>  create mode 100644 drivers/rpmon/Makefile
>  create mode 100644 drivers/rpmon/rpmon.c
>  create mode 100644 drivers/rpmon/rpmon_qmi.c
>  create mode 100644 drivers/rpmon/rpmon_qmi.h
>  create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
>  create mode 100644 include/linux/rpmon.h
> 
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor
  2020-04-14 22:58   ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Bjorn Andersson
@ 2020-04-15 15:16     ` Wang Wenhu
  0 siblings, 0 replies; 7+ messages in thread
From: Wang Wenhu @ 2020-04-15 15:16 UTC (permalink / raw)
  To: bjorn.andersson
  Cc: agross, gregkh, kernel, linux-arm-msm, linux-kernel,
	linux-remoteproc, ohad, rdunlap, wenhu.wang

Hi Bjorn,
Mainly two aspects:
 - Different message drivers modularly added to support different platforms.
   Currently, RPMON_QMI is available, and further maybe mailbox or so.
 - Different events to be notified and different actions to be taken out.
   Currently, connection check action is available, and remote endpoint's
   registeration event would be notified.

I hope the Remote Porcessor Monitor would eventually do something more
and be used by more users and platforms, and more actions and events
would be added as enhancement. At the same time, it is better to support
different SoC platforms. So I wrote the codes in kernel.

Thanks,
Wenhu

>> Changes since v1:
>>  - Addressed review comments from Randy
>> Changes since v2:
>>  - Added Cc list
>>  - Commit log typo fixing
>>  - Use the ARRAY_SIZE instead of calculations of multiple sizeof()
>>  - Use micros for qmi message tly_type fields
>> 
>> Wang Wenhu (3):
>>   driver: rpmon: new driver Remote Processor Monitor
>>   driver: rpmon: qmi message version 01
>>   driver: rpmon: add rpmon_qmi driver
>> 
>>  drivers/Kconfig                  |   2 +
>>  drivers/Makefile                 |   1 +
>>  drivers/rpmon/Kconfig            |  54 ++++
>>  drivers/rpmon/Makefile           |   3 +
>>  drivers/rpmon/rpmon.c            | 506 +++++++++++++++++++++++++++++++
>>  drivers/rpmon/rpmon_qmi.c        | 431 ++++++++++++++++++++++++++
>>  drivers/rpmon/rpmon_qmi.h        |  76 +++++
>>  drivers/rpmon/rpmon_qmi_msg_v1.c | 258 ++++++++++++++++
>>  include/linux/rpmon.h            |  68 +++++
>>  9 files changed, 1399 insertions(+)
>>  create mode 100644 drivers/rpmon/Kconfig
>>  create mode 100644 drivers/rpmon/Makefile
>>  create mode 100644 drivers/rpmon/rpmon.c
>>  create mode 100644 drivers/rpmon/rpmon_qmi.c
>>  create mode 100644 drivers/rpmon/rpmon_qmi.h
>>  create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
>>  create mode 100644 include/linux/rpmon.h
>> 
>> -- 
>> 2.17.1
>> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3,1/3] driver: rpmon: new driver Remote Processor Monitor
  2020-04-14  3:59   ` [PATCH v3,1/3] driver: " Wang Wenhu
@ 2020-04-28 12:57     ` Greg KH
  0 siblings, 0 replies; 7+ messages in thread
From: Greg KH @ 2020-04-28 12:57 UTC (permalink / raw)
  To: Wang Wenhu
  Cc: linux-kernel, rdunlap, kernel, agross, bjorn.andersson, ohad,
	linux-remoteproc, linux-arm-msm

On Mon, Apr 13, 2020 at 08:59:47PM -0700, Wang Wenhu wrote:
> RPMON is a driver framework. It supports remote processor monitor
> from user level. The basic components are a character device
> with sysfs interfaces for user space communication and different
> kinds of message drivers introduced modularly, which are used to
> communicate with remote processors.
> 
> As for user space, one can get notifications of different events
> of remote processors, like their registrations, through standard
> file read operation of the file descriptors related to the exported
> character devices. Actions can also be taken into account via
> standard write operations to the devices. Besides, the sysfs class
> attributes could be accessed conveniently.
> 
> Message drivers act as engines to communicate with remote processors.
> Currently RPMON_QMI is available which uses QMI infrastructures
> on Qualcomm SoC Platforms.
> 
> Cc: Andy Gross <agross@kernel.org>
> Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
> Cc: Ohad Ben-Cohen <ohad@wizery.com>
> Cc: linux-remoteproc@vger.kernel.org
> Cc: linux-arm-msm@vger.kernel.org
> Signed-off-by: Wang Wenhu <wenhu.wang@vivo.com>
> ---
> Changes since v1:
>  - Addressed review comments from Randy
> Changes since v2:
>  - Log message typo
>  - Added Cc list
> ---
>  drivers/Kconfig        |   2 +
>  drivers/Makefile       |   1 +
>  drivers/rpmon/Kconfig  |  26 +++
>  drivers/rpmon/Makefile |   1 +
>  drivers/rpmon/rpmon.c  | 506 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/rpmon.h  |  68 ++++++
>  6 files changed, 604 insertions(+)
>  create mode 100644 drivers/rpmon/Kconfig
>  create mode 100644 drivers/rpmon/Makefile
>  create mode 100644 drivers/rpmon/rpmon.c
>  create mode 100644 include/linux/rpmon.h

You create a bunch of sysfs files, but you do not have any
Documentation/ABI/ updates showing what those files are for?  Please fix
that up.

> +config RPMON
> +	tristate "Remote Processor Monitor Core Framework"
> +	help
> +	  RPMON is a driver framework. It supports remote processor monitor
> +	  from user level. The basic components are a character device
> +	  with sysfs interfaces for user space communication and different
> +	  kinds of message drivers introduced modularly, which are used to
> +	  communicate with remote processors.
> +
> +	  As for user space, one can get notifications of different events
> +	  of remote processors, like their registrations, through standard
> +	  file read operation of the file descriptors related to the exported
> +	  character devices. Actions can also be taken into account via
> +	  standard write operations to the devices. Besides, the sysfs class
> +	  attributes could be accessed conveniently.

So you don't need the char dev node?  The sysfs files are sufficient?
Or do they both do different things?

How does the user/kernel api work for the char node?

> +#define RPMON_MAX_DEVICES	(1U << MINORBITS)

Why do you have a limit?

Why not just make it dynamic?

> +#define RPMON_NAME			"rpmon"
> +
> +static int rpmon_major;

Why do you need a whole major for this?  Why not use a misc device?

> +static struct cdev *rpmon_cdev;
> +static DEFINE_IDR(rpmon_idr);
> +static const struct file_operations rpmon_fops;
> +
> +/* Protect idr accesses */
> +static DEFINE_MUTEX(minor_lock);

Are you sure you need this?



> +
> +static ssize_t name_show(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	struct rpmon_device *rpmondev = dev_get_drvdata(dev);
> +	int ret;
> +
> +	mutex_lock(&rpmondev->info_lock);
> +	if (!rpmondev->info) {
> +		ret = -EINVAL;
> +		dev_err(dev, "the device has been unregistered\n");

How can that happen in your sysfs file?  Shouldn't the name be part of
the structure itself?  And what's wrong with the default name in struct
device?

> +static ssize_t rpmon_read(struct file *filep, char __user *buf,
> +			  size_t count, loff_t *ppos)
> +{
> +	struct rpmon_device *rpmondev = filep->private_data;
> +	DECLARE_WAITQUEUE(wait, current);
> +	ssize_t ret = 0;
> +	u32 event;
> +
> +	if (count != sizeof(u32))
> +		return -EINVAL;
> +
> +	add_wait_queue(&rpmondev->wait, &wait);
> +
> +	do {
> +		mutex_lock(&rpmondev->info_lock);
> +		if (!rpmondev->info) {
> +			ret = -EIO;
> +			mutex_unlock(&rpmondev->info_lock);
> +			break;
> +		}
> +		mutex_unlock(&rpmondev->info_lock);
> +
> +		set_current_state(TASK_INTERRUPTIBLE);
> +
> +		event = atomic_read(&rpmondev->event);
> +		if (event) {
> +			__set_current_state(TASK_RUNNING);
> +			if (copy_to_user(buf, &event, count))
> +				ret = -EFAULT;
> +			else {
> +				atomic_set(&rpmondev->event, 0);
> +				ret = count;
> +			}
> +			break;
> +		}
> +
> +		if (filep->f_flags & O_NONBLOCK) {
> +			ret = -EAGAIN;
> +			break;
> +		}
> +
> +		if (signal_pending(current)) {
> +			ret = -ERESTARTSYS;
> +			break;
> +		}
> +		schedule();
> +	} while (1);
> +
> +	__set_current_state(TASK_RUNNING);

Are you _sure_ that is the right way to do this???

> +	remove_wait_queue(&rpmondev->wait, &wait);
> +
> +	return ret;
> +}
> +
> +static ssize_t rpmon_write(struct file *filep, const char __user *buf,
> +			   size_t count, loff_t *ppos)
> +{
> +	struct rpmon_device *rpmondev = filep->private_data;
> +	ssize_t ret;
> +	u32 action;
> +
> +	if (count != sizeof(u32))
> +		return -EINVAL;

That's rude, how can you enforce userspace doing this?  What about short
writes?

> +
> +	if (copy_from_user(&action, buf, count))
> +		return -EFAULT;
> +
> +	mutex_lock(&rpmondev->info_lock);
> +	if (!rpmondev->info) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!rpmondev->info->monitor) {
> +		ret = -ENOTSUPP;
> +		goto out;
> +	}
> +
> +	if (rpmondev->info->monitor)
> +		ret = rpmondev->info->monitor(rpmondev->info, action);
> +out:
> +	mutex_unlock(&rpmondev->info_lock);
> +	return ret ? ret : sizeof(u32);
> +}
> +
> +static const struct file_operations rpmon_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= rpmon_open,
> +	.read		= rpmon_read,
> +	.write		= rpmon_write,
> +	.poll		= rpmon_poll,
> +	.release	= rpmon_release,
> +};
> +
> +static int rpmon_major_init(void)
> +{
> +	static const char name[] = RPMON_NAME;
> +	struct cdev *cdev = NULL;
> +	dev_t rpmon_dev = 0;
> +	int ret;
> +
> +	ret = alloc_chrdev_region(&rpmon_dev, 0, RPMON_MAX_DEVICES, name);
> +	if (ret)
> +		goto out;
> +
> +	ret = -ENOMEM;
> +	cdev = cdev_alloc();
> +	if (!cdev)
> +		goto out_unregister;
> +
> +	cdev->owner = THIS_MODULE;
> +	cdev->ops = &rpmon_fops;
> +	kobject_set_name(&cdev->kobj, "%s", name);

That doesn't do what you think it does :)

Just use a misc device please.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-04-28 12:57 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20200412112405.24116-1-wenhu.wang@vivo.com>
2020-04-14  3:59 ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Wang Wenhu
2020-04-14  3:59   ` [PATCH v3,1/3] driver: " Wang Wenhu
2020-04-28 12:57     ` Greg KH
2020-04-14  3:59   ` [PATCH v3,2/3] driver: rpmon: qmi message version 01 Wang Wenhu
2020-04-14  3:59   ` [PATCH v3,3/3] driver: rpmon: add rpmon_qmi driver Wang Wenhu
2020-04-14 22:58   ` [PATCH v3,0/3] drivers: rpmon: new driver Remote Processor Monitor Bjorn Andersson
2020-04-15 15:16     ` Wang Wenhu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).