]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/panfrost: Introduce JM contexts for manging job resources
authorBoris Brezillon <boris.brezillon@collabora.com>
Wed, 17 Sep 2025 19:18:38 +0000 (20:18 +0100)
committerSteven Price <steven.price@arm.com>
Thu, 18 Sep 2025 13:09:58 +0000 (14:09 +0100)
A JM context describes user-requested priorities for the JM queues.

Context creation leads to the initialization of scheduling entities of
the same priority for all the device's job slots.

Until context creation and destruction are exposed to UM, all issued
jobs shall be bound to the default Panfrost file context, which has
medium priority.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Link: https://lore.kernel.org/r/20250917191859.500279-3-adrian.larumbe@collabora.com
drivers/gpu/drm/panfrost/panfrost_device.h
drivers/gpu/drm/panfrost/panfrost_drv.c
drivers/gpu/drm/panfrost/panfrost_job.c
drivers/gpu/drm/panfrost/panfrost_job.h

index 077525a3ad685eb9b00f642cd90843b6096e8ff3..1e73efad02a843c8ba08829cfbd802aff1c59238 100644 (file)
 #include <linux/pm.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spinlock.h>
+#include <drm/drm_auth.h>
 #include <drm/drm_device.h>
 #include <drm/drm_mm.h>
 #include <drm/gpu_scheduler.h>
 
 #include "panfrost_devfreq.h"
+#include "panfrost_job.h"
 
 struct panfrost_device;
 struct panfrost_mmu;
@@ -22,7 +24,6 @@ struct panfrost_job_slot;
 struct panfrost_job;
 struct panfrost_perfcnt;
 
-#define NUM_JOB_SLOTS 3
 #define MAX_PM_DOMAINS 5
 
 enum panfrost_drv_comp_bits {
@@ -206,13 +207,19 @@ struct panfrost_engine_usage {
 struct panfrost_file_priv {
        struct panfrost_device *pfdev;
 
-       struct drm_sched_entity sched_entity[NUM_JOB_SLOTS];
+       struct xarray jm_ctxs;
 
        struct panfrost_mmu *mmu;
 
        struct panfrost_engine_usage engine_usage;
 };
 
+static inline bool panfrost_high_prio_allowed(struct drm_file *file)
+{
+       /* Higher priorities require CAP_SYS_NICE or DRM_MASTER */
+       return (capable(CAP_SYS_NICE) || drm_is_current_master(file));
+}
+
 static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev)
 {
        return ddev->dev_private;
index 1ea6c509a5d599e296fbba3cd16f7be3b9769665..be384b18e8fd4f9786d90116d71d2dd22b5804b2 100644 (file)
@@ -279,9 +279,16 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
        struct panfrost_file_priv *file_priv = file->driver_priv;
        struct drm_panfrost_submit *args = data;
        struct drm_syncobj *sync_out = NULL;
+       struct panfrost_jm_ctx *jm_ctx;
        struct panfrost_job *job;
        int ret = 0, slot;
 
+       if (args->pad)
+               return -EINVAL;
+
+       if (args->jm_ctx_handle)
+               return -EINVAL;
+
        if (!args->jc)
                return -EINVAL;
 
@@ -294,10 +301,16 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
                        return -ENODEV;
        }
 
+       jm_ctx = panfrost_jm_ctx_from_handle(file, args->jm_ctx_handle);
+       if (!jm_ctx) {
+               ret = -EINVAL;
+               goto out_put_syncout;
+       }
+
        job = kzalloc(sizeof(*job), GFP_KERNEL);
        if (!job) {
                ret = -ENOMEM;
-               goto out_put_syncout;
+               goto out_put_jm_ctx;
        }
 
        kref_init(&job->refcount);
@@ -307,12 +320,13 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
        job->requirements = args->requirements;
        job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev);
        job->mmu = file_priv->mmu;
+       job->ctx = panfrost_jm_ctx_get(jm_ctx);
        job->engine_usage = &file_priv->engine_usage;
 
        slot = panfrost_job_get_slot(job);
 
        ret = drm_sched_job_init(&job->base,
-                                &file_priv->sched_entity[slot],
+                                &jm_ctx->slot_entity[slot],
                                 1, NULL, file->client_id);
        if (ret)
                goto out_put_job;
@@ -338,6 +352,8 @@ out_cleanup_job:
                drm_sched_job_cleanup(&job->base);
 out_put_job:
        panfrost_job_put(job);
+out_put_jm_ctx:
+       panfrost_jm_ctx_put(jm_ctx);
 out_put_syncout:
        if (sync_out)
                drm_syncobj_put(sync_out);
@@ -564,7 +580,7 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
                goto err_free;
        }
 
-       ret = panfrost_job_open(panfrost_priv);
+       ret = panfrost_job_open(file);
        if (ret)
                goto err_job;
 
@@ -583,7 +599,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
        struct panfrost_file_priv *panfrost_priv = file->driver_priv;
 
        panfrost_perfcnt_close(file);
-       panfrost_job_close(panfrost_priv);
+       panfrost_job_close(file);
 
        panfrost_mmu_ctx_put(panfrost_priv->mmu);
        kfree(panfrost_priv);
index 82acabb21b2789c550cee10555686948ea870c30..c47d14eabbae00fc428237f6ee432954b5157e49 100644 (file)
@@ -22,6 +22,7 @@
 #include "panfrost_mmu.h"
 #include "panfrost_dump.h"
 
+#define MAX_JM_CTX_PER_FILE 64
 #define JOB_TIMEOUT_MS 500
 
 #define job_write(dev, reg, data) writel(data, dev->iomem + (reg))
@@ -359,6 +360,7 @@ static void panfrost_job_cleanup(struct kref *ref)
                kvfree(job->bos);
        }
 
+       panfrost_jm_ctx_put(job->ctx);
        kfree(job);
 }
 
@@ -383,6 +385,9 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
        int slot = panfrost_job_get_slot(job);
        struct dma_fence *fence = NULL;
 
+       if (job->ctx->destroyed)
+               return ERR_PTR(-ECANCELED);
+
        if (unlikely(job->base.s_fence->finished.error))
                return NULL;
 
@@ -917,39 +922,176 @@ void panfrost_job_fini(struct panfrost_device *pfdev)
        destroy_workqueue(pfdev->reset.wq);
 }
 
-int panfrost_job_open(struct panfrost_file_priv *panfrost_priv)
+int panfrost_job_open(struct drm_file *file)
+{
+       struct panfrost_file_priv *panfrost_priv = file->driver_priv;
+       int ret;
+
+       struct drm_panfrost_jm_ctx_create default_jm_ctx = {
+               .priority = PANFROST_JM_CTX_PRIORITY_MEDIUM,
+       };
+
+       xa_init_flags(&panfrost_priv->jm_ctxs, XA_FLAGS_ALLOC);
+
+       ret = panfrost_jm_ctx_create(file, &default_jm_ctx);
+       if (ret)
+               return ret;
+
+       /* We expect the default context to be assigned handle 0. */
+       if (WARN_ON(default_jm_ctx.handle))
+               return -EINVAL;
+
+       return 0;
+}
+
+void panfrost_job_close(struct drm_file *file)
+{
+       struct panfrost_file_priv *panfrost_priv = file->driver_priv;
+       struct panfrost_jm_ctx *jm_ctx;
+       unsigned long i;
+
+       xa_for_each(&panfrost_priv->jm_ctxs, i, jm_ctx)
+               panfrost_jm_ctx_destroy(file, i);
+
+       xa_destroy(&panfrost_priv->jm_ctxs);
+}
+
+int panfrost_job_is_idle(struct panfrost_device *pfdev)
 {
-       struct panfrost_device *pfdev = panfrost_priv->pfdev;
        struct panfrost_job_slot *js = pfdev->js;
-       struct drm_gpu_scheduler *sched;
-       int ret, i;
+       int i;
 
        for (i = 0; i < NUM_JOB_SLOTS; i++) {
-               sched = &js->queue[i].sched;
-               ret = drm_sched_entity_init(&panfrost_priv->sched_entity[i],
-                                           DRM_SCHED_PRIORITY_NORMAL, &sched,
-                                           1, NULL);
-               if (WARN_ON(ret))
-                       return ret;
+               /* If there are any jobs in the HW queue, we're not idle */
+               if (atomic_read(&js->queue[i].sched.credit_count))
+                       return false;
+       }
+
+       return true;
+}
+
+static void panfrost_jm_ctx_release(struct kref *kref)
+{
+       struct panfrost_jm_ctx *jm_ctx = container_of(kref, struct panfrost_jm_ctx, refcnt);
+
+       WARN_ON(!jm_ctx->destroyed);
+
+       for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++)
+               drm_sched_entity_destroy(&jm_ctx->slot_entity[i]);
+
+       kfree(jm_ctx);
+}
+
+void
+panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx)
+{
+       if (jm_ctx)
+               kref_put(&jm_ctx->refcnt, panfrost_jm_ctx_release);
+}
+
+struct panfrost_jm_ctx *
+panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx)
+{
+       if (jm_ctx)
+               kref_get(&jm_ctx->refcnt);
+
+       return jm_ctx;
+}
+
+struct panfrost_jm_ctx *
+panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle)
+{
+       struct panfrost_file_priv *priv = file->driver_priv;
+       struct panfrost_jm_ctx *jm_ctx;
+
+       xa_lock(&priv->jm_ctxs);
+       jm_ctx = panfrost_jm_ctx_get(xa_load(&priv->jm_ctxs, handle));
+       xa_unlock(&priv->jm_ctxs);
+
+       return jm_ctx;
+}
+
+static int jm_ctx_prio_to_drm_sched_prio(struct drm_file *file,
+                                        enum drm_panfrost_jm_ctx_priority in,
+                                        enum drm_sched_priority *out)
+{
+       switch (in) {
+       case PANFROST_JM_CTX_PRIORITY_LOW:
+               *out = DRM_SCHED_PRIORITY_LOW;
+               return 0;
+       case PANFROST_JM_CTX_PRIORITY_MEDIUM:
+               *out = DRM_SCHED_PRIORITY_NORMAL;
+               return 0;
+       case PANFROST_JM_CTX_PRIORITY_HIGH:
+               if (!panfrost_high_prio_allowed(file))
+                       return -EACCES;
+
+               *out = DRM_SCHED_PRIORITY_HIGH;
+               return 0;
+       default:
+               return -EINVAL;
        }
+}
+
+int panfrost_jm_ctx_create(struct drm_file *file,
+                          struct drm_panfrost_jm_ctx_create *args)
+{
+       struct panfrost_file_priv *priv = file->driver_priv;
+       struct panfrost_device *pfdev = priv->pfdev;
+       enum drm_sched_priority sched_prio;
+       struct panfrost_jm_ctx *jm_ctx;
+       int ret;
+
+       jm_ctx = kzalloc(sizeof(*jm_ctx), GFP_KERNEL);
+       if (!jm_ctx)
+               return -ENOMEM;
+
+       kref_init(&jm_ctx->refcnt);
+
+       ret = jm_ctx_prio_to_drm_sched_prio(file, args->priority, &sched_prio);
+       if (ret)
+               goto err_put_jm_ctx;
+
+       for (u32 i = 0; i < NUM_JOB_SLOTS; i++) {
+               struct drm_gpu_scheduler *sched = &pfdev->js->queue[i].sched;
+
+               ret = drm_sched_entity_init(&jm_ctx->slot_entity[i], sched_prio,
+                                           &sched, 1, NULL);
+               if (ret)
+                       goto err_put_jm_ctx;
+       }
+
+       ret = xa_alloc(&priv->jm_ctxs, &args->handle, jm_ctx,
+                      XA_LIMIT(0, MAX_JM_CTX_PER_FILE), GFP_KERNEL);
+       if (ret)
+               goto err_put_jm_ctx;
+
        return 0;
+
+err_put_jm_ctx:
+       jm_ctx->destroyed = true;
+       panfrost_jm_ctx_put(jm_ctx);
+       return ret;
 }
 
-void panfrost_job_close(struct panfrost_file_priv *panfrost_priv)
+int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle)
 {
-       struct panfrost_device *pfdev = panfrost_priv->pfdev;
-       int i;
+       struct panfrost_file_priv *priv = file->driver_priv;
+       struct panfrost_device *pfdev = priv->pfdev;
+       struct panfrost_jm_ctx *jm_ctx;
 
-       for (i = 0; i < NUM_JOB_SLOTS; i++)
-               drm_sched_entity_destroy(&panfrost_priv->sched_entity[i]);
+       jm_ctx = xa_erase(&priv->jm_ctxs, handle);
+       if (!jm_ctx)
+               return -EINVAL;
+
+       jm_ctx->destroyed = true;
 
        /* Kill in-flight jobs */
        spin_lock(&pfdev->js->job_lock);
-       for (i = 0; i < NUM_JOB_SLOTS; i++) {
-               struct drm_sched_entity *entity = &panfrost_priv->sched_entity[i];
-               int j;
+       for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++) {
+               struct drm_sched_entity *entity = &jm_ctx->slot_entity[i];
 
-               for (j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) {
+               for (int j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) {
                        struct panfrost_job *job = pfdev->jobs[i][j];
                        u32 cmd;
 
@@ -980,18 +1122,7 @@ void panfrost_job_close(struct panfrost_file_priv *panfrost_priv)
                }
        }
        spin_unlock(&pfdev->js->job_lock);
-}
-
-int panfrost_job_is_idle(struct panfrost_device *pfdev)
-{
-       struct panfrost_job_slot *js = pfdev->js;
-       int i;
 
-       for (i = 0; i < NUM_JOB_SLOTS; i++) {
-               /* If there are any jobs in the HW queue, we're not idle */
-               if (atomic_read(&js->queue[i].sched.credit_count))
-                       return false;
-       }
-
-       return true;
+       panfrost_jm_ctx_put(jm_ctx);
+       return 0;
 }
index ec581b97852b61847330c78b77b97b049d042b9f..5a30ff1503c6902c6767df410df657192542f4a5 100644 (file)
@@ -18,6 +18,7 @@ struct panfrost_job {
 
        struct panfrost_device *pfdev;
        struct panfrost_mmu *mmu;
+       struct panfrost_jm_ctx *ctx;
 
        /* Fence to be signaled by IRQ handler when the job is complete. */
        struct dma_fence *done_fence;
@@ -39,10 +40,30 @@ struct panfrost_job {
        u64 start_cycles;
 };
 
+struct panfrost_js_ctx {
+       struct drm_sched_entity sched_entity;
+       bool enabled;
+};
+
+#define NUM_JOB_SLOTS 3
+
+struct panfrost_jm_ctx {
+       struct kref refcnt;
+       bool destroyed;
+       struct drm_sched_entity slot_entity[NUM_JOB_SLOTS];
+};
+
+int panfrost_jm_ctx_create(struct drm_file *file,
+                          struct drm_panfrost_jm_ctx_create *args);
+int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle);
+void panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx);
+struct panfrost_jm_ctx *panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx);
+struct panfrost_jm_ctx *panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle);
+
 int panfrost_job_init(struct panfrost_device *pfdev);
 void panfrost_job_fini(struct panfrost_device *pfdev);
-int panfrost_job_open(struct panfrost_file_priv *panfrost_priv);
-void panfrost_job_close(struct panfrost_file_priv *panfrost_priv);
+int panfrost_job_open(struct drm_file *file);
+void panfrost_job_close(struct drm_file *file);
 int panfrost_job_get_slot(struct panfrost_job *job);
 int panfrost_job_push(struct panfrost_job *job);
 void panfrost_job_put(struct panfrost_job *job);