VCSM: New option to import a DMABUF for VPU use
authorDave Stevenson <dave.stevenson@raspberrypi.org>
Wed, 15 Feb 2017 11:17:44 +0000 (11:17 +0000)
committerRaspbian kernel package updater <root@raspbian.org>
Sat, 31 Mar 2018 14:55:23 +0000 (15:55 +0100)
Takes a dmabuf, and then calls over to the VPU to wrap
it into a suitable handle.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
drivers/char/broadcom/vc_sm/vc_sm_defs.h
drivers/char/broadcom/vc_sm/vc_sm_knl.h
drivers/char/broadcom/vc_sm/vc_vchi_sm.c
drivers/char/broadcom/vc_sm/vc_vchi_sm.h
drivers/char/broadcom/vc_sm/vmcs_sm.c
include/linux/broadcom/vmcs_sm_ioctl.h

index 6c19aac6dc9df90edb9432bc32e7477d532d3278..bf057969644546d8f036044aa14d6606173b6a35 100644 (file)
@@ -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__ */
index 965f9a209a025202fea8065d3947c36f4fa43d0a..31050d3eb242b744c1ca43d6672d63b8439f7bc5 100644 (file)
@@ -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__ */
index 7c6ba1a244a8aff8132e0177e02b2b2a6cd364e0..5690b265e3fcc650449f009abb792ee84b97e788 100644 (file)
@@ -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);
+}
index f8e555dc30074e7ff226cf34a96aa30a34176775..562217831fa744db99d94295c1c5a46f3a4780fb 100644 (file)
@@ -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__ */
index 57cd47f22449ad26eaedc86b4540764ec880dd5b..91e34dcbe88411a639f077fdf93da70762b1b1a6 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/device.h>
 #include <linux/debugfs.h>
 #include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/hugetlb.h>
@@ -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(&current->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
 
 /*
index 2de7f1f41070689c99cad3bd43d117458549cb51..b75729d762f25aace133f7a008633b4094ae2de2 100644 (file)
@@ -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 ---------------------------------------------- */