[PATCH v5 07/10] gna: add GNA_COMPUTE ioctl

Maciej Kwapulinski maciej.kwapulinski at linux.intel.com
Thu Oct 20 17:53:31 UTC 2022


From: Tomasz Jankowski <tomasz1.jankowski at intel.com>

This is the ioctl context part of score request + "do nothing" work queue
implementation. Work queue context actual part will be added by next patch.

signed-off-by: Tomasz Jankowski <tomasz1.jankowski at intel.com>
Tested-by: Mikolaj Grzybowski <mikolajx.grzybowski at intel.com>
Co-developed-by: Anisha Dattatraya Kulkarni <anisha.dattatraya.kulkarni at intel.com>
Signed-off-by: Anisha Dattatraya Kulkarni <anisha.dattatraya.kulkarni at intel.com>
Co-developed-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
Co-developed-by: Maciej Kwapulinski <maciej.kwapulinski at linux.intel.com>
Signed-off-by: Maciej Kwapulinski <maciej.kwapulinski at linux.intel.com>
---
 drivers/gpu/drm/gna/Kbuild        |   2 +-
 drivers/gpu/drm/gna/gna_device.c  |  33 +++
 drivers/gpu/drm/gna/gna_device.h  |  21 ++
 drivers/gpu/drm/gna/gna_gem.h     |   4 +
 drivers/gpu/drm/gna/gna_ioctl.c   |  27 +++
 drivers/gpu/drm/gna/gna_mem.c     |  43 ++++
 drivers/gpu/drm/gna/gna_mem.h     |   2 +
 drivers/gpu/drm/gna/gna_request.c | 338 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/gna/gna_request.h |  47 +++++
 include/uapi/drm/gna_drm.h        |  57 +++++
 10 files changed, 573 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/gna/gna_request.c
 create mode 100644 drivers/gpu/drm/gna/gna_request.h

diff --git a/drivers/gpu/drm/gna/Kbuild b/drivers/gpu/drm/gna/Kbuild
index 101880869dc4..15c5e4fe7e4d 100644
--- a/drivers/gpu/drm/gna/Kbuild
+++ b/drivers/gpu/drm/gna/Kbuild
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-gna-y := gna_device.o gna_ioctl.o gna_mem.o gna_pci.o
+gna-y := gna_device.o gna_ioctl.o gna_mem.o gna_pci.o gna_request.o
 
 obj-$(CONFIG_DRM_GNA) += gna.o
diff --git a/drivers/gpu/drm/gna/gna_device.c b/drivers/gpu/drm/gna/gna_device.c
index ab9e16121dcf..0f8ec5a9dde4 100644
--- a/drivers/gpu/drm/gna/gna_device.c
+++ b/drivers/gpu/drm/gna/gna_device.c
@@ -11,11 +11,14 @@
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 #include <uapi/drm/gna_drm.h>
 
 #include "gna_device.h"
 #include "gna_gem.h"
+#include "gna_request.h"
+
 #define GNA_DDI_VERSION_CURRENT GNA_DDI_VERSION_3
 
 DEFINE_DRM_GEM_FOPS(gna_drm_fops);
@@ -24,6 +27,7 @@ static const struct drm_ioctl_desc gna_drm_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(GNA_GET_PARAMETER, gna_getparam_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(GNA_GEM_NEW, gna_gem_new_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(GNA_GEM_FREE, gna_gem_free_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(GNA_COMPUTE, gna_score_ioctl, DRM_RENDER_ALLOW),
 };
 
 
@@ -43,6 +47,24 @@ static int gna_drm_dev_init(struct drm_device *dev)
 	return drmm_add_action_or_reset(dev, gna_drm_dev_fini, NULL);
 }
 
+static void gna_workqueue_fini(struct drm_device *drm, void *data)
+{
+	struct workqueue_struct *request_wq = data;
+
+	destroy_workqueue(request_wq);
+}
+
+static int gna_workqueue_init(struct gna_device *gna_priv)
+{
+	const char *name = gna_name(gna_priv);
+
+	gna_priv->request_wq = create_singlethread_workqueue(name);
+	if (!gna_priv->request_wq)
+		return -EFAULT;
+
+	return drmm_add_action_or_reset(&gna_priv->drm, gna_workqueue_fini, gna_priv->request_wq);
+}
+
 static struct drm_gem_object *gna_create_gem_object(struct drm_device *dev,
 						size_t size)
 {
@@ -90,6 +112,8 @@ int gna_probe(struct device *parent, struct gna_dev_info *dev_info, void __iomem
 	gna_priv->iobase = iobase;
 	gna_priv->info = *dev_info;
 
+	atomic_set(&gna_priv->enqueued_requests, 0);
+
 	if (!(sizeof(dma_addr_t) > 4) ||
 		dma_set_mask(parent, DMA_BIT_MASK(64))) {
 		err = dma_set_mask(parent, DMA_BIT_MASK(32));
@@ -106,6 +130,15 @@ int gna_probe(struct device *parent, struct gna_dev_info *dev_info, void __iomem
 
 	dev_dbg(parent, "maximum memory size %llu num pd %d\n",
 		gna_priv->info.max_hw_mem, gna_priv->info.num_pagetables);
+	atomic_set(&gna_priv->request_count, 0);
+
+	mutex_init(&gna_priv->reqlist_lock);
+	INIT_LIST_HEAD(&gna_priv->request_list);
+
+	err = gna_workqueue_init(gna_priv);
+	if (err)
+		return err;
+
 
 	dev_set_drvdata(parent, drm_dev);
 
diff --git a/drivers/gpu/drm/gna/gna_device.h b/drivers/gpu/drm/gna/gna_device.h
index 329729f392d8..b628ffa9970f 100644
--- a/drivers/gpu/drm/gna/gna_device.h
+++ b/drivers/gpu/drm/gna/gna_device.h
@@ -7,7 +7,10 @@
 #include <drm/drm_device.h>
 #include <drm/drm_gem_shmem_helper.h>
 
+#include <linux/atomic.h>
 #include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/types.h>
 
 #include "gna_gem.h"
@@ -22,6 +25,7 @@
 #define DRIVER_MINOR		0
 #define DRIVER_PATCHLEVEL	0
 
+struct workqueue_struct;
 union gna_parameter;
 struct drm_file;
 struct device;
@@ -37,6 +41,15 @@ struct gna_device {
 	struct gna_hw_info hw_info;
 
 	struct gna_mmu_object mmu;
+
+	struct list_head request_list;
+	/* protects request_list */
+	struct mutex reqlist_lock;
+	struct workqueue_struct *request_wq;
+	atomic_t request_count;
+
+	/* requests that are in queue to be run +1 for currently processed one */
+	atomic_t enqueued_requests;
 };
 
 int gna_probe(struct device *parent, struct gna_dev_info *dev_info, void __iomem *iobase);
@@ -51,11 +64,19 @@ int gna_gem_new_ioctl(struct drm_device *dev, void *data,
 int gna_gem_free_ioctl(struct drm_device *dev, void *data,
 		struct drm_file *file);
 
+int gna_score_ioctl(struct drm_device *dev, void *data,
+		struct drm_file *file);
+
 static inline u32 gna_reg_read(struct gna_device *gna_priv, u32 reg)
 {
 	return readl(gna_priv->iobase + reg);
 }
 
+static inline const char *gna_name(struct gna_device *gna_priv)
+{
+	return gna_priv->drm.unique;
+}
+
 static inline struct device *gna_dev(struct gna_device *gna_priv)
 {
 	return gna_priv->drm.dev;
diff --git a/drivers/gpu/drm/gna/gna_gem.h b/drivers/gpu/drm/gna/gna_gem.h
index f8fa6f35a788..18ffa8245380 100644
--- a/drivers/gpu/drm/gna/gna_gem.h
+++ b/drivers/gpu/drm/gna/gna_gem.h
@@ -6,10 +6,14 @@
 
 #include <drm/drm_gem_shmem_helper.h>
 
+#include <linux/workqueue.h>
+
 struct gna_gem_object {
 	struct drm_gem_shmem_object base;
 
 	uint32_t handle;
+
+	struct work_struct work;
 };
 
 #endif /* __GNA_GEM_H__ */
diff --git a/drivers/gpu/drm/gna/gna_ioctl.c b/drivers/gpu/drm/gna/gna_ioctl.c
index 5051e9af6b06..ab3a2b789589 100644
--- a/drivers/gpu/drm/gna/gna_ioctl.c
+++ b/drivers/gpu/drm/gna/gna_ioctl.c
@@ -5,10 +5,33 @@
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_file.h>
 
+#include <linux/workqueue.h>
+
 #include <uapi/drm/gna_drm.h>
 
 #include "gna_device.h"
 #include "gna_gem.h"
+#include "gna_request.h"
+
+int gna_score_ioctl(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	union gna_compute *score_args = data;
+	u64 request_id;
+	int ret;
+
+	ret = gna_validate_score_config(&score_args->in.config, to_gna_device(dev));
+	if (ret)
+		return ret;
+
+	ret = gna_enqueue_request(&score_args->in.config, file, &request_id);
+	if (ret)
+		return ret;
+
+	score_args->out.request_id = request_id;
+
+	return 0;
+}
 
 int gna_gem_free_ioctl(struct drm_device *dev, void *data,
 		struct drm_file *file)
@@ -25,6 +48,9 @@ int gna_gem_free_ioctl(struct drm_device *dev, void *data,
 
 	gnagemo = to_gna_gem_obj(to_drm_gem_shmem_obj(drmgemo));
 
+	queue_work(gna_priv->request_wq, &gnagemo->work);
+	cancel_work_sync(&gnagemo->work);
+
 	ret = drm_gem_handle_delete(file, args->handle);
 
 	drm_gem_object_put(drmgemo);
@@ -84,5 +110,6 @@ int gna_gem_new_ioctl(struct drm_device *dev, void *data,
 	gnagemo = to_gna_gem_obj(drmgemshm);
 	gnagemo->handle = args->out.handle;
 
+	INIT_WORK(&gnagemo->work, gna_gem_obj_release_work);
 	return 0;
 }
diff --git a/drivers/gpu/drm/gna/gna_mem.c b/drivers/gpu/drm/gna/gna_mem.c
index 21e266583e27..54c5a4d68d06 100644
--- a/drivers/gpu/drm/gna/gna_mem.c
+++ b/drivers/gpu/drm/gna/gna_mem.c
@@ -1,15 +1,23 @@
 // SPDX-License-Identifier: GPL-2.0-only
 // Copyright(c) 2017-2022 Intel Corporation
 
+#include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_managed.h>
 
+#include <linux/atomic.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/kref.h>
+#include <linux/list.h>
 #include <linux/math.h>
 #include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #include "gna_device.h"
+#include "gna_gem.h"
 #include "gna_mem.h"
+#include "gna_request.h"
 
 static void gna_mmu_set(struct gna_device *gna_priv)
 {
@@ -76,3 +84,38 @@ int gna_mmu_init(struct gna_device *gna_priv)
 
 	return 0;
 }
+
+static void gna_delete_score_requests(u32 handle, struct gna_device *gna_priv)
+{
+	struct gna_request *req, *temp_req;
+	struct list_head *reqs_list;
+	int i;
+
+	mutex_lock(&gna_priv->reqlist_lock);
+
+	reqs_list = &gna_priv->request_list;
+	if (!list_empty(reqs_list)) {
+		list_for_each_entry_safe(req, temp_req, reqs_list, node) {
+			for (i = 0; i < req->buffer_count; ++i) {
+				if (req->buffer_list[i].gna.handle == handle) {
+					list_del_init(&req->node);
+					cancel_work_sync(&req->work);
+					atomic_dec(&gna_priv->enqueued_requests);
+					kref_put(&req->refcount, gna_request_release);
+					break;
+				}
+			}
+		}
+	}
+
+	mutex_unlock(&gna_priv->reqlist_lock);
+}
+
+void gna_gem_obj_release_work(struct work_struct *work)
+{
+	struct gna_gem_object *gnagemo;
+
+	gnagemo = container_of(work, struct gna_gem_object, work);
+
+	gna_delete_score_requests(gnagemo->handle, to_gna_device(gnagemo->base.base.dev));
+}
diff --git a/drivers/gpu/drm/gna/gna_mem.h b/drivers/gpu/drm/gna/gna_mem.h
index f9b8dcb1399f..9d8251900231 100644
--- a/drivers/gpu/drm/gna/gna_mem.h
+++ b/drivers/gpu/drm/gna/gna_mem.h
@@ -47,4 +47,6 @@ struct gna_mmu_object {
 
 int gna_mmu_init(struct gna_device *gna_priv);
 
+void gna_gem_obj_release_work(struct work_struct *work);
+
 #endif // __GNA_MEM_H__
diff --git a/drivers/gpu/drm/gna/gna_request.c b/drivers/gpu/drm/gna/gna_request.c
new file mode 100644
index 000000000000..7151d7c2b353
--- /dev/null
+++ b/drivers/gpu/drm/gna/gna_request.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2017-2022 Intel Corporation
+
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/math.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "gna_device.h"
+#include "gna_request.h"
+
+int gna_validate_score_config(struct gna_compute_cfg *compute_cfg,
+			struct gna_device *gna_priv)
+{
+	size_t buffers_size;
+
+	if (compute_cfg->gna_mode > GNA_MODE_XNN) {
+		dev_dbg(gna_dev(gna_priv), "invalid mode: %d\n", compute_cfg->gna_mode);
+		return -EINVAL;
+	}
+
+	if (compute_cfg->layer_count > gna_priv->info.max_layer_count) {
+		dev_dbg(gna_dev(gna_priv), "max layer count exceeded: %u > %u\n",
+			compute_cfg->layer_count, gna_priv->info.max_layer_count);
+		return -EINVAL;
+	}
+
+	if (compute_cfg->buffer_count == 0) {
+		dev_dbg(gna_dev(gna_priv), "no buffers\n");
+		return -EINVAL;
+	}
+
+	buffers_size = sizeof(struct gna_buffer) * compute_cfg->buffer_count;
+	if (!access_ok(u64_to_user_ptr(compute_cfg->buffers_ptr), buffers_size))
+		return -EACCES;
+
+	return 0;
+}
+
+static void gna_request_make_zombie(struct gna_request *score_request)
+{
+	int i;
+
+	for (i = 0; i < score_request->buffer_count; i++) {
+		kvfree((void *)(uintptr_t)score_request->buffer_list[i].gna.patches_ptr);
+		drm_gem_object_put(&score_request->buffer_list[i].gem->base.base);
+	}
+	kvfree(score_request->buffer_list);
+	score_request->buffer_list = NULL;
+	score_request->buffer_count = 0;
+}
+
+static void gna_request_process(struct work_struct *work)
+{
+	struct gna_request *score_request;
+	struct gna_device *gna_priv;
+
+	score_request = container_of(work, struct gna_request, work);
+	gna_priv = to_gna_device(score_request->drm_f->minor->dev);
+
+	gna_request_make_zombie(score_request);
+
+	atomic_dec(&gna_priv->enqueued_requests);
+}
+
+static struct gna_request *gna_request_create(struct drm_file *file,
+				       struct gna_compute_cfg *compute_cfg)
+{
+
+	struct gna_device *gna_priv = file->driver_priv;
+	struct gna_request *score_request;
+
+	if (IS_ERR(gna_priv))
+		return NULL;
+
+	score_request = kzalloc(sizeof(*score_request), GFP_KERNEL);
+	if (!score_request)
+		return NULL;
+	kref_init(&score_request->refcount);
+
+	dev_dbg(gna_dev(gna_priv), "labase: %d, lacount: %d\n",
+		compute_cfg->layer_base, compute_cfg->layer_count);
+
+	score_request->request_id = atomic_inc_return(&gna_priv->request_count);
+	score_request->compute_cfg = *compute_cfg;
+	score_request->drm_f = file;
+	INIT_WORK(&score_request->work, gna_request_process);
+	INIT_LIST_HEAD(&score_request->node);
+
+	return score_request;
+}
+
+/*
+ * returns true if [inner_offset, inner_size) is embraced by [0, outer_size). False otherwise.
+ */
+static bool gna_validate_ranges(u64 outer_size, u64 inner_offset, u64 inner_size)
+{
+	return inner_offset < outer_size &&
+		inner_size <= (outer_size - inner_offset);
+}
+
+static int gna_validate_patches(struct gna_device *gna_priv, __u64 buffer_size,
+				struct gna_memory_patch *patches, u64 count)
+{
+	u64 idx;
+
+	for (idx = 0; idx < count; ++idx) {
+		if (patches[idx].size > 8) {
+			dev_dbg(gna_dev(gna_priv), "invalid patch size: %llu\n", patches[idx].size);
+			return -EINVAL;
+		}
+
+		if (!gna_validate_ranges(buffer_size, patches[idx].offset, patches[idx].size)) {
+			dev_dbg(gna_dev(gna_priv),
+				"patch out of bounds. buffer size: %llu, patch offset/size:%llu/%llu\n",
+				buffer_size, patches[idx].offset, patches[idx].size);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int gna_buffer_fill_patches(struct gna_buffer *buffer, struct gna_device *gna_priv)
+{
+	__u64 patches_user = buffer->patches_ptr;
+	struct gna_memory_patch *patches;
+	/* At this point, the buffer points to a memory region in kernel space where the copied
+	 * patches_ptr also lives, but the value of it is still an address from user space. This
+	 * function will set patches_ptr to either an address in kernel space or null before it
+	 * exits.
+	 */
+	u64 patch_count;
+	int ret;
+
+	buffer->patches_ptr = 0;
+	patch_count = buffer->patch_count;
+	if (!patch_count)
+		return 0;
+
+	patches = kvmalloc_array(patch_count, sizeof(struct gna_memory_patch), GFP_KERNEL);
+	if (!patches)
+		return -ENOMEM;
+
+	if (copy_from_user(patches, u64_to_user_ptr(patches_user),
+				sizeof(struct gna_memory_patch) * patch_count)) {
+		ret = -EFAULT;
+		goto err_fill_patches;
+	}
+
+	ret = gna_validate_patches(gna_priv, buffer->size, patches, patch_count);
+	if (ret) {
+		dev_dbg(gna_dev(gna_priv), "buffer %p: patches' validation failed\n", buffer);
+		goto err_fill_patches;
+	}
+
+	buffer->patches_ptr = (uintptr_t)patches;
+
+	return 0;
+
+err_fill_patches:
+	kvfree(patches);
+	return ret;
+}
+
+static int gna_request_fill_buffers(struct gna_request *score_request,
+				    struct gna_compute_cfg *compute_cfg)
+{
+	struct gna_buffer_with_object *buffer_list;
+	struct gna_buffer_with_object *buffer;
+	struct gna_buffer *cfg_buffers;
+	struct drm_gem_object *drmgemo;
+	struct gna_device *gna_priv;
+	u64 buffers_total_size = 0;
+	size_t gem_obj_size;
+	u64 buffer_count;
+	u32 handle;
+	u64 i, j;
+	int ret;
+
+
+	gna_priv = to_gna_device(score_request->drm_f->minor->dev);
+
+	buffer_count = compute_cfg->buffer_count;
+	buffer_list = kvmalloc_array(buffer_count, sizeof(*buffer_list), GFP_KERNEL);
+	if (!buffer_list)
+		return -ENOMEM;
+
+	cfg_buffers = u64_to_user_ptr(compute_cfg->buffers_ptr);
+	for (i = 0; i < buffer_count; ++i) {
+		if (copy_from_user(&buffer_list[i].gna, cfg_buffers+i,
+					sizeof(*buffer_list))) {
+			ret = -EFAULT;
+			goto err_free_buffers;
+		}
+		buffer_list[i].gem = NULL;
+	}
+
+	for (i = 0; i < buffer_count; i++) {
+		buffer = &buffer_list[i];
+		handle = buffer->gna.handle;
+
+		if (buffer->gna.offset != 0) {
+			dev_dbg(gna_dev(gna_priv), "buffer->offset = %llu for handle %u in score config\n",
+				buffer->gna.offset, buffer->gna.handle);
+			return -EINVAL;
+		}
+
+		for (j = 0; j < i; j++) {
+			if (buffer_list[j].gna.handle == handle) {
+				dev_dbg(gna_dev(gna_priv),
+					"doubled memory id in score config; id:%u\n", handle);
+				ret = -EINVAL;
+				goto err_zero_patch_user_ptr;
+			}
+		}
+
+		buffers_total_size +=
+			round_up(buffer->gna.size, PAGE_SIZE);
+		if (buffers_total_size > gna_priv->info.max_hw_mem) {
+			dev_dbg(gna_dev(gna_priv), "buffers' %p total size too big\n", buffer);
+			ret = -EINVAL;
+			goto err_zero_patch_user_ptr;
+		}
+
+		drmgemo = drm_gem_object_lookup(score_request->drm_f, handle);
+
+		if (!drmgemo) {
+			dev_dbg(gna_dev(gna_priv), "memory object %u not found\n", handle);
+			ret = -EINVAL;
+			goto err_zero_patch_user_ptr;
+		}
+
+		// we are still in sys call context, but prior request is enqueued.
+		// request may slip into queue while some gna_gem_object being deleted
+		// border case + not too much harm.
+		buffer->gem = to_gna_gem_obj(to_drm_gem_shmem_obj(drmgemo));
+
+		gem_obj_size = drmgemo->size;
+
+		if (!gna_validate_ranges(gem_obj_size, 0, buffer->gna.size)) {
+			dev_dbg(gna_dev(gna_priv),
+				"buffer out of bounds. mo size: %zu, buffer size:%llu\n",
+				gem_obj_size, buffer->gna.size);
+			ret = -EINVAL;
+			goto err_zero_patch_user_ptr;
+		}
+
+		ret = gna_buffer_fill_patches(&buffer->gna, gna_priv);
+		if (ret)
+			goto err_free_patches;
+	}
+
+	score_request->buffer_list = buffer_list;
+	score_request->buffer_count = buffer_count;
+
+	return 0;
+
+err_zero_patch_user_ptr:
+	/* patches_ptr may still hold an address in userspace.
+	 * Don't pass it to kvfree().
+	 */
+	buffer->gna.patches_ptr = 0;
+
+err_free_patches:
+	/* patches_ptr of each processed buffer should be either
+	 * null or pointing to an allocated memory block in the
+	 * kernel at this point.
+	 */
+	for (j = 0; j <= i; j++) {
+		kvfree((void *)(uintptr_t)buffer_list[j].gna.patches_ptr);
+		drm_gem_object_put(&buffer_list[j].gem->base.base);
+	}
+
+err_free_buffers:
+	kvfree(buffer_list);
+	return ret;
+}
+
+int gna_enqueue_request(struct gna_compute_cfg *compute_cfg,
+			struct drm_file *file, u64 *request_id)
+{
+	bool is_qos = !!(compute_cfg->flags & GNA_FLAG_SCORE_QOS);
+	struct gna_device *gna_priv = file->driver_priv;
+	struct gna_request *score_request;
+	u64 pos_in_queue;
+	int ret;
+
+	pos_in_queue = atomic_inc_return(&gna_priv->enqueued_requests);
+	if (is_qos && pos_in_queue != 1) {
+		ret = -EBUSY;
+		goto ERR_UNQUEUE_REQUEST;
+	}
+
+	score_request = gna_request_create(file, compute_cfg);
+	if (!score_request) {
+		ret = -ENOMEM;
+		goto ERR_UNQUEUE_REQUEST;
+	}
+
+	ret = gna_request_fill_buffers(score_request, compute_cfg);
+	if (ret) {
+		kref_put(&score_request->refcount, gna_request_release);
+		goto ERR_UNQUEUE_REQUEST;
+	}
+
+	kref_get(&score_request->refcount);
+	mutex_lock(&gna_priv->reqlist_lock);
+	list_add_tail(&score_request->node, &gna_priv->request_list);
+	mutex_unlock(&gna_priv->reqlist_lock);
+
+	queue_work(gna_priv->request_wq, &score_request->work);
+	kref_put(&score_request->refcount, gna_request_release);
+
+	*request_id = score_request->request_id;
+
+	return 0;
+
+ERR_UNQUEUE_REQUEST:
+	atomic_dec(&gna_priv->enqueued_requests);
+	return ret;
+}
+
+void gna_request_release(struct kref *ref)
+{
+	struct gna_request *score_request =
+		container_of(ref, struct gna_request, refcount);
+	gna_request_make_zombie(score_request);
+	kfree(score_request);
+}
diff --git a/drivers/gpu/drm/gna/gna_request.h b/drivers/gpu/drm/gna/gna_request.h
new file mode 100644
index 000000000000..432c30863e7e
--- /dev/null
+++ b/drivers/gpu/drm/gna/gna_request.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright(c) 2017-2022 Intel Corporation */
+
+#ifndef __GNA_REQUEST_H__
+#define __GNA_REQUEST_H__
+
+#include <linux/kref.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <uapi/drm/gna_drm.h>
+
+struct gna_device;
+struct gna_gem_object;
+struct drm_file;
+
+struct gna_buffer_with_object {
+	struct gna_buffer gna;
+	struct gna_gem_object *gem;
+};
+
+struct gna_request {
+	u64 request_id;
+
+	struct kref refcount;
+
+	struct drm_file *drm_f;
+
+	struct list_head node;
+
+	struct gna_compute_cfg compute_cfg;
+
+	struct gna_buffer_with_object *buffer_list;
+	u64 buffer_count;
+
+	struct work_struct work;
+};
+
+int gna_validate_score_config(struct gna_compute_cfg *compute_cfg,
+			struct gna_device *gna_priv);
+
+int gna_enqueue_request(struct gna_compute_cfg *compute_cfg,
+			struct drm_file *file, u64 *request_id);
+
+void gna_request_release(struct kref *ref);
+
+#endif // __GNA_REQUEST_H__
diff --git a/include/uapi/drm/gna_drm.h b/include/uapi/drm/gna_drm.h
index 5391446bad7d..aaae9a46dec5 100644
--- a/include/uapi/drm/gna_drm.h
+++ b/include/uapi/drm/gna_drm.h
@@ -4,12 +4,17 @@
 #ifndef _GNA_DRM_H_
 #define _GNA_DRM_H_
 
+#include <linux/const.h>
 #include <linux/types.h>
 
 #include "drm.h"
 
 #define GNA_DDI_VERSION_3 3
 
+/* Operation modes */
+#define GNA_MODE_GMM	0
+#define GNA_MODE_XNN	1
+
 #define GNA_PARAM_RECOVERY_TIMEOUT	1
 #define GNA_PARAM_DEVICE_TYPE		2
 #define GNA_PARAM_INPUT_BUFFER_S	3
@@ -21,6 +26,46 @@
 #define GNA_DEV_TYPE_3_0	0x30
 #define GNA_DEV_TYPE_3_5	0x35
 
+#define GNA_FLAG_SCORE_QOS	_BITUL(0)
+
+/*
+ * Structure describes part of memory to be overwritten before starting GNA
+ */
+struct gna_memory_patch {
+	/* offset from targeted memory */
+	__u64 offset;
+
+	__u64 size;
+	__u64 value;
+};
+
+struct gna_buffer {
+	__u32 handle;
+	__u32 pad;
+
+	__u64 offset;
+	__u64 size;
+
+	__u64 patch_count;
+	__u64 patches_ptr;
+};
+
+struct gna_compute_cfg {
+	__u32 layer_base;
+	__u32 layer_count;
+
+	/* List of GNA memory buffers */
+	__u64 buffers_ptr;
+	__u64 buffer_count;
+
+	__u8 active_list_on;
+	__u8 gna_mode;
+	__u8 hw_perf_encoding;
+	__u8 flags;
+
+	__u8 pad[4];
+};
+
 typedef __u64 gna_param_id;
 
 union gna_parameter {
@@ -33,6 +78,16 @@ union gna_parameter {
 	} out;
 };
 
+union gna_compute {
+	struct {
+		struct gna_compute_cfg config;
+	} in;
+
+	struct {
+		__u64 request_id;
+	} out;
+};
+
 struct gna_mem_id {
 	__u32 handle;
 	__u32 pad;
@@ -55,9 +110,11 @@ struct gna_gem_free {
 #define DRM_GNA_GET_PARAMETER		0x00
 #define DRM_GNA_GEM_NEW			0x01
 #define DRM_GNA_GEM_FREE		0x02
+#define DRM_GNA_COMPUTE			0x03
 
 #define DRM_IOCTL_GNA_GET_PARAMETER	DRM_IOWR(DRM_COMMAND_BASE + DRM_GNA_GET_PARAMETER, union gna_parameter)
 #define DRM_IOCTL_GNA_GEM_NEW		DRM_IOWR(DRM_COMMAND_BASE + DRM_GNA_GEM_NEW, union gna_gem_new)
 #define DRM_IOCTL_GNA_GEM_FREE		DRM_IOWR(DRM_COMMAND_BASE + DRM_GNA_GEM_FREE, struct gna_gem_free)
+#define DRM_IOCTL_GNA_COMPUTE		DRM_IOWR(DRM_COMMAND_BASE + DRM_GNA_COMPUTE, union gna_compute)
 
 #endif /* _GNA_DRM_H_ */
-- 
2.25.1



More information about the dri-devel mailing list