qseecom: fix listener unregister issues
Abort listener thread waiting for listener request when getting
unregister cmd from userspace; to avoid TZ busy error, don't
register/unregister listener when TZ is busy; and create a kthread
to process pending listener unregister request.
Change-Id: I23607e8ecf1eee61cfb241b4d046887776507480
Signed-off-by: Zhen Kong <zkong@codeaurora.org>
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 9e71584..96e15c0 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -50,6 +50,7 @@
#include <linux/compat.h>
#include "compat_qseecom.h"
+#include <linux/kthread.h>
#define QSEECOM_DEV "qseecom"
#define QSEOS_VERSION_14 0x14
@@ -140,6 +141,11 @@
CLK_INVALID,
};
+enum qseecom_listener_unregister_kthread_state {
+ LSNR_UNREG_KT_SLEEP = 0,
+ LSNR_UNREG_KT_WAKEUP,
+};
+
static struct class *driver_class;
static dev_t qseecom_device_no;
@@ -298,6 +304,9 @@
struct list_head unregister_lsnr_pending_list_head;
wait_queue_head_t register_lsnr_pending_wq;
+ struct task_struct *unregister_lsnr_kthread_task;
+ wait_queue_head_t unregister_lsnr_kthread_wq;
+ atomic_t unregister_lsnr_kthread_state;
};
struct qseecom_sec_buf_fd_info {
@@ -1149,8 +1158,14 @@
resp.result = QSEOS_RESULT_INCOMPLETE;
+ mutex_unlock(&listener_access_lock);
+ mutex_lock(&app_access_lock);
+ __qseecom_reentrancy_check_if_no_app_blocked(
+ TZ_OS_REGISTER_LISTENER_SMCINVOKE_ID);
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, cmd_buf, cmd_len,
&resp, sizeof(resp));
+ mutex_unlock(&app_access_lock);
+ mutex_lock(&listener_access_lock);
if (ret) {
pr_err("qseecom_scm_call failed with err: %d\n", ret);
return -EINVAL;
@@ -1246,8 +1261,14 @@
req.listener_id = data->listener.id;
resp.result = QSEOS_RESULT_INCOMPLETE;
+ mutex_unlock(&listener_access_lock);
+ mutex_lock(&app_access_lock);
+ __qseecom_reentrancy_check_if_no_app_blocked(
+ TZ_OS_DEREGISTER_LISTENER_ID);
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
sizeof(req), &resp, sizeof(resp));
+ mutex_unlock(&app_access_lock);
+ mutex_lock(&listener_access_lock);
if (ret) {
pr_err("scm_call() failed with err: %d (lstnr id=%d)\n",
ret, data->listener.id);
@@ -1263,9 +1284,6 @@
goto exit;
}
- data->abort = 1;
- wake_up_all(&ptr_svc->rcv_req_wq);
-
while (atomic_read(&data->ioctl_count) > 1) {
if (wait_event_freezable(data->abort_wq,
atomic_read(&data->ioctl_count) <= 1)) {
@@ -1304,6 +1322,10 @@
ptr_svc->abort = 1;
wake_up_interruptible_all(&qseecom.send_resp_wq);
+ /* stop listener thread waiting for listener request */
+ data->abort = 1;
+ wake_up_all(&ptr_svc->rcv_req_wq);
+
/* return directly if pending*/
if (ptr_svc->unregister_pending)
return 0;
@@ -1361,6 +1383,30 @@
wake_up_interruptible(&qseecom.register_lsnr_pending_wq);
}
+static void __wakeup_unregister_listener_kthread(void)
+{
+ atomic_set(&qseecom.unregister_lsnr_kthread_state,
+ LSNR_UNREG_KT_WAKEUP);
+ wake_up_interruptible(&qseecom.unregister_lsnr_kthread_wq);
+}
+
+static int __qseecom_unregister_listener_kthread_func(void *data)
+{
+ while (!kthread_should_stop()) {
+ wait_event_freezable(
+ qseecom.unregister_lsnr_kthread_wq,
+ atomic_read(&qseecom.unregister_lsnr_kthread_state)
+ == LSNR_UNREG_KT_WAKEUP);
+ pr_debug("kthread to unregister listener is called %d\n",
+ atomic_read(&qseecom.unregister_lsnr_kthread_state));
+ __qseecom_processing_pending_lsnr_unregister();
+ atomic_set(&qseecom.unregister_lsnr_kthread_state,
+ LSNR_UNREG_KT_SLEEP);
+ }
+ pr_warn("kthread to unregister listener stopped\n");
+ return 0;
+}
+
static int __qseecom_set_msm_bus_request(uint32_t mode)
{
int ret = 0;
@@ -3297,7 +3343,6 @@
pr_err("cache operation failed %d\n", ret2);
return ret2;
}
- __qseecom_processing_pending_lsnr_unregister();
return ret;
}
@@ -4515,7 +4560,7 @@
uint32_t fw_size, app_arch;
uint32_t app_id = 0;
- __qseecom_processing_pending_lsnr_unregister();
+ __wakeup_unregister_listener_kthread();
if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
pr_err("Not allowed to be called in %d state\n",
@@ -4689,7 +4734,7 @@
unsigned long flags = 0;
bool found_handle = false;
- __qseecom_processing_pending_lsnr_unregister();
+ __wakeup_unregister_listener_kthread();
if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
pr_err("Not allowed to be called in %d state\n",
@@ -4739,7 +4784,7 @@
struct qseecom_dev_handle *data;
bool perf_enabled = false;
- __qseecom_processing_pending_lsnr_unregister();
+ __wakeup_unregister_listener_kthread();
if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
pr_err("Not allowed to be called in %d state\n",
@@ -7057,7 +7102,7 @@
cmd != QSEECOM_IOCTL_SEND_RESP_REQ &&
cmd != QSEECOM_IOCTL_SEND_MODFD_RESP &&
cmd != QSEECOM_IOCTL_SEND_MODFD_RESP_64)
- __qseecom_processing_pending_lsnr_unregister();
+ __wakeup_unregister_listener_kthread();
switch (cmd) {
case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
@@ -8657,6 +8702,7 @@
spin_lock_init(&qseecom.registered_kclient_list_lock);
init_waitqueue_head(&qseecom.send_resp_wq);
init_waitqueue_head(&qseecom.register_lsnr_pending_wq);
+ init_waitqueue_head(&qseecom.unregister_lsnr_kthread_wq);
qseecom.send_resp_flag = 0;
qseecom.qsee_version = QSEEE_VERSION_00;
@@ -8857,6 +8903,17 @@
if (!qseecom.qsee_perf_client)
pr_err("Unable to register bus client\n");
+ /*create a kthread to process pending listener unregister task */
+ qseecom.unregister_lsnr_kthread_task = kthread_run(
+ __qseecom_unregister_listener_kthread_func,
+ NULL, "qseecom-unreg-lsnr");
+ if (IS_ERR(qseecom.unregister_lsnr_kthread_task)) {
+ pr_err("failed to create kthread to unregister listener\n");
+ rc = -EINVAL;
+ goto exit_deinit_clock;
+ }
+ atomic_set(&qseecom.unregister_lsnr_kthread_state,
+ LSNR_UNREG_KT_SLEEP);
atomic_set(&qseecom.qseecom_state, QSEECOM_STATE_READY);
return 0;
@@ -8973,6 +9030,8 @@
ion_client_destroy(qseecom.ion_clnt);
+ kthread_stop(qseecom.unregister_lsnr_kthread_task);
+
cdev_del(&qseecom.cdev);
device_destroy(driver_class, qseecom_device_no);