diff options
Diffstat (limited to 'drivers/soc')
| -rw-r--r-- | drivers/soc/qcom/qdsp6v2/voice_svc.c | 26 | ||||
| -rw-r--r-- | drivers/soc/qcom/scm.c | 27 |
2 files changed, 39 insertions, 14 deletions
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index 67c58d1e6d4..c54d9dda420 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,12 @@ struct voice_svc_prvt { struct list_head response_queue; wait_queue_head_t response_wait; spinlock_t response_lock; + /* + * This mutex ensures responses are processed in sequential order and + * that no two threads access and free the same response at the same + * time. + */ + struct mutex response_mutex_lock; }; struct apr_data { @@ -448,6 +454,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, goto done; } + mutex_lock(&prtd->response_mutex_lock); spin_lock_irqsave(&prtd->response_lock, spin_flags); if (list_empty(&prtd->response_queue)) { @@ -461,7 +468,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, pr_debug("%s: Read timeout\n", __func__); ret = -ETIMEDOUT; - goto done; + goto unlock; } else if (ret > 0 && !list_empty(&prtd->response_queue)) { pr_debug("%s: Interrupt recieved for response\n", __func__); @@ -469,7 +476,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, pr_debug("%s: Interrupted by SIGNAL %d\n", __func__, ret); - goto done; + goto unlock; } spin_lock_irqsave(&prtd->response_lock, spin_flags); @@ -488,7 +495,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, __func__, count, size); ret = -ENOMEM; - goto done; + goto unlock; } if (!access_ok(VERIFY_WRITE, arg, size)) { @@ -496,7 +503,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, __func__); ret = -EPERM; - goto done; + goto unlock; } ret = copy_to_user(arg, &resp->resp, @@ -506,7 +513,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, pr_err("%s: copy_to_user failed %d\n", __func__, ret); ret = -EPERM; - goto done; + goto unlock; } spin_lock_irqsave(&prtd->response_lock, spin_flags); @@ -520,6 +527,8 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg, ret = count; +unlock: + mutex_unlock(&prtd->response_mutex_lock); done: return ret; } @@ -575,6 +584,7 @@ static int voice_svc_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&prtd->response_queue); init_waitqueue_head(&prtd->response_wait); spin_lock_init(&prtd->response_lock); + mutex_init(&prtd->response_mutex_lock); file->private_data = (void *)prtd; /* Current APR implementation doesn't support session based @@ -625,6 +635,7 @@ static int voice_svc_release(struct inode *inode, struct file *file) pr_err("%s: Failed to dereg MVM %d\n", __func__, ret); } + mutex_lock(&prtd->response_mutex_lock); spin_lock_irqsave(&prtd->response_lock, spin_flags); while (!list_empty(&prtd->response_queue)) { @@ -638,6 +649,9 @@ static int voice_svc_release(struct inode *inode, struct file *file) } spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + mutex_unlock(&prtd->response_mutex_lock); + + mutex_destroy(&prtd->response_mutex_lock); kfree(file->private_data); file->private_data = NULL; diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c index 05da047c526..1ffda2eca13 100644 --- a/drivers/soc/qcom/scm.c +++ b/drivers/soc/qcom/scm.c @@ -46,9 +46,6 @@ static DEFINE_MUTEX(scm_lock); #define SMC_ATOMIC_MASK 0x80000000 #define IS_CALL_AVAIL_CMD 1 -#define SCM_BUF_LEN(__cmd_size, __resp_size) \ - (sizeof(struct scm_command) + sizeof(struct scm_response) + \ - __cmd_size + __resp_size) /** * struct scm_command - one SCM command buffer * @len: total available memory for command and response @@ -120,6 +117,21 @@ struct scm_response { #endif +/* Calculate size for buffer given cmd_size and resp_size. + * Returns 0 in case the result would overflow size_t. + */ +static size_t scm_get_buf_len(size_t cmd_size, size_t resp_size) +{ + size_t contents = cmd_size + resp_size; + size_t structs = sizeof(struct scm_command) + + sizeof(struct scm_response); + size_t buf_len = contents + structs; + + if (contents < cmd_size || buf_len < contents) + buf_len = 0; + return buf_len; +} + /** * scm_command_to_response() - Get a pointer to a scm_response * @cmd: command @@ -342,10 +354,9 @@ int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf, void *scm_buf, size_t scm_buf_len) { int ret; - size_t len = SCM_BUF_LEN(cmd_len, resp_len); + size_t len = scm_get_buf_len(cmd_len, resp_len); - if (cmd_len > scm_buf_len || resp_len > scm_buf_len || - len > scm_buf_len) + if (len == 0 || len > scm_buf_len) return -EINVAL; if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE)) @@ -739,9 +750,9 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, { struct scm_command *cmd; int ret; - size_t len = SCM_BUF_LEN(cmd_len, resp_len); + size_t len = scm_get_buf_len(cmd_len, resp_len); - if (cmd_len > len || resp_len > len) + if (len == 0 || PAGE_ALIGN(len) < len) return -EINVAL; cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); |
