From a97390852b14e8f06cf579adaaf8b664184e92d2 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Feb 2017 11:17:44 +0000 Subject: [PATCH] VCSM: New option to import a DMABUF for VPU use Takes a dmabuf, and then calls over to the VPU to wrap it into a suitable handle. Signed-off-by: Dave Stevenson --- drivers/char/broadcom/vc_sm/vc_sm_defs.h | 57 ++++- drivers/char/broadcom/vc_sm/vc_sm_knl.h | 21 +- drivers/char/broadcom/vc_sm/vc_vchi_sm.c | 9 + drivers/char/broadcom/vc_sm/vc_vchi_sm.h | 7 + drivers/char/broadcom/vc_sm/vmcs_sm.c | 269 ++++++++++++++++++++++- include/linux/broadcom/vmcs_sm_ioctl.h | 18 +- 6 files changed, 359 insertions(+), 22 deletions(-) diff --git a/drivers/char/broadcom/vc_sm/vc_sm_defs.h b/drivers/char/broadcom/vc_sm/vc_sm_defs.h index 6c19aac6dc9d..bf0579696445 100644 --- a/drivers/char/broadcom/vc_sm/vc_sm_defs.h +++ b/drivers/char/broadcom/vc_sm/vc_sm_defs.h @@ -26,8 +26,9 @@ /* Resource name maximum size */ #define VC_SM_RESOURCE_NAME 32 -/* All message types supported for HOST->VC direction */ typedef enum { + /* Message types supported for HOST->VC direction */ + /* Allocate shared memory block */ VC_SM_MSG_TYPE_ALLOC, /* Lock allocated shared memory block */ @@ -45,6 +46,21 @@ typedef enum { /* A previously applied action will need to be reverted */ VC_SM_MSG_TYPE_ACTION_CLEAN, + + /* + * Import a physical address and wrap into a MEM_HANDLE_T. + * Release with VC_SM_MSG_TYPE_FREE. + */ + VC_SM_MSG_TYPE_IMPORT, + + /* Message types supported for VC->HOST direction */ + + /* + * VC has finished with an imported memory allocation. + * Release any Linux reference counts on the underlying block. + */ + VC_SM_MSG_TYPE_RELEASED, + VC_SM_MSG_TYPE_MAX } VC_SM_MSG_TYPE; @@ -165,6 +181,41 @@ typedef struct { } VC_SM_FREE_ALL_T; +/* Request to import memory (HOST->VC) */ +struct vc_sm_import { + /* type of memory to allocate */ + VC_SM_ALLOC_TYPE_T type; + /* pointer to the VC (ie physical) address of the allocated memory */ + uint32_t addr; + /* size of buffer */ + uint32_t size; + /* opaque handle returned in RELEASED messages */ + int32_t kernel_id; + /* Allocator identifier */ + uint32_t allocator; + /* resource name (for easier tracking on vc side) */ + char name[VC_SM_RESOURCE_NAME]; +}; + +/* Result of a requested memory import (VC->HOST) */ +struct vc_sm_import_result { + /* Transaction identifier */ + uint32_t trans_id; + + /* Resource handle */ + uint32_t res_handle; +}; + +/* Notification that VC has finished with an allocation (VC->HOST) */ +struct vc_sm_released { + /* pointer to the VC (ie physical) address of the allocated memory */ + uint32_t addr; + /* size of buffer */ + uint32_t size; + /* opaque handle returned in RELEASED messages */ + int32_t kernel_id; +}; + /* Union of ALL messages */ typedef union { VC_SM_ALLOC_T alloc; @@ -175,7 +226,9 @@ typedef union { VC_SM_LOCK_RESULT_T lock_result; VC_SM_RESULT_T result; VC_SM_FREE_ALL_T free_all; - + struct vc_sm_import import; + struct vc_sm_import_result import_result; + struct vc_sm_released released; } VC_SM_MSG_UNION_T; #endif /* __VC_SM_DEFS_H__INCLUDED__ */ diff --git a/drivers/char/broadcom/vc_sm/vc_sm_knl.h b/drivers/char/broadcom/vc_sm/vc_sm_knl.h index 965f9a209a02..31050d3eb242 100644 --- a/drivers/char/broadcom/vc_sm/vc_sm_knl.h +++ b/drivers/char/broadcom/vc_sm/vc_sm_knl.h @@ -26,30 +26,27 @@ typedef enum { } VC_SM_LOCK_CACHE_MODE_T; -/* Allocate a shared memory handle and block. -*/ +/* Allocate a shared memory handle and block. */ int vc_sm_alloc(VC_SM_ALLOC_T *alloc, int *handle); -/* Free a previously allocated shared memory handle and block. -*/ +/* Free a previously allocated shared memory handle and block. */ int vc_sm_free(int handle); -/* Lock a memory handle for use by kernel. -*/ +/* Lock a memory handle for use by kernel. */ int vc_sm_lock(int handle, VC_SM_LOCK_CACHE_MODE_T mode, long unsigned int *data); -/* Unlock a memory handle in use by kernel. -*/ +/* Unlock a memory handle in use by kernel. */ int vc_sm_unlock(int handle, int flush, int no_vc_unlock); -/* Get an internal resource handle mapped from the external one. -*/ +/* Get an internal resource handle mapped from the external one. */ int vc_sm_int_handle(int handle); -/* Map a shared memory region for use by kernel. -*/ +/* Map a shared memory region for use by kernel. */ int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, long unsigned int *data); +/* Import a block of memory into the GPU space. */ +int vc_sm_import_dmabuf(struct dma_buf *dmabuf, int *handle); + #endif /* __VC_SM_KNL_H__INCLUDED__ */ diff --git a/drivers/char/broadcom/vc_sm/vc_vchi_sm.c b/drivers/char/broadcom/vc_sm/vc_vchi_sm.c index 7c6ba1a244a8..5690b265e3fc 100644 --- a/drivers/char/broadcom/vc_sm/vc_vchi_sm.c +++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.c @@ -490,3 +490,12 @@ int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, VC_SM_ACTION_CLEAN_T *msg) return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_ACTION_CLEAN, msg, sizeof(*msg), 0, 0, 0, 0); } + +int vc_vchi_sm_import(VC_VCHI_SM_HANDLE_T handle, struct vc_sm_import *msg, + struct vc_sm_import_result *result, + uint32_t *cur_trans_id) +{ + return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_IMPORT, + msg, sizeof(*msg), result, sizeof(*result), + cur_trans_id, 1); +} diff --git a/drivers/char/broadcom/vc_sm/vc_vchi_sm.h b/drivers/char/broadcom/vc_sm/vc_vchi_sm.h index f8e555dc3007..562217831fa7 100644 --- a/drivers/char/broadcom/vc_sm/vc_vchi_sm.h +++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.h @@ -89,4 +89,11 @@ int vc_vchi_sm_walk_alloc(VC_VCHI_SM_HANDLE_T handle); int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, VC_SM_ACTION_CLEAN_T *action_clean); +/* + * Import a contiguous block of memory and wrap it in a GPU MEM_HANDLE_T. + */ +int vc_vchi_sm_import(VC_VCHI_SM_HANDLE_T handle, struct vc_sm_import *msg, + struct vc_sm_import_result *result, + uint32_t *cur_trans_id); + #endif /* __VC_VCHI_SM_H__INCLUDED__ */ diff --git a/drivers/char/broadcom/vc_sm/vmcs_sm.c b/drivers/char/broadcom/vc_sm/vmcs_sm.c index 57cd47f22449..91e34dcbe884 100644 --- a/drivers/char/broadcom/vc_sm/vmcs_sm.c +++ b/drivers/char/broadcom/vc_sm/vmcs_sm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ enum SM_STATS_T { MAP, FLUSH, INVALID, + IMPORT, END_ATTEMPT, @@ -80,6 +82,7 @@ enum SM_STATS_T { MAP_FAIL, FLUSH_FAIL, INVALID_FAIL, + IMPORT_FAIL, END_ALL, @@ -93,6 +96,7 @@ static const char *const sm_stats_human_read[] = { "Map", "Cache Flush", "Cache Invalidate", + "Import", }; typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v); @@ -144,6 +148,12 @@ struct SM_RESOURCE_T { uint8_t map_count; /* Counter of mappings for this resource. */ struct list_head map_list; /* Maps associated with a resource. */ + /* DMABUF related fields */ + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + dma_addr_t dma_addr; + struct SM_PRIV_DATA_T *private; bool map; /* whether to map pages up front */ }; @@ -500,7 +510,9 @@ static void vmcs_sm_remove_map(struct SM_STATE_T *state, static int vc_sm_global_state_show(struct seq_file *s, void *v) { struct sm_mmap *map = NULL; + struct SM_RESOURCE_T *resource = NULL; int map_count = 0; + int resource_count = 0; if (sm_state == NULL) return 0; @@ -512,7 +524,41 @@ static int vc_sm_global_state_show(struct seq_file *s, void *v) */ mutex_lock(&(sm_state->map_lock)); + seq_puts(s, "\nResources\n"); + if (!list_empty(&sm_state->resource_list)) { + list_for_each_entry(resource, &sm_state->resource_list, + global_resource_list) { + resource_count++; + + seq_printf(s, "\nResource %p\n", + resource); + seq_printf(s, " PID %u\n", + resource->pid); + seq_printf(s, " RES_GUID 0x%x\n", + resource->res_guid); + seq_printf(s, " LOCK_COUNT %u\n", + resource->lock_count); + seq_printf(s, " REF_COUNT %u\n", + resource->ref_count); + seq_printf(s, " res_handle 0x%X\n", + resource->res_handle); + seq_printf(s, " res_base_mem %p\n", + resource->res_base_mem); + seq_printf(s, " SIZE %d\n", + resource->res_size); + seq_printf(s, " DMABUF %p\n", + resource->dma_buf); + seq_printf(s, " ATTACH %p\n", + resource->attach); + seq_printf(s, " SGT %p\n", + resource->sgt); + seq_printf(s, " DMA_ADDR 0x%08X\n", + resource->dma_addr); + } + } + seq_printf(s, "\n\nTotal resource count: %d\n\n", resource_count); + seq_puts(s, "\nMappings\n"); if (!list_empty(&sm_state->map_list)) { list_for_each_entry(map, &sm_state->map_list, map_list) { map_count++; @@ -527,6 +573,8 @@ static int vc_sm_global_state_show(struct seq_file *s, void *v) map->res_usr_hdl); seq_printf(s, " USR-ADDR 0x%lx\n", map->res_addr); + seq_printf(s, " SIZE %d\n", + map->resource->res_size); } } @@ -843,7 +891,8 @@ static void vmcs_sm_release_resource(struct SM_RESOURCE_T *resource, int force) list_del(&resource->resource_list); list_del(&resource->global_resource_list); - /* Walk the global resource list, find out if the resource is used + /* + * Walk the global resource list, find out if the resource is used * somewhere else. In which case we don't want to delete it. */ list_for_each_entry(res_tmp, &sm_state->resource_list, @@ -877,8 +926,7 @@ static void vmcs_sm_release_resource(struct SM_RESOURCE_T *resource, int force) up_write(¤t->mm->mmap_sem); } - /* Free up the videocore allocated resource. - */ + /* Free up the videocore allocated resource. */ if (resource->res_handle) { VC_SM_FREE_T free = { resource->res_handle, (uint32_t)resource->res_base_mem @@ -893,13 +941,19 @@ static void vmcs_sm_release_resource(struct SM_RESOURCE_T *resource, int force) } } - /* Free up the shared resource. - */ + if (resource->sgt) + dma_buf_unmap_attachment(resource->attach, resource->sgt, + DMA_BIDIRECTIONAL); + if (resource->attach) + dma_buf_detach(resource->dma_buf, resource->attach); + if (resource->dma_buf) + dma_buf_put(resource->dma_buf); + + /* Free up the shared resource. */ if (resource->res_shared) vmcs_sm_release_resource(resource->res_shared, 0); - /* Free up the local resource tracking this allocation. - */ + /* Free up the local resource tracking this allocation. */ vc_sm_resource_deceased(resource, force); kfree(resource); } @@ -2055,6 +2109,137 @@ error: return ret; } +/* + * Import a contiguous block of memory to be shared with VC. + */ +int vc_sm_ioctl_import_dmabuf(struct SM_PRIV_DATA_T *private, + struct vmcs_sm_ioctl_import_dmabuf *ioparam, + struct dma_buf *src_dma_buf) +{ + int ret = 0; + int status; + struct SM_RESOURCE_T *resource = NULL; + struct vc_sm_import import = { 0 }; + struct vc_sm_import_result result = { 0 }; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach = NULL; + struct sg_table *sgt = NULL; + + /* Setup our allocation parameters */ + if (src_dma_buf) { + get_dma_buf(src_dma_buf); + dma_buf = src_dma_buf; + } else { + dma_buf = dma_buf_get(ioparam->dmabuf_fd); + } + if (IS_ERR(dma_buf)) + return PTR_ERR(dma_buf); + + attach = dma_buf_attach(dma_buf, &sm_state->pdev->dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto error; + } + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto error; + } + + /* Verify that the address block is contiguous */ + if (sgt->nents != 1) { + ret = -ENOMEM; + goto error; + } + + import.type = ((ioparam->cached == VMCS_SM_CACHE_VC) || + (ioparam->cached == VMCS_SM_CACHE_BOTH)) ? + VC_SM_ALLOC_CACHED : VC_SM_ALLOC_NON_CACHED; + import.addr = (uint32_t)sg_dma_address(sgt->sgl); + import.size = sg_dma_len(sgt->sgl); + import.allocator = current->tgid; + + if (*ioparam->name) + memcpy(import.name, ioparam->name, sizeof(import.name) - 1); + else + memcpy(import.name, VMCS_SM_RESOURCE_NAME_DEFAULT, + sizeof(VMCS_SM_RESOURCE_NAME_DEFAULT)); + + pr_debug("[%s]: attempt to import \"%s\" data - type %u, addr %p, size %u\n", + __func__, import.name, import.type, + (void *)import.addr, import.size); + + /* Allocate local resource to track this allocation. */ + resource = kzalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) { + ret = -ENOMEM; + goto error; + } + INIT_LIST_HEAD(&resource->map_list); + resource->ref_count++; + resource->pid = current->tgid; + + /* Allocate the videocore resource. */ + status = vc_vchi_sm_import(sm_state->sm_handle, &import, &result, + &private->int_trans_id); + if (status == -EINTR) { + pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n", + __func__, private->int_trans_id); + ret = -ERESTARTSYS; + private->restart_sys = -EINTR; + private->int_action = VC_SM_MSG_TYPE_IMPORT; + goto error; + } else if (status || !result.res_handle) { + pr_debug("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n", + __func__, status, private->int_trans_id); + ret = -ENOMEM; + resource->res_stats[ALLOC_FAIL]++; + goto error; + } + + /* Keep track of the resource we created. + */ + resource->private = private; + resource->res_handle = result.res_handle; + resource->res_size = import.size; + resource->res_cached = ioparam->cached; + + resource->dma_buf = dma_buf; + resource->attach = attach; + resource->sgt = sgt; + resource->dma_addr = sg_dma_address(sgt->sgl); + + /* Kernel/user GUID. This global identifier is used for mmap'ing the + * allocated region from user space, it is passed as the mmap'ing + * offset, we use it to 'hide' the videocore handle/address. + */ + mutex_lock(&sm_state->lock); + resource->res_guid = ++sm_state->guid; + mutex_unlock(&sm_state->lock); + resource->res_guid <<= PAGE_SHIFT; + + vmcs_sm_add_resource(private, resource); + + /* We're done */ + resource->res_stats[IMPORT]++; + ioparam->handle = resource->res_guid; + return 0; + +error: + resource->res_stats[IMPORT_FAIL]++; + if (resource) { + vc_sm_resource_deceased(resource, 1); + kfree(resource); + } + if (sgt) + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); + if (attach) + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + return ret; +} + /* Handle control from host. */ static long vc_sm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -2166,6 +2351,40 @@ static long vc_sm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } break; + case VMCS_SM_CMD_IMPORT_DMABUF: + { + struct vmcs_sm_ioctl_import_dmabuf ioparam; + + /* Get the parameter data. + */ + if (copy_from_user + (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { + pr_err("[%s]: failed to copy-from-user for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + goto out; + } + + ret = vc_sm_ioctl_import_dmabuf(file_data, &ioparam, + NULL); + if (!ret && + (copy_to_user((void *)arg, + &ioparam, sizeof(ioparam)) != 0)) { + struct vmcs_sm_ioctl_free freeparam = { + ioparam.handle + }; + pr_err("[%s]: failed to copy-to-user for cmd %x\n", + __func__, cmdnr); + vc_sm_ioctl_free(file_data, &freeparam); + ret = -EFAULT; + } + + /* Done. + */ + goto out; + } + break; + /* Lock (attempt to) *and* register a cache behavior change. */ case VMCS_SM_CMD_LOCK_CACHE: @@ -3301,6 +3520,42 @@ int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, return ret; } EXPORT_SYMBOL_GPL(vc_sm_map); + +/* Import a dmabuf to be shared with VC. */ +int vc_sm_import_dmabuf(struct dma_buf *dmabuf, int *handle) +{ + struct vmcs_sm_ioctl_import_dmabuf ioparam = { 0 }; + int ret; + struct SM_RESOURCE_T *resource; + + /* Validate we can work with this device. */ + if (!sm_state || !dmabuf || !handle) { + pr_err("[%s]: invalid input\n", __func__); + return -EPERM; + } + + ioparam.cached = 0; + strcpy(ioparam.name, "KRNL DMABUF"); + + ret = vc_sm_ioctl_import_dmabuf(sm_state->data_knl, &ioparam, dmabuf); + + if (!ret) { + resource = vmcs_sm_acquire_resource(sm_state->data_knl, + ioparam.handle); + if (resource) { + resource->pid = 0; + vmcs_sm_release_resource(resource, 0); + + /* Assign valid handle at this time.*/ + *handle = ioparam.handle; + } else { + ret = -ENOMEM; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(vc_sm_import_dmabuf); #endif /* diff --git a/include/linux/broadcom/vmcs_sm_ioctl.h b/include/linux/broadcom/vmcs_sm_ioctl.h index 2de7f1f41070..b75729d762f2 100644 --- a/include/linux/broadcom/vmcs_sm_ioctl.h +++ b/include/linux/broadcom/vmcs_sm_ioctl.h @@ -64,7 +64,9 @@ enum vmcs_sm_cmd_e { VMCS_SM_CMD_CLEAN_INVALID, VMCS_SM_CMD_CLEAN_INVALID2, - VMCS_SM_CMD_LAST /* Do no delete */ + VMCS_SM_CMD_IMPORT_DMABUF, + + VMCS_SM_CMD_LAST /* Do not delete */ }; /* Cache type supported, conveniently matches the user space definition in @@ -188,6 +190,16 @@ struct vmcs_sm_ioctl_clean_invalid2 { } s[0]; }; +struct vmcs_sm_ioctl_import_dmabuf { + /* user -> kernel */ + int dmabuf_fd; + enum vmcs_sm_cache_e cached; + char name[VMCS_SM_RESOURCE_NAME]; + + /* kernel -> user */ + unsigned int handle; +}; + /* IOCTL numbers */ #define VMCS_SM_IOCTL_MEM_ALLOC\ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_ALLOC,\ @@ -257,6 +269,10 @@ struct vmcs_sm_ioctl_clean_invalid2 { _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_PID_MAP,\ struct vmcs_sm_ioctl_walk) +#define VMCS_SM_IOCTL_MEM_IMPORT_DMABUF\ + _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_IMPORT_DMABUF,\ + struct vmcs_sm_ioctl_import_dmabuf) + /* ---- Variable Externs ------------------------------------------------- */ /* ---- Function Prototypes ---------------------------------------------- */ -- 2.30.2