diff --git a/drivers/soc/qcom/mem_buf/mem-buf-dev-gh.c b/drivers/soc/qcom/mem_buf/mem-buf-dev-gh.c index 810f62273533..a990ed5a47ab 100644 --- a/drivers/soc/qcom/mem_buf/mem-buf-dev-gh.c +++ b/drivers/soc/qcom/mem_buf/mem-buf-dev-gh.c @@ -131,6 +131,24 @@ struct sg_table *dup_gh_sgl_desc_to_sgt(struct gh_sgl_desc *sgl_desc) } EXPORT_SYMBOL(dup_gh_sgl_desc_to_sgt); +struct gh_sgl_desc *dup_gh_sgl_desc(struct gh_sgl_desc *sgl_desc) +{ + size_t size; + struct gh_sgl_desc *copy; + + if (!sgl_desc) + return NULL; + + size = offsetof(struct gh_sgl_desc, sgl_entries[sgl_desc->n_sgl_entries]); + copy = kvmalloc(size, GFP_KERNEL); + if (!copy) + return ERR_PTR(-ENOMEM); + + memcpy(copy, sgl_desc, size); + return copy; +} +EXPORT_SYMBOL(dup_gh_sgl_desc); + size_t mem_buf_get_sgl_buf_size(struct gh_sgl_desc *sgl_desc) { size_t size = 0; @@ -176,13 +194,17 @@ static int mem_buf_hyp_assign_table_gh(struct gh_sgl_desc *sgl_desc, int src_vmi /* * @memparcel_hdl: * GH_RM_TRANS_TYPE_DONATE - memparcel_hdl will be set to MEM_BUF_MEMPARCEL_INVALID - on success, and (possibly) set to a different valid memparcel on error. This is - because accepting a donated memparcel handle destroys that handle. - GH_RM_TRANS_TYPE_LEND - unmodified. - GH_RM_TRANS_TYPE_SHARE - unmodified. + * on success, and (possibly) set to a different valid memparcel on error. This is + * because accepting a donated memparcel handle destroys that handle. + * GH_RM_TRANS_TYPE_LEND - unmodified. + * GH_RM_TRANS_TYPE_SHARE - unmodified. + * @sgl_desc: + * If *sgl_desc is not NULL, request specific IPA address(es). Otherwise, hypervisor + * will choose the IPA address, and return it here. */ -struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, - struct gh_acl_desc *acl_desc, int src_vmid) +int mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, + struct gh_acl_desc *acl_desc, struct gh_sgl_desc **__sgl_desc, + int src_vmid) { int ret, ret2; struct gh_sgl_desc *sgl_desc; @@ -190,24 +212,24 @@ struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparce GH_RM_MEM_ACCEPT_DONE; gh_memparcel_handle_t memparcel_hdl = *__memparcel_hdl; - if (!acl_desc) - return ERR_PTR(-EINVAL); + if (!acl_desc || !__sgl_desc) + return -EINVAL; /* * memory returns to its original IPA address when accepted by HLOS. For example, * scattered memory returns to being scattered memory. */ - if (current_vmid != VMID_HLOS) + if (current_vmid != VMID_HLOS || (*__sgl_desc && (*__sgl_desc)->n_sgl_entries == 1)) flags |= GH_RM_MEM_ACCEPT_MAP_IPA_CONTIGUOUS; pr_debug("%s: adding CPU MMU stage 2 mappings\n", __func__); sgl_desc = gh_rm_mem_accept(memparcel_hdl, GH_RM_MEM_TYPE_NORMAL, op, - flags, 0, acl_desc, NULL, + flags, 0, acl_desc, *__sgl_desc, NULL, 0); if (IS_ERR(sgl_desc)) { pr_err("%s failed to map memory in stage 2 rc: %d\n", __func__, PTR_ERR(sgl_desc)); - return sgl_desc; + return PTR_ERR(sgl_desc); } if (op == GH_RM_TRANS_TYPE_DONATE) @@ -218,7 +240,8 @@ struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparce goto err_relinquish; trace_map_mem_s2(memparcel_hdl, sgl_desc); - return sgl_desc; + *__sgl_desc = sgl_desc; + return 0; err_relinquish: if (op == GH_RM_TRANS_TYPE_DONATE) @@ -226,12 +249,18 @@ struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparce __memparcel_hdl); else ret2 = mem_buf_unmap_mem_s2(memparcel_hdl); - kvfree(sgl_desc); + + /* + * Only free sgl_desc if caller passed NULL in *__sgl_desc to request + * gh_rm_mem_accept to allocate new IPA/sgl_desc. + */ + if (sgl_desc != *__sgl_desc) + kvfree(sgl_desc); if (ret2) { pr_err("%s failed to recover\n", __func__); - return ERR_PTR(-EADDRNOTAVAIL); + return -EADDRNOTAVAIL; } - return ERR_PTR(ret); + return ret; } EXPORT_SYMBOL(mem_buf_map_mem_s2); diff --git a/drivers/soc/qcom/mem_buf/mem-buf-dev.h b/drivers/soc/qcom/mem_buf/mem-buf-dev.h index ea578b792cf6..9316e4709458 100644 --- a/drivers/soc/qcom/mem_buf/mem-buf-dev.h +++ b/drivers/soc/qcom/mem_buf/mem-buf-dev.h @@ -50,13 +50,15 @@ static inline int mem_buf_unmap_mem_s1(struct gh_sgl_desc *sgl_desc) struct gh_acl_desc *mem_buf_vmid_perm_list_to_gh_acl(int *vmids, int *perms, unsigned int nr_acl_entries); struct gh_sgl_desc *mem_buf_sgt_to_gh_sgl_desc(struct sg_table *sgt); -struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, - struct gh_acl_desc *acl_desc, int src_vmid); +int mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, + struct gh_acl_desc *acl_desc, struct gh_sgl_desc **sgl_desc, + int src_vmid); int mem_buf_unmap_mem_s2(gh_memparcel_handle_t memparcel_hdl); int mem_buf_gh_acl_desc_to_vmid_perm_list(struct gh_acl_desc *acl_desc, int **vmids, int **perms); size_t mem_buf_get_sgl_buf_size(struct gh_sgl_desc *sgl_desc); struct sg_table *dup_gh_sgl_desc_to_sgt(struct gh_sgl_desc *sgl_desc); +struct gh_sgl_desc *dup_gh_sgl_desc(struct gh_sgl_desc *sgl_desc); int mem_buf_assign_mem_gunyah(u32 op, struct sg_table *sgt, struct mem_buf_lend_kernel_arg *arg); int mem_buf_unassign_mem_gunyah(gh_memparcel_handle_t memparcel_hdl); @@ -82,10 +84,11 @@ static inline struct gh_sgl_desc *mem_buf_sgt_to_gh_sgl_desc(struct sg_table *sg return ERR_PTR(-EINVAL); } -static inline struct gh_sgl_desc *mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, - struct gh_acl_desc *acl_desc, int src_vmid) +static inline int mem_buf_map_mem_s2(u32 op, gh_memparcel_handle_t *__memparcel_hdl, + struct gh_acl_desc *acl_desc, struct gh_sgl_desc **__sgl_desc, + int src_vmid) { - return ERR_PTR(-EINVAL); + return -EINVAL; } static inline int mem_buf_unmap_mem_s2(gh_memparcel_handle_t memparcel_hdl) @@ -109,6 +112,11 @@ static inline struct sg_table *dup_gh_sgl_desc_to_sgt(struct gh_sgl_desc *sgl_de return ERR_PTR(-EINVAL); } +static inline struct gh_sgl_desc *dup_gh_sgl_desc(struct gh_sgl_desc *sgl_desc) +{ + return ERR_PTR(-EINVAL); +} + static inline int mem_buf_assign_mem_gunyah(u32 op, struct sg_table *sgt, struct mem_buf_lend_kernel_arg *arg) { diff --git a/drivers/soc/qcom/mem_buf/mem-buf-gh.c b/drivers/soc/qcom/mem_buf/mem-buf-gh.c index b81c523a479e..09ed104768d6 100644 --- a/drivers/soc/qcom/mem_buf/mem-buf-gh.c +++ b/drivers/soc/qcom/mem_buf/mem-buf-gh.c @@ -396,7 +396,7 @@ static void mem_buf_cleanup_alloc_req(struct mem_buf_xfer_mem *xfer_mem, if (ret < 0) return; } else { - struct gh_sgl_desc *sgl_desc; + struct gh_sgl_desc *sgl_desc = NULL; struct gh_acl_desc *acl_desc; size_t size; @@ -409,9 +409,9 @@ static void mem_buf_cleanup_alloc_req(struct mem_buf_xfer_mem *xfer_mem, acl_desc->acl_entries[0].vmid = VMID_HLOS; acl_desc->acl_entries[0].perms = GH_RM_ACL_X | GH_RM_ACL_W | GH_RM_ACL_R; - sgl_desc = mem_buf_map_mem_s2(GH_RM_TRANS_TYPE_DONATE, &memparcel_hdl, - acl_desc, VMID_TUIVM); - if (IS_ERR(sgl_desc)) { + ret = mem_buf_map_mem_s2(GH_RM_TRANS_TYPE_DONATE, &memparcel_hdl, + acl_desc, &sgl_desc, VMID_TUIVM); + if (ret) { kfree(acl_desc); return; } @@ -879,7 +879,6 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) int ret; struct file *filp; struct mem_buf_desc *membuf; - struct gh_sgl_desc *sgl_desc; int perms = PERM_READ | PERM_WRITE | PERM_EXEC; if (!(mem_buf_capability & MEM_BUF_CAP_CONSUMER)) @@ -897,6 +896,8 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) pr_debug("%s: mem buf alloc begin\n", __func__); membuf->size = ALIGN(alloc_data->size, MEM_BUF_MHP_ALIGNMENT); + + /* Create copies of data structures from alloc_data as they may be on-stack */ membuf->acl_desc = mem_buf_vmid_perm_list_to_gh_acl( alloc_data->vmids, &perms, alloc_data->nr_acl_entries); @@ -904,6 +905,15 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) ret = PTR_ERR(membuf->acl_desc); goto err_alloc_acl_list; } + + if (alloc_data->sgl_desc) { + membuf->sgl_desc = dup_gh_sgl_desc(alloc_data->sgl_desc); + if (IS_ERR(membuf->sgl_desc)) { + ret = PTR_ERR(membuf->sgl_desc); + goto err_alloc_sgl_desc; + } + } + membuf->trans_type = alloc_data->trans_type; membuf->src_mem_type = alloc_data->src_mem_type; membuf->dst_mem_type = alloc_data->dst_mem_type; @@ -936,11 +946,10 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) if (ret) goto err_mem_req; - sgl_desc = mem_buf_map_mem_s2(membuf->trans_type, &membuf->memparcel_hdl, - membuf->acl_desc, VMID_HLOS); - if (IS_ERR(sgl_desc)) + ret = mem_buf_map_mem_s2(membuf->trans_type, &membuf->memparcel_hdl, + membuf->acl_desc, &membuf->sgl_desc, VMID_HLOS); + if (ret) goto err_map_mem_s2; - membuf->sgl_desc = sgl_desc; ret = mem_buf_map_mem_s1(membuf->sgl_desc); if (ret) @@ -975,7 +984,6 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) err_map_mem_s1: err_map_mem_s2: mem_buf_relinquish_mem(membuf); - kvfree(membuf->sgl_desc); err_mem_req: mem_buf_destroy_txn(mem_buf_msgq_hdl, membuf->txn); err_init_txn: @@ -983,6 +991,9 @@ static void *mem_buf_alloc(struct mem_buf_allocation_data *alloc_data) err_alloc_dst_data: mem_buf_free_mem_type_data(membuf->src_mem_type, membuf->src_data); err_alloc_src_data: + if (membuf->sgl_desc) + kvfree(membuf->sgl_desc); +err_alloc_sgl_desc: kfree(membuf->acl_desc); err_alloc_acl_list: kfree(membuf); @@ -1077,7 +1088,8 @@ struct dma_buf *mem_buf_retrieve(struct mem_buf_retrieve_kernel_arg *arg) int ret, op; struct qcom_sg_buffer *buffer; struct gh_acl_desc *acl_desc; - struct gh_sgl_desc *sgl_desc; + /* Hypervisor picks the IPA address */ + struct gh_sgl_desc *sgl_desc = NULL; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); struct dma_buf *dmabuf; struct sg_table *sgt; @@ -1100,11 +1112,10 @@ struct dma_buf *mem_buf_retrieve(struct mem_buf_retrieve_kernel_arg *arg) } op = mem_buf_get_mem_xfer_type_gh(acl_desc, arg->sender_vmid); - sgl_desc = mem_buf_map_mem_s2(op, &arg->memparcel_hdl, acl_desc, arg->sender_vmid); - if (IS_ERR(sgl_desc)) { - ret = PTR_ERR(sgl_desc); + ret = mem_buf_map_mem_s2(op, &arg->memparcel_hdl, acl_desc, &sgl_desc, + arg->sender_vmid); + if (ret) goto err_map_s2; - } ret = mem_buf_map_mem_s1(sgl_desc); if (ret < 0) @@ -1173,6 +1184,7 @@ static int mem_buf_prep_alloc_data(struct mem_buf_allocation_data *alloc_data, goto err_acl; /* alloc_data->trans_type set later according to src&dest_mem_type */ + alloc_data->sgl_desc = NULL; alloc_data->src_mem_type = allocation_args->src_mem_type; alloc_data->dst_mem_type = allocation_args->dst_mem_type; diff --git a/include/linux/mem-buf.h b/include/linux/mem-buf.h index ad76a15ab279..f9e1ecb07b3f 100644 --- a/include/linux/mem-buf.h +++ b/include/linux/mem-buf.h @@ -40,6 +40,7 @@ int mem_buf_dma_buf_set_destructor(struct dma_buf *dmabuf, * @acl_list: A list of VMID and permission pairs that describe what VMIDs will * have access to the memory, and with what permissions * @trans_type: One of GH_RM_TRANS_TYPE_DONATE/LEND/SHARE + * @sgl_desc: Optional. Requests a specific set of IPA addresses. * @src_mem_type: The type of memory that the remote VM should allocate * (e.g. ION memory) * @src_data: A pointer to memory type specific data that the remote VM may need @@ -55,6 +56,7 @@ struct mem_buf_allocation_data { int *vmids; int *perms; u32 trans_type; + struct gh_sgl_desc *sgl_desc; enum mem_buf_mem_type src_mem_type; void *src_data; enum mem_buf_mem_type dst_mem_type;