RPMON is a driver framework. It supports remote processor monitor
from user level. The baisc 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 discriptors 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 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.
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.
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 | 505 +++++++++++++++++++++++++++++++
drivers/rpmon/rpmon_qmi.c | 434 ++++++++++++++++++++++++++
drivers/rpmon/rpmon_qmi.h | 77 +++++
drivers/rpmon/rpmon_qmi_msg_v1.c | 240 +++++++++++++++
include/linux/rpmon.h | 68 +++++
9 files changed, 1384 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
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.
Signed-off-by: Wang Wenhu <[email protected]>
---
drivers/rpmon/Kconfig | 13 ++
drivers/rpmon/Makefile | 1 +
drivers/rpmon/rpmon_qmi.h | 77 ++++++++++
drivers/rpmon/rpmon_qmi_msg_v1.c | 240 +++++++++++++++++++++++++++++++
4 files changed, 331 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 505d263e0867..4118d4fab433 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..f6e7cfc97a3f
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * All rights reserved.
+ */
+
+#ifndef RPMON_QMI_INTERFACE_H
+#define RPMON_QMI_INTERFACE_H
+
+#define RP_NAME_LEN 255
+#define RPQMI_BUF_SIZE 4096
+#define QMI_TLV_TL_SIZE 3
+
+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..10d38d8133b0
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * All rights reserved.
+ *
+ * RPMON An implementation of remote processor monitor freamwork
+ * for platforms that multi-processors exists. RPMON is implemented
+ * with chardev and sysfs class as interfaces to communicate with
+ * the user level. It supports different communication interfaces
+ * added modularly with remote processors. Currently QMI implementation
+ * is available, and this file implements the message type v1.
+ *
+ * RPMON could be used to detect the stabilities of remote processors,
+ * collect any kinds of information you are interested with, take
+ * actions like connection status check, and so on. Enhancements can
+ * be made upon current implementation.
+ */
+
+#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
+
+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 = 0x10,
+ .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 = 0x11,
+ .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 = 0x11,
+ .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 = 0x10,
+ .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 = 0x10,
+ .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 = 0x02,
+ .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 respons 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 respons 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");
+MODULE_LICENSE("GPL v2");
--
2.17.1
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.
Signed-off-by: Wang Wenhu <[email protected]>
---
drivers/rpmon/Kconfig | 15 ++
drivers/rpmon/Makefile | 1 +
drivers/rpmon/rpmon_qmi.c | 434 ++++++++++++++++++++++++++++++++++++++
3 files changed, 450 insertions(+)
create mode 100644 drivers/rpmon/rpmon_qmi.c
diff --git a/drivers/rpmon/Kconfig b/drivers/rpmon/Kconfig
index 4118d4fab433..e44d587db0d2 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..ae49510cbb83
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * All rights reserved.
+ *
+ * RPMON: An implementation of remote processor monitor freamwork
+ * for platforms that multi-processors exists. RPMON is implemented
+ * with chardev and sysfs class as interfaces to communicate with
+ * the user level. It supports different communication interfaces
+ * added modularly with remote processors. Currently QMI implementation
+ * is available.
+ *
+ * RPMON could be used to detect the stabilities of remote processors,
+ * collect any kinds of information you are interested with, take
+ * actions like connection status check, and so on. Enhancements can
+ * be made upon current implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/rpmon.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/of_platform.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 signle 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 -ENOANO;
+ }
+
+ 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 -ENOANO;
+
+ 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;
+
+ for (i = 0; i < RPMON_EXEC_MAX; i++) {
+ if (event & RPMON_ACTION(i)) {
+ if (i >= (sizeof(rpmon_qmi_exec_calls) /
+ sizeof(struct rpmon_qmi_exec_fn)))
+ return -ENOTSUPP;
+
+ if (rpmon_qmi_exec_calls[i].exec_call)
+ return rpmon_qmi_exec_calls[i].exec_call(rdev);
+ else
+ return -ENOTSUPP;
+ }
+ }
+
+ 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 >= (sizeof(rpmon_qmi_event_callbacks) /
+ sizeof(struct rpmon_qmi_cb_fn))) {
+ pr_err("Error none 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");
+MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
--
2.17.1
RPMON is a driver framework. It supports remote processor monitor
from user level. The baisc 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 discriptors 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.
Signed-off-by: Wang Wenhu <[email protected]>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/rpmon/Kconfig | 26 +++
drivers/rpmon/Makefile | 1 +
drivers/rpmon/rpmon.c | 505 +++++++++++++++++++++++++++++++++++++++++
include/linux/rpmon.h | 68 ++++++
6 files changed, 603 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..505d263e0867
--- /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 baisc 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 discriptors 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..65aab4de6733
--- /dev/null
+++ b/drivers/rpmon/rpmon.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * All rights reserved.
+ *
+ * RPMON: An implementation of remote processor monitor freamwork
+ * for platforms that multi-processors exists. RPMON is implemented
+ * with chardev and sysfs class as interfaces to communicate with
+ * the user level. It supports different communication interfaces
+ * added modularly with remote processors. Currently QMI implementation
+ * is available.
+ *
+ * RPMON could be used to detect the stabilities of remote processors,
+ * collect any kinds of information you are interested with, 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 un-supported 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: romon device capabilities
+ *
+ * returns 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");
+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..40983a3b5655
--- /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 <[email protected]>
+ * 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
Hi--
On 4/11/20 2:52 AM, Wang Wenhu wrote:
> RPMON is a driver framework. It supports remote processor monitor
> from user level. The baisc components are a character device
basic
> 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 discriptors related to the exported
descriptors
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/rpmon/Kconfig | 26 +++
> drivers/rpmon/Makefile | 1 +
> drivers/rpmon/rpmon.c | 505 +++++++++++++++++++++++++++++++++++++++++
> include/linux/rpmon.h | 68 ++++++
> 6 files changed, 603 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/rpmon/Kconfig b/drivers/rpmon/Kconfig
> new file mode 100644
> index 000000000000..505d263e0867
> --- /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 baisc components are a character device
basic
> + 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 discriptors related to the exported
descriptors
> + 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/rpmon.c b/drivers/rpmon/rpmon.c
> new file mode 100644
> index 000000000000..65aab4de6733
> --- /dev/null
> +++ b/drivers/rpmon/rpmon.c
> @@ -0,0 +1,505 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
> + * All rights reserved.
> + *
> + * RPMON: An implementation of remote processor monitor freamwork
framework
> + * for platforms that multi-processors exists. RPMON is implemented
confusing wording above ^^^^^^^^^^^^^^^^^
> + * with chardev and sysfs class as interfaces to communicate with
> + * the user level. It supports different communication interfaces
> + * added modularly with remote processors. Currently QMI implementation
> + * is available.
> + *
> + * RPMON could be used to detect the stabilities of remote processors,
> + * collect any kinds of information you are interested with, take
interested in,
> + * 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);
> +
[snip]
> +/**
> + * 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 un-supported rpmon event %d", event);
unsupported
> + 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);
[snip]
> +/**
> + * rpmon_register_device - register a new rpmon interface device
> + * @owner: module that creates the new device
> + * @parent: parent device
> + * @info: romon device capabilities
s/romon/rpmon/
> + *
> + * returns zero on success or a negative error code.
use kernel-doc notation:
* 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);
[snip]
> +module_exit(rpmon_exit);
> +
> +MODULE_AUTHOR("Wang Wenhu");
Please add email address in the MODULE_AUTHOR() string.
About 3/4 of all uses of MODULE_AUTHOR() do so.
> +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..40983a3b5655
> --- /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 <[email protected]>
> + * 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)
Unless you are very sure that 'x' above is never more than a simple
expression, you should put x in parentheses, like so:
> +#define RPMON_EVENT(x) (1 << (x))
> +#define RPMON_ACTION(x) (1 << (x))
so that there cannot be any operator precedence problems.
thanks.
--
~Randy
Hi--
On 4/11/20 2:53 AM, Wang Wenhu wrote:
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> drivers/rpmon/Kconfig | 13 ++
> drivers/rpmon/Makefile | 1 +
> drivers/rpmon/rpmon_qmi.h | 77 ++++++++++
> drivers/rpmon/rpmon_qmi_msg_v1.c | 240 +++++++++++++++++++++++++++++++
> 4 files changed, 331 insertions(+)
> create mode 100644 drivers/rpmon/rpmon_qmi.h
> create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
>
> diff --git a/drivers/rpmon/rpmon_qmi.h b/drivers/rpmon/rpmon_qmi.h
> new file mode 100644
> index 000000000000..f6e7cfc97a3f
> --- /dev/null
> +++ b/drivers/rpmon/rpmon_qmi.h
> @@ -0,0 +1,77 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
> + * All rights reserved.
> + */
> +
> +#ifndef RPMON_QMI_INTERFACE_H
> +#define RPMON_QMI_INTERFACE_H
> +
> +#define RP_NAME_LEN 255
> +#define RPQMI_BUF_SIZE 4096
> +#define QMI_TLV_TL_SIZE 3
> +
> +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;
> +};
Is this struct packed or padded?
Is it shared with/used by other drivers?
> +
> +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..10d38d8133b0
> --- /dev/null
> +++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
> @@ -0,0 +1,240 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
> + * All rights reserved.
> + *
> + * RPMON An implementation of remote processor monitor freamwork
typo: framework {in all 3 source files}
> + * for platforms that multi-processors exists. RPMON is implemented
but that sentence is confusing at best.
> + * with chardev and sysfs class as interfaces to communicate with
> + * the user level. It supports different communication interfaces
> + * added modularly with remote processors. Currently QMI implementation
> + * is available, and this file implements the message type v1.
> + *
> + * RPMON could be used to detect the stabilities of remote processors,
> + * collect any kinds of information you are interested with, take
interested in, take
> + * actions like connection status check, and so on. Enhancements can
> + * be made upon current implementation.
> + */
> +
> +#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
> +
> +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,
Can you use an enum or #define value for the tlv_type magic numbers?
(multiple places)
> + .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 = 0x10,
> + .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 = 0x11,
> + .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 = 0x11,
> + .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 = 0x10,
> + .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 = 0x10,
> + .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 = 0x02,
> + .offset = offsetof(
> + struct rpmon_response,
> + resp),
multiple lines not needed above.
> + .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 respons 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 respons failed", ret);
response
> +
> + 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");
Please include email address in the string above.
About 3/4 of all uses of MODULE_AUTHOR() do so.
> +MODULE_LICENSE("GPL v2");
>
thanks.
--
~Randy
Hi--
On 4/11/20 2:53 AM, Wang Wenhu wrote:
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> drivers/rpmon/Kconfig | 15 ++
> drivers/rpmon/Makefile | 1 +
> drivers/rpmon/rpmon_qmi.c | 434 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 450 insertions(+)
> create mode 100644 drivers/rpmon/rpmon_qmi.c
>
> diff --git a/drivers/rpmon/rpmon_qmi.c b/drivers/rpmon/rpmon_qmi.c
> new file mode 100644
> index 000000000000..ae49510cbb83
> --- /dev/null
> +++ b/drivers/rpmon/rpmon_qmi.c
> @@ -0,0 +1,434 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
> + * All rights reserved.
> + *
> + * RPMON: An implementation of remote processor monitor freamwork
framework
> + * for platforms that multi-processors exists. RPMON is implemented
confusing sentence above.
> + * with chardev and sysfs class as interfaces to communicate with
> + * the user level. It supports different communication interfaces
> + * added modularly with remote processors. Currently QMI implementation
> + * is available.
> + *
> + * RPMON could be used to detect the stabilities of remote processors,
> + * collect any kinds of information you are interested with, take
interested in, take
> + * actions like connection status check, and so on. Enhancements can
> + * be made upon current implementation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rpmon.h>
> +#include <linux/soc/qcom/qmi.h>
> +#include <linux/of_platform.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;
[snip]
> +/**
> + * rpmon_qmi_exec_cb_worker - callback worker for execution
> + * @work: work to been done
> + *
> + * Called as worker handler by the signle worker thread of rpmon_wq.
single
> + * 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 -ENOANO;
Why ENOANO?
> + }
> +
> + 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 -ENOANO;
ditto.
> + 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;
> +
> + for (i = 0; i < RPMON_EXEC_MAX; i++) {
> + if (event & RPMON_ACTION(i)) {
> + if (i >= (sizeof(rpmon_qmi_exec_calls) /
> + sizeof(struct rpmon_qmi_exec_fn)))
> + return -ENOTSUPP;
> +
I'm not totally up about speculative fetches, but is any of
<linux/nospec.h> needed here?
> + if (rpmon_qmi_exec_calls[i].exec_call)
> + return rpmon_qmi_exec_calls[i].exec_call(rdev);
> + else
> + return -ENOTSUPP;
> + }
> + }
> +
> + return -EINVAL;
> +}
[snip]
> +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 >= (sizeof(rpmon_qmi_event_callbacks) /
> + sizeof(struct rpmon_qmi_cb_fn))) {
> + pr_err("Error none supported message type.\n");
non-supported
> + 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);
> + }
> +}
[snip]
> +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");
Please add email address above.
> +MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_LICENSE("GPL v2");
>
thanks.
--
~Randy
Hi,
From: Randy Dunlap <[email protected]>
Date: 2020-04-12 02:08:09
To: Wang Wenhu <[email protected]>,[email protected],[email protected]
Subject: Re: [PATCH 1/3] driver: rpmon: new driver Remote Processor Monitor>Hi--
>
>On 4/11/20 2:52 AM, Wang Wenhu wrote:
>> RPMON is a driver framework. It supports remote processor monitor
>> from user level. The baisc components are a character device
>
> basic
Addressed in v2
>
>> 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 discriptors related to the exported
>
> descriptors
Addressed in v2
>
>> 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.
>>
>> Signed-off-by: Wang Wenhu <[email protected]>
>> ---
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/rpmon/Kconfig | 26 +++
>> drivers/rpmon/Makefile | 1 +
>> drivers/rpmon/rpmon.c | 505 +++++++++++++++++++++++++++++++++++++++++
>> include/linux/rpmon.h | 68 ++++++
>> 6 files changed, 603 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/rpmon/Kconfig b/drivers/rpmon/Kconfig
>> new file mode 100644
>> index 000000000000..505d263e0867
>> --- /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 baisc components are a character device
>
> basic
Addressed in v2
>
>> + 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 discriptors related to the exported
>
> descriptors
Addressed in v2
>
>> + 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/rpmon.c b/drivers/rpmon/rpmon.c
>> new file mode 100644
>> index 000000000000..65aab4de6733
>> --- /dev/null
>> +++ b/drivers/rpmon/rpmon.c
>> @@ -0,0 +1,505 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
>> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
>> + * All rights reserved.
>> + *
>> + * RPMON: An implementation of remote processor monitor freamwork
>
> framework
Addressed in v2
>
>> + * for platforms that multi-processors exists. RPMON is implemented
>
> confusing wording above ^^^^^^^^^^^^^^^^^
Addressed in v2
>
>> + * with chardev and sysfs class as interfaces to communicate with
>> + * the user level. It supports different communication interfaces
>> + * added modularly with remote processors. Currently QMI implementation
>> + * is available.
>> + *
>> + * RPMON could be used to detect the stabilities of remote processors,
>> + * collect any kinds of information you are interested with, take
>
> interested in,
Addressed in v2
>
>> + * 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);
>> +
>
>[snip]
>
>> +/**
>> + * 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 un-supported rpmon event %d", event);
>
> unsupported
Addressed in v2
>
>> + 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);
>
>[snip]
>
>> +/**
>> + * rpmon_register_device - register a new rpmon interface device
>> + * @owner: module that creates the new device
>> + * @parent: parent device
>> + * @info: romon device capabilities
>
> s/romon/rpmon/
Addressed in v2
>
>> + *
>> + * returns zero on success or a negative error code.
>
>use kernel-doc notation:
>
> * return: zero on success or a negative error code.
>
Addressed in v2
>> + */
>> +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);
>
>[snip]
>
>> +module_exit(rpmon_exit);
>> +
>> +MODULE_AUTHOR("Wang Wenhu");
>
>Please add email address in the MODULE_AUTHOR() string.
>About 3/4 of all uses of MODULE_AUTHOR() do so.
>
Addressed in v2
>> +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..40983a3b5655
>> --- /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 <[email protected]>
>> + * 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)
>
>Unless you are very sure that 'x' above is never more than a simple
>expression, you should put x in parentheses, like so:
>
Addressed in v2
>> +#define RPMON_EVENT(x) (1 << (x))
>> +#define RPMON_ACTION(x) (1 << (x))
>
>so that there cannot be any operator precedence problems.
>
>
>thanks.
>--
>~Randy
>
thanks,
Wenhu
Hi,
From: Randy Dunlap <[email protected]>
Date: 2020-04-12 03:07:11
To: Wang Wenhu <[email protected]>,[email protected],[email protected]
Subject: Re: [PATCH 2/3] driver: rpmon: qmi message version 01>Hi--
>
>On 4/11/20 2:53 AM, Wang Wenhu wrote:
>> 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.
>>
>> Signed-off-by: Wang Wenhu <[email protected]>
>> ---
>> drivers/rpmon/Kconfig | 13 ++
>> drivers/rpmon/Makefile | 1 +
>> drivers/rpmon/rpmon_qmi.h | 77 ++++++++++
>> drivers/rpmon/rpmon_qmi_msg_v1.c | 240 +++++++++++++++++++++++++++++++
>> 4 files changed, 331 insertions(+)
>> create mode 100644 drivers/rpmon/rpmon_qmi.h
>> create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
>>
>
>> diff --git a/drivers/rpmon/rpmon_qmi.h b/drivers/rpmon/rpmon_qmi.h
>> new file mode 100644
>> index 000000000000..f6e7cfc97a3f
>> --- /dev/null
>> +++ b/drivers/rpmon/rpmon_qmi.h
>> @@ -0,0 +1,77 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
>> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
>> + * All rights reserved.
>> + */
>> +
>> +#ifndef RPMON_QMI_INTERFACE_H
>> +#define RPMON_QMI_INTERFACE_H
>> +
>> +#define RP_NAME_LEN 255
>> +#define RPQMI_BUF_SIZE 4096
>> +#define QMI_TLV_TL_SIZE 3
>> +
>> +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;
>> +};
>
>Is this struct packed or padded?
>Is it shared with/used by other drivers?
>
It is passed and repacked by qmi interfaces and then sent out as QRTR packet,
non-shared with others. So it is safe to be left without any locking protection here.
>> +
>> +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..10d38d8133b0
>> --- /dev/null
>> +++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
>> @@ -0,0 +1,240 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
>> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
>> + * All rights reserved.
>> + *
>> + * RPMON An implementation of remote processor monitor freamwork
>
>typo: framework {in all 3 source files}
>
>> + * for platforms that multi-processors exists. RPMON is implemented
>
>but that sentence is confusing at best.
>
Both addressed in v2
>> + * with chardev and sysfs class as interfaces to communicate with
>> + * the user level. It supports different communication interfaces
>> + * added modularly with remote processors. Currently QMI implementation
>> + * is available, and this file implements the message type v1.
>> + *
>> + * RPMON could be used to detect the stabilities of remote processors,
>> + * collect any kinds of information you are interested with, take
>
> interested in, take
>
Addressed in v2
>> + * actions like connection status check, and so on. Enhancements can
>> + * be made upon current implementation.
>> + */
>> +
>> +#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
>> +
>> +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,
>
>Can you use an enum or #define value for the tlv_type magic numbers?
>(multiple places)
Definitely. But actually the definitions belong to QMI module, and I will
figure this out with another patch of #include <linux/soc/qcom/qmi.h> or so.
I will Cc you if not mind?
>
>> + .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 = 0x10,
>> + .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 = 0x11,
>> + .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 = 0x11,
>> + .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 = 0x10,
>> + .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 = 0x10,
>> + .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 = 0x02,
>> + .offset = offsetof(
>> + struct rpmon_response,
>> + resp),
>
>multiple lines not needed above.
Addressed in v2
>
>> + .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 respons 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 respons failed", ret);
>
> response
>
Addressed in v2
>> +
>> + 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");
>
>Please include email address in the string above.
>About 3/4 of all uses of MODULE_AUTHOR() do so.
>
Addressed in v2
>> +MODULE_LICENSE("GPL v2");
>>
>
>thanks.
>--
>~Randy
>
Thanks,
Wenhu
Hi,
From: Randy Dunlap <[email protected]>
Date: 2020-04-12 03:23:00
To: Wang Wenhu <[email protected]>,[email protected],[email protected]
Subject: Re: [PATCH 3/3] driver: rpmon: add rpmon_qmi driver>Hi--
>
>On 4/11/20 2:53 AM, Wang Wenhu wrote:
>> 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.
>>
>> Signed-off-by: Wang Wenhu <[email protected]>
>> ---
>> drivers/rpmon/Kconfig | 15 ++
>> drivers/rpmon/Makefile | 1 +
>> drivers/rpmon/rpmon_qmi.c | 434 ++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 450 insertions(+)
>> create mode 100644 drivers/rpmon/rpmon_qmi.c
>>
>
>> diff --git a/drivers/rpmon/rpmon_qmi.c b/drivers/rpmon/rpmon_qmi.c
>> new file mode 100644
>> index 000000000000..ae49510cbb83
>> --- /dev/null
>> +++ b/drivers/rpmon/rpmon_qmi.c
>> @@ -0,0 +1,434 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
>> + * Copyright (C) 2020 Wang Wenhu <[email protected]>
>> + * All rights reserved.
>> + *
>> + * RPMON: An implementation of remote processor monitor freamwork
>
> framework
>
Addressed in v2
>> + * for platforms that multi-processors exists. RPMON is implemented
>
> confusing sentence above.
>
Addressed in v2
>> + * with chardev and sysfs class as interfaces to communicate with
>> + * the user level. It supports different communication interfaces
>> + * added modularly with remote processors. Currently QMI implementation
>> + * is available.
>> + *
>> + * RPMON could be used to detect the stabilities of remote processors,
>> + * collect any kinds of information you are interested with, take
>
> interested in, take
>
Addressed in v2
>> + * actions like connection status check, and so on. Enhancements can
>> + * be made upon current implementation.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/rpmon.h>
>> +#include <linux/soc/qcom/qmi.h>
>> +#include <linux/of_platform.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;
>
>[snip]
>
>> +/**
>> + * rpmon_qmi_exec_cb_worker - callback worker for execution
>> + * @work: work to been done
>> + *
>> + * Called as worker handler by the signle worker thread of rpmon_wq.
>
> single
>
Addressed in v2
>> + * 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 -ENOANO;
>
>Why ENOANO?
>
My fault, should be ENONET cause the remote endpoint has not registered,
meaning "Machine is not on the network".
Addressed in v2
>> + }
>> +
>> + 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 -ENOANO;
>
>ditto.
My fault, should be ENOMEM;
Addressed in v2
>
>> + 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;
>> +
>> + for (i = 0; i < RPMON_EXEC_MAX; i++) {
>> + if (event & RPMON_ACTION(i)) {
>> + if (i >= (sizeof(rpmon_qmi_exec_calls) /
>> + sizeof(struct rpmon_qmi_exec_fn)))
>> + return -ENOTSUPP;
>> +
>
>I'm not totally up about speculative fetches, but is any of
><linux/nospec.h> needed here?
>
Surely, addressed in v2
>> + if (rpmon_qmi_exec_calls[i].exec_call)
>> + return rpmon_qmi_exec_calls[i].exec_call(rdev);
>> + else
>> + return -ENOTSUPP;
>> + }
>> + }
>> +
>> + return -EINVAL;
>> +}
>
>[snip]
>
>> +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 >= (sizeof(rpmon_qmi_event_callbacks) /
>> + sizeof(struct rpmon_qmi_cb_fn))) {
>> + pr_err("Error none supported message type.\n");
>
> non-supported
>
Addressed in v2
>> + 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);
>> + }
>> +}
>
>[snip]
>
>> +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");
>
>Please add email address above.
>
Addressed in v2
>> +MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_LICENSE("GPL v2");
>>
>
>thanks.
>--
>~Randy
>
Thanks,
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
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 | 77 +++++
drivers/rpmon/rpmon_qmi_msg_v1.c | 242 +++++++++++++++
include/linux/rpmon.h | 68 +++++
9 files changed, 1384 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
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.
Signed-off-by: Wang Wenhu <[email protected]>
---
Changes since v1:
- Addressed review comments from Randy
---
drivers/rpmon/Kconfig | 13 ++
drivers/rpmon/Makefile | 1 +
drivers/rpmon/rpmon_qmi.h | 77 ++++++++++
drivers/rpmon/rpmon_qmi_msg_v1.c | 242 +++++++++++++++++++++++++++++++
4 files changed, 333 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..f6e7cfc97a3f
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * All rights reserved.
+ */
+
+#ifndef RPMON_QMI_INTERFACE_H
+#define RPMON_QMI_INTERFACE_H
+
+#define RP_NAME_LEN 255
+#define RPQMI_BUF_SIZE 4096
+#define QMI_TLV_TL_SIZE 3
+
+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..3fe6d6a4cf31
--- /dev/null
+++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2020 Wang Wenhu <[email protected]>
+ * 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
+
+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 = 0x10,
+ .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 = 0x11,
+ .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 = 0x11,
+ .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 = 0x10,
+ .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 = 0x10,
+ .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 = 0x02,
+ .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 <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.17.1
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.
Signed-off-by: Wang Wenhu <[email protected]>
---
Changes since v1:
- Addressed review comments from Randy
---
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..fe3b48c23cb9
--- /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 <[email protected]>
+ * 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 >= (sizeof(rpmon_qmi_event_callbacks) /
+ sizeof(struct rpmon_qmi_cb_fn))) {
+ 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 <[email protected]>");
+MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
--
2.17.1
RPMON is a driver framework. It supports remote processor monitor
from user level. The baisc 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 discriptors 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.
Signed-off-by: Wang Wenhu <[email protected]>
---
Changes since v1:
- Addressed review comments from Randy
---
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 <[email protected]>
+ * 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 <[email protected]>");
+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 <[email protected]>
+ * 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
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
Hi--
On 4/12/20 4:24 AM, Wang Wenhu wrote:
> RPMON is a driver framework. It supports remote processor monitor
> from user level. The baisc components are a character device
basic
> 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 discriptors related to the exported
descriptors
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
The rest of the changes to patch 1/3 look good. Thanks.
> ---
> Changes since v1:
> - Addressed review comments from Randy
>
> ---
> 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
--
~Randy
Hi--
On 4/12/20 4:24 AM, Wang Wenhu wrote:
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> Changes since v1:
> - Addressed review comments from Randy
>
> ---
> drivers/rpmon/Kconfig | 13 ++
> drivers/rpmon/Makefile | 1 +
> drivers/rpmon/rpmon_qmi.h | 77 ++++++++++
> drivers/rpmon/rpmon_qmi_msg_v1.c | 242 +++++++++++++++++++++++++++++++
> 4 files changed, 333 insertions(+)
> create mode 100644 drivers/rpmon/rpmon_qmi.h
> create mode 100644 drivers/rpmon/rpmon_qmi_msg_v1.c
>
> diff --git a/drivers/rpmon/rpmon_qmi_msg_v1.c b/drivers/rpmon/rpmon_qmi_msg_v1.c
> new file mode 100644
> index 000000000000..3fe6d6a4cf31
> --- /dev/null
> +++ b/drivers/rpmon/rpmon_qmi_msg_v1.c
> @@ -0,0 +1,242 @@
I am hoping that your patch:
[PATCH] soc: qmi: move tlv-micros to header file
will eventually allow the .tlv_type constants below to become #define values.
Otherwise these changes (patch 2/3) look good. Thanks.
> +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 = 0x10,
> + .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 = 0x11,
> + .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 = 0x11,
> + .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 = 0x10,
> + .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 = 0x10,
> + .offset = offsetof(struct rpmon_conn_check_resp,
> + result),
> + },
> + {
> + .data_type = QMI_EOTI,
> + .array_type = NO_ARRAY,
> + .tlv_type = QMI_COMMON_TLV_TYPE,
> + },
> +};
--
~Randy
Hi--
On 4/12/20 4:24 AM, Wang Wenhu wrote:
> 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.
>
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> Changes since v1:
> - Addressed review comments from Randy
>
> ---
> 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/rpmon_qmi.c b/drivers/rpmon/rpmon_qmi.c
> new file mode 100644
> index 000000000000..fe3b48c23cb9
> --- /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 <[email protected]>
> + * 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)
service(s)
> + * like stability checking of remote processors. Currently a set of
> + * messages, implemented by RPMON_QMI_MSG_V1 is available.
> + */
> +
> +
> +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;
> +}
Missing blank line between functions here.
> +static void rpmon_qmi_msg_callback(enum rpmon_qmi_msg_type type,
> + struct sockaddr_qrtr *sq,
> + const void *msg)
> +{
The rest of the changes to patch 3/3 look good.
Thanks.
--
~Randy
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 <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Ohad Ben-Cohen <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Wang Wenhu <[email protected]>
---
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 <[email protected]>
+ * 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 <[email protected]>");
+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 <[email protected]>
+ * 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
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 <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Ohad Ben-Cohen <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Wang Wenhu <[email protected]>
---
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 <[email protected]>
+ * 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 <[email protected]>");
+MODULE_DESCRIPTION("Subsystem Monitor via QMI platform driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
--
2.17.1
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 <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Ohad Ben-Cohen <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Wang Wenhu <[email protected]>
---
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/[email protected]/
- 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.17.1
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
>
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
>>
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 <[email protected]>
> Cc: Bjorn Andersson <[email protected]>
> Cc: Ohad Ben-Cohen <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Wang Wenhu <[email protected]>
> ---
> 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