tzcom: Implement abort and cleanup of driver

Add a new IOCTL call to the driver to properly
abort all threads blocked on wait queues. Updated
release call for proper cleanup.

CRs-fixed: 303637, 304152
Signed-off-by: Sachin Shah <sachins@codeaurora.org>
This commit is contained in:
Sachin Shah
2011-09-12 20:31:02 -07:00
committed by Bryan Huntsman
parent e0b11453c1
commit d7e02d4d60
2 changed files with 111 additions and 4 deletions

View File

@@ -88,6 +88,9 @@ struct tzcom_data_t {
wait_queue_head_t cont_cmd_wq; wait_queue_head_t cont_cmd_wq;
int cont_cmd_flag; int cont_cmd_flag;
u32 handled_cmd_svc_instance_id; u32 handled_cmd_svc_instance_id;
int abort;
wait_queue_head_t abort_wq;
atomic_t ioctl_count;
}; };
static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len, static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len,
@@ -233,6 +236,13 @@ static int tzcom_unregister_service(struct tzcom_data_t *data,
return -EINVAL; return -EINVAL;
} }
static int __tzcom_is_cont_cmd(struct tzcom_data_t *data)
{
int ret;
ret = (data->cont_cmd_flag != 0);
return ret || data->abort;
}
/** /**
* +---------+ +-----+ +-----------------+ * +---------+ +-----+ +-----------------+
* | TZCOM | | SCM | | TZCOM_SCHEDULER | * | TZCOM | | SCM | | TZCOM_SCHEDULER |
@@ -409,10 +419,15 @@ static int tzcom_send_cmd(struct tzcom_data_t *data, void __user *argp)
PDEBUG("waking up next_cmd_wq and " PDEBUG("waking up next_cmd_wq and "
"waiting for cont_cmd_wq"); "waiting for cont_cmd_wq");
if (wait_event_interruptible(data->cont_cmd_wq, if (wait_event_interruptible(data->cont_cmd_wq,
data->cont_cmd_flag != 0)) { __tzcom_is_cont_cmd(data))) {
PWARN("Interrupted: exiting send_cmd loop"); PWARN("Interrupted: exiting send_cmd loop");
return -ERESTARTSYS; return -ERESTARTSYS;
} }
if (data->abort) {
PERR("Aborting driver");
return -ENODEV;
}
data->cont_cmd_flag = 0; data->cont_cmd_flag = 0;
cmd.cmd_type = TZ_SCHED_CMD_PENDING; cmd.cmd_type = TZ_SCHED_CMD_PENDING;
mutex_lock(&sb_in_lock); mutex_lock(&sb_in_lock);
@@ -515,6 +530,14 @@ static int __tzcom_copy_cmd(struct tzcom_data_t *data,
return ret; return ret;
} }
static int __tzcom_is_next_cmd(struct tzcom_data_t *data,
struct tzcom_registered_svc_list *svc)
{
int ret;
ret = (svc->next_cmd_flag != 0);
return ret || data->abort;
}
static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp) static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
{ {
int ret = 0; int ret = 0;
@@ -543,11 +566,16 @@ static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
while (1) { while (1) {
PDEBUG("Before wait_event next_cmd."); PDEBUG("Before wait_event next_cmd.");
if (wait_event_interruptible(this_svc->next_cmd_wq, if (wait_event_interruptible(this_svc->next_cmd_wq,
this_svc->next_cmd_flag != 0)) { __tzcom_is_next_cmd(data, this_svc))) {
PWARN("Interrupted: exiting wait_next_cmd loop"); PWARN("Interrupted: exiting wait_next_cmd loop");
/* woken up for different reason */ /* woken up for different reason */
return -ERESTARTSYS; return -ERESTARTSYS;
} }
if (data->abort) {
PERR("Aborting driver");
return -ENODEV;
}
PDEBUG("After wait_event next_cmd."); PDEBUG("After wait_event next_cmd.");
this_svc->next_cmd_flag = 0; this_svc->next_cmd_flag = 0;
@@ -604,6 +632,41 @@ static int tzcom_cont_cmd(struct tzcom_data_t *data, void __user *argp)
return ret; return ret;
} }
static int tzcom_abort(struct tzcom_data_t *data)
{
int ret = 0;
unsigned long flags;
struct tzcom_registered_svc_list *lsvc, *nsvc;
if (data->abort) {
PERR("Already aborting");
return -EINVAL;
}
data->abort = 1;
PDEBUG("Waking up cont_cmd_wq");
wake_up_all(&data->cont_cmd_wq);
spin_lock_irqsave(&data->registered_svc_list_lock, flags);
PDEBUG("Before waking up service wait queues");
list_for_each_entry_safe(lsvc, nsvc,
&data->registered_svc_list_head, list) {
wake_up_all(&lsvc->next_cmd_wq);
}
spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
PDEBUG("ioctl_count before loop: %d", atomic_read(&data->ioctl_count));
while (atomic_read(&data->ioctl_count) > 0) {
if (wait_event_interruptible(data->abort_wq,
atomic_read(&data->ioctl_count) <= 0)) {
PERR("Interrupted from abort");
ret = -ERESTARTSYS;
break;
}
}
return ret;
}
static long tzcom_ioctl(struct file *file, unsigned cmd, static long tzcom_ioctl(struct file *file, unsigned cmd,
unsigned long arg) unsigned long arg)
{ {
@@ -611,17 +674,28 @@ static long tzcom_ioctl(struct file *file, unsigned cmd,
struct tzcom_data_t *tzcom_data = file->private_data; struct tzcom_data_t *tzcom_data = file->private_data;
void __user *argp = (void __user *) arg; void __user *argp = (void __user *) arg;
PDEBUG("enter tzcom_ioctl()"); PDEBUG("enter tzcom_ioctl()");
if (tzcom_data->abort) {
PERR("Aborting tzcom driver");
return -ENODEV;
}
switch (cmd) { switch (cmd) {
case TZCOM_IOCTL_REGISTER_SERVICE_REQ: { case TZCOM_IOCTL_REGISTER_SERVICE_REQ: {
PDEBUG("ioctl register_service_req()"); PDEBUG("ioctl register_service_req()");
atomic_inc(&tzcom_data->ioctl_count);
ret = tzcom_register_service(tzcom_data, argp); ret = tzcom_register_service(tzcom_data, argp);
atomic_dec(&tzcom_data->ioctl_count);
wake_up_interruptible(&tzcom_data->abort_wq);
if (ret) if (ret)
PERR("failed tzcom_register_service: %d", ret); PERR("failed tzcom_register_service: %d", ret);
break; break;
} }
case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: { case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: {
PDEBUG("ioctl unregister_service_req()"); PDEBUG("ioctl unregister_service_req()");
atomic_inc(&tzcom_data->ioctl_count);
ret = tzcom_unregister_service(tzcom_data, argp); ret = tzcom_unregister_service(tzcom_data, argp);
atomic_dec(&tzcom_data->ioctl_count);
wake_up_interruptible(&tzcom_data->abort_wq);
if (ret) if (ret)
PERR("failed tzcom_unregister_service: %d", ret); PERR("failed tzcom_unregister_service: %d", ret);
break; break;
@@ -630,7 +704,10 @@ static long tzcom_ioctl(struct file *file, unsigned cmd,
PDEBUG("ioctl send_cmd_req()"); PDEBUG("ioctl send_cmd_req()");
/* Only one client allowed here at a time */ /* Only one client allowed here at a time */
mutex_lock(&send_cmd_lock); mutex_lock(&send_cmd_lock);
atomic_inc(&tzcom_data->ioctl_count);
ret = tzcom_send_cmd(tzcom_data, argp); ret = tzcom_send_cmd(tzcom_data, argp);
atomic_dec(&tzcom_data->ioctl_count);
wake_up_interruptible(&tzcom_data->abort_wq);
mutex_unlock(&send_cmd_lock); mutex_unlock(&send_cmd_lock);
if (ret) if (ret)
PERR("failed tzcom_send_cmd: %d", ret); PERR("failed tzcom_send_cmd: %d", ret);
@@ -638,18 +715,31 @@ static long tzcom_ioctl(struct file *file, unsigned cmd,
} }
case TZCOM_IOCTL_READ_NEXT_CMD_REQ: { case TZCOM_IOCTL_READ_NEXT_CMD_REQ: {
PDEBUG("ioctl read_next_cmd_req()"); PDEBUG("ioctl read_next_cmd_req()");
atomic_inc(&tzcom_data->ioctl_count);
ret = tzcom_read_next_cmd(tzcom_data, argp); ret = tzcom_read_next_cmd(tzcom_data, argp);
atomic_dec(&tzcom_data->ioctl_count);
wake_up_interruptible(&tzcom_data->abort_wq);
if (ret) if (ret)
PERR("failed tzcom_read_next: %d", ret); PERR("failed tzcom_read_next: %d", ret);
break; break;
} }
case TZCOM_IOCTL_CONTINUE_CMD_REQ: { case TZCOM_IOCTL_CONTINUE_CMD_REQ: {
PDEBUG("ioctl continue_cmd_req()"); PDEBUG("ioctl continue_cmd_req()");
atomic_inc(&tzcom_data->ioctl_count);
ret = tzcom_cont_cmd(tzcom_data, argp); ret = tzcom_cont_cmd(tzcom_data, argp);
atomic_dec(&tzcom_data->ioctl_count);
wake_up_interruptible(&tzcom_data->abort_wq);
if (ret) if (ret)
PERR("failed tzcom_cont_cmd: %d", ret); PERR("failed tzcom_cont_cmd: %d", ret);
break; break;
} }
case TZCOM_IOCTL_ABORT_REQ: {
PDEBUG("ioctl abort_req()");
ret = tzcom_abort(tzcom_data);
if (ret)
PERR("failed tzcom_abort: %d", ret);
break;
}
default: default:
return -EINVAL; return -EINVAL;
} }
@@ -754,6 +844,9 @@ static int tzcom_open(struct inode *inode, struct file *file)
init_waitqueue_head(&tzcom_data->cont_cmd_wq); init_waitqueue_head(&tzcom_data->cont_cmd_wq);
tzcom_data->cont_cmd_flag = 0; tzcom_data->cont_cmd_flag = 0;
tzcom_data->handled_cmd_svc_instance_id = 0; tzcom_data->handled_cmd_svc_instance_id = 0;
tzcom_data->abort = 0;
init_waitqueue_head(&tzcom_data->abort_wq);
atomic_set(&tzcom_data->ioctl_count, 0);
return 0; return 0;
} }
@@ -762,23 +855,35 @@ static int tzcom_release(struct inode *inode, struct file *file)
struct tzcom_data_t *tzcom_data = file->private_data; struct tzcom_data_t *tzcom_data = file->private_data;
struct tzcom_callback_list *lcb, *ncb; struct tzcom_callback_list *lcb, *ncb;
struct tzcom_registered_svc_list *lsvc, *nsvc; struct tzcom_registered_svc_list *lsvc, *nsvc;
unsigned long flags;
PDEBUG("In here"); PDEBUG("In here");
wake_up_all(&tzcom_data->cont_cmd_wq); if (!tzcom_data->abort) {
PDEBUG("Calling abort");
tzcom_abort(tzcom_data);
}
PDEBUG("Before removing callback list");
mutex_lock(&tzcom_data->callback_list_lock);
list_for_each_entry_safe(lcb, ncb, list_for_each_entry_safe(lcb, ncb,
&tzcom_data->callback_list_head, list) { &tzcom_data->callback_list_head, list) {
list_del(&lcb->list); list_del(&lcb->list);
kfree(lcb); kfree(lcb);
} }
mutex_unlock(&tzcom_data->callback_list_lock);
PDEBUG("After removing callback list");
PDEBUG("Before removing svc list");
spin_lock_irqsave(&tzcom_data->registered_svc_list_lock, flags);
list_for_each_entry_safe(lsvc, nsvc, list_for_each_entry_safe(lsvc, nsvc,
&tzcom_data->registered_svc_list_head, list) { &tzcom_data->registered_svc_list_head, list) {
wake_up_all(&lsvc->next_cmd_wq);
list_del(&lsvc->list); list_del(&lsvc->list);
kfree(lsvc); kfree(lsvc);
} }
spin_unlock_irqrestore(&tzcom_data->registered_svc_list_lock, flags);
PDEBUG("After removing svc list");
PDEBUG("Freeing tzcom data");
kfree(tzcom_data); kfree(tzcom_data);
return 0; return 0;
} }

View File

@@ -98,4 +98,6 @@ struct tzcom_cont_cmd_op_req {
#define TZCOM_IOCTL_CONTINUE_CMD_REQ \ #define TZCOM_IOCTL_CONTINUE_CMD_REQ \
_IOWR(TZCOM_IOC_MAGIC, 5, struct tzcom_cont_cmd_op_req) _IOWR(TZCOM_IOC_MAGIC, 5, struct tzcom_cont_cmd_op_req)
#define TZCOM_IOCTL_ABORT_REQ _IO(TZCOM_IOC_MAGIC, 6)
#endif /* __TZCOM_H_ */ #endif /* __TZCOM_H_ */