diff --git a/drivers/soc/qcom/mem-buf.c b/drivers/soc/qcom/mem-buf.c index ad7beba7a815..b921870babf8 100644 --- a/drivers/soc/qcom/mem-buf.c +++ b/drivers/soc/qcom/mem-buf.c @@ -786,12 +786,19 @@ static int mem_buf_msgq_recv_fn(void *unused) int ret; while (!kthread_should_stop()) { - ret = hh_msgq_recv(mem_buf_hh_msgq_hdl, &buf, &size, 0); - if (ret < 0) + buf = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL); + if (!buf) + continue; + + ret = hh_msgq_recv(mem_buf_hh_msgq_hdl, buf, + HH_MSGQ_MAX_MSG_SIZE_BYTES, &size, 0); + if (ret < 0) { + kfree(buf); pr_err_ratelimited("%s failed to receive message rc: %d\n", __func__, ret); - else + } else { mem_buf_process_msg(buf, size); + } } return 0; diff --git a/drivers/virt/haven/hh_msgq.c b/drivers/virt/haven/hh_msgq.c index 575f8211bc81..34a899d80805 100644 --- a/drivers/virt/haven/hh_msgq.c +++ b/drivers/virt/haven/hh_msgq.c @@ -71,7 +71,8 @@ static irqreturn_t hh_msgq_tx_isr(int irq, void *dev) } static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry, - void *buff, size_t *size, u64 rx_flags) + void *buff, size_t buff_size, + size_t *recv_size, u64 rx_flags) { struct hh_hcall_msgq_recv_resp resp = {}; unsigned long flags; @@ -83,11 +84,11 @@ static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry, spin_lock_irqsave(&cap_table_entry->rx_lock, flags); hh_ret = hh_hcall_msgq_recv(cap_table_entry->rx_cap_id, buff, - HH_MSGQ_MAX_MSG_SIZE_BYTES, &resp); + buff_size, &resp); switch (hh_ret) { case HH_ERROR_OK: - *size = resp.recv_size; + *recv_size = resp.recv_size; ret = 0; break; case HH_ERROR_MSGQUEUE_EMPTY: @@ -110,30 +111,36 @@ static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry, /** * hh_msgq_recv: Receive a message from the client running on a different VM * @client_desc: The client descriptor that was obtained via hh_msgq_register() - * @buff: Pointer to the buffer where the received data must be placed. Note - * that the caller is responsible to free the data contained in buff - * @size: The size of the buffer received + * @buff: Pointer to the buffer where the received data must be placed + * @buff_size: The size of the buffer space available + * @recv_size: The actual amount of data that is copied into buff * @flags: Optional flags to pass to receive the data. For the list of flags, * see linux/haven/hh_msgq.h * - * The function returns -EINVAL if the caller passes invalid arguments, -EAGAIN + * The function returns 0 if the data is successfully received and recv_size + * would contain the actual amount of data copied into buff. + * It returns -EINVAL if the caller passes invalid arguments, -EAGAIN * if the message queue is not yet ready to communicate, and -EPERM if the - * caller doesn't have permissions to receive the data. 0 is the data is - * successfully received. + * caller doesn't have permissions to receive the data. In all these failure + * cases, recv_size is unmodified. * * Note: this function may sleep and should not be called from interrupt * context */ int hh_msgq_recv(void *msgq_client_desc, - void **buff, size_t *size, unsigned long flags) + void *buff, size_t buff_size, + size_t *recv_size, unsigned long flags) { struct hh_msgq_desc *client_desc = msgq_client_desc; struct hh_msgq_cap_table *cap_table_entry; int ret; - if (!client_desc || !(*buff) || !size) + if (!client_desc || !buff || !buff_size || !recv_size) return -EINVAL; + if (buff_size > HH_MSGQ_MAX_MSG_SIZE_BYTES) + return -E2BIG; + cap_table_entry = &hh_msgq_cap_table[client_desc->label]; spin_lock(&cap_table_entry->cap_entry_lock); @@ -170,31 +177,18 @@ int hh_msgq_recv(void *msgq_client_desc, spin_unlock(&cap_table_entry->cap_entry_lock); - *buff = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL); - if (!(*buff)) - return -ENOMEM; - do { - if (cap_table_entry->rx_empty && (flags & HH_MSGQ_NONBLOCK)) { - ret = -EAGAIN; - goto buff_free; - } + if (cap_table_entry->rx_empty && (flags & HH_MSGQ_NONBLOCK)) + return -EAGAIN; if (wait_event_interruptible(cap_table_entry->rx_wq, - !cap_table_entry->rx_empty)) { - ret = -ERESTARTSYS; - goto buff_free; - } + !cap_table_entry->rx_empty)) + return -ERESTARTSYS; - ret = __hh_msgq_recv(cap_table_entry, *buff, size, flags); + ret = __hh_msgq_recv(cap_table_entry, buff, buff_size, + recv_size, flags); } while (ret == -EAGAIN); -buff_free: - if (ret < 0) { - kfree(*buff); - *buff = NULL; - } - return ret; err: diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index d0c7db026dff..823b4da580b0 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -417,6 +417,7 @@ static void hh_rm_process_recv_work(struct work_struct *work) * to release the original packet that arrived. */ kfree(recv_buff); + kfree(msgq_data); } static int hh_rm_recv_task_fn(void *data) @@ -427,12 +428,18 @@ static int hh_rm_recv_task_fn(void *data) int ret; while (!kthread_should_stop()) { + recv_buff = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL); + if (!recv_buff) + continue; + /* Block until a new message is received */ - ret = hh_msgq_recv(hh_rm_msgq_desc, &recv_buff, + ret = hh_msgq_recv(hh_rm_msgq_desc, recv_buff, + HH_MSGQ_MAX_MSG_SIZE_BYTES, &recv_buff_size, 0); if (ret < 0) { pr_err("%s: Failed to receive the message: %d\n", __func__, ret); + kfree(recv_buff); continue; } else if (recv_buff_size <= sizeof(struct hh_rm_rpc_hdr)) { pr_err("%s: Invalid message size received\n", __func__); diff --git a/include/linux/haven/hh_msgq.h b/include/linux/haven/hh_msgq.h index 7990a61b8ec2..59bacd7decc3 100644 --- a/include/linux/haven/hh_msgq.h +++ b/include/linux/haven/hh_msgq.h @@ -31,7 +31,8 @@ int hh_msgq_unregister(void *msgq_client_desc); int hh_msgq_send(void *msgq_client_desc, void *buff, size_t size, unsigned long flags); int hh_msgq_recv(void *msgq_client_desc, - void **buff, size_t *size, unsigned long flags); + void *buff, size_t buff_size, + size_t *recv_size, unsigned long flags); int hh_msgq_populate_cap_info(enum hh_msgq_label label, u64 cap_id, int direction, int irq);