aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch')
-rw-r--r--common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch690
1 files changed, 690 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch b/common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch
new file mode 100644
index 00000000..ca0a437f
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-yocto-4.14.71/1561-drm-amdkfd-initial-implementation-of-handle-based-IP.patch
@@ -0,0 +1,690 @@
+From 67115bb6b36683f65f4baf1c3e4c47b6799bd663 Mon Sep 17 00:00:00 2001
+From: Harish Kasiviswanathan <Harish.Kasiviswanathan@amd.com>
+Date: Sun, 20 Nov 2016 10:14:05 -0500
+Subject: [PATCH 1561/4131] drm/amdkfd: initial implementation of handle based
+ IPC
+
+To implement IPC support for legacy compute APIs, we need a handle based
+sharing interface. The interfaces are incompatible with DMABUF, which
+would be preferable for enhanced security (explicit sharing, and no
+extra code for handle management that increases the attack vectors).
+
+The handles are 128bit wide to prevent inter-process snooping.
+
+The buffer lifetime is refcounted. As long as one process maintains a
+reference to the buffer, the handle and the memory will be valid.
+
+Change-Id: I9f9b8e149eb1c540f6b1ef6429e56bf9533f0a9a
+Signed-off-by: Harish Kasiviswanathan <Harish.Kasiviswanathan@amd.com>
+---
+ drivers/gpu/drm/amd/amdkfd/Makefile | 2 +-
+ drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 87 +++++---
+ drivers/gpu/drm/amd/amdkfd/kfd_ipc.c | 331 +++++++++++++++++++++++++++++++
+ drivers/gpu/drm/amd/amdkfd/kfd_ipc.h | 52 +++++
+ drivers/gpu/drm/amd/amdkfd/kfd_module.c | 4 +
+ drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 9 +-
+ drivers/gpu/drm/amd/amdkfd/kfd_process.c | 10 +-
+ 7 files changed, 459 insertions(+), 36 deletions(-)
+ create mode 100644 drivers/gpu/drm/amd/amdkfd/kfd_ipc.c
+ create mode 100644 drivers/gpu/drm/amd/amdkfd/kfd_ipc.h
+
+diff --git a/drivers/gpu/drm/amd/amdkfd/Makefile b/drivers/gpu/drm/amd/amdkfd/Makefile
+index 8f3cbf6..84646ed 100644
+--- a/drivers/gpu/drm/amd/amdkfd/Makefile
++++ b/drivers/gpu/drm/amd/amdkfd/Makefile
+@@ -17,7 +17,7 @@ amdkfd-y := kfd_module.o kfd_device.o kfd_chardev.o kfd_topology.o \
+ kfd_device_queue_manager_cik.o kfd_device_queue_manager_vi.o \
+ kfd_interrupt.o kfd_events.o cik_event_interrupt.o \
+ kfd_dbgdev.o kfd_dbgmgr.o kfd_flat_memory.o kfd_crat.o kfd_rdma.o \
+- kfd_peerdirect.o
++ kfd_peerdirect.o kfd_ipc.o
+
+ amdkfd-$(CONFIG_DEBUG_FS) += kfd_debugfs.o
+
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+index f28b1c3..e664471 100644
+--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+@@ -37,6 +37,7 @@
+ #include "kfd_priv.h"
+ #include "kfd_device_queue_manager.h"
+ #include "kfd_dbgmgr.h"
++#include "kfd_ipc.h"
+ #include "cik_regs.h"
+
+ static long kfd_ioctl(struct file *, unsigned int, unsigned long);
+@@ -1179,7 +1180,7 @@ static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep,
+
+ down_write(&p->lock);
+ idr_handle = kfd_process_device_create_obj_handle(pdd, mem,
+- args->va_addr, args->size);
++ args->va_addr, args->size, NULL);
+ up_write(&p->lock);
+ if (idr_handle < 0) {
+ dev->kfd2kgd->free_memory_of_gpu(dev->kgd,
+@@ -1320,7 +1321,7 @@ static int kfd_ioctl_alloc_memory_of_gpu_new(struct file *filep,
+
+ down_write(&p->lock);
+ idr_handle = kfd_process_device_create_obj_handle(pdd, mem,
+- args->va_addr, args->size);
++ args->va_addr, args->size, NULL);
+ up_write(&p->lock);
+ if (idr_handle < 0) {
+ dev->kfd2kgd->free_memory_of_gpu(dev->kgd,
+@@ -1671,7 +1672,7 @@ static int kfd_ioctl_open_graphic_handle(struct file *filep,
+ * the corresponding interval tree. We need to know the size of
+ * the buffer through open_graphic_handle(). We use 1 for now.*/
+ idr_handle = kfd_process_device_create_obj_handle(pdd, mem,
+- args->va_addr, 1);
++ args->va_addr, 1, NULL);
+ up_write(&p->lock);
+ if (idr_handle < 0) {
+ /* FIXME: destroy_process_gpumem doesn't seem to be
+@@ -1776,47 +1777,62 @@ static int kfd_ioctl_get_dmabuf_info(struct file *filep,
+ }
+
+ static int kfd_ioctl_import_dmabuf(struct file *filep,
+- struct kfd_process *p, void *data)
++ struct kfd_process *p, void *data)
+ {
+ struct kfd_ioctl_import_dmabuf_args *args = data;
+ struct kfd_dev *dev;
+- struct kfd_process_device *pdd;
+- void *mem;
+- uint64_t size;
+- int idr_handle;
+ int r;
+
+ dev = kfd_device_by_id(args->gpu_id);
+- if (!dev || !dev->kfd2kgd->import_dmabuf)
++ if (!dev)
+ return -EINVAL;
+
+- down_write(&p->lock);
+- pdd = kfd_bind_process_to_device(dev, p);
+- up_write(&p->lock);
+- if (IS_ERR(pdd) < 0)
+- return PTR_ERR(pdd);
++ r = kfd_ipc_import_dmabuf(dev, p, args->gpu_id, args->dmabuf_fd,
++ args->va_addr, &args->handle, NULL);
++ if (r)
++ dev_err(kfd_device, "Failed to import dmabuf\n");
++
++ return r;
++}
++
++static int kfd_ioctl_ipc_export_handle(struct file *filep,
++ struct kfd_process *p,
++ void *data)
++{
++ struct kfd_ioctl_ipc_export_handle_args *args = data;
++ struct kfd_dev *dev;
++ int r;
+
+- r = dev->kfd2kgd->import_dmabuf(dev->kgd, args->dmabuf_fd,
+- args->va_addr, pdd->vm,
+- (struct kgd_mem **)&mem, &size,
+- NULL);
++ dev = kfd_device_by_id(args->gpu_id);
++ if (!dev)
++ return -EINVAL;
++
++ r = kfd_ipc_export_as_handle(dev, p, args->handle, args->share_handle);
+ if (r)
+- return r;
++ dev_err(kfd_device, "Failed to export IPC handle\n");
+
+- down_write(&p->lock);
+- idr_handle = kfd_process_device_create_obj_handle(pdd, mem,
+- args->va_addr, size);
+- up_write(&p->lock);
+- if (idr_handle < 0) {
+- dev->kfd2kgd->free_memory_of_gpu(dev->kgd,
+- (struct kgd_mem *)mem,
+- pdd->vm);
+- return -EFAULT;
+- }
++ return r;
++}
+
+- args->handle = MAKE_HANDLE(args->gpu_id, idr_handle);
++static int kfd_ioctl_ipc_import_handle(struct file *filep,
++ struct kfd_process *p,
++ void *data)
++{
++ struct kfd_ioctl_ipc_import_handle_args *args = data;
++ struct kfd_dev *dev = NULL;
++ int r;
+
+- return 0;
++ dev = kfd_device_by_id(args->gpu_id);
++ if (!dev)
++ return -EINVAL;
++
++ r = kfd_ipc_import_handle(dev, p, args->gpu_id, args->share_handle,
++ args->va_addr, &args->handle,
++ &args->mmap_offset);
++ if (r)
++ dev_err(kfd_device, "Failed to import IPC handle\n");
++
++ return r;
+ }
+
+ static int kfd_ioctl_get_tile_config(struct file *filep,
+@@ -1958,7 +1974,14 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = {
+ kfd_ioctl_import_dmabuf, 0),
+
+ AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_TILE_CONFIG,
+- kfd_ioctl_get_tile_config, 0)
++ kfd_ioctl_get_tile_config, 0),
++
++ AMDKFD_IOCTL_DEF(AMDKFD_IOC_IPC_IMPORT_HANDLE,
++ kfd_ioctl_ipc_import_handle, 0),
++
++ AMDKFD_IOCTL_DEF(AMDKFD_IOC_IPC_EXPORT_HANDLE,
++ kfd_ioctl_ipc_export_handle, 0)
++
+ };
+
+ #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls)
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_ipc.c b/drivers/gpu/drm/amd/amdkfd/kfd_ipc.c
+new file mode 100644
+index 0000000..edd2d43
+--- /dev/null
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_ipc.c
+@@ -0,0 +1,331 @@
++/*
++ * Copyright 2014 Advanced Micro Devices, Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include <linux/dma-buf.h>
++#include <linux/assoc_array.h>
++#include <linux/slab.h>
++#include <linux/random.h>
++
++#include "kfd_ipc.h"
++#include "kfd_priv.h"
++
++/**
++ * The assoc_array data structure provides a non-intrusive
++ * key-value data store mechanism where the key can be
++ * arbitratily long (and value is just a void*).
++ *
++ * This provides us good control for managing IPC handle length
++ * in case we require extra entropy to discourage handle-guessing.
++ */
++static struct assoc_array ipc_handles;
++
++static unsigned long ipc_get_key_chunk(const void *index_key, int level)
++{
++ unsigned long chunk;
++ int index = level/ASSOC_ARRAY_KEY_CHUNK_SIZE;
++
++ /* no more data, but we can't fail */
++ if (level > IPC_KEY_SIZE_BYTES*8)
++ return 0;
++
++ chunk = ((unsigned long *)index_key)[index];
++ return chunk;
++}
++
++static unsigned long ipc_get_object_key_chunk(const void *object, int level)
++{
++ const struct kfd_ipc_obj *obj = (const struct kfd_ipc_obj *)object;
++
++ return ipc_get_key_chunk(&obj->key, level);
++}
++
++static bool ipc_compare_object(const void *object, const void *index_key)
++{
++ const struct kfd_ipc_obj *obj = (const struct kfd_ipc_obj *)object;
++
++ return memcmp(obj->key, index_key, IPC_KEY_SIZE_BYTES) == 0;
++}
++
++/*
++ * Compare the index keys of a pair of objects and determine the bit position
++ * at which they differ - if they differ.
++ */
++static int ipc_diff_objects(const void *object, const void *index_key)
++{
++ const struct kfd_ipc_obj *obj = (const struct kfd_ipc_obj *)object;
++ int i;
++
++ /* naive linear byte search */
++ for (i = 0; i < IPC_KEY_SIZE_BYTES; ++i) {
++ if (obj->key[i] != ((char *)index_key)[i])
++ return i * 8;
++ }
++
++ return -1;
++}
++
++/*
++ * Free an object after stripping the ipc flag off of the pointer.
++ */
++static void ipc_free_object(void *object)
++{
++}
++
++/*
++ * Operations for ipc management by the index-tree routines.
++ */
++static const struct assoc_array_ops ipc_assoc_array_ops = {
++ .get_key_chunk = ipc_get_key_chunk,
++ .get_object_key_chunk = ipc_get_object_key_chunk,
++ .compare_object = ipc_compare_object,
++ .diff_objects = ipc_diff_objects,
++ .free_object = ipc_free_object,
++};
++
++static void ipc_gen_key(void *buf)
++{
++ uint32_t *key = (uint32_t *) buf;
++
++ get_random_bytes(buf, IPC_KEY_SIZE_BYTES);
++
++ /* last byte of the key is reserved */
++ key[3] = (key[3] & ~0xFF) | 0x1;
++}
++
++static int ipc_store_insert(void *val, void *key, struct kfd_ipc_obj **ipc_obj)
++{
++ struct kfd_ipc_obj *obj;
++ struct assoc_array_edit *edit;
++
++ obj = kmalloc(sizeof(struct kfd_ipc_obj), GFP_KERNEL);
++ if (!obj)
++ return -ENOMEM;
++
++ /* The initial ref belongs to the allocator process.
++ * The IPC object store itself does not hold a ref since
++ * there is no specific moment in time where that ref should
++ * be dropped, except "when there are no more userspace processes
++ * holding a ref to the object". Therefore the removal from IPC
++ * storage happens at ipc_obj release time.
++ */
++ kref_init(&obj->ref);
++ obj->data = val;
++ ipc_gen_key(obj->key);
++
++ memcpy(key, obj->key, IPC_KEY_SIZE_BYTES);
++
++ pr_debug("ipc: val::%p ref:%p\n", val, &obj->ref);
++
++ edit = assoc_array_insert(&ipc_handles,
++ &ipc_assoc_array_ops,
++ obj->key, obj);
++ assoc_array_apply_edit(edit);
++
++ if (ipc_obj)
++ *ipc_obj = obj;
++
++ return 0;
++}
++
++static int ipc_store_remove(void *key)
++{
++ struct assoc_array_edit *edit;
++
++ edit = assoc_array_delete(&ipc_handles, &ipc_assoc_array_ops, key);
++ assoc_array_apply_edit(edit);
++ return 0;
++}
++
++static void ipc_obj_release(struct kref *r)
++{
++ struct kfd_ipc_obj *obj;
++
++ obj = container_of(r, struct kfd_ipc_obj, ref);
++
++ ipc_store_remove(obj->key);
++ dma_buf_put(obj->data);
++ kfree(obj);
++}
++
++void ipc_obj_get(struct kfd_ipc_obj *obj)
++{
++ kref_get(&obj->ref);
++}
++
++void ipc_obj_put(struct kfd_ipc_obj **obj)
++{
++ kref_put(&(*obj)->ref, ipc_obj_release);
++ *obj = NULL;
++}
++
++int kfd_ipc_init(void)
++{
++ assoc_array_init(&ipc_handles);
++ return 0;
++}
++
++static int kfd_import_dmabuf_create_kfd_bo(struct kfd_dev *dev,
++ struct kfd_process *p,
++ uint32_t gpu_id, int dmabuf_fd,
++ uint64_t va_addr, uint64_t *handle,
++ uint64_t *mmap_offset,
++ struct kfd_ipc_obj *ipc_obj)
++{
++ int r;
++ void *mem;
++ uint64_t size;
++ int idr_handle;
++ struct kfd_process_device *pdd = NULL;
++ uint64_t kfd_mmap_flags = KFD_MMAP_TYPE_MAP_BO |
++ KFD_MMAP_GPU_ID(gpu_id);
++
++ if (!handle)
++ return -EINVAL;
++
++ if (!dev || !dev->kfd2kgd->import_dmabuf)
++ return -EINVAL;
++
++ down_write(&p->lock);
++ pdd = kfd_bind_process_to_device(dev, p);
++ up_write(&p->lock);
++ if (IS_ERR(pdd) < 0)
++ return PTR_ERR(pdd);
++
++ r = dev->kfd2kgd->import_dmabuf(dev->kgd, dmabuf_fd,
++ va_addr, pdd->vm,
++ (struct kgd_mem **)&mem, &size,
++ mmap_offset);
++ if (r)
++ return r;
++
++ down_write(&p->lock);
++ idr_handle = kfd_process_device_create_obj_handle(pdd, mem,
++ va_addr, size,
++ ipc_obj);
++ up_write(&p->lock);
++ if (idr_handle < 0) {
++ dev->kfd2kgd->free_memory_of_gpu(dev->kgd,
++ (struct kgd_mem *)mem,
++ pdd->vm);
++ return -EFAULT;
++ }
++
++ *handle = MAKE_HANDLE(gpu_id, idr_handle);
++
++ if (mmap_offset)
++ *mmap_offset = (kfd_mmap_flags << PAGE_SHIFT) | *mmap_offset;
++
++ return r;
++}
++
++int kfd_ipc_import_dmabuf(struct kfd_dev *dev,
++ struct kfd_process *p,
++ uint32_t gpu_id, int dmabuf_fd,
++ uint64_t va_addr, uint64_t *handle,
++ uint64_t *mmap_offset)
++{
++ return kfd_import_dmabuf_create_kfd_bo(dev, p, gpu_id, dmabuf_fd,
++ va_addr, handle, mmap_offset,
++ NULL);
++}
++
++int kfd_ipc_import_handle(struct kfd_dev *dev, struct kfd_process *p,
++ uint32_t gpu_id, uint32_t *share_handle,
++ uint64_t va_addr, uint64_t *handle,
++ uint64_t *mmap_offset)
++{
++ int r;
++ int dmabuf_fd;
++ struct kfd_ipc_obj *found;
++
++ found = assoc_array_find(&ipc_handles,
++ &ipc_assoc_array_ops,
++ share_handle);
++ if (!found)
++ return -EINVAL;
++ ipc_obj_get(found);
++
++ pr_debug("ipc: found ipc_dma_buf: %p\n", found->data);
++
++ dmabuf_fd = dma_buf_fd(found->data, 0);
++ r = kfd_import_dmabuf_create_kfd_bo(dev, p, gpu_id, dmabuf_fd,
++ va_addr, handle, mmap_offset,
++ found);
++ if (r)
++ goto error_unref;
++
++ return r;
++
++error_unref:
++ ipc_obj_put(&found);
++ return r;
++}
++
++int kfd_ipc_export_as_handle(struct kfd_dev *dev, struct kfd_process *p,
++ uint64_t handle, uint32_t *ipc_handle)
++{
++ struct kfd_process_device *pdd = NULL;
++ struct kfd_ipc_obj *obj;
++ struct kfd_bo *kfd_bo = NULL;
++ int dmabuf_fd;
++ int r;
++
++ if (!dev || !ipc_handle)
++ return -EINVAL;
++
++ down_write(&p->lock);
++ pdd = kfd_bind_process_to_device(dev, p);
++ up_write(&p->lock);
++
++ if (IS_ERR(pdd) < 0) {
++ pr_err("failed to get pdd\n");
++ return PTR_ERR(pdd);
++ }
++
++ down_write(&p->lock);
++ kfd_bo = kfd_process_device_find_bo(pdd, GET_IDR_HANDLE(handle));
++ up_write(&p->lock);
++
++ if (!kfd_bo) {
++ pr_err("failed to get bo");
++ return -EINVAL;
++ }
++ if (kfd_bo->kfd_ipc_obj) {
++ memcpy(ipc_handle, kfd_bo->kfd_ipc_obj->key,
++ sizeof(kfd_bo->kfd_ipc_obj->key));
++ return 0;
++ }
++
++ r = dev->kfd2kgd->export_dmabuf(dev->kgd, pdd->vm,
++ (struct kgd_mem *)kfd_bo->mem,
++ &dmabuf_fd);
++ if (r)
++ goto err;
++
++ r = ipc_store_insert(dma_buf_get(dmabuf_fd), ipc_handle, &obj);
++ if (r)
++ goto err;
++
++ kfd_bo->kfd_ipc_obj = obj;
++err:
++ return r;
++}
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_ipc.h b/drivers/gpu/drm/amd/amdkfd/kfd_ipc.h
+new file mode 100644
+index 0000000..52049d2
+--- /dev/null
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_ipc.h
+@@ -0,0 +1,52 @@
++/*
++ * Copyright 2014 Advanced Micro Devices, Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef KFD_IPC_H_
++#define KFD_IPC_H_
++
++#include <linux/types.h>
++#include "kfd_priv.h"
++
++#define IPC_KEY_SIZE_BYTES 16
++
++struct kfd_ipc_obj {
++ struct kref ref;
++ void *data;
++ char key[IPC_KEY_SIZE_BYTES];
++};
++
++int kfd_ipc_import_handle(struct kfd_dev *dev, struct kfd_process *p,
++ uint32_t gpu_id, uint32_t *share_handle,
++ uint64_t va_addr, uint64_t *handle,
++ uint64_t *mmap_offset);
++int kfd_ipc_import_dmabuf(struct kfd_dev *kfd, struct kfd_process *p,
++ uint32_t gpu_id, int dmabuf_fd,
++ uint64_t va_addr, uint64_t *handle,
++ uint64_t *mmap_offset);
++int kfd_ipc_export_as_handle(struct kfd_dev *dev, struct kfd_process *p,
++ uint64_t handle, uint32_t *ipc_handle);
++
++void ipc_obj_get(struct kfd_ipc_obj *obj);
++void ipc_obj_put(struct kfd_ipc_obj **obj);
++
++#endif /* KFD_IPC_H_ */
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c
+index ca82a02..274312f 100644
+--- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c
+@@ -133,6 +133,10 @@ static int __init kfd_module_init(void)
+ if (err < 0)
+ goto err_topology;
+
++ err = kfd_ipc_init();
++ if (err < 0)
++ goto err_topology;
++
+ kfd_process_create_wq();
+
+ kfd_init_peer_direct();
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+index d96d256..40dd5db 100644
+--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+@@ -280,11 +280,14 @@ struct kfd_dev {
+ uint32_t ib_size;
+ };
+
++struct kfd_ipc_obj;
++
+ struct kfd_bo {
+ void *mem;
+ struct interval_tree_node it;
+ struct kfd_dev *dev;
+ struct list_head cb_data_head;
++ struct kfd_ipc_obj *kfd_ipc_obj;
+ };
+
+ /* KGD2KFD callbacks */
+@@ -729,7 +732,8 @@ int kfd_reserved_mem_mmap(struct kfd_process *process, struct vm_area_struct *vm
+ /* KFD process API for creating and translating handles */
+ int kfd_process_device_create_obj_handle(struct kfd_process_device *pdd,
+ void *mem, uint64_t start,
+- uint64_t length);
++ uint64_t length,
++ struct kfd_ipc_obj *ipc_obj);
+ void *kfd_process_device_translate_handle(struct kfd_process_device *p,
+ int handle);
+ struct kfd_bo *kfd_process_device_find_bo(struct kfd_process_device *pdd,
+@@ -964,6 +968,9 @@ int dbgdev_wave_reset_wavefronts(struct kfd_dev *dev, struct kfd_process *p);
+ void kfd_init_peer_direct(void);
+ void kfd_close_peer_direct(void);
+
++/* IPC Support */
++int kfd_ipc_init(void);
++
+ /* Debugfs */
+ #if defined(CONFIG_DEBUG_FS)
+
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+index fa13696..63e85de 100644
+--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+@@ -32,6 +32,7 @@
+ #include <asm/tlb.h>
+ #include <linux/highmem.h>
+ #include <uapi/asm-generic/mman-common.h>
++#include "kfd_ipc.h"
+
+ struct mm_struct;
+
+@@ -118,7 +119,7 @@ static int kfd_process_alloc_gpuvm(struct kfd_process *p,
+ * created and the ioctls have not had the chance to run.
+ */
+ if (kfd_process_device_create_obj_handle(
+- pdd, mem, gpu_va, size) < 0) {
++ pdd, mem, gpu_va, size, NULL) < 0) {
+ err = -ENOMEM;
+ *kptr = NULL;
+ goto free_gpuvm;
+@@ -803,7 +804,8 @@ bool kfd_has_process_device_data(struct kfd_process *p)
+ * Assumes that the process lock is held. */
+ int kfd_process_device_create_obj_handle(struct kfd_process_device *pdd,
+ void *mem, uint64_t start,
+- uint64_t length)
++ uint64_t length,
++ struct kfd_ipc_obj *ipc_obj)
+ {
+ int handle;
+ struct kfd_bo *buf_obj;
+@@ -825,6 +827,7 @@ int kfd_process_device_create_obj_handle(struct kfd_process_device *pdd,
+
+ buf_obj->mem = mem;
+ buf_obj->dev = pdd->dev;
++ buf_obj->kfd_ipc_obj = ipc_obj;
+
+ INIT_LIST_HEAD(&buf_obj->cb_data_head);
+
+@@ -904,6 +907,9 @@ void kfd_process_device_remove_obj_handle(struct kfd_process_device *pdd,
+
+ buf_obj = kfd_process_device_find_bo(pdd, handle);
+
++ if (buf_obj->kfd_ipc_obj)
++ ipc_obj_put(&buf_obj->kfd_ipc_obj);
++
+ idr_remove(&pdd->alloc_idr, handle);
+
+ interval_tree_remove(&buf_obj->it, &p->bo_interval_tree);
+--
+2.7.4
+