@@ -10,6 +10,7 @@ panthor-y := \
panthor_heap.o \
panthor_mmu.o \
panthor_sched.o \
- panthor_syncobj.o
+ panthor_syncobj.o \
+ panthor_xgs.o
obj-$(CONFIG_DRM_PANTHOR) += panthor.o
@@ -17,6 +17,7 @@
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
+struct panthor_xgs_queue_pool;
struct panthor_csf;
struct panthor_csf_ctx;
struct panthor_device;
@@ -182,6 +183,9 @@ struct panthor_file {
/** @groups: Scheduling group pool attached to this file. */
struct panthor_group_pool *groups;
+
+ /** @xgs_queues: XGS queues attached to this file. */
+ struct panthor_xgs_queue_pool *xgs_queues;
};
int panthor_device_init(struct panthor_device *ptdev);
@@ -19,6 +19,7 @@
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
+#include "panthor_xgs.h"
#include "panthor_device.h"
#include "panthor_fw.h"
#include "panthor_gem.h"
@@ -215,7 +216,8 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride,
PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_submit, syncs), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_create, ringbuf_size), \
- PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs))
+ PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs), \
+ PANTHOR_UOBJ_DECL(struct drm_panthor_xgs_op, pad))
/**
* PANTHOR_UOBJ_SET() - Copy a kernel object to a user object.
@@ -1316,6 +1318,114 @@ static int panthor_ioctl_group_kick(struct drm_device *ddev, void *data,
return 0;
}
+static int panthor_ioctl_xgs_queue_create(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_panthor_xgs_queue_create *args = data;
+ struct panthor_file *pfile = file->driver_priv;
+
+ if (args->pad)
+ return -EINVAL;
+
+ return panthor_xgs_queue_create(pfile, args->vm_id,
+ args->eventfd_sync_update, &args->handle);
+}
+
+static int panthor_ioctl_xgs_queue_destroy(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_panthor_xgs_queue_destroy *args = data;
+ struct panthor_file *pfile = file->driver_priv;
+
+ if (args->pad)
+ return -EINVAL;
+
+ return panthor_xgs_queue_destroy(pfile, args->handle);
+}
+
+#define XGS_QUEUE_SUBMIT_FLAGS (DRM_PANTHOR_XGS_QUEUE_SUBMIT_ERROR_BARRIER_PRE | \
+ DRM_PANTHOR_XGS_QUEUE_SUBMIT_ERROR_BARRIER_POST)
+
+static int panthor_ioctl_xgs_queue_submit(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_panthor_xgs_queue_submit *args = data;
+ struct panthor_file *pfile = file->driver_priv;
+ struct drm_panthor_xgs_op *ops_args;
+ struct panthor_xgs_queue *queue;
+ struct panthor_submit_ctx ctx;
+ struct drm_sched_job *job;
+ struct panthor_vm *vm;
+ int ret;
+
+ if (args->flags & ~XGS_QUEUE_SUBMIT_FLAGS)
+ return -EINVAL;
+
+ if (args->ops.count) {
+ ret = PANTHOR_UOBJ_GET_ARRAY(ops_args, &args->ops);
+ if (ret)
+ return ret;
+ } else {
+ ops_args = NULL;
+ }
+
+ queue = panthor_xgs_queue_pool_get_xgs_queue(pfile->xgs_queues, args->handle);
+ if (!queue)
+ goto out_free_ops_args;
+
+ ret = panthor_submit_ctx_init(&ctx, file, 1);
+ if (ret)
+ goto out_put_queue;
+
+ /* Create job object */
+ job = panthor_xgs_job_create(queue, ops_args, args->ops.count);
+ if (IS_ERR(job)) {
+ ret = PTR_ERR(job);
+ goto out_cleanup_submit_ctx;
+ }
+
+ /* handed over to the job object */
+ ops_args = NULL;
+
+ /* attach sync operations */
+ ret = panthor_submit_ctx_add_job(&ctx, 0, job, &args->syncs);
+ if (ret)
+ goto out_cleanup_submit_ctx;
+
+ /* Collect signal operations on all */
+ ret = panthor_submit_ctx_collect_jobs_signal_ops(&ctx);
+ if (ret)
+ goto out_cleanup_submit_ctx;
+
+ /* The group already have a VM ref, so we don't need to take an extra one */
+ vm = panthor_xgs_queue_vm(queue);
+
+ /* We acquire/prepare revs on the job */
+ drm_exec_until_all_locked(&ctx.exec) {
+ ret = panthor_vm_prepare_mapped_bos_resvs(&ctx.exec, vm, 1);
+ }
+
+ if (ret)
+ goto out_cleanup_submit_ctx;
+
+ /* Add deps, arm job fence and register the job fence to signal array */
+ ret = panthor_submit_ctx_add_deps_and_arm_jobs(&ctx);
+ if (ret)
+ goto out_cleanup_submit_ctx;
+
+ /* Nothing can fail after that point */
+ panthor_submit_ctx_push_jobs(&ctx, panthor_xgs_job_update_resvs);
+
+out_cleanup_submit_ctx:
+ panthor_submit_ctx_cleanup(&ctx, panthor_xgs_job_put);
+out_put_queue:
+ panthor_xgs_queue_put(queue);
+out_free_ops_args:
+ kvfree(ops_args);
+
+ return ret;
+}
+
static int
panthor_open(struct drm_device *ddev, struct drm_file *file)
{
@@ -1343,9 +1453,16 @@ panthor_open(struct drm_device *ddev, struct drm_file *file)
if (ret)
goto err_destroy_vm_pool;
+ ret = panthor_xgs_queue_pool_create(pfile);
+ if (ret)
+ goto err_destroy_group_pool;
+
file->driver_priv = pfile;
return 0;
+err_destroy_group_pool:
+ panthor_group_pool_destroy(pfile);
+
err_destroy_vm_pool:
panthor_vm_pool_destroy(pfile);
@@ -1363,6 +1480,7 @@ panthor_postclose(struct drm_device *ddev, struct drm_file *file)
struct panthor_file *pfile = file->driver_priv;
panthor_group_pool_destroy(pfile);
+ panthor_xgs_queue_pool_destroy(pfile);
panthor_vm_pool_destroy(pfile);
kfree(pfile);
@@ -1387,6 +1505,9 @@ static const struct drm_ioctl_desc panthor_drm_driver_ioctls[] = {
PANTHOR_IOCTL(TILER_HEAP_DESTROY, tiler_heap_destroy, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(GROUP_SUBMIT, group_submit, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(GROUP_KICK, group_kick, DRM_RENDER_ALLOW),
+ PANTHOR_IOCTL(XGS_QUEUE_CREATE, xgs_queue_create, DRM_RENDER_ALLOW),
+ PANTHOR_IOCTL(XGS_QUEUE_DESTROY, xgs_queue_destroy, DRM_RENDER_ALLOW),
+ PANTHOR_IOCTL(XGS_QUEUE_SUBMIT, xgs_queue_submit, DRM_RENDER_ALLOW),
};
static int panthor_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include "panthor_xgs.h"
#include "panthor_devfreq.h"
#include "panthor_device.h"
#include "panthor_fw.h"
@@ -1496,8 +1497,13 @@ static void csg_slot_sync_update_locked(struct panthor_device *ptdev,
lockdep_assert_held(&ptdev->scheduler->lock);
- if (group && !group->user_submit)
- group_queue_work(group, sync_upd);
+ if (group) {
+ /* Rerun XGS jobs immediately, as this can potentially unblock the group */
+ panthor_xgs_queue_pool_recheck(group->pfile);
+
+ if (!group->user_submit)
+ group_queue_work(group, sync_upd);
+ }
sched_queue_work(ptdev->scheduler, sync_upd);
}
@@ -1691,9 +1697,15 @@ static const char *queue_fence_get_timeline_name(struct dma_fence *fence)
return "queue-fence";
}
+static void job_fence_free(struct dma_fence *fence)
+{
+ dma_fence_free(fence);
+}
+
static const struct dma_fence_ops panthor_queue_fence_ops = {
.get_driver_name = fence_get_driver_name,
.get_timeline_name = queue_fence_get_timeline_name,
+ .release = job_fence_free,
};
struct panthor_csg_slots_upd_ctx {
@@ -2431,6 +2443,10 @@ static void sync_upd_work(struct work_struct *work)
if (unblocked_queues) {
group->blocked_queues &= ~unblocked_queues;
+ /* Sync updates from XGS queue could happen when we are not ticking */
+ if (sched->resched_target == U64_MAX)
+ immediate_tick = true;
+
if (group->csg_id < 0) {
list_move(&group->run_node,
&sched->groups.runnable[group->priority]);
@@ -2788,6 +2804,11 @@ void panthor_sched_kick(struct panthor_file *pfile, u32 group_handle, u32 queue_
group_put(group);
}
+void panthor_sched_sync_update(struct panthor_device *ptdev)
+{
+ sched_queue_work(ptdev->scheduler, sync_upd);
+}
+
static void group_sync_upd_work(struct work_struct *work)
{
struct panthor_group *group =
@@ -49,5 +49,6 @@ void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events);
u8 panthor_sched_doorbell_id(struct panthor_file *pfile, u32 group_handle);
void panthor_sched_kick(struct panthor_file *pfile, u32 group_handle, u32 queue_mask);
+void panthor_sched_sync_update(struct panthor_device *ptdev);
#endif
new file mode 100644
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2024 ARM Limited. All rights reserved. */
+
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_managed.h>
+#include <drm/gpu_scheduler.h>
+#include <drm/panthor_drm.h>
+
+#include <linux/iosys-map.h>
+
+#include "panthor_xgs.h"
+#include "panthor_device.h"
+#include "panthor_gem.h"
+#include "panthor_mmu.h"
+#include "panthor_sched.h"
+#include "panthor_syncobj.h"
+
+#define JOB_TIMEOUT_MS 5000
+
+/**
+ * struct panthor_xgs_queue - Scheduling group object
+ */
+struct panthor_xgs_queue {
+ /** @refcount: Reference count */
+ struct kref refcount;
+
+ /** @lock: Lock to synchronize access to this queue */
+ struct mutex lock;
+
+ /** @handle: integer value used by user to refer to this queue */
+ u32 handle;
+
+ /** @ptdev: Panthor device for which this queue was created */
+ struct panthor_device *ptdev;
+
+ /** @vm: VM associated with this XGS queue */
+ struct panthor_vm *vm;
+
+ /** @eventfd_sync_update_ctx: eventfd context to signal on XGS set ops */
+ struct eventfd_ctx *eventfd_sync_update_ctx;
+
+ /** @scheduler: scheduler instance used to execute XGS jobs */
+ struct drm_gpu_scheduler scheduler;
+
+ /** @entity: Scheduler entity with XGS jobs */
+ struct drm_sched_entity entity;
+
+ /** @fence_ctx: Fence context fields. */
+ struct {
+ /** @lock: Used to protect access to all fences allocated by this context. */
+ spinlock_t lock;
+
+ /**
+ * @id: Fence context ID.
+ *
+ * Allocated with dma_fence_context_alloc().
+ */
+ u64 id;
+
+ /** @seqno: Sequence number of the last initialized fence. */
+ atomic64_t seqno;
+
+ /**
+ * @in_flight_jobs: List containing all in-flight jobs.
+ *
+ * Used to keep track and signal panthor_job::done_fence when the
+ * synchronization object attached to the queue is signaled.
+ */
+ struct list_head in_flight_jobs;
+ } fence_ctx;
+
+ /** @destroyed: True if queue is marked for destruction and should not be used */
+ bool destroyed;
+
+ /**
+ * @release_work: Work used to release XGS queue resources.
+ *
+ * We need to postpone the queue release to avoid a deadlock,
+ * otherwise "free_job" could end up calling back into DRM sched.
+ */
+ struct work_struct release_work;
+};
+
+/*
+ * We currently set the maximum of XGS queues per file to an arbitrary low value.
+ * But this can be updated if we need more.
+ */
+#define MAX_XGS_QUEUES_PER_POOL 128
+
+/**
+ * struct panthor_xgs_queue_pool - XGS queue pool
+ *
+ * Each file get assigned a XGS queue pool.
+ */
+struct panthor_xgs_queue_pool {
+ /** @xa: Xarray used to manage XGS queue handles. */
+ struct xarray xa;
+};
+
+/**
+ * struct panthor_xgs_job - Used to manage XGS job
+ */
+struct panthor_xgs_job {
+ /** @base: Inherit from drm_sched_job. */
+ struct drm_sched_job base;
+
+ /** @refcount: Reference count. */
+ struct kref refcount;
+
+ /** @group: XGS queue this job will be pushed to. */
+ struct panthor_xgs_queue *queue;
+
+ /** @ops: List of XGS operations to execute */
+ struct drm_panthor_xgs_op *ops;
+
+ /** @ops_count: Number of operations in the ops array */
+ u32 ops_count;
+
+ /** @done_fence: Fence signaled when the job is finished or cancelled. */
+ struct dma_fence *done_fence;
+
+ /** @node: Node used to insert job into in_flight_jobs list of queue */
+ struct list_head node;
+
+};
+
+static int panthor_xgs_try_run_job(struct panthor_xgs_job *job);
+
+static const char *xgs_fence_get_driver_name(struct dma_fence *fence)
+{
+ return "panthor";
+}
+
+static const char *xgs_fence_get_timeline_name(struct dma_fence *fence)
+{
+ return "xgs-fence";
+}
+
+static void xgs_fence_free(struct dma_fence *fence)
+{
+ dma_fence_free(fence);
+}
+
+static const struct dma_fence_ops panthor_xgs_fence_ops = {
+ .get_driver_name = xgs_fence_get_driver_name,
+ .get_timeline_name = xgs_fence_get_timeline_name,
+ .release = xgs_fence_free,
+};
+
+static void xgs_queue_release_work(struct work_struct *work)
+{
+ struct panthor_xgs_queue *queue = container_of(work, struct panthor_xgs_queue,
+ release_work);
+
+ if (queue->entity.fence_context)
+ drm_sched_entity_destroy(&queue->entity);
+
+ if (queue->scheduler.ops)
+ drm_sched_fini(&queue->scheduler);
+
+ panthor_vm_put(queue->vm);
+
+ if (queue->eventfd_sync_update_ctx)
+ eventfd_ctx_put(queue->eventfd_sync_update_ctx);
+
+ kfree(queue);
+}
+
+static void xgs_queue_release(struct kref *kref)
+{
+ struct panthor_xgs_queue *queue = container_of(kref, struct panthor_xgs_queue, refcount);
+ struct panthor_device *ptdev = queue->ptdev;
+
+ drm_WARN_ON(&ptdev->base, !list_empty(&queue->fence_ctx.in_flight_jobs));
+
+ queue_work(panthor_cleanup_wq, &queue->release_work);
+}
+
+static struct panthor_xgs_queue *xgs_queue_get(struct panthor_xgs_queue *queue)
+{
+ if (queue)
+ kref_get(&queue->refcount);
+
+ return queue;
+}
+
+static void xgs_queue_recheck(struct panthor_xgs_queue *queue)
+{
+ struct panthor_xgs_job *job, *tmp;
+ int ret;
+
+ mutex_lock(&queue->lock);
+
+ list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) {
+ ret = panthor_xgs_try_run_job(job);
+
+ if (ret != -EBUSY) {
+ /* completed or failed */
+ list_del_init(&job->node);
+
+ if (ret)
+ dma_fence_set_error(job->done_fence, -ETIMEDOUT);
+
+ dma_fence_signal(job->done_fence);
+
+ /* Ditch ref we took when adding it to the in_flight_jobs */
+ panthor_xgs_job_put(&job->base);
+ }
+ }
+
+ mutex_unlock(&queue->lock);
+}
+
+/* Internal flag to mark operations as completed. Avoid clashes with drm_panthor_xgs_op_flags */
+#define DRM_PANTHOR_XGS_OP_COMPLETED (1 << 15)
+
+static int panthor_xgs_wait(struct panthor_xgs_queue *queue, struct drm_panthor_xgs_op *xgs_op)
+{
+ struct panthor_device *ptdev = queue->ptdev;
+ struct panthor_syncobj *syncobj;
+ int ret;
+ u64 value;
+
+ if (xgs_op->flags & DRM_PANTHOR_XGS_OP_COMPLETED)
+ return 0;
+
+ syncobj = panthor_syncobj_create(ptdev, queue->vm, xgs_op->addr,
+ xgs_op->format == DRM_PANTHOR_XGS_OP_FORMAT_U64);
+ if (IS_ERR_OR_NULL(syncobj))
+ return PTR_ERR(syncobj);
+
+ value = panthor_syncobj_get_value(syncobj);
+
+ ret = -EBUSY;
+
+ if (xgs_op->op == DRM_PANTHOR_XGS_OP_WAIT_LE) {
+ if (value <= xgs_op->value) {
+ ret = 0;
+ xgs_op->flags |= DRM_PANTHOR_XGS_OP_COMPLETED;
+ }
+ } else if (xgs_op->op == DRM_PANTHOR_XGS_OP_WAIT_GT) {
+ if (value > xgs_op->value) {
+ ret = 0;
+ xgs_op->flags |= DRM_PANTHOR_XGS_OP_COMPLETED;
+ }
+ }
+
+ panthor_syncobj_release(syncobj);
+
+ return ret;
+}
+
+static void
+panthor_xgs_signal(struct panthor_xgs_queue *queue, struct drm_panthor_xgs_op *xgs_op, u32 error)
+{
+ struct panthor_device *ptdev = queue->ptdev;
+ struct panthor_syncobj *syncobj;
+ u64 value;
+
+ if (xgs_op->flags & DRM_PANTHOR_XGS_OP_COMPLETED)
+ return;
+
+ syncobj = panthor_syncobj_create(ptdev, queue->vm, xgs_op->addr,
+ xgs_op->format == DRM_PANTHOR_XGS_OP_FORMAT_U64);
+ if (IS_ERR_OR_NULL(syncobj))
+ return;
+
+ value = panthor_syncobj_get_value(syncobj);
+
+ if (xgs_op->op == DRM_PANTHOR_XGS_OP_SIGNAL_SET)
+ value = xgs_op->value;
+ else if (xgs_op->op == DRM_PANTHOR_XGS_OP_SIGNAL_ADD)
+ value += xgs_op->value;
+
+ if (!error)
+ panthor_syncobj_signal(syncobj, value);
+ else
+ panthor_syncobj_signal_with_error(syncobj, value, error);
+
+ panthor_sched_sync_update(ptdev);
+
+ if (queue->eventfd_sync_update_ctx)
+ eventfd_signal(queue->eventfd_sync_update_ctx);
+
+ xgs_op->flags |= DRM_PANTHOR_XGS_OP_COMPLETED;
+
+ panthor_syncobj_release(syncobj);
+}
+
+static int panthor_xgs_try_run_job(struct panthor_xgs_job *job)
+{
+ int i;
+ int err_wait = 0;
+ struct drm_panthor_xgs_op *xgs_op;
+
+ lockdep_assert_held(&job->queue->lock);
+
+ for (i = 0; i < job->ops_count; i++) {
+ xgs_op = &job->ops[i];
+
+ switch (xgs_op->op & ~DRM_PANTHOR_XGS_OP_COMPLETED) {
+ case DRM_PANTHOR_XGS_OP_WAIT_LE:
+ case DRM_PANTHOR_XGS_OP_WAIT_GT:
+ if (!err_wait)
+ err_wait = panthor_xgs_wait(job->queue, &job->ops[i]);
+ if (err_wait == -EBUSY)
+ return err_wait;
+ break;
+ case DRM_PANTHOR_XGS_OP_SIGNAL_SET:
+ case DRM_PANTHOR_XGS_OP_SIGNAL_ADD:
+ panthor_xgs_signal(job->queue, &job->ops[i], err_wait);
+ break;
+ default:
+ /* unknown operation, assume this could be a critical error */
+ err_wait = -EINVAL;
+ break;
+ }
+ }
+
+ return err_wait;
+}
+
+static struct dma_fence *panthor_xgs_run_job(struct drm_sched_job *sched_job)
+{
+ struct panthor_xgs_job *job = container_of(sched_job, struct panthor_xgs_job, base);
+ struct panthor_xgs_queue *queue = job->queue;
+ struct dma_fence *done_fence;
+ int ret;
+
+ mutex_lock(&queue->lock);
+
+ ret = panthor_xgs_try_run_job(job);
+ if (ret == -EBUSY) {
+ dma_fence_init(job->done_fence,
+ &panthor_xgs_fence_ops,
+ &queue->fence_ctx.lock,
+ queue->fence_ctx.id,
+ atomic64_inc_return(&queue->fence_ctx.seqno));
+
+ done_fence = dma_fence_get(job->done_fence);
+ panthor_xgs_job_get(&job->base);
+
+ list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs);
+
+ } else if (ret) {
+ done_fence = ERR_PTR(ret);
+ } else {
+ /* job completed immediately, no need to return fence */
+ done_fence = NULL;
+ }
+
+ mutex_unlock(&queue->lock);
+
+ return done_fence;
+}
+
+static enum drm_gpu_sched_stat
+panthor_xgs_job_timedout(struct drm_sched_job *sched_job)
+{
+ struct panthor_xgs_job *job = container_of(sched_job, struct panthor_xgs_job, base);
+ struct panthor_xgs_queue *queue = job->queue;
+ int ret;
+
+ mutex_lock(&queue->lock);
+
+ list_del_init(&job->node);
+
+ /* Ditch ref we took when adding it to the in_flight_jobs */
+ panthor_xgs_job_put(&job->base);
+
+ ret = panthor_xgs_try_run_job(job);
+ if (ret)
+ dma_fence_set_error(job->done_fence, -ETIMEDOUT);
+
+ mutex_unlock(&queue->lock);
+
+ dma_fence_signal(job->done_fence);
+
+ panthor_xgs_job_put(sched_job);
+
+ return DRM_GPU_SCHED_STAT_NOMINAL;
+}
+
+static const struct drm_sched_backend_ops panthor_xgs_sched_ops = {
+ .run_job = panthor_xgs_run_job,
+ .timedout_job = panthor_xgs_job_timedout,
+ .free_job = panthor_xgs_job_put,
+};
+
+int panthor_xgs_queue_create(struct panthor_file *pfile, u32 vm_id,
+ int eventfd_sync_update, u32 *handle)
+{
+ struct panthor_device *ptdev = pfile->ptdev;
+ struct panthor_xgs_queue_pool *xgs_queue_pool = pfile->xgs_queues;
+ struct panthor_xgs_queue *queue;
+ struct drm_gpu_scheduler *drm_sched;
+ int ret;
+ int qid;
+
+ queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+ if (!queue)
+ return -ENOMEM;
+
+ kref_init(&queue->refcount);
+ INIT_LIST_HEAD(&queue->fence_ctx.in_flight_jobs);
+ INIT_WORK(&queue->release_work, xgs_queue_release_work);
+ queue->ptdev = ptdev;
+
+ ret = drmm_mutex_init(&ptdev->base, &queue->lock);
+ if (ret)
+ goto err_put_queue;
+
+ if (eventfd_sync_update >= 0) {
+ queue->eventfd_sync_update_ctx = eventfd_ctx_fdget(eventfd_sync_update);
+ ret = PTR_ERR_OR_ZERO(queue->eventfd_sync_update_ctx);
+ if (ret)
+ goto err_put_queue;
+ }
+
+ queue->vm = panthor_vm_pool_get_vm(pfile->vms, vm_id);
+ if (!queue->vm) {
+ ret = -EINVAL;
+ goto err_put_queue;
+ }
+
+ ret = drm_sched_init(&queue->scheduler, &panthor_xgs_sched_ops,
+ NULL, 1, 1, 0,
+ msecs_to_jiffies(JOB_TIMEOUT_MS),
+ NULL, NULL,
+ "panthor_xgs",
+ ptdev->base.dev);
+ if (ret)
+ goto err_put_queue;
+
+ drm_sched = &queue->scheduler;
+ ret = drm_sched_entity_init(&queue->entity, 0, &drm_sched, 1, NULL);
+ if (ret)
+ goto err_put_queue;
+
+ queue->fence_ctx.id = dma_fence_context_alloc(1);
+ spin_lock_init(&queue->fence_ctx.lock);
+
+ ret = xa_alloc(&xgs_queue_pool->xa, &qid, queue,
+ XA_LIMIT(1, MAX_XGS_QUEUES_PER_POOL), GFP_KERNEL);
+ if (ret)
+ goto err_put_queue;
+
+ queue->handle = qid;
+ *handle = qid;
+
+ return 0;
+
+err_put_queue:
+ panthor_xgs_queue_put(queue);
+ return ret;
+}
+
+int panthor_xgs_queue_destroy(struct panthor_file *pfile, u32 handle)
+{
+ struct panthor_xgs_queue_pool *pool = pfile->xgs_queues;
+ struct panthor_xgs_queue *queue;
+ struct panthor_xgs_job *job, *tmp;
+ LIST_HEAD(faulty_jobs);
+ int err = -ECANCELED;
+
+ queue = xa_erase(&pool->xa, handle);
+ if (!queue)
+ return -EINVAL;
+
+ queue->destroyed = true;
+
+ mutex_lock(&queue->lock);
+
+ list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) {
+ list_move_tail(&job->node, &faulty_jobs);
+ dma_fence_set_error(job->done_fence, err);
+ dma_fence_signal(job->done_fence);
+ }
+
+ mutex_unlock(&queue->lock);
+
+ list_for_each_entry_safe(job, tmp, &faulty_jobs, node) {
+ list_del_init(&job->node);
+ /* Ditch ref we took when adding it to the in_flight_jobs */
+ panthor_xgs_job_put(&job->base);
+ }
+
+ panthor_xgs_queue_put(queue);
+
+ return 0;
+}
+
+void panthor_xgs_queue_put(struct panthor_xgs_queue *queue)
+{
+ if (queue)
+ kref_put(&queue->refcount, xgs_queue_release);
+}
+
+struct panthor_vm *panthor_xgs_queue_vm(struct panthor_xgs_queue *queue)
+{
+ return queue->vm;
+}
+
+int panthor_xgs_queue_pool_create(struct panthor_file *pfile)
+{
+ struct panthor_xgs_queue_pool *pool;
+
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return -ENOMEM;
+
+ xa_init_flags(&pool->xa, XA_FLAGS_ALLOC1);
+ pfile->xgs_queues = pool;
+ return 0;
+}
+
+void panthor_xgs_queue_pool_destroy(struct panthor_file *pfile)
+{
+ struct panthor_xgs_queue_pool *pool = pfile->xgs_queues;
+ struct panthor_xgs_queue *queue;
+ unsigned long i;
+
+ if (IS_ERR_OR_NULL(pool))
+ return;
+
+ xa_for_each(&pool->xa, i, queue)
+ panthor_xgs_queue_destroy(pfile, i);
+
+ xa_destroy(&pool->xa);
+ kfree(pool);
+ pfile->xgs_queues = NULL;
+}
+
+struct panthor_xgs_queue *panthor_xgs_queue_pool_get_xgs_queue(struct panthor_xgs_queue_pool *pool,
+ u32 handle)
+{
+ struct panthor_xgs_queue *queue;
+
+ queue = xgs_queue_get(xa_load(&pool->xa, handle));
+
+ return queue;
+}
+
+void panthor_xgs_queue_pool_recheck(struct panthor_file *ptfile)
+{
+ unsigned long i;
+ struct panthor_xgs_queue *queue;
+
+ xa_for_each(&ptfile->xgs_queues->xa, i, queue)
+ xgs_queue_recheck(queue);
+}
+
+struct drm_sched_job *panthor_xgs_job_create(struct panthor_xgs_queue *queue,
+ struct drm_panthor_xgs_op *ops, u32 ops_count)
+{
+ struct panthor_xgs_job *job;
+ int ret;
+
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&job->refcount);
+ INIT_LIST_HEAD(&job->node);
+
+ job->queue = xgs_queue_get(queue);
+ if (!job->queue) {
+ ret = -EINVAL;
+ goto err_put_job;
+ }
+
+ job->done_fence = kzalloc(sizeof(*job->done_fence), GFP_KERNEL);
+ if (!job->done_fence) {
+ ret = -ENOMEM;
+ goto err_put_job;
+ }
+
+ ret = drm_sched_job_init(&job->base, &queue->entity, 1, queue);
+ if (ret)
+ goto err_put_job;
+
+ /* take ownership of ops array */
+ job->ops = ops;
+ job->ops_count = ops_count;
+
+ return &job->base;
+
+err_put_job:
+ panthor_xgs_job_put(&job->base);
+ return ERR_PTR(ret);
+}
+
+static void xgs_job_release(struct kref *ref)
+{
+ struct panthor_xgs_job *job = container_of(ref, struct panthor_xgs_job, refcount);
+
+ drm_WARN_ON(&job->queue->ptdev->base, !list_empty(&job->node));
+
+ if (job->base.s_fence)
+ drm_sched_job_cleanup(&job->base);
+
+ if (job->done_fence && job->done_fence->ops)
+ dma_fence_put(job->done_fence);
+ else
+ dma_fence_free(job->done_fence);
+
+ panthor_xgs_queue_put(job->queue);
+ kvfree(job->ops);
+ kfree(job);
+}
+
+struct drm_sched_job *panthor_xgs_job_get(struct drm_sched_job *sched_job)
+{
+ if (sched_job) {
+ struct panthor_xgs_job *job = container_of(sched_job, struct panthor_xgs_job, base);
+
+ kref_get(&job->refcount);
+ }
+
+ return sched_job;
+}
+
+void panthor_xgs_job_put(struct drm_sched_job *sched_job)
+{
+ struct panthor_xgs_job *job = container_of(sched_job, struct panthor_xgs_job, base);
+
+ if (sched_job)
+ kref_put(&job->refcount, xgs_job_release);
+}
+
+void panthor_xgs_job_update_resvs(struct drm_exec *exec, struct drm_sched_job *sched_job)
+{
+ struct panthor_xgs_job *job = container_of(sched_job, struct panthor_xgs_job, base);
+
+ panthor_vm_update_resvs(job->queue->vm, exec, &sched_job->s_fence->finished,
+ DMA_RESV_USAGE_BOOKKEEP, DMA_RESV_USAGE_WRITE);
+}
new file mode 100644
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2024 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_XGS_H__
+#define __PANTHOR_XGS_H__
+
+struct drm_exec;
+struct drm_panthor_xgs_op;
+struct drm_panthor_xgs_queue_create;
+struct drm_sched_job;
+struct panthor_xgs_queue;
+struct panthor_xgs_queue_pool;
+struct panthor_file;
+struct panthor_vm;
+
+int panthor_xgs_queue_create(struct panthor_file *pfile, u32 vm_id,
+ int eventfd_sync_update, u32 *handle);
+int panthor_xgs_queue_destroy(struct panthor_file *pfile, u32 handle);
+
+void panthor_xgs_queue_put(struct panthor_xgs_queue *queue);
+
+struct panthor_vm *panthor_xgs_queue_vm(struct panthor_xgs_queue *queue);
+
+int panthor_xgs_queue_pool_create(struct panthor_file *pfile);
+void panthor_xgs_queue_pool_destroy(struct panthor_file *pfile);
+
+struct panthor_xgs_queue *
+panthor_xgs_queue_pool_get_xgs_queue(struct panthor_xgs_queue_pool *pool, u32 handle);
+
+void panthor_xgs_queue_pool_recheck(struct panthor_file *ptfile);
+
+struct drm_sched_job *
+panthor_xgs_job_create(struct panthor_xgs_queue *queue,
+ struct drm_panthor_xgs_op *ops,
+ u32 ops_count);
+
+void panthor_xgs_job_put(struct drm_sched_job *sched_job);
+struct drm_sched_job *panthor_xgs_job_get(struct drm_sched_job *sched_job);
+
+void panthor_xgs_job_update_resvs(struct drm_exec *exec, struct drm_sched_job *sched_job);
+
+#endif