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

Kwapulinski, Maciej maciej.kwapulinski at intel.com
Thu Oct 20 13:35:22 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

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.



More information about the dri-devel mailing list