drm: move amd_gpu_scheduler into common location
authorLucas Stach <l.stach@pengutronix.de>
Wed, 6 Dec 2017 16:49:39 +0000 (17:49 +0100)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 7 Dec 2017 16:51:56 +0000 (11:51 -0500)
This moves and renames the AMDGPU scheduler to a common location in DRM
in order to facilitate re-use by other drivers. This is mostly a straight
forward rename with no code changes.

One notable exception is the function to_drm_sched_fence(), which is no
longer a inline header function to avoid the need to export the
drm_sched_fence_ops_scheduled and drm_sched_fence_ops_finished structures.

Reviewed-by: Chunming Zhou <david1.zhou@amd.com>
Tested-by: Dieter Nützel <Dieter@nuetzel-hh.de>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
39 files changed:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/Makefile
drivers/gpu/drm/amd/amdgpu/amdgpu.h
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c
drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h
drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h
drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c
drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h [deleted file]
drivers/gpu/drm/amd/scheduler/gpu_scheduler.c [deleted file]
drivers/gpu/drm/amd/scheduler/gpu_scheduler.h [deleted file]
drivers/gpu/drm/amd/scheduler/sched_fence.c [deleted file]
drivers/gpu/drm/amd/scheduler/spsc_queue.h [deleted file]
drivers/gpu/drm/scheduler/Makefile [new file with mode: 0644]
drivers/gpu/drm/scheduler/gpu_scheduler.c [new file with mode: 0644]
drivers/gpu/drm/scheduler/sched_fence.c [new file with mode: 0644]
include/drm/gpu_scheduler.h [new file with mode: 0644]
include/drm/gpu_scheduler_trace.h [new file with mode: 0644]
include/drm/spsc_queue.h [new file with mode: 0644]

index 4d9f21831741efe0fcd9237f543b0a88873fec09..ee38a3db1890a05efaa7274a6b72667d97c3f49f 100644 (file)
@@ -149,6 +149,10 @@ config DRM_VM
        bool
        depends on DRM && MMU
 
+config DRM_SCHED
+       tristate
+       depends on DRM
+
 source "drivers/gpu/drm/i2c/Kconfig"
 
 source "drivers/gpu/drm/arm/Kconfig"
@@ -178,6 +182,7 @@ config DRM_AMDGPU
        depends on DRM && PCI && MMU
        select FW_LOADER
         select DRM_KMS_HELPER
+       select DRM_SCHED
         select DRM_TTM
        select POWER_SUPPLY
        select HWMON
index e9500844333e1652fed9b0325d322a3c260da08a..1f6ba9e34e31cbb4bfd23f228b31310c9f8c9b63 100644 (file)
@@ -101,3 +101,4 @@ obj-$(CONFIG_DRM_MXSFB)     += mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
 obj-$(CONFIG_DRM_PL111) += pl111/
 obj-$(CONFIG_DRM_TVE200) += tve200/
+obj-$(CONFIG_DRM_SCHED)        += scheduler/
index 90202cf4cd1e0e6569b5adf83c83400d2839a510..a7391d49ad4014e42f3ad147dc340f7ed590515c 100644 (file)
@@ -135,10 +135,7 @@ amdgpu-y += \
 amdgpu-y += amdgpu_cgs.o
 
 # GPU scheduler
-amdgpu-y += \
-       ../scheduler/gpu_scheduler.o \
-       ../scheduler/sched_fence.o \
-       amdgpu_job.o
+amdgpu-y += amdgpu_job.o
 
 # ACP componet
 ifneq ($(CONFIG_DRM_AMD_ACP),)
index 5e2958a79928d0b4b4a0571cb50bca734f48cb28..5c8648ec2cd2b44cd77f9f38bfcec6fe1e12b697 100644 (file)
@@ -45,6 +45,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_gem.h>
 #include <drm/amdgpu_drm.h>
+#include <drm/gpu_scheduler.h>
 
 #include <kgd_kfd_interface.h>
 #include "dm_pp_interface.h"
@@ -68,7 +69,6 @@
 #include "amdgpu_vcn.h"
 #include "amdgpu_mn.h"
 #include "amdgpu_dm.h"
-#include "gpu_scheduler.h"
 #include "amdgpu_virt.h"
 #include "amdgpu_gart.h"
 
@@ -689,7 +689,7 @@ struct amdgpu_ib {
        uint32_t                        flags;
 };
 
-extern const struct amd_sched_backend_ops amdgpu_sched_ops;
+extern const struct drm_sched_backend_ops amdgpu_sched_ops;
 
 int amdgpu_job_alloc(struct amdgpu_device *adev, unsigned num_ibs,
                     struct amdgpu_job **job, struct amdgpu_vm *vm);
@@ -699,7 +699,7 @@ int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, unsigned size,
 void amdgpu_job_free_resources(struct amdgpu_job *job);
 void amdgpu_job_free(struct amdgpu_job *job);
 int amdgpu_job_submit(struct amdgpu_job *job, struct amdgpu_ring *ring,
-                     struct amd_sched_entity *entity, void *owner,
+                     struct drm_sched_entity *entity, void *owner,
                      struct dma_fence **f);
 
 /*
@@ -732,7 +732,7 @@ int amdgpu_queue_mgr_map(struct amdgpu_device *adev,
 struct amdgpu_ctx_ring {
        uint64_t                sequence;
        struct dma_fence        **fences;
-       struct amd_sched_entity entity;
+       struct drm_sched_entity entity;
 };
 
 struct amdgpu_ctx {
@@ -746,8 +746,8 @@ struct amdgpu_ctx {
        struct dma_fence        **fences;
        struct amdgpu_ctx_ring  rings[AMDGPU_MAX_RINGS];
        bool                    preamble_presented;
-       enum amd_sched_priority init_priority;
-       enum amd_sched_priority override_priority;
+       enum drm_sched_priority init_priority;
+       enum drm_sched_priority override_priority;
        struct mutex            lock;
        atomic_t        guilty;
 };
@@ -767,7 +767,7 @@ int amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx, struct amdgpu_ring *ring,
 struct dma_fence *amdgpu_ctx_get_fence(struct amdgpu_ctx *ctx,
                                   struct amdgpu_ring *ring, uint64_t seq);
 void amdgpu_ctx_priority_override(struct amdgpu_ctx *ctx,
-                                 enum amd_sched_priority priority);
+                                 enum drm_sched_priority priority);
 
 int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
                     struct drm_file *filp);
@@ -1116,7 +1116,7 @@ struct amdgpu_cs_parser {
 #define AMDGPU_HAVE_CTX_SWITCH              (1 << 2) /* bit set means context switch occured */
 
 struct amdgpu_job {
-       struct amd_sched_job    base;
+       struct drm_sched_job    base;
        struct amdgpu_device    *adev;
        struct amdgpu_vm        *vm;
        struct amdgpu_ring      *ring;
index 4cea9ab237ac3cd36813a610b5623746932a1a24..44523a88ebb27a300a48483a9b6b1fd279a10425 100644 (file)
@@ -1150,7 +1150,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
                            union drm_amdgpu_cs *cs)
 {
        struct amdgpu_ring *ring = p->job->ring;
-       struct amd_sched_entity *entity = &p->ctx->rings[ring->idx].entity;
+       struct drm_sched_entity *entity = &p->ctx->rings[ring->idx].entity;
        struct amdgpu_job *job;
        unsigned i;
        uint64_t seq;
@@ -1173,7 +1173,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
        job = p->job;
        p->job = NULL;
 
-       r = amd_sched_job_init(&job->base, &ring->sched, entity, p->filp);
+       r = drm_sched_job_init(&job->base, &ring->sched, entity, p->filp);
        if (r) {
                amdgpu_job_free(job);
                amdgpu_mn_unlock(p->mn);
@@ -1202,7 +1202,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
        amdgpu_ring_priority_get(job->ring, job->base.s_priority);
 
        trace_amdgpu_cs_ioctl(job);
-       amd_sched_entity_push_job(&job->base, entity);
+       drm_sched_entity_push_job(&job->base, entity);
 
        ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence);
        amdgpu_mn_unlock(p->mn);
index d71dc164b4693b5b0e67405eaee5990a0e4af5e1..09d35051fdd68689ac00d69504765166e738076f 100644 (file)
 #include "amdgpu_sched.h"
 
 static int amdgpu_ctx_priority_permit(struct drm_file *filp,
-                                     enum amd_sched_priority priority)
+                                     enum drm_sched_priority priority)
 {
        /* NORMAL and below are accessible by everyone */
-       if (priority <= AMD_SCHED_PRIORITY_NORMAL)
+       if (priority <= DRM_SCHED_PRIORITY_NORMAL)
                return 0;
 
        if (capable(CAP_SYS_NICE))
@@ -44,14 +44,14 @@ static int amdgpu_ctx_priority_permit(struct drm_file *filp,
 }
 
 static int amdgpu_ctx_init(struct amdgpu_device *adev,
-                          enum amd_sched_priority priority,
+                          enum drm_sched_priority priority,
                           struct drm_file *filp,
                           struct amdgpu_ctx *ctx)
 {
        unsigned i, j;
        int r;
 
-       if (priority < 0 || priority >= AMD_SCHED_PRIORITY_MAX)
+       if (priority < 0 || priority >= DRM_SCHED_PRIORITY_MAX)
                return -EINVAL;
 
        r = amdgpu_ctx_priority_permit(filp, priority);
@@ -78,19 +78,19 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev,
        ctx->reset_counter_query = ctx->reset_counter;
        ctx->vram_lost_counter = atomic_read(&adev->vram_lost_counter);
        ctx->init_priority = priority;
-       ctx->override_priority = AMD_SCHED_PRIORITY_UNSET;
+       ctx->override_priority = DRM_SCHED_PRIORITY_UNSET;
 
        /* create context entity for each ring */
        for (i = 0; i < adev->num_rings; i++) {
                struct amdgpu_ring *ring = adev->rings[i];
-               struct amd_sched_rq *rq;
+               struct drm_sched_rq *rq;
 
                rq = &ring->sched.sched_rq[priority];
 
                if (ring == &adev->gfx.kiq.ring)
                        continue;
 
-               r = amd_sched_entity_init(&ring->sched, &ctx->rings[i].entity,
+               r = drm_sched_entity_init(&ring->sched, &ctx->rings[i].entity,
                                          rq, amdgpu_sched_jobs, &ctx->guilty);
                if (r)
                        goto failed;
@@ -104,7 +104,7 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev,
 
 failed:
        for (j = 0; j < i; j++)
-               amd_sched_entity_fini(&adev->rings[j]->sched,
+               drm_sched_entity_fini(&adev->rings[j]->sched,
                                      &ctx->rings[j].entity);
        kfree(ctx->fences);
        ctx->fences = NULL;
@@ -126,7 +126,7 @@ static void amdgpu_ctx_fini(struct amdgpu_ctx *ctx)
        ctx->fences = NULL;
 
        for (i = 0; i < adev->num_rings; i++)
-               amd_sched_entity_fini(&adev->rings[i]->sched,
+               drm_sched_entity_fini(&adev->rings[i]->sched,
                                      &ctx->rings[i].entity);
 
        amdgpu_queue_mgr_fini(adev, &ctx->queue_mgr);
@@ -137,7 +137,7 @@ static void amdgpu_ctx_fini(struct amdgpu_ctx *ctx)
 static int amdgpu_ctx_alloc(struct amdgpu_device *adev,
                            struct amdgpu_fpriv *fpriv,
                            struct drm_file *filp,
-                           enum amd_sched_priority priority,
+                           enum drm_sched_priority priority,
                            uint32_t *id)
 {
        struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
@@ -266,7 +266,7 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
 {
        int r;
        uint32_t id;
-       enum amd_sched_priority priority;
+       enum drm_sched_priority priority;
 
        union drm_amdgpu_ctx *args = data;
        struct amdgpu_device *adev = dev->dev_private;
@@ -278,8 +278,8 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
 
        /* For backwards compatibility reasons, we need to accept
         * ioctls with garbage in the priority field */
-       if (priority == AMD_SCHED_PRIORITY_INVALID)
-               priority = AMD_SCHED_PRIORITY_NORMAL;
+       if (priority == DRM_SCHED_PRIORITY_INVALID)
+               priority = DRM_SCHED_PRIORITY_NORMAL;
 
        switch (args->in.op) {
        case AMDGPU_CTX_OP_ALLOC_CTX:
@@ -385,18 +385,18 @@ struct dma_fence *amdgpu_ctx_get_fence(struct amdgpu_ctx *ctx,
 }
 
 void amdgpu_ctx_priority_override(struct amdgpu_ctx *ctx,
-                                 enum amd_sched_priority priority)
+                                 enum drm_sched_priority priority)
 {
        int i;
        struct amdgpu_device *adev = ctx->adev;
-       struct amd_sched_rq *rq;
-       struct amd_sched_entity *entity;
+       struct drm_sched_rq *rq;
+       struct drm_sched_entity *entity;
        struct amdgpu_ring *ring;
-       enum amd_sched_priority ctx_prio;
+       enum drm_sched_priority ctx_prio;
 
        ctx->override_priority = priority;
 
-       ctx_prio = (ctx->override_priority == AMD_SCHED_PRIORITY_UNSET) ?
+       ctx_prio = (ctx->override_priority == DRM_SCHED_PRIORITY_UNSET) ?
                        ctx->init_priority : ctx->override_priority;
 
        for (i = 0; i < adev->num_rings; i++) {
@@ -407,7 +407,7 @@ void amdgpu_ctx_priority_override(struct amdgpu_ctx *ctx,
                if (ring->funcs->type == AMDGPU_RING_TYPE_KIQ)
                        continue;
 
-               amd_sched_entity_set_rq(entity, rq);
+               drm_sched_entity_set_rq(entity, rq);
        }
 }
 
index 70c9e5756b02a66fdda95f5340ad860337ec1328..98cc4df02b14e0abd6b3b5d5d03d414b7c3c7768 100644 (file)
@@ -3058,7 +3058,7 @@ int amdgpu_gpu_recover(struct amdgpu_device *adev, struct amdgpu_job *job)
                        continue;
 
                kthread_park(ring->sched.thread);
-               amd_sched_hw_job_reset(&ring->sched, &job->base);
+               drm_sched_hw_job_reset(&ring->sched, &job->base);
 
                /* after all hw jobs are reset, hw fence is meaningless, so force_completion */
                amdgpu_fence_driver_force_completion(ring);
@@ -3111,7 +3111,7 @@ int amdgpu_gpu_recover(struct amdgpu_device *adev, struct amdgpu_job *job)
                        if (job && job->ring->idx != i)
                                continue;
 
-                       amd_sched_job_recovery(&ring->sched);
+                       drm_sched_job_recovery(&ring->sched);
                        kthread_unpark(ring->sched.thread);
                }
        } else {
index 31383e0049476153cd87d2dcfe2cf90805941751..1d8011bca182e9db0a9de0cdb1057de436fb6349 100644 (file)
@@ -912,7 +912,7 @@ static int __init amdgpu_init(void)
        if (r)
                goto error_fence;
 
-       r = amd_sched_fence_slab_init();
+       r = drm_sched_fence_slab_init();
        if (r)
                goto error_sched;
 
@@ -944,7 +944,7 @@ static void __exit amdgpu_exit(void)
        pci_unregister_driver(pdriver);
        amdgpu_unregister_atpx_handler();
        amdgpu_sync_fini();
-       amd_sched_fence_slab_fini();
+       drm_sched_fence_slab_fini();
        amdgpu_fence_slab_fini();
 }
 
index 604ac03a42e4f3858d87ab2d53ca563bcb4e7b93..14699637913ad479da59eeb273473b62f04b456c 100644 (file)
@@ -445,7 +445,7 @@ int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring,
                         */
                        timeout = MAX_SCHEDULE_TIMEOUT;
                }
-               r = amd_sched_init(&ring->sched, &amdgpu_sched_ops,
+               r = drm_sched_init(&ring->sched, &amdgpu_sched_ops,
                                   num_hw_submission, amdgpu_job_hang_limit,
                                   timeout, ring->name);
                if (r) {
@@ -503,7 +503,7 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev)
                }
                amdgpu_irq_put(adev, ring->fence_drv.irq_src,
                               ring->fence_drv.irq_type);
-               amd_sched_fini(&ring->sched);
+               drm_sched_fini(&ring->sched);
                del_timer_sync(&ring->fence_drv.fallback_timer);
                for (j = 0; j <= ring->fence_drv.num_fences_mask; ++j)
                        dma_fence_put(ring->fence_drv.fences[j]);
index bdc210ac74f800edb99629c872c9fdc1af658b14..013c0a8cfb604a805096c1518667ff539ea83021 100644 (file)
@@ -28,7 +28,7 @@
 #include "amdgpu.h"
 #include "amdgpu_trace.h"
 
-static void amdgpu_job_timedout(struct amd_sched_job *s_job)
+static void amdgpu_job_timedout(struct drm_sched_job *s_job)
 {
        struct amdgpu_job *job = container_of(s_job, struct amdgpu_job, base);
 
@@ -96,7 +96,7 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
                amdgpu_ib_free(job->adev, &job->ibs[i], f);
 }
 
-static void amdgpu_job_free_cb(struct amd_sched_job *s_job)
+static void amdgpu_job_free_cb(struct drm_sched_job *s_job)
 {
        struct amdgpu_job *job = container_of(s_job, struct amdgpu_job, base);
 
@@ -118,7 +118,7 @@ void amdgpu_job_free(struct amdgpu_job *job)
 }
 
 int amdgpu_job_submit(struct amdgpu_job *job, struct amdgpu_ring *ring,
-                     struct amd_sched_entity *entity, void *owner,
+                     struct drm_sched_entity *entity, void *owner,
                      struct dma_fence **f)
 {
        int r;
@@ -127,7 +127,7 @@ int amdgpu_job_submit(struct amdgpu_job *job, struct amdgpu_ring *ring,
        if (!f)
                return -EINVAL;
 
-       r = amd_sched_job_init(&job->base, &ring->sched, entity, owner);
+       r = drm_sched_job_init(&job->base, &ring->sched, entity, owner);
        if (r)
                return r;
 
@@ -136,13 +136,13 @@ int amdgpu_job_submit(struct amdgpu_job *job, struct amdgpu_ring *ring,
        *f = dma_fence_get(&job->base.s_fence->finished);
        amdgpu_job_free_resources(job);
        amdgpu_ring_priority_get(job->ring, job->base.s_priority);
-       amd_sched_entity_push_job(&job->base, entity);
+       drm_sched_entity_push_job(&job->base, entity);
 
        return 0;
 }
 
-static struct dma_fence *amdgpu_job_dependency(struct amd_sched_job *sched_job,
-                                              struct amd_sched_entity *s_entity)
+static struct dma_fence *amdgpu_job_dependency(struct drm_sched_job *sched_job,
+                                              struct drm_sched_entity *s_entity)
 {
        struct amdgpu_job *job = to_amdgpu_job(sched_job);
        struct amdgpu_vm *vm = job->vm;
@@ -151,7 +151,7 @@ static struct dma_fence *amdgpu_job_dependency(struct amd_sched_job *sched_job,
        struct dma_fence *fence = amdgpu_sync_get_fence(&job->sync, &explicit);
 
        if (fence && explicit) {
-               if (amd_sched_dependency_optimized(fence, s_entity)) {
+               if (drm_sched_dependency_optimized(fence, s_entity)) {
                        r = amdgpu_sync_fence(job->adev, &job->sched_sync, fence, false);
                        if (r)
                                DRM_ERROR("Error adding fence to sync (%d)\n", r);
@@ -173,7 +173,7 @@ static struct dma_fence *amdgpu_job_dependency(struct amd_sched_job *sched_job,
        return fence;
 }
 
-static struct dma_fence *amdgpu_job_run(struct amd_sched_job *sched_job)
+static struct dma_fence *amdgpu_job_run(struct drm_sched_job *sched_job)
 {
        struct dma_fence *fence = NULL, *finished;
        struct amdgpu_device *adev;
@@ -211,7 +211,7 @@ static struct dma_fence *amdgpu_job_run(struct amd_sched_job *sched_job)
        return fence;
 }
 
-const struct amd_sched_backend_ops amdgpu_sched_ops = {
+const struct drm_sched_backend_ops amdgpu_sched_ops = {
        .dependency = amdgpu_job_dependency,
        .run_job = amdgpu_job_run,
        .timedout_job = amdgpu_job_timedout,
index a98fbbb4739f65dad15dc717c1e66722770b5420..41c75f9632dc2f42d18488b11a45a006f33136c9 100644 (file)
@@ -164,7 +164,7 @@ void amdgpu_ring_undo(struct amdgpu_ring *ring)
  * Release a request for executing at @priority
  */
 void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
-                             enum amd_sched_priority priority)
+                             enum drm_sched_priority priority)
 {
        int i;
 
@@ -175,7 +175,7 @@ void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
                return;
 
        /* no need to restore if the job is already at the lowest priority */
-       if (priority == AMD_SCHED_PRIORITY_NORMAL)
+       if (priority == DRM_SCHED_PRIORITY_NORMAL)
                return;
 
        mutex_lock(&ring->priority_mutex);
@@ -184,8 +184,8 @@ void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
                goto out_unlock;
 
        /* decay priority to the next level with a job available */
-       for (i = priority; i >= AMD_SCHED_PRIORITY_MIN; i--) {
-               if (i == AMD_SCHED_PRIORITY_NORMAL
+       for (i = priority; i >= DRM_SCHED_PRIORITY_MIN; i--) {
+               if (i == DRM_SCHED_PRIORITY_NORMAL
                                || atomic_read(&ring->num_jobs[i])) {
                        ring->priority = i;
                        ring->funcs->set_priority(ring, i);
@@ -206,7 +206,7 @@ out_unlock:
  * Request a ring's priority to be raised to @priority (refcounted).
  */
 void amdgpu_ring_priority_get(struct amdgpu_ring *ring,
-                             enum amd_sched_priority priority)
+                             enum drm_sched_priority priority)
 {
        if (!ring->funcs->set_priority)
                return;
@@ -317,12 +317,12 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
        }
 
        ring->max_dw = max_dw;
-       ring->priority = AMD_SCHED_PRIORITY_NORMAL;
+       ring->priority = DRM_SCHED_PRIORITY_NORMAL;
        mutex_init(&ring->priority_mutex);
        INIT_LIST_HEAD(&ring->lru_list);
        amdgpu_ring_lru_touch(adev, ring);
 
-       for (i = 0; i < AMD_SCHED_PRIORITY_MAX; ++i)
+       for (i = 0; i < DRM_SCHED_PRIORITY_MAX; ++i)
                atomic_set(&ring->num_jobs[i], 0);
 
        if (amdgpu_debugfs_ring_init(adev, ring)) {
index a6b89e3932a5fcb7ebe1f0b7a2030940f57722cd..641e3fd7ba3c97ec5377de386d87dc61e73939b9 100644 (file)
@@ -25,7 +25,7 @@
 #define __AMDGPU_RING_H__
 
 #include <drm/amdgpu_drm.h>
-#include "gpu_scheduler.h"
+#include <drm/gpu_scheduler.h>
 
 /* max number of rings */
 #define AMDGPU_MAX_RINGS               18
@@ -154,14 +154,14 @@ struct amdgpu_ring_funcs {
        void (*emit_tmz)(struct amdgpu_ring *ring, bool start);
        /* priority functions */
        void (*set_priority) (struct amdgpu_ring *ring,
-                             enum amd_sched_priority priority);
+                             enum drm_sched_priority priority);
 };
 
 struct amdgpu_ring {
        struct amdgpu_device            *adev;
        const struct amdgpu_ring_funcs  *funcs;
        struct amdgpu_fence_driver      fence_drv;
-       struct amd_gpu_scheduler        sched;
+       struct drm_gpu_scheduler        sched;
        struct list_head                lru_list;
 
        struct amdgpu_bo        *ring_obj;
@@ -196,7 +196,7 @@ struct amdgpu_ring {
        unsigned                vm_inv_eng;
        bool                    has_compute_vm_bug;
 
-       atomic_t                num_jobs[AMD_SCHED_PRIORITY_MAX];
+       atomic_t                num_jobs[DRM_SCHED_PRIORITY_MAX];
        struct mutex            priority_mutex;
        /* protected by priority_mutex */
        int                     priority;
@@ -212,9 +212,9 @@ void amdgpu_ring_generic_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib);
 void amdgpu_ring_commit(struct amdgpu_ring *ring);
 void amdgpu_ring_undo(struct amdgpu_ring *ring);
 void amdgpu_ring_priority_get(struct amdgpu_ring *ring,
-                             enum amd_sched_priority priority);
+                             enum drm_sched_priority priority);
 void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
-                             enum amd_sched_priority priority);
+                             enum drm_sched_priority priority);
 int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
                     unsigned ring_size, struct amdgpu_irq_src *irq_src,
                     unsigned irq_type);
index 290cc3f9c433a3df7de41f1c1db228db30377ee8..86a0715d9431ded69bac338a38143d82789ad949 100644 (file)
 
 #include "amdgpu_vm.h"
 
-enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority)
+enum drm_sched_priority amdgpu_to_sched_priority(int amdgpu_priority)
 {
        switch (amdgpu_priority) {
        case AMDGPU_CTX_PRIORITY_VERY_HIGH:
-               return AMD_SCHED_PRIORITY_HIGH_HW;
+               return DRM_SCHED_PRIORITY_HIGH_HW;
        case AMDGPU_CTX_PRIORITY_HIGH:
-               return AMD_SCHED_PRIORITY_HIGH_SW;
+               return DRM_SCHED_PRIORITY_HIGH_SW;
        case AMDGPU_CTX_PRIORITY_NORMAL:
-               return AMD_SCHED_PRIORITY_NORMAL;
+               return DRM_SCHED_PRIORITY_NORMAL;
        case AMDGPU_CTX_PRIORITY_LOW:
        case AMDGPU_CTX_PRIORITY_VERY_LOW:
-               return AMD_SCHED_PRIORITY_LOW;
+               return DRM_SCHED_PRIORITY_LOW;
        case AMDGPU_CTX_PRIORITY_UNSET:
-               return AMD_SCHED_PRIORITY_UNSET;
+               return DRM_SCHED_PRIORITY_UNSET;
        default:
                WARN(1, "Invalid context priority %d\n", amdgpu_priority);
-               return AMD_SCHED_PRIORITY_INVALID;
+               return DRM_SCHED_PRIORITY_INVALID;
        }
 }
 
 static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev,
                                                  int fd,
-                                                 enum amd_sched_priority priority)
+                                                 enum drm_sched_priority priority)
 {
        struct file *filp = fcheck(fd);
        struct drm_file *file;
@@ -86,11 +86,11 @@ int amdgpu_sched_ioctl(struct drm_device *dev, void *data,
 {
        union drm_amdgpu_sched *args = data;
        struct amdgpu_device *adev = dev->dev_private;
-       enum amd_sched_priority priority;
+       enum drm_sched_priority priority;
        int r;
 
        priority = amdgpu_to_sched_priority(args->in.priority);
-       if (args->in.flags || priority == AMD_SCHED_PRIORITY_INVALID)
+       if (args->in.flags || priority == DRM_SCHED_PRIORITY_INVALID)
                return -EINVAL;
 
        switch (args->in.op) {
index b28c067d38223f2c78a3b523d628a234745f0c72..2a1a0c734bddb76e77a142f854e1e049973dde6a 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <drm/drmP.h>
 
-enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority);
+enum drm_sched_priority amdgpu_to_sched_priority(int amdgpu_priority);
 int amdgpu_sched_ioctl(struct drm_device *dev, void *data,
                       struct drm_file *filp);
 
index bb79fd3f3c36859b780a4f98bbb2a796ef405e74..df65c66dc956f7500810b5e3e42080650a484193 100644 (file)
@@ -64,7 +64,7 @@ void amdgpu_sync_create(struct amdgpu_sync *sync)
 static bool amdgpu_sync_same_dev(struct amdgpu_device *adev,
                                 struct dma_fence *f)
 {
-       struct amd_sched_fence *s_fence = to_amd_sched_fence(f);
+       struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
 
        if (s_fence) {
                struct amdgpu_ring *ring;
@@ -85,7 +85,7 @@ static bool amdgpu_sync_same_dev(struct amdgpu_device *adev,
  */
 static void *amdgpu_sync_get_owner(struct dma_fence *f)
 {
-       struct amd_sched_fence *s_fence = to_amd_sched_fence(f);
+       struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
 
        if (s_fence)
                return s_fence->owner;
@@ -248,7 +248,7 @@ struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync,
 
        hash_for_each_safe(sync->fences, i, tmp, e, node) {
                struct dma_fence *f = e->fence;
-               struct amd_sched_fence *s_fence = to_amd_sched_fence(f);
+               struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
 
                if (dma_fence_is_signaled(f)) {
                        hash_del(&e->node);
index 952e0bf3bc8426c54fa531c121709ce60b9eea17..7db9556b389b8312383481a1b5bafc2eacf79324 100644 (file)
@@ -76,7 +76,7 @@ static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
 {
        struct drm_global_reference *global_ref;
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        int r;
 
        adev->mman.mem_global_referenced = false;
@@ -108,8 +108,8 @@ static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
        mutex_init(&adev->mman.gtt_window_lock);
 
        ring = adev->mman.buffer_funcs_ring;
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_KERNEL];
-       r = amd_sched_entity_init(&ring->sched, &adev->mman.entity,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_KERNEL];
+       r = drm_sched_entity_init(&ring->sched, &adev->mman.entity,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r) {
                DRM_ERROR("Failed setting up TTM BO move run queue.\n");
@@ -131,7 +131,7 @@ error_mem:
 static void amdgpu_ttm_global_fini(struct amdgpu_device *adev)
 {
        if (adev->mman.mem_global_referenced) {
-               amd_sched_entity_fini(adev->mman.entity.sched,
+               drm_sched_entity_fini(adev->mman.entity.sched,
                                      &adev->mman.entity);
                mutex_destroy(&adev->mman.gtt_window_lock);
                drm_global_item_unref(&adev->mman.bo_global_ref.ref);
index 4f9433e6140622b5aff6569c8bf8c4941cbd375a..167856f6080fa432f23417f6e6117593d129ce59 100644 (file)
@@ -25,7 +25,7 @@
 #define __AMDGPU_TTM_H__
 
 #include "amdgpu.h"
-#include "gpu_scheduler.h"
+#include <drm/gpu_scheduler.h>
 
 #define AMDGPU_PL_GDS          (TTM_PL_PRIV + 0)
 #define AMDGPU_PL_GWS          (TTM_PL_PRIV + 1)
@@ -55,7 +55,7 @@ struct amdgpu_mman {
 
        struct mutex                            gtt_window_lock;
        /* Scheduler entity for buffer moves */
-       struct amd_sched_entity                 entity;
+       struct drm_sched_entity                 entity;
 };
 
 struct amdgpu_copy_mem {
index 2f2a9e17fdb4df1ad5456c66aa1060f679c9246f..916e51670bfd279b7ac3f9fa41da9586c0f5a339 100644 (file)
@@ -116,7 +116,7 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work);
 int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
 {
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        unsigned long bo_size;
        const char *fw_name;
        const struct common_firmware_header *hdr;
@@ -230,8 +230,8 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
        }
 
        ring = &adev->uvd.ring;
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-       r = amd_sched_entity_init(&ring->sched, &adev->uvd.entity,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+       r = drm_sched_entity_init(&ring->sched, &adev->uvd.entity,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r != 0) {
                DRM_ERROR("Failed setting up UVD run queue.\n");
@@ -272,7 +272,7 @@ int amdgpu_uvd_sw_fini(struct amdgpu_device *adev)
        int i;
        kfree(adev->uvd.saved_bo);
 
-       amd_sched_entity_fini(&adev->uvd.ring.sched, &adev->uvd.entity);
+       drm_sched_entity_fini(&adev->uvd.ring.sched, &adev->uvd.entity);
 
        amdgpu_bo_free_kernel(&adev->uvd.vcpu_bo,
                              &adev->uvd.gpu_addr,
index 845eea993f75ca5b567f2ac514b24e95e37b9a64..32ea20b99e53a3818417945877f8e1df56ef14fc 100644 (file)
@@ -51,8 +51,8 @@ struct amdgpu_uvd {
        struct amdgpu_irq_src   irq;
        bool                    address_64_bit;
        bool                    use_ctx_buf;
-       struct amd_sched_entity entity;
-       struct amd_sched_entity entity_enc;
+       struct drm_sched_entity entity;
+       struct drm_sched_entity entity_enc;
        uint32_t                srbm_soft_reset;
        unsigned                num_enc_rings;
 };
index ba6d846b08ff62e33a9f5b8ad0235d26e6c05a47..641deb0527ae531da07953e04b195651957ea36b 100644 (file)
@@ -85,7 +85,7 @@ static void amdgpu_vce_idle_work_handler(struct work_struct *work);
 int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
 {
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        const char *fw_name;
        const struct common_firmware_header *hdr;
        unsigned ucode_version, version_major, version_minor, binary_id;
@@ -174,8 +174,8 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
        }
 
        ring = &adev->vce.ring[0];
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-       r = amd_sched_entity_init(&ring->sched, &adev->vce.entity,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+       r = drm_sched_entity_init(&ring->sched, &adev->vce.entity,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r != 0) {
                DRM_ERROR("Failed setting up VCE run queue.\n");
@@ -207,7 +207,7 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev)
        if (adev->vce.vcpu_bo == NULL)
                return 0;
 
-       amd_sched_entity_fini(&adev->vce.ring[0].sched, &adev->vce.entity);
+       drm_sched_entity_fini(&adev->vce.ring[0].sched, &adev->vce.entity);
 
        amdgpu_bo_free_kernel(&adev->vce.vcpu_bo, &adev->vce.gpu_addr,
                (void **)&adev->vce.cpu_addr);
index 5ce54cde472d8dbfb7c9b9be556359ffe04527a2..162cae94e3b1444ac572d7fee704970ade81e337 100644 (file)
@@ -46,7 +46,7 @@ struct amdgpu_vce {
        struct amdgpu_ring      ring[AMDGPU_MAX_VCE_RINGS];
        struct amdgpu_irq_src   irq;
        unsigned                harvest_config;
-       struct amd_sched_entity entity;
+       struct drm_sched_entity entity;
        uint32_t                srbm_soft_reset;
        unsigned                num_rings;
 };
index d7ba048c2f80c136e4a2f998985966e78e872abd..88e204d537f5dc114c896caad4efa9efcf576559 100644 (file)
@@ -51,7 +51,7 @@ static void amdgpu_vcn_idle_work_handler(struct work_struct *work);
 int amdgpu_vcn_sw_init(struct amdgpu_device *adev)
 {
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        unsigned long bo_size;
        const char *fw_name;
        const struct common_firmware_header *hdr;
@@ -104,8 +104,8 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev)
        }
 
        ring = &adev->vcn.ring_dec;
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-       r = amd_sched_entity_init(&ring->sched, &adev->vcn.entity_dec,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+       r = drm_sched_entity_init(&ring->sched, &adev->vcn.entity_dec,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r != 0) {
                DRM_ERROR("Failed setting up VCN dec run queue.\n");
@@ -113,8 +113,8 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev)
        }
 
        ring = &adev->vcn.ring_enc[0];
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-       r = amd_sched_entity_init(&ring->sched, &adev->vcn.entity_enc,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+       r = drm_sched_entity_init(&ring->sched, &adev->vcn.entity_enc,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r != 0) {
                DRM_ERROR("Failed setting up VCN enc run queue.\n");
@@ -130,9 +130,9 @@ int amdgpu_vcn_sw_fini(struct amdgpu_device *adev)
 
        kfree(adev->vcn.saved_bo);
 
-       amd_sched_entity_fini(&adev->vcn.ring_dec.sched, &adev->vcn.entity_dec);
+       drm_sched_entity_fini(&adev->vcn.ring_dec.sched, &adev->vcn.entity_dec);
 
-       amd_sched_entity_fini(&adev->vcn.ring_enc[0].sched, &adev->vcn.entity_enc);
+       drm_sched_entity_fini(&adev->vcn.ring_enc[0].sched, &adev->vcn.entity_enc);
 
        amdgpu_bo_free_kernel(&adev->vcn.vcpu_bo,
                              &adev->vcn.gpu_addr,
index d50ba06578542e5a7df60dbc963a75edd1d1b672..2fd7db891689f514c0b98f72eb2d905311b31ffc 100644 (file)
@@ -56,8 +56,8 @@ struct amdgpu_vcn {
        struct amdgpu_ring      ring_dec;
        struct amdgpu_ring      ring_enc[AMDGPU_VCN_MAX_ENC_RINGS];
        struct amdgpu_irq_src   irq;
-       struct amd_sched_entity entity_dec;
-       struct amd_sched_entity entity_enc;
+       struct drm_sched_entity entity_dec;
+       struct drm_sched_entity entity_enc;
        unsigned                num_enc_rings;
 };
 
index 3ecdbdfb04dd72899c64f9f22c8b9f2dfb893b74..dbe37d621796aa2f260ec5b0d2fd81816b208307 100644 (file)
@@ -2643,7 +2643,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
                AMDGPU_VM_PTE_COUNT(adev) * 8);
        unsigned ring_instance;
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        int r, i;
        u64 flags;
        uint64_t init_pde_value = 0;
@@ -2663,8 +2663,8 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
        ring_instance = atomic_inc_return(&adev->vm_manager.vm_pte_next_ring);
        ring_instance %= adev->vm_manager.vm_pte_num_rings;
        ring = adev->vm_manager.vm_pte_rings[ring_instance];
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_KERNEL];
-       r = amd_sched_entity_init(&ring->sched, &vm->entity,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_KERNEL];
+       r = drm_sched_entity_init(&ring->sched, &vm->entity,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r)
                return r;
@@ -2744,7 +2744,7 @@ error_free_root:
        vm->root.base.bo = NULL;
 
 error_free_sched_entity:
-       amd_sched_entity_fini(&ring->sched, &vm->entity);
+       drm_sched_entity_fini(&ring->sched, &vm->entity);
 
        return r;
 }
@@ -2803,7 +2803,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
                spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags);
        }
 
-       amd_sched_entity_fini(vm->entity.sched, &vm->entity);
+       drm_sched_entity_fini(vm->entity.sched, &vm->entity);
 
        if (!RB_EMPTY_ROOT(&vm->va.rb_root)) {
                dev_err(adev->dev, "still active bo inside vm\n");
index 43ea131dd411b8626816e7df608e203a5b929fe2..159980414964d187c3d90311928453ee12d457e4 100644 (file)
 #ifndef __AMDGPU_VM_H__
 #define __AMDGPU_VM_H__
 
-#include <linux/rbtree.h>
 #include <linux/idr.h>
+#include <linux/kfifo.h>
+#include <linux/rbtree.h>
+#include <drm/gpu_scheduler.h>
 
-#include "gpu_scheduler.h"
 #include "amdgpu_sync.h"
 #include "amdgpu_ring.h"
 
@@ -175,7 +176,7 @@ struct amdgpu_vm {
        spinlock_t              freed_lock;
 
        /* Scheduler entity for page table updates */
-       struct amd_sched_entity entity;
+       struct drm_sched_entity entity;
 
        /* client id and PASID (TODO: replace client_id with PASID) */
        u64                     client_id;
index d02493cf91759a9e497454dde0dc7a811aebe041..c7dc69031fb56db38d3ed373a6954b8ef7660b19 100644 (file)
@@ -6472,10 +6472,10 @@ static void gfx_v8_0_hqd_set_priority(struct amdgpu_device *adev,
        mutex_unlock(&adev->srbm_mutex);
 }
 static void gfx_v8_0_ring_set_priority_compute(struct amdgpu_ring *ring,
-                                              enum amd_sched_priority priority)
+                                              enum drm_sched_priority priority)
 {
        struct amdgpu_device *adev = ring->adev;
-       bool acquire = priority == AMD_SCHED_PRIORITY_HIGH_HW;
+       bool acquire = priority == DRM_SCHED_PRIORITY_HIGH_HW;
 
        if (ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE)
                return;
index 0e8b887cf03e78ddb236d590ad48bcdda61f9180..86123448a8fffbc03d985566cc217a11db6443d8 100644 (file)
@@ -412,10 +412,10 @@ static int uvd_v6_0_sw_init(void *handle)
                return r;
 
        if (uvd_v6_0_enc_support(adev)) {
-               struct amd_sched_rq *rq;
+               struct drm_sched_rq *rq;
                ring = &adev->uvd.ring_enc[0];
-               rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-               r = amd_sched_entity_init(&ring->sched, &adev->uvd.entity_enc,
+               rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+               r = drm_sched_entity_init(&ring->sched, &adev->uvd.entity_enc,
                                          rq, amdgpu_sched_jobs, NULL);
                if (r) {
                        DRM_ERROR("Failed setting up UVD ENC run queue.\n");
@@ -456,7 +456,7 @@ static int uvd_v6_0_sw_fini(void *handle)
                return r;
 
        if (uvd_v6_0_enc_support(adev)) {
-               amd_sched_entity_fini(&adev->uvd.ring_enc[0].sched, &adev->uvd.entity_enc);
+               drm_sched_entity_fini(&adev->uvd.ring_enc[0].sched, &adev->uvd.entity_enc);
 
                for (i = 0; i < adev->uvd.num_enc_rings; ++i)
                        amdgpu_ring_fini(&adev->uvd.ring_enc[i]);
index 660fa41dc877aa6c920184532c6e212fe6077bfb..416611150edd7b84d7faa905582a54218f648ce4 100644 (file)
@@ -385,7 +385,7 @@ static int uvd_v7_0_early_init(void *handle)
 static int uvd_v7_0_sw_init(void *handle)
 {
        struct amdgpu_ring *ring;
-       struct amd_sched_rq *rq;
+       struct drm_sched_rq *rq;
        int i, r;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
@@ -416,8 +416,8 @@ static int uvd_v7_0_sw_init(void *handle)
        }
 
        ring = &adev->uvd.ring_enc[0];
-       rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
-       r = amd_sched_entity_init(&ring->sched, &adev->uvd.entity_enc,
+       rq = &ring->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL];
+       r = drm_sched_entity_init(&ring->sched, &adev->uvd.entity_enc,
                                  rq, amdgpu_sched_jobs, NULL);
        if (r) {
                DRM_ERROR("Failed setting up UVD ENC run queue.\n");
@@ -472,7 +472,7 @@ static int uvd_v7_0_sw_fini(void *handle)
        if (r)
                return r;
 
-       amd_sched_entity_fini(&adev->uvd.ring_enc[0].sched, &adev->uvd.entity_enc);
+       drm_sched_entity_fini(&adev->uvd.ring_enc[0].sched, &adev->uvd.entity_enc);
 
        for (i = 0; i < adev->uvd.num_enc_rings; ++i)
                amdgpu_ring_fini(&adev->uvd.ring_enc[i]);
diff --git a/drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h b/drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h
deleted file mode 100644 (file)
index b42a789..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2017 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.
- *
- */
-
-#if !defined(_GPU_SCHED_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define _GPU_SCHED_TRACE_H_
-
-#include <linux/stringify.h>
-#include <linux/types.h>
-#include <linux/tracepoint.h>
-
-#include <drm/drmP.h>
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM gpu_sched
-#define TRACE_INCLUDE_FILE gpu_sched_trace
-
-TRACE_EVENT(amd_sched_job,
-           TP_PROTO(struct amd_sched_job *sched_job, struct amd_sched_entity *entity),
-           TP_ARGS(sched_job, entity),
-           TP_STRUCT__entry(
-                            __field(struct amd_sched_entity *, entity)
-                            __field(struct dma_fence *, fence)
-                            __field(const char *, name)
-                            __field(uint64_t, id)
-                            __field(u32, job_count)
-                            __field(int, hw_job_count)
-                            ),
-
-           TP_fast_assign(
-                          __entry->entity = entity;
-                          __entry->id = sched_job->id;
-                          __entry->fence = &sched_job->s_fence->finished;
-                          __entry->name = sched_job->sched->name;
-                          __entry->job_count = spsc_queue_count(&entity->job_queue);
-                          __entry->hw_job_count = atomic_read(
-                                  &sched_job->sched->hw_rq_count);
-                          ),
-           TP_printk("entity=%p, id=%llu, fence=%p, ring=%s, job count:%u, hw job count:%d",
-                     __entry->entity, __entry->id,
-                     __entry->fence, __entry->name,
-                     __entry->job_count, __entry->hw_job_count)
-);
-
-TRACE_EVENT(amd_sched_process_job,
-           TP_PROTO(struct amd_sched_fence *fence),
-           TP_ARGS(fence),
-           TP_STRUCT__entry(
-                   __field(struct dma_fence *, fence)
-                   ),
-
-           TP_fast_assign(
-                   __entry->fence = &fence->finished;
-                   ),
-           TP_printk("fence=%p signaled", __entry->fence)
-);
-
-#endif
-
-/* This part must be outside protection */
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
deleted file mode 100644 (file)
index dcb987e..0000000
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright 2015 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/kthread.h>
-#include <linux/wait.h>
-#include <linux/sched.h>
-#include <uapi/linux/sched/types.h>
-#include <drm/drmP.h>
-#include "gpu_scheduler.h"
-
-#include "spsc_queue.h"
-
-#define CREATE_TRACE_POINTS
-#include "gpu_sched_trace.h"
-
-#define to_amd_sched_job(sched_job)            \
-               container_of((sched_job), struct amd_sched_job, queue_node)
-
-static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity);
-static void amd_sched_wakeup(struct amd_gpu_scheduler *sched);
-static void amd_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
-
-/* Initialize a given run queue struct */
-static void amd_sched_rq_init(struct amd_sched_rq *rq)
-{
-       spin_lock_init(&rq->lock);
-       INIT_LIST_HEAD(&rq->entities);
-       rq->current_entity = NULL;
-}
-
-static void amd_sched_rq_add_entity(struct amd_sched_rq *rq,
-                                   struct amd_sched_entity *entity)
-{
-       if (!list_empty(&entity->list))
-               return;
-       spin_lock(&rq->lock);
-       list_add_tail(&entity->list, &rq->entities);
-       spin_unlock(&rq->lock);
-}
-
-static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq,
-                                      struct amd_sched_entity *entity)
-{
-       if (list_empty(&entity->list))
-               return;
-       spin_lock(&rq->lock);
-       list_del_init(&entity->list);
-       if (rq->current_entity == entity)
-               rq->current_entity = NULL;
-       spin_unlock(&rq->lock);
-}
-
-/**
- * Select an entity which could provide a job to run
- *
- * @rq         The run queue to check.
- *
- * Try to find a ready entity, returns NULL if none found.
- */
-static struct amd_sched_entity *
-amd_sched_rq_select_entity(struct amd_sched_rq *rq)
-{
-       struct amd_sched_entity *entity;
-
-       spin_lock(&rq->lock);
-
-       entity = rq->current_entity;
-       if (entity) {
-               list_for_each_entry_continue(entity, &rq->entities, list) {
-                       if (amd_sched_entity_is_ready(entity)) {
-                               rq->current_entity = entity;
-                               spin_unlock(&rq->lock);
-                               return entity;
-                       }
-               }
-       }
-
-       list_for_each_entry(entity, &rq->entities, list) {
-
-               if (amd_sched_entity_is_ready(entity)) {
-                       rq->current_entity = entity;
-                       spin_unlock(&rq->lock);
-                       return entity;
-               }
-
-               if (entity == rq->current_entity)
-                       break;
-       }
-
-       spin_unlock(&rq->lock);
-
-       return NULL;
-}
-
-/**
- * Init a context entity used by scheduler when submit to HW ring.
- *
- * @sched      The pointer to the scheduler
- * @entity     The pointer to a valid amd_sched_entity
- * @rq         The run queue this entity belongs
- * @kernel     If this is an entity for the kernel
- * @jobs       The max number of jobs in the job queue
- *
- * return 0 if succeed. negative error code on failure
-*/
-int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
-                         struct amd_sched_entity *entity,
-                         struct amd_sched_rq *rq,
-                         uint32_t jobs, atomic_t *guilty)
-{
-       if (!(sched && entity && rq))
-               return -EINVAL;
-
-       memset(entity, 0, sizeof(struct amd_sched_entity));
-       INIT_LIST_HEAD(&entity->list);
-       entity->rq = rq;
-       entity->sched = sched;
-       entity->guilty = guilty;
-
-       spin_lock_init(&entity->rq_lock);
-       spin_lock_init(&entity->queue_lock);
-       spsc_queue_init(&entity->job_queue);
-
-       atomic_set(&entity->fence_seq, 0);
-       entity->fence_context = dma_fence_context_alloc(2);
-
-       return 0;
-}
-
-/**
- * Query if entity is initialized
- *
- * @sched       Pointer to scheduler instance
- * @entity     The pointer to a valid scheduler entity
- *
- * return true if entity is initialized, false otherwise
-*/
-static bool amd_sched_entity_is_initialized(struct amd_gpu_scheduler *sched,
-                                           struct amd_sched_entity *entity)
-{
-       return entity->sched == sched &&
-               entity->rq != NULL;
-}
-
-/**
- * Check if entity is idle
- *
- * @entity     The pointer to a valid scheduler entity
- *
- * Return true if entity don't has any unscheduled jobs.
- */
-static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity)
-{
-       rmb();
-       if (spsc_queue_peek(&entity->job_queue) == NULL)
-               return true;
-
-       return false;
-}
-
-/**
- * Check if entity is ready
- *
- * @entity     The pointer to a valid scheduler entity
- *
- * Return true if entity could provide a job.
- */
-static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity)
-{
-       if (spsc_queue_peek(&entity->job_queue) == NULL)
-               return false;
-
-       if (READ_ONCE(entity->dependency))
-               return false;
-
-       return true;
-}
-
-/**
- * Destroy a context entity
- *
- * @sched       Pointer to scheduler instance
- * @entity     The pointer to a valid scheduler entity
- *
- * Cleanup and free the allocated resources.
- */
-void amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
-                          struct amd_sched_entity *entity)
-{
-       int r;
-
-       if (!amd_sched_entity_is_initialized(sched, entity))
-               return;
-       /**
-        * The client will not queue more IBs during this fini, consume existing
-        * queued IBs or discard them on SIGKILL
-       */
-       if ((current->flags & PF_SIGNALED) && current->exit_code == SIGKILL)
-               r = -ERESTARTSYS;
-       else
-               r = wait_event_killable(sched->job_scheduled,
-                                       amd_sched_entity_is_idle(entity));
-       amd_sched_entity_set_rq(entity, NULL);
-       if (r) {
-               struct amd_sched_job *job;
-
-               /* Park the kernel for a moment to make sure it isn't processing
-                * our enity.
-                */
-               kthread_park(sched->thread);
-               kthread_unpark(sched->thread);
-               if (entity->dependency) {
-                       dma_fence_remove_callback(entity->dependency,
-                                                 &entity->cb);
-                       dma_fence_put(entity->dependency);
-                       entity->dependency = NULL;
-               }
-
-               while ((job = to_amd_sched_job(spsc_queue_pop(&entity->job_queue)))) {
-                       struct amd_sched_fence *s_fence = job->s_fence;
-                       amd_sched_fence_scheduled(s_fence);
-                       dma_fence_set_error(&s_fence->finished, -ESRCH);
-                       amd_sched_fence_finished(s_fence);
-                       WARN_ON(s_fence->parent);
-                       dma_fence_put(&s_fence->finished);
-                       sched->ops->free_job(job);
-               }
-       }
-}
-
-static void amd_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
-{
-       struct amd_sched_entity *entity =
-               container_of(cb, struct amd_sched_entity, cb);
-       entity->dependency = NULL;
-       dma_fence_put(f);
-       amd_sched_wakeup(entity->sched);
-}
-
-static void amd_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb)
-{
-       struct amd_sched_entity *entity =
-               container_of(cb, struct amd_sched_entity, cb);
-       entity->dependency = NULL;
-       dma_fence_put(f);
-}
-
-void amd_sched_entity_set_rq(struct amd_sched_entity *entity,
-                            struct amd_sched_rq *rq)
-{
-       if (entity->rq == rq)
-               return;
-
-       spin_lock(&entity->rq_lock);
-
-       if (entity->rq)
-               amd_sched_rq_remove_entity(entity->rq, entity);
-
-       entity->rq = rq;
-       if (rq)
-               amd_sched_rq_add_entity(rq, entity);
-
-       spin_unlock(&entity->rq_lock);
-}
-
-bool amd_sched_dependency_optimized(struct dma_fence* fence,
-                                   struct amd_sched_entity *entity)
-{
-       struct amd_gpu_scheduler *sched = entity->sched;
-       struct amd_sched_fence *s_fence;
-
-       if (!fence || dma_fence_is_signaled(fence))
-               return false;
-       if (fence->context == entity->fence_context)
-               return true;
-       s_fence = to_amd_sched_fence(fence);
-       if (s_fence && s_fence->sched == sched)
-               return true;
-
-       return false;
-}
-
-static bool amd_sched_entity_add_dependency_cb(struct amd_sched_entity *entity)
-{
-       struct amd_gpu_scheduler *sched = entity->sched;
-       struct dma_fence * fence = entity->dependency;
-       struct amd_sched_fence *s_fence;
-
-       if (fence->context == entity->fence_context) {
-               /* We can ignore fences from ourself */
-               dma_fence_put(entity->dependency);
-               return false;
-       }
-
-       s_fence = to_amd_sched_fence(fence);
-       if (s_fence && s_fence->sched == sched) {
-
-               /*
-                * Fence is from the same scheduler, only need to wait for
-                * it to be scheduled
-                */
-               fence = dma_fence_get(&s_fence->scheduled);
-               dma_fence_put(entity->dependency);
-               entity->dependency = fence;
-               if (!dma_fence_add_callback(fence, &entity->cb,
-                                           amd_sched_entity_clear_dep))
-                       return true;
-
-               /* Ignore it when it is already scheduled */
-               dma_fence_put(fence);
-               return false;
-       }
-
-       if (!dma_fence_add_callback(entity->dependency, &entity->cb,
-                                   amd_sched_entity_wakeup))
-               return true;
-
-       dma_fence_put(entity->dependency);
-       return false;
-}
-
-static struct amd_sched_job *
-amd_sched_entity_pop_job(struct amd_sched_entity *entity)
-{
-       struct amd_gpu_scheduler *sched = entity->sched;
-       struct amd_sched_job *sched_job = to_amd_sched_job(
-                                               spsc_queue_peek(&entity->job_queue));
-
-       if (!sched_job)
-               return NULL;
-
-       while ((entity->dependency = sched->ops->dependency(sched_job, entity)))
-               if (amd_sched_entity_add_dependency_cb(entity))
-                       return NULL;
-
-       /* skip jobs from entity that marked guilty */
-       if (entity->guilty && atomic_read(entity->guilty))
-               dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
-
-       spsc_queue_pop(&entity->job_queue);
-       return sched_job;
-}
-
-/**
- * Submit a job to the job queue
- *
- * @sched_job          The pointer to job required to submit
- *
- * Returns 0 for success, negative error code otherwise.
- */
-void amd_sched_entity_push_job(struct amd_sched_job *sched_job,
-                              struct amd_sched_entity *entity)
-{
-       struct amd_gpu_scheduler *sched = sched_job->sched;
-       bool first = false;
-
-       trace_amd_sched_job(sched_job, entity);
-
-       spin_lock(&entity->queue_lock);
-       first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
-
-       spin_unlock(&entity->queue_lock);
-
-       /* first job wakes up scheduler */
-       if (first) {
-               /* Add the entity to the run queue */
-               spin_lock(&entity->rq_lock);
-               amd_sched_rq_add_entity(entity->rq, entity);
-               spin_unlock(&entity->rq_lock);
-               amd_sched_wakeup(sched);
-       }
-}
-
-/* job_finish is called after hw fence signaled
- */
-static void amd_sched_job_finish(struct work_struct *work)
-{
-       struct amd_sched_job *s_job = container_of(work, struct amd_sched_job,
-                                                  finish_work);
-       struct amd_gpu_scheduler *sched = s_job->sched;
-
-       /* remove job from ring_mirror_list */
-       spin_lock(&sched->job_list_lock);
-       list_del_init(&s_job->node);
-       if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
-               struct amd_sched_job *next;
-
-               spin_unlock(&sched->job_list_lock);
-               cancel_delayed_work_sync(&s_job->work_tdr);
-               spin_lock(&sched->job_list_lock);
-
-               /* queue TDR for next job */
-               next = list_first_entry_or_null(&sched->ring_mirror_list,
-                                               struct amd_sched_job, node);
-
-               if (next)
-                       schedule_delayed_work(&next->work_tdr, sched->timeout);
-       }
-       spin_unlock(&sched->job_list_lock);
-       dma_fence_put(&s_job->s_fence->finished);
-       sched->ops->free_job(s_job);
-}
-
-static void amd_sched_job_finish_cb(struct dma_fence *f,
-                                   struct dma_fence_cb *cb)
-{
-       struct amd_sched_job *job = container_of(cb, struct amd_sched_job,
-                                                finish_cb);
-       schedule_work(&job->finish_work);
-}
-
-static void amd_sched_job_begin(struct amd_sched_job *s_job)
-{
-       struct amd_gpu_scheduler *sched = s_job->sched;
-
-       dma_fence_add_callback(&s_job->s_fence->finished, &s_job->finish_cb,
-                              amd_sched_job_finish_cb);
-
-       spin_lock(&sched->job_list_lock);
-       list_add_tail(&s_job->node, &sched->ring_mirror_list);
-       if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
-           list_first_entry_or_null(&sched->ring_mirror_list,
-                                    struct amd_sched_job, node) == s_job)
-               schedule_delayed_work(&s_job->work_tdr, sched->timeout);
-       spin_unlock(&sched->job_list_lock);
-}
-
-static void amd_sched_job_timedout(struct work_struct *work)
-{
-       struct amd_sched_job *job = container_of(work, struct amd_sched_job,
-                                                work_tdr.work);
-
-       job->sched->ops->timedout_job(job);
-}
-
-void amd_sched_hw_job_reset(struct amd_gpu_scheduler *sched, struct amd_sched_job *bad)
-{
-       struct amd_sched_job *s_job;
-       struct amd_sched_entity *entity, *tmp;
-       int i;;
-
-       spin_lock(&sched->job_list_lock);
-       list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) {
-               if (s_job->s_fence->parent &&
-                   dma_fence_remove_callback(s_job->s_fence->parent,
-                                             &s_job->s_fence->cb)) {
-                       dma_fence_put(s_job->s_fence->parent);
-                       s_job->s_fence->parent = NULL;
-                       atomic_dec(&sched->hw_rq_count);
-               }
-       }
-       spin_unlock(&sched->job_list_lock);
-
-       if (bad && bad->s_priority != AMD_SCHED_PRIORITY_KERNEL) {
-               atomic_inc(&bad->karma);
-               /* don't increase @bad's karma if it's from KERNEL RQ,
-                * becuase sometimes GPU hang would cause kernel jobs (like VM updating jobs)
-                * corrupt but keep in mind that kernel jobs always considered good.
-                */
-               for (i = AMD_SCHED_PRIORITY_MIN; i < AMD_SCHED_PRIORITY_KERNEL; i++ ) {
-                       struct amd_sched_rq *rq = &sched->sched_rq[i];
-
-                       spin_lock(&rq->lock);
-                       list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
-                               if (bad->s_fence->scheduled.context == entity->fence_context) {
-                                   if (atomic_read(&bad->karma) > bad->sched->hang_limit)
-                                               if (entity->guilty)
-                                                       atomic_set(entity->guilty, 1);
-                                       break;
-                               }
-                       }
-                       spin_unlock(&rq->lock);
-                       if (&entity->list != &rq->entities)
-                               break;
-               }
-       }
-}
-
-void amd_sched_job_kickout(struct amd_sched_job *s_job)
-{
-       struct amd_gpu_scheduler *sched = s_job->sched;
-
-       spin_lock(&sched->job_list_lock);
-       list_del_init(&s_job->node);
-       spin_unlock(&sched->job_list_lock);
-}
-
-void amd_sched_job_recovery(struct amd_gpu_scheduler *sched)
-{
-       struct amd_sched_job *s_job, *tmp;
-       bool found_guilty = false;
-       int r;
-
-       spin_lock(&sched->job_list_lock);
-       s_job = list_first_entry_or_null(&sched->ring_mirror_list,
-                                        struct amd_sched_job, node);
-       if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT)
-               schedule_delayed_work(&s_job->work_tdr, sched->timeout);
-
-       list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
-               struct amd_sched_fence *s_fence = s_job->s_fence;
-               struct dma_fence *fence;
-               uint64_t guilty_context;
-
-               if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) {
-                       found_guilty = true;
-                       guilty_context = s_job->s_fence->scheduled.context;
-               }
-
-               if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
-                       dma_fence_set_error(&s_fence->finished, -ECANCELED);
-
-               spin_unlock(&sched->job_list_lock);
-               fence = sched->ops->run_job(s_job);
-               atomic_inc(&sched->hw_rq_count);
-               if (fence) {
-                       s_fence->parent = dma_fence_get(fence);
-                       r = dma_fence_add_callback(fence, &s_fence->cb,
-                                                  amd_sched_process_job);
-                       if (r == -ENOENT)
-                               amd_sched_process_job(fence, &s_fence->cb);
-                       else if (r)
-                               DRM_ERROR("fence add callback failed (%d)\n",
-                                         r);
-                       dma_fence_put(fence);
-               } else {
-                       amd_sched_process_job(NULL, &s_fence->cb);
-               }
-               spin_lock(&sched->job_list_lock);
-       }
-       spin_unlock(&sched->job_list_lock);
-}
-
-/* init a sched_job with basic field */
-int amd_sched_job_init(struct amd_sched_job *job,
-                      struct amd_gpu_scheduler *sched,
-                      struct amd_sched_entity *entity,
-                      void *owner)
-{
-       job->sched = sched;
-       job->s_priority = entity->rq - sched->sched_rq;
-       job->s_fence = amd_sched_fence_create(entity, owner);
-       if (!job->s_fence)
-               return -ENOMEM;
-       job->id = atomic64_inc_return(&sched->job_id_count);
-
-       INIT_WORK(&job->finish_work, amd_sched_job_finish);
-       INIT_LIST_HEAD(&job->node);
-       INIT_DELAYED_WORK(&job->work_tdr, amd_sched_job_timedout);
-
-       return 0;
-}
-
-/**
- * Return ture if we can push more jobs to the hw.
- */
-static bool amd_sched_ready(struct amd_gpu_scheduler *sched)
-{
-       return atomic_read(&sched->hw_rq_count) <
-               sched->hw_submission_limit;
-}
-
-/**
- * Wake up the scheduler when it is ready
- */
-static void amd_sched_wakeup(struct amd_gpu_scheduler *sched)
-{
-       if (amd_sched_ready(sched))
-               wake_up_interruptible(&sched->wake_up_worker);
-}
-
-/**
- * Select next entity to process
-*/
-static struct amd_sched_entity *
-amd_sched_select_entity(struct amd_gpu_scheduler *sched)
-{
-       struct amd_sched_entity *entity;
-       int i;
-
-       if (!amd_sched_ready(sched))
-               return NULL;
-
-       /* Kernel run queue has higher priority than normal run queue*/
-       for (i = AMD_SCHED_PRIORITY_MAX - 1; i >= AMD_SCHED_PRIORITY_MIN; i--) {
-               entity = amd_sched_rq_select_entity(&sched->sched_rq[i]);
-               if (entity)
-                       break;
-       }
-
-       return entity;
-}
-
-static void amd_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
-{
-       struct amd_sched_fence *s_fence =
-               container_of(cb, struct amd_sched_fence, cb);
-       struct amd_gpu_scheduler *sched = s_fence->sched;
-
-       dma_fence_get(&s_fence->finished);
-       atomic_dec(&sched->hw_rq_count);
-       amd_sched_fence_finished(s_fence);
-
-       trace_amd_sched_process_job(s_fence);
-       dma_fence_put(&s_fence->finished);
-       wake_up_interruptible(&sched->wake_up_worker);
-}
-
-static bool amd_sched_blocked(struct amd_gpu_scheduler *sched)
-{
-       if (kthread_should_park()) {
-               kthread_parkme();
-               return true;
-       }
-
-       return false;
-}
-
-static int amd_sched_main(void *param)
-{
-       struct sched_param sparam = {.sched_priority = 1};
-       struct amd_gpu_scheduler *sched = (struct amd_gpu_scheduler *)param;
-       int r;
-
-       sched_setscheduler(current, SCHED_FIFO, &sparam);
-
-       while (!kthread_should_stop()) {
-               struct amd_sched_entity *entity = NULL;
-               struct amd_sched_fence *s_fence;
-               struct amd_sched_job *sched_job;
-               struct dma_fence *fence;
-
-               wait_event_interruptible(sched->wake_up_worker,
-                                        (!amd_sched_blocked(sched) &&
-                                         (entity = amd_sched_select_entity(sched))) ||
-                                        kthread_should_stop());
-
-               if (!entity)
-                       continue;
-
-               sched_job = amd_sched_entity_pop_job(entity);
-               if (!sched_job)
-                       continue;
-
-               s_fence = sched_job->s_fence;
-
-               atomic_inc(&sched->hw_rq_count);
-               amd_sched_job_begin(sched_job);
-
-               fence = sched->ops->run_job(sched_job);
-               amd_sched_fence_scheduled(s_fence);
-
-               if (fence) {
-                       s_fence->parent = dma_fence_get(fence);
-                       r = dma_fence_add_callback(fence, &s_fence->cb,
-                                                  amd_sched_process_job);
-                       if (r == -ENOENT)
-                               amd_sched_process_job(fence, &s_fence->cb);
-                       else if (r)
-                               DRM_ERROR("fence add callback failed (%d)\n",
-                                         r);
-                       dma_fence_put(fence);
-               } else {
-                       amd_sched_process_job(NULL, &s_fence->cb);
-               }
-
-               wake_up(&sched->job_scheduled);
-       }
-       return 0;
-}
-
-/**
- * Init a gpu scheduler instance
- *
- * @sched              The pointer to the scheduler
- * @ops                        The backend operations for this scheduler.
- * @hw_submissions     Number of hw submissions to do.
- * @name               Name used for debugging
- *
- * Return 0 on success, otherwise error code.
-*/
-int amd_sched_init(struct amd_gpu_scheduler *sched,
-                  const struct amd_sched_backend_ops *ops,
-                  unsigned hw_submission,
-                  unsigned hang_limit,
-                  long timeout,
-                  const char *name)
-{
-       int i;
-       sched->ops = ops;
-       sched->hw_submission_limit = hw_submission;
-       sched->name = name;
-       sched->timeout = timeout;
-       sched->hang_limit = hang_limit;
-       for (i = AMD_SCHED_PRIORITY_MIN; i < AMD_SCHED_PRIORITY_MAX; i++)
-               amd_sched_rq_init(&sched->sched_rq[i]);
-
-       init_waitqueue_head(&sched->wake_up_worker);
-       init_waitqueue_head(&sched->job_scheduled);
-       INIT_LIST_HEAD(&sched->ring_mirror_list);
-       spin_lock_init(&sched->job_list_lock);
-       atomic_set(&sched->hw_rq_count, 0);
-       atomic64_set(&sched->job_id_count, 0);
-
-       /* Each scheduler will run on a seperate kernel thread */
-       sched->thread = kthread_run(amd_sched_main, sched, sched->name);
-       if (IS_ERR(sched->thread)) {
-               DRM_ERROR("Failed to create scheduler for %s.\n", name);
-               return PTR_ERR(sched->thread);
-       }
-
-       return 0;
-}
-
-/**
- * Destroy a gpu scheduler
- *
- * @sched      The pointer to the scheduler
- */
-void amd_sched_fini(struct amd_gpu_scheduler *sched)
-{
-       if (sched->thread)
-               kthread_stop(sched->thread);
-}
diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
deleted file mode 100644 (file)
index b590fcc..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2015 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 _GPU_SCHEDULER_H_
-#define _GPU_SCHEDULER_H_
-
-#include <linux/kfifo.h>
-#include <linux/dma-fence.h>
-#include "spsc_queue.h"
-
-struct amd_gpu_scheduler;
-struct amd_sched_rq;
-
-enum amd_sched_priority {
-       AMD_SCHED_PRIORITY_MIN,
-       AMD_SCHED_PRIORITY_LOW = AMD_SCHED_PRIORITY_MIN,
-       AMD_SCHED_PRIORITY_NORMAL,
-       AMD_SCHED_PRIORITY_HIGH_SW,
-       AMD_SCHED_PRIORITY_HIGH_HW,
-       AMD_SCHED_PRIORITY_KERNEL,
-       AMD_SCHED_PRIORITY_MAX,
-       AMD_SCHED_PRIORITY_INVALID = -1,
-       AMD_SCHED_PRIORITY_UNSET = -2
-};
-
-
-/**
- * A scheduler entity is a wrapper around a job queue or a group
- * of other entities. Entities take turns emitting jobs from their
- * job queues to corresponding hardware ring based on scheduling
- * policy.
-*/
-struct amd_sched_entity {
-       struct list_head                list;
-       struct amd_sched_rq             *rq;
-       spinlock_t                      rq_lock;
-       struct amd_gpu_scheduler        *sched;
-
-       spinlock_t                      queue_lock;
-       struct spsc_queue       job_queue;
-
-       atomic_t                        fence_seq;
-       uint64_t                        fence_context;
-
-       struct dma_fence                *dependency;
-       struct dma_fence_cb             cb;
-       atomic_t        *guilty; /* points to ctx's guilty */
-};
-
-/**
- * Run queue is a set of entities scheduling command submissions for
- * one specific ring. It implements the scheduling policy that selects
- * the next entity to emit commands from.
-*/
-struct amd_sched_rq {
-       spinlock_t              lock;
-       struct list_head        entities;
-       struct amd_sched_entity *current_entity;
-};
-
-struct amd_sched_fence {
-       struct dma_fence                scheduled;
-       struct dma_fence                finished;
-       struct dma_fence_cb             cb;
-       struct dma_fence                *parent;
-       struct amd_gpu_scheduler        *sched;
-       spinlock_t                      lock;
-       void                            *owner;
-};
-
-struct amd_sched_job {
-       struct spsc_node queue_node;
-       struct amd_gpu_scheduler        *sched;
-       struct amd_sched_fence          *s_fence;
-       struct dma_fence_cb             finish_cb;
-       struct work_struct              finish_work;
-       struct list_head                node;
-       struct delayed_work             work_tdr;
-       uint64_t                        id;
-       atomic_t karma;
-       enum amd_sched_priority s_priority;
-};
-
-extern const struct dma_fence_ops amd_sched_fence_ops_scheduled;
-extern const struct dma_fence_ops amd_sched_fence_ops_finished;
-static inline struct amd_sched_fence *to_amd_sched_fence(struct dma_fence *f)
-{
-       if (f->ops == &amd_sched_fence_ops_scheduled)
-               return container_of(f, struct amd_sched_fence, scheduled);
-
-       if (f->ops == &amd_sched_fence_ops_finished)
-               return container_of(f, struct amd_sched_fence, finished);
-
-       return NULL;
-}
-
-static inline bool amd_sched_invalidate_job(struct amd_sched_job *s_job, int threshold)
-{
-       return (s_job && atomic_inc_return(&s_job->karma) > threshold);
-}
-
-/**
- * Define the backend operations called by the scheduler,
- * these functions should be implemented in driver side
-*/
-struct amd_sched_backend_ops {
-       struct dma_fence *(*dependency)(struct amd_sched_job *sched_job,
-                                       struct amd_sched_entity *s_entity);
-       struct dma_fence *(*run_job)(struct amd_sched_job *sched_job);
-       void (*timedout_job)(struct amd_sched_job *sched_job);
-       void (*free_job)(struct amd_sched_job *sched_job);
-};
-
-/**
- * One scheduler is implemented for each hardware ring
-*/
-struct amd_gpu_scheduler {
-       const struct amd_sched_backend_ops      *ops;
-       uint32_t                        hw_submission_limit;
-       long                            timeout;
-       const char                      *name;
-       struct amd_sched_rq             sched_rq[AMD_SCHED_PRIORITY_MAX];
-       wait_queue_head_t               wake_up_worker;
-       wait_queue_head_t               job_scheduled;
-       atomic_t                        hw_rq_count;
-       atomic64_t                      job_id_count;
-       struct task_struct              *thread;
-       struct list_head        ring_mirror_list;
-       spinlock_t                      job_list_lock;
-       int hang_limit;
-};
-
-int amd_sched_init(struct amd_gpu_scheduler *sched,
-                  const struct amd_sched_backend_ops *ops,
-                  uint32_t hw_submission, unsigned hang_limit, long timeout, const char *name);
-void amd_sched_fini(struct amd_gpu_scheduler *sched);
-
-int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
-                         struct amd_sched_entity *entity,
-                         struct amd_sched_rq *rq,
-                         uint32_t jobs, atomic_t* guilty);
-void amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
-                          struct amd_sched_entity *entity);
-void amd_sched_entity_push_job(struct amd_sched_job *sched_job,
-                              struct amd_sched_entity *entity);
-void amd_sched_entity_set_rq(struct amd_sched_entity *entity,
-                            struct amd_sched_rq *rq);
-
-int amd_sched_fence_slab_init(void);
-void amd_sched_fence_slab_fini(void);
-
-struct amd_sched_fence *amd_sched_fence_create(
-       struct amd_sched_entity *s_entity, void *owner);
-void amd_sched_fence_scheduled(struct amd_sched_fence *fence);
-void amd_sched_fence_finished(struct amd_sched_fence *fence);
-int amd_sched_job_init(struct amd_sched_job *job,
-                      struct amd_gpu_scheduler *sched,
-                      struct amd_sched_entity *entity,
-                      void *owner);
-void amd_sched_hw_job_reset(struct amd_gpu_scheduler *sched, struct amd_sched_job *job);
-void amd_sched_job_recovery(struct amd_gpu_scheduler *sched);
-bool amd_sched_dependency_optimized(struct dma_fence* fence,
-                                   struct amd_sched_entity *entity);
-void amd_sched_job_kickout(struct amd_sched_job *s_job);
-
-#endif
diff --git a/drivers/gpu/drm/amd/scheduler/sched_fence.c b/drivers/gpu/drm/amd/scheduler/sched_fence.c
deleted file mode 100644 (file)
index 33f54d0..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2015 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/kthread.h>
-#include <linux/wait.h>
-#include <linux/sched.h>
-#include <drm/drmP.h>
-#include "gpu_scheduler.h"
-
-static struct kmem_cache *sched_fence_slab;
-
-int amd_sched_fence_slab_init(void)
-{
-       sched_fence_slab = kmem_cache_create(
-               "amd_sched_fence", sizeof(struct amd_sched_fence), 0,
-               SLAB_HWCACHE_ALIGN, NULL);
-       if (!sched_fence_slab)
-               return -ENOMEM;
-
-       return 0;
-}
-
-void amd_sched_fence_slab_fini(void)
-{
-       rcu_barrier();
-       kmem_cache_destroy(sched_fence_slab);
-}
-
-struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *entity,
-                                              void *owner)
-{
-       struct amd_sched_fence *fence = NULL;
-       unsigned seq;
-
-       fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL);
-       if (fence == NULL)
-               return NULL;
-
-       fence->owner = owner;
-       fence->sched = entity->sched;
-       spin_lock_init(&fence->lock);
-
-       seq = atomic_inc_return(&entity->fence_seq);
-       dma_fence_init(&fence->scheduled, &amd_sched_fence_ops_scheduled,
-                      &fence->lock, entity->fence_context, seq);
-       dma_fence_init(&fence->finished, &amd_sched_fence_ops_finished,
-                      &fence->lock, entity->fence_context + 1, seq);
-
-       return fence;
-}
-
-void amd_sched_fence_scheduled(struct amd_sched_fence *fence)
-{
-       int ret = dma_fence_signal(&fence->scheduled);
-
-       if (!ret)
-               DMA_FENCE_TRACE(&fence->scheduled,
-                               "signaled from irq context\n");
-       else
-               DMA_FENCE_TRACE(&fence->scheduled,
-                               "was already signaled\n");
-}
-
-void amd_sched_fence_finished(struct amd_sched_fence *fence)
-{
-       int ret = dma_fence_signal(&fence->finished);
-
-       if (!ret)
-               DMA_FENCE_TRACE(&fence->finished,
-                               "signaled from irq context\n");
-       else
-               DMA_FENCE_TRACE(&fence->finished,
-                               "was already signaled\n");
-}
-
-static const char *amd_sched_fence_get_driver_name(struct dma_fence *fence)
-{
-       return "amd_sched";
-}
-
-static const char *amd_sched_fence_get_timeline_name(struct dma_fence *f)
-{
-       struct amd_sched_fence *fence = to_amd_sched_fence(f);
-       return (const char *)fence->sched->name;
-}
-
-static bool amd_sched_fence_enable_signaling(struct dma_fence *f)
-{
-       return true;
-}
-
-/**
- * amd_sched_fence_free - free up the fence memory
- *
- * @rcu: RCU callback head
- *
- * Free up the fence memory after the RCU grace period.
- */
-static void amd_sched_fence_free(struct rcu_head *rcu)
-{
-       struct dma_fence *f = container_of(rcu, struct dma_fence, rcu);
-       struct amd_sched_fence *fence = to_amd_sched_fence(f);
-
-       dma_fence_put(fence->parent);
-       kmem_cache_free(sched_fence_slab, fence);
-}
-
-/**
- * amd_sched_fence_release_scheduled - callback that fence can be freed
- *
- * @fence: fence
- *
- * This function is called when the reference count becomes zero.
- * It just RCU schedules freeing up the fence.
- */
-static void amd_sched_fence_release_scheduled(struct dma_fence *f)
-{
-       struct amd_sched_fence *fence = to_amd_sched_fence(f);
-
-       call_rcu(&fence->finished.rcu, amd_sched_fence_free);
-}
-
-/**
- * amd_sched_fence_release_finished - drop extra reference
- *
- * @f: fence
- *
- * Drop the extra reference from the scheduled fence to the base fence.
- */
-static void amd_sched_fence_release_finished(struct dma_fence *f)
-{
-       struct amd_sched_fence *fence = to_amd_sched_fence(f);
-
-       dma_fence_put(&fence->scheduled);
-}
-
-const struct dma_fence_ops amd_sched_fence_ops_scheduled = {
-       .get_driver_name = amd_sched_fence_get_driver_name,
-       .get_timeline_name = amd_sched_fence_get_timeline_name,
-       .enable_signaling = amd_sched_fence_enable_signaling,
-       .signaled = NULL,
-       .wait = dma_fence_default_wait,
-       .release = amd_sched_fence_release_scheduled,
-};
-
-const struct dma_fence_ops amd_sched_fence_ops_finished = {
-       .get_driver_name = amd_sched_fence_get_driver_name,
-       .get_timeline_name = amd_sched_fence_get_timeline_name,
-       .enable_signaling = amd_sched_fence_enable_signaling,
-       .signaled = NULL,
-       .wait = dma_fence_default_wait,
-       .release = amd_sched_fence_release_finished,
-};
diff --git a/drivers/gpu/drm/amd/scheduler/spsc_queue.h b/drivers/gpu/drm/amd/scheduler/spsc_queue.h
deleted file mode 100644 (file)
index 5902f35..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2017 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 AMD_SCHEDULER_SPSC_QUEUE_H_
-#define AMD_SCHEDULER_SPSC_QUEUE_H_
-
-#include <linux/atomic.h>
-
-/** SPSC lockless queue */
-
-struct spsc_node {
-
-       /* Stores spsc_node* */
-       struct spsc_node *next;
-};
-
-struct spsc_queue {
-
-        struct spsc_node *head;
-
-       /* atomic pointer to struct spsc_node* */
-       atomic_long_t tail;
-
-       atomic_t job_count;
-};
-
-static inline void spsc_queue_init(struct spsc_queue *queue)
-{
-       queue->head = NULL;
-       atomic_long_set(&queue->tail, (long)&queue->head);
-       atomic_set(&queue->job_count, 0);
-}
-
-static inline struct spsc_node *spsc_queue_peek(struct spsc_queue *queue)
-{
-       return queue->head;
-}
-
-static inline int spsc_queue_count(struct spsc_queue *queue)
-{
-       return atomic_read(&queue->job_count);
-}
-
-static inline bool spsc_queue_push(struct spsc_queue *queue, struct spsc_node *node)
-{
-       struct spsc_node **tail;
-
-       node->next = NULL;
-
-       preempt_disable();
-
-       tail = (struct spsc_node **)atomic_long_xchg(&queue->tail, (long)&node->next);
-       WRITE_ONCE(*tail, node);
-       atomic_inc(&queue->job_count);
-
-       /*
-        * In case of first element verify new node will be visible to the consumer
-        * thread when we ping the kernel thread that there is new work to do.
-        */
-       smp_wmb();
-
-       preempt_enable();
-
-       return tail == &queue->head;
-}
-
-
-static inline struct spsc_node *spsc_queue_pop(struct spsc_queue *queue)
-{
-       struct spsc_node *next, *node;
-
-       /* Verify reading from memory and not the cache */
-       smp_rmb();
-
-       node = READ_ONCE(queue->head);
-
-       if (!node)
-               return NULL;
-
-       next = READ_ONCE(node->next);
-       WRITE_ONCE(queue->head, next);
-
-       if (unlikely(!next)) {
-               /* slowpath for the last element in the queue */
-
-               if (atomic_long_cmpxchg(&queue->tail,
-                               (long)&node->next, (long) &queue->head) != (long)&node->next) {
-                       /* Updating tail failed wait for new next to appear */
-                       do {
-                               smp_rmb();
-                       } while (unlikely(!(queue->head = READ_ONCE(node->next))));
-               }
-       }
-
-       atomic_dec(&queue->job_count);
-       return node;
-}
-
-
-
-#endif /* AMD_SCHEDULER_SPSC_QUEUE_H_ */
diff --git a/drivers/gpu/drm/scheduler/Makefile b/drivers/gpu/drm/scheduler/Makefile
new file mode 100644 (file)
index 0000000..ed87791
--- /dev/null
@@ -0,0 +1,4 @@
+ccflags-y := -Iinclude/drm
+gpu-sched-y := gpu_scheduler.o sched_fence.o
+
+obj-$(CONFIG_DRM_SCHED) += gpu-sched.o
diff --git a/drivers/gpu/drm/scheduler/gpu_scheduler.c b/drivers/gpu/drm/scheduler/gpu_scheduler.c
new file mode 100644 (file)
index 0000000..2c18996
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2015 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/kthread.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <uapi/linux/sched/types.h>
+#include <drm/drmP.h>
+#include <drm/gpu_scheduler.h>
+#include <drm/spsc_queue.h>
+
+#define CREATE_TRACE_POINTS
+#include <drm/gpu_scheduler_trace.h>
+
+#define to_drm_sched_job(sched_job)            \
+               container_of((sched_job), struct drm_sched_job, queue_node)
+
+static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity);
+static void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
+static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
+
+/* Initialize a given run queue struct */
+static void drm_sched_rq_init(struct drm_sched_rq *rq)
+{
+       spin_lock_init(&rq->lock);
+       INIT_LIST_HEAD(&rq->entities);
+       rq->current_entity = NULL;
+}
+
+static void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
+                                   struct drm_sched_entity *entity)
+{
+       if (!list_empty(&entity->list))
+               return;
+       spin_lock(&rq->lock);
+       list_add_tail(&entity->list, &rq->entities);
+       spin_unlock(&rq->lock);
+}
+
+static void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
+                                      struct drm_sched_entity *entity)
+{
+       if (list_empty(&entity->list))
+               return;
+       spin_lock(&rq->lock);
+       list_del_init(&entity->list);
+       if (rq->current_entity == entity)
+               rq->current_entity = NULL;
+       spin_unlock(&rq->lock);
+}
+
+/**
+ * Select an entity which could provide a job to run
+ *
+ * @rq         The run queue to check.
+ *
+ * Try to find a ready entity, returns NULL if none found.
+ */
+static struct drm_sched_entity *
+drm_sched_rq_select_entity(struct drm_sched_rq *rq)
+{
+       struct drm_sched_entity *entity;
+
+       spin_lock(&rq->lock);
+
+       entity = rq->current_entity;
+       if (entity) {
+               list_for_each_entry_continue(entity, &rq->entities, list) {
+                       if (drm_sched_entity_is_ready(entity)) {
+                               rq->current_entity = entity;
+                               spin_unlock(&rq->lock);
+                               return entity;
+                       }
+               }
+       }
+
+       list_for_each_entry(entity, &rq->entities, list) {
+
+               if (drm_sched_entity_is_ready(entity)) {
+                       rq->current_entity = entity;
+                       spin_unlock(&rq->lock);
+                       return entity;
+               }
+
+               if (entity == rq->current_entity)
+                       break;
+       }
+
+       spin_unlock(&rq->lock);
+
+       return NULL;
+}
+
+/**
+ * Init a context entity used by scheduler when submit to HW ring.
+ *
+ * @sched      The pointer to the scheduler
+ * @entity     The pointer to a valid drm_sched_entity
+ * @rq         The run queue this entity belongs
+ * @kernel     If this is an entity for the kernel
+ * @jobs       The max number of jobs in the job queue
+ *
+ * return 0 if succeed. negative error code on failure
+*/
+int drm_sched_entity_init(struct drm_gpu_scheduler *sched,
+                         struct drm_sched_entity *entity,
+                         struct drm_sched_rq *rq,
+                         uint32_t jobs, atomic_t *guilty)
+{
+       if (!(sched && entity && rq))
+               return -EINVAL;
+
+       memset(entity, 0, sizeof(struct drm_sched_entity));
+       INIT_LIST_HEAD(&entity->list);
+       entity->rq = rq;
+       entity->sched = sched;
+       entity->guilty = guilty;
+
+       spin_lock_init(&entity->rq_lock);
+       spin_lock_init(&entity->queue_lock);
+       spsc_queue_init(&entity->job_queue);
+
+       atomic_set(&entity->fence_seq, 0);
+       entity->fence_context = dma_fence_context_alloc(2);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sched_entity_init);
+
+/**
+ * Query if entity is initialized
+ *
+ * @sched       Pointer to scheduler instance
+ * @entity     The pointer to a valid scheduler entity
+ *
+ * return true if entity is initialized, false otherwise
+*/
+static bool drm_sched_entity_is_initialized(struct drm_gpu_scheduler *sched,
+                                           struct drm_sched_entity *entity)
+{
+       return entity->sched == sched &&
+               entity->rq != NULL;
+}
+
+/**
+ * Check if entity is idle
+ *
+ * @entity     The pointer to a valid scheduler entity
+ *
+ * Return true if entity don't has any unscheduled jobs.
+ */
+static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
+{
+       rmb();
+       if (spsc_queue_peek(&entity->job_queue) == NULL)
+               return true;
+
+       return false;
+}
+
+/**
+ * Check if entity is ready
+ *
+ * @entity     The pointer to a valid scheduler entity
+ *
+ * Return true if entity could provide a job.
+ */
+static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity)
+{
+       if (spsc_queue_peek(&entity->job_queue) == NULL)
+               return false;
+
+       if (READ_ONCE(entity->dependency))
+               return false;
+
+       return true;
+}
+
+/**
+ * Destroy a context entity
+ *
+ * @sched       Pointer to scheduler instance
+ * @entity     The pointer to a valid scheduler entity
+ *
+ * Cleanup and free the allocated resources.
+ */
+void drm_sched_entity_fini(struct drm_gpu_scheduler *sched,
+                          struct drm_sched_entity *entity)
+{
+       int r;
+
+       if (!drm_sched_entity_is_initialized(sched, entity))
+               return;
+       /**
+        * The client will not queue more IBs during this fini, consume existing
+        * queued IBs or discard them on SIGKILL
+       */
+       if ((current->flags & PF_SIGNALED) && current->exit_code == SIGKILL)
+               r = -ERESTARTSYS;
+       else
+               r = wait_event_killable(sched->job_scheduled,
+                                       drm_sched_entity_is_idle(entity));
+       drm_sched_entity_set_rq(entity, NULL);
+       if (r) {
+               struct drm_sched_job *job;
+
+               /* Park the kernel for a moment to make sure it isn't processing
+                * our enity.
+                */
+               kthread_park(sched->thread);
+               kthread_unpark(sched->thread);
+               if (entity->dependency) {
+                       dma_fence_remove_callback(entity->dependency,
+                                                 &entity->cb);
+                       dma_fence_put(entity->dependency);
+                       entity->dependency = NULL;
+               }
+
+               while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) {
+                       struct drm_sched_fence *s_fence = job->s_fence;
+                       drm_sched_fence_scheduled(s_fence);
+                       dma_fence_set_error(&s_fence->finished, -ESRCH);
+                       drm_sched_fence_finished(s_fence);
+                       WARN_ON(s_fence->parent);
+                       dma_fence_put(&s_fence->finished);
+                       sched->ops->free_job(job);
+               }
+       }
+}
+EXPORT_SYMBOL(drm_sched_entity_fini);
+
+static void drm_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       struct drm_sched_entity *entity =
+               container_of(cb, struct drm_sched_entity, cb);
+       entity->dependency = NULL;
+       dma_fence_put(f);
+       drm_sched_wakeup(entity->sched);
+}
+
+static void drm_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       struct drm_sched_entity *entity =
+               container_of(cb, struct drm_sched_entity, cb);
+       entity->dependency = NULL;
+       dma_fence_put(f);
+}
+
+void drm_sched_entity_set_rq(struct drm_sched_entity *entity,
+                            struct drm_sched_rq *rq)
+{
+       if (entity->rq == rq)
+               return;
+
+       spin_lock(&entity->rq_lock);
+
+       if (entity->rq)
+               drm_sched_rq_remove_entity(entity->rq, entity);
+
+       entity->rq = rq;
+       if (rq)
+               drm_sched_rq_add_entity(rq, entity);
+
+       spin_unlock(&entity->rq_lock);
+}
+EXPORT_SYMBOL(drm_sched_entity_set_rq);
+
+bool drm_sched_dependency_optimized(struct dma_fence* fence,
+                                   struct drm_sched_entity *entity)
+{
+       struct drm_gpu_scheduler *sched = entity->sched;
+       struct drm_sched_fence *s_fence;
+
+       if (!fence || dma_fence_is_signaled(fence))
+               return false;
+       if (fence->context == entity->fence_context)
+               return true;
+       s_fence = to_drm_sched_fence(fence);
+       if (s_fence && s_fence->sched == sched)
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL(drm_sched_dependency_optimized);
+
+static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity)
+{
+       struct drm_gpu_scheduler *sched = entity->sched;
+       struct dma_fence * fence = entity->dependency;
+       struct drm_sched_fence *s_fence;
+
+       if (fence->context == entity->fence_context) {
+               /* We can ignore fences from ourself */
+               dma_fence_put(entity->dependency);
+               return false;
+       }
+
+       s_fence = to_drm_sched_fence(fence);
+       if (s_fence && s_fence->sched == sched) {
+
+               /*
+                * Fence is from the same scheduler, only need to wait for
+                * it to be scheduled
+                */
+               fence = dma_fence_get(&s_fence->scheduled);
+               dma_fence_put(entity->dependency);
+               entity->dependency = fence;
+               if (!dma_fence_add_callback(fence, &entity->cb,
+                                           drm_sched_entity_clear_dep))
+                       return true;
+
+               /* Ignore it when it is already scheduled */
+               dma_fence_put(fence);
+               return false;
+       }
+
+       if (!dma_fence_add_callback(entity->dependency, &entity->cb,
+                                   drm_sched_entity_wakeup))
+               return true;
+
+       dma_fence_put(entity->dependency);
+       return false;
+}
+
+static struct drm_sched_job *
+drm_sched_entity_pop_job(struct drm_sched_entity *entity)
+{
+       struct drm_gpu_scheduler *sched = entity->sched;
+       struct drm_sched_job *sched_job = to_drm_sched_job(
+                                               spsc_queue_peek(&entity->job_queue));
+
+       if (!sched_job)
+               return NULL;
+
+       while ((entity->dependency = sched->ops->dependency(sched_job, entity)))
+               if (drm_sched_entity_add_dependency_cb(entity))
+                       return NULL;
+
+       /* skip jobs from entity that marked guilty */
+       if (entity->guilty && atomic_read(entity->guilty))
+               dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
+
+       spsc_queue_pop(&entity->job_queue);
+       return sched_job;
+}
+
+/**
+ * Submit a job to the job queue
+ *
+ * @sched_job          The pointer to job required to submit
+ *
+ * Returns 0 for success, negative error code otherwise.
+ */
+void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
+                              struct drm_sched_entity *entity)
+{
+       struct drm_gpu_scheduler *sched = sched_job->sched;
+       bool first = false;
+
+       trace_drm_sched_job(sched_job, entity);
+
+       spin_lock(&entity->queue_lock);
+       first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
+
+       spin_unlock(&entity->queue_lock);
+
+       /* first job wakes up scheduler */
+       if (first) {
+               /* Add the entity to the run queue */
+               spin_lock(&entity->rq_lock);
+               drm_sched_rq_add_entity(entity->rq, entity);
+               spin_unlock(&entity->rq_lock);
+               drm_sched_wakeup(sched);
+       }
+}
+EXPORT_SYMBOL(drm_sched_entity_push_job);
+
+/* job_finish is called after hw fence signaled
+ */
+static void drm_sched_job_finish(struct work_struct *work)
+{
+       struct drm_sched_job *s_job = container_of(work, struct drm_sched_job,
+                                                  finish_work);
+       struct drm_gpu_scheduler *sched = s_job->sched;
+
+       /* remove job from ring_mirror_list */
+       spin_lock(&sched->job_list_lock);
+       list_del_init(&s_job->node);
+       if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
+               struct drm_sched_job *next;
+
+               spin_unlock(&sched->job_list_lock);
+               cancel_delayed_work_sync(&s_job->work_tdr);
+               spin_lock(&sched->job_list_lock);
+
+               /* queue TDR for next job */
+               next = list_first_entry_or_null(&sched->ring_mirror_list,
+                                               struct drm_sched_job, node);
+
+               if (next)
+                       schedule_delayed_work(&next->work_tdr, sched->timeout);
+       }
+       spin_unlock(&sched->job_list_lock);
+       dma_fence_put(&s_job->s_fence->finished);
+       sched->ops->free_job(s_job);
+}
+
+static void drm_sched_job_finish_cb(struct dma_fence *f,
+                                   struct dma_fence_cb *cb)
+{
+       struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
+                                                finish_cb);
+       schedule_work(&job->finish_work);
+}
+
+static void drm_sched_job_begin(struct drm_sched_job *s_job)
+{
+       struct drm_gpu_scheduler *sched = s_job->sched;
+
+       dma_fence_add_callback(&s_job->s_fence->finished, &s_job->finish_cb,
+                              drm_sched_job_finish_cb);
+
+       spin_lock(&sched->job_list_lock);
+       list_add_tail(&s_job->node, &sched->ring_mirror_list);
+       if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
+           list_first_entry_or_null(&sched->ring_mirror_list,
+                                    struct drm_sched_job, node) == s_job)
+               schedule_delayed_work(&s_job->work_tdr, sched->timeout);
+       spin_unlock(&sched->job_list_lock);
+}
+
+static void drm_sched_job_timedout(struct work_struct *work)
+{
+       struct drm_sched_job *job = container_of(work, struct drm_sched_job,
+                                                work_tdr.work);
+
+       job->sched->ops->timedout_job(job);
+}
+
+void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
+{
+       struct drm_sched_job *s_job;
+       struct drm_sched_entity *entity, *tmp;
+       int i;;
+
+       spin_lock(&sched->job_list_lock);
+       list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) {
+               if (s_job->s_fence->parent &&
+                   dma_fence_remove_callback(s_job->s_fence->parent,
+                                             &s_job->s_fence->cb)) {
+                       dma_fence_put(s_job->s_fence->parent);
+                       s_job->s_fence->parent = NULL;
+                       atomic_dec(&sched->hw_rq_count);
+               }
+       }
+       spin_unlock(&sched->job_list_lock);
+
+       if (bad && bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
+               atomic_inc(&bad->karma);
+               /* don't increase @bad's karma if it's from KERNEL RQ,
+                * becuase sometimes GPU hang would cause kernel jobs (like VM updating jobs)
+                * corrupt but keep in mind that kernel jobs always considered good.
+                */
+               for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; i++ ) {
+                       struct drm_sched_rq *rq = &sched->sched_rq[i];
+
+                       spin_lock(&rq->lock);
+                       list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
+                               if (bad->s_fence->scheduled.context == entity->fence_context) {
+                                   if (atomic_read(&bad->karma) > bad->sched->hang_limit)
+                                               if (entity->guilty)
+                                                       atomic_set(entity->guilty, 1);
+                                       break;
+                               }
+                       }
+                       spin_unlock(&rq->lock);
+                       if (&entity->list != &rq->entities)
+                               break;
+               }
+       }
+}
+EXPORT_SYMBOL(drm_sched_hw_job_reset);
+
+void drm_sched_job_recovery(struct drm_gpu_scheduler *sched)
+{
+       struct drm_sched_job *s_job, *tmp;
+       bool found_guilty = false;
+       int r;
+
+       spin_lock(&sched->job_list_lock);
+       s_job = list_first_entry_or_null(&sched->ring_mirror_list,
+                                        struct drm_sched_job, node);
+       if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT)
+               schedule_delayed_work(&s_job->work_tdr, sched->timeout);
+
+       list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
+               struct drm_sched_fence *s_fence = s_job->s_fence;
+               struct dma_fence *fence;
+               uint64_t guilty_context;
+
+               if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) {
+                       found_guilty = true;
+                       guilty_context = s_job->s_fence->scheduled.context;
+               }
+
+               if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
+                       dma_fence_set_error(&s_fence->finished, -ECANCELED);
+
+               spin_unlock(&sched->job_list_lock);
+               fence = sched->ops->run_job(s_job);
+               atomic_inc(&sched->hw_rq_count);
+               if (fence) {
+                       s_fence->parent = dma_fence_get(fence);
+                       r = dma_fence_add_callback(fence, &s_fence->cb,
+                                                  drm_sched_process_job);
+                       if (r == -ENOENT)
+                               drm_sched_process_job(fence, &s_fence->cb);
+                       else if (r)
+                               DRM_ERROR("fence add callback failed (%d)\n",
+                                         r);
+                       dma_fence_put(fence);
+               } else {
+                       drm_sched_process_job(NULL, &s_fence->cb);
+               }
+               spin_lock(&sched->job_list_lock);
+       }
+       spin_unlock(&sched->job_list_lock);
+}
+EXPORT_SYMBOL(drm_sched_job_recovery);
+
+/* init a sched_job with basic field */
+int drm_sched_job_init(struct drm_sched_job *job,
+                      struct drm_gpu_scheduler *sched,
+                      struct drm_sched_entity *entity,
+                      void *owner)
+{
+       job->sched = sched;
+       job->s_priority = entity->rq - sched->sched_rq;
+       job->s_fence = drm_sched_fence_create(entity, owner);
+       if (!job->s_fence)
+               return -ENOMEM;
+       job->id = atomic64_inc_return(&sched->job_id_count);
+
+       INIT_WORK(&job->finish_work, drm_sched_job_finish);
+       INIT_LIST_HEAD(&job->node);
+       INIT_DELAYED_WORK(&job->work_tdr, drm_sched_job_timedout);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sched_job_init);
+
+/**
+ * Return ture if we can push more jobs to the hw.
+ */
+static bool drm_sched_ready(struct drm_gpu_scheduler *sched)
+{
+       return atomic_read(&sched->hw_rq_count) <
+               sched->hw_submission_limit;
+}
+
+/**
+ * Wake up the scheduler when it is ready
+ */
+static void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
+{
+       if (drm_sched_ready(sched))
+               wake_up_interruptible(&sched->wake_up_worker);
+}
+
+/**
+ * Select next entity to process
+*/
+static struct drm_sched_entity *
+drm_sched_select_entity(struct drm_gpu_scheduler *sched)
+{
+       struct drm_sched_entity *entity;
+       int i;
+
+       if (!drm_sched_ready(sched))
+               return NULL;
+
+       /* Kernel run queue has higher priority than normal run queue*/
+       for (i = DRM_SCHED_PRIORITY_MAX - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
+               entity = drm_sched_rq_select_entity(&sched->sched_rq[i]);
+               if (entity)
+                       break;
+       }
+
+       return entity;
+}
+
+static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       struct drm_sched_fence *s_fence =
+               container_of(cb, struct drm_sched_fence, cb);
+       struct drm_gpu_scheduler *sched = s_fence->sched;
+
+       dma_fence_get(&s_fence->finished);
+       atomic_dec(&sched->hw_rq_count);
+       drm_sched_fence_finished(s_fence);
+
+       trace_drm_sched_process_job(s_fence);
+       dma_fence_put(&s_fence->finished);
+       wake_up_interruptible(&sched->wake_up_worker);
+}
+
+static bool drm_sched_blocked(struct drm_gpu_scheduler *sched)
+{
+       if (kthread_should_park()) {
+               kthread_parkme();
+               return true;
+       }
+
+       return false;
+}
+
+static int drm_sched_main(void *param)
+{
+       struct sched_param sparam = {.sched_priority = 1};
+       struct drm_gpu_scheduler *sched = (struct drm_gpu_scheduler *)param;
+       int r;
+
+       sched_setscheduler(current, SCHED_FIFO, &sparam);
+
+       while (!kthread_should_stop()) {
+               struct drm_sched_entity *entity = NULL;
+               struct drm_sched_fence *s_fence;
+               struct drm_sched_job *sched_job;
+               struct dma_fence *fence;
+
+               wait_event_interruptible(sched->wake_up_worker,
+                                        (!drm_sched_blocked(sched) &&
+                                         (entity = drm_sched_select_entity(sched))) ||
+                                        kthread_should_stop());
+
+               if (!entity)
+                       continue;
+
+               sched_job = drm_sched_entity_pop_job(entity);
+               if (!sched_job)
+                       continue;
+
+               s_fence = sched_job->s_fence;
+
+               atomic_inc(&sched->hw_rq_count);
+               drm_sched_job_begin(sched_job);
+
+               fence = sched->ops->run_job(sched_job);
+               drm_sched_fence_scheduled(s_fence);
+
+               if (fence) {
+                       s_fence->parent = dma_fence_get(fence);
+                       r = dma_fence_add_callback(fence, &s_fence->cb,
+                                                  drm_sched_process_job);
+                       if (r == -ENOENT)
+                               drm_sched_process_job(fence, &s_fence->cb);
+                       else if (r)
+                               DRM_ERROR("fence add callback failed (%d)\n",
+                                         r);
+                       dma_fence_put(fence);
+               } else {
+                       drm_sched_process_job(NULL, &s_fence->cb);
+               }
+
+               wake_up(&sched->job_scheduled);
+       }
+       return 0;
+}
+
+/**
+ * Init a gpu scheduler instance
+ *
+ * @sched              The pointer to the scheduler
+ * @ops                        The backend operations for this scheduler.
+ * @hw_submissions     Number of hw submissions to do.
+ * @name               Name used for debugging
+ *
+ * Return 0 on success, otherwise error code.
+*/
+int drm_sched_init(struct drm_gpu_scheduler *sched,
+                  const struct drm_sched_backend_ops *ops,
+                  unsigned hw_submission,
+                  unsigned hang_limit,
+                  long timeout,
+                  const char *name)
+{
+       int i;
+       sched->ops = ops;
+       sched->hw_submission_limit = hw_submission;
+       sched->name = name;
+       sched->timeout = timeout;
+       sched->hang_limit = hang_limit;
+       for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_MAX; i++)
+               drm_sched_rq_init(&sched->sched_rq[i]);
+
+       init_waitqueue_head(&sched->wake_up_worker);
+       init_waitqueue_head(&sched->job_scheduled);
+       INIT_LIST_HEAD(&sched->ring_mirror_list);
+       spin_lock_init(&sched->job_list_lock);
+       atomic_set(&sched->hw_rq_count, 0);
+       atomic64_set(&sched->job_id_count, 0);
+
+       /* Each scheduler will run on a seperate kernel thread */
+       sched->thread = kthread_run(drm_sched_main, sched, sched->name);
+       if (IS_ERR(sched->thread)) {
+               DRM_ERROR("Failed to create scheduler for %s.\n", name);
+               return PTR_ERR(sched->thread);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sched_init);
+
+/**
+ * Destroy a gpu scheduler
+ *
+ * @sched      The pointer to the scheduler
+ */
+void drm_sched_fini(struct drm_gpu_scheduler *sched)
+{
+       if (sched->thread)
+               kthread_stop(sched->thread);
+}
+EXPORT_SYMBOL(drm_sched_fini);
diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c
new file mode 100644 (file)
index 0000000..f6f2955
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2015 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/kthread.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <drm/drmP.h>
+#include <drm/gpu_scheduler.h>
+
+static struct kmem_cache *sched_fence_slab;
+
+int drm_sched_fence_slab_init(void)
+{
+       sched_fence_slab = kmem_cache_create(
+               "drm_sched_fence", sizeof(struct drm_sched_fence), 0,
+               SLAB_HWCACHE_ALIGN, NULL);
+       if (!sched_fence_slab)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_sched_fence_slab_init);
+
+void drm_sched_fence_slab_fini(void)
+{
+       rcu_barrier();
+       kmem_cache_destroy(sched_fence_slab);
+}
+EXPORT_SYMBOL_GPL(drm_sched_fence_slab_fini);
+
+void drm_sched_fence_scheduled(struct drm_sched_fence *fence)
+{
+       int ret = dma_fence_signal(&fence->scheduled);
+
+       if (!ret)
+               DMA_FENCE_TRACE(&fence->scheduled,
+                               "signaled from irq context\n");
+       else
+               DMA_FENCE_TRACE(&fence->scheduled,
+                               "was already signaled\n");
+}
+
+void drm_sched_fence_finished(struct drm_sched_fence *fence)
+{
+       int ret = dma_fence_signal(&fence->finished);
+
+       if (!ret)
+               DMA_FENCE_TRACE(&fence->finished,
+                               "signaled from irq context\n");
+       else
+               DMA_FENCE_TRACE(&fence->finished,
+                               "was already signaled\n");
+}
+
+static const char *drm_sched_fence_get_driver_name(struct dma_fence *fence)
+{
+       return "drm_sched";
+}
+
+static const char *drm_sched_fence_get_timeline_name(struct dma_fence *f)
+{
+       struct drm_sched_fence *fence = to_drm_sched_fence(f);
+       return (const char *)fence->sched->name;
+}
+
+static bool drm_sched_fence_enable_signaling(struct dma_fence *f)
+{
+       return true;
+}
+
+/**
+ * amd_sched_fence_free - free up the fence memory
+ *
+ * @rcu: RCU callback head
+ *
+ * Free up the fence memory after the RCU grace period.
+ */
+static void drm_sched_fence_free(struct rcu_head *rcu)
+{
+       struct dma_fence *f = container_of(rcu, struct dma_fence, rcu);
+       struct drm_sched_fence *fence = to_drm_sched_fence(f);
+
+       dma_fence_put(fence->parent);
+       kmem_cache_free(sched_fence_slab, fence);
+}
+
+/**
+ * amd_sched_fence_release_scheduled - callback that fence can be freed
+ *
+ * @fence: fence
+ *
+ * This function is called when the reference count becomes zero.
+ * It just RCU schedules freeing up the fence.
+ */
+static void drm_sched_fence_release_scheduled(struct dma_fence *f)
+{
+       struct drm_sched_fence *fence = to_drm_sched_fence(f);
+
+       call_rcu(&fence->finished.rcu, drm_sched_fence_free);
+}
+
+/**
+ * amd_sched_fence_release_finished - drop extra reference
+ *
+ * @f: fence
+ *
+ * Drop the extra reference from the scheduled fence to the base fence.
+ */
+static void drm_sched_fence_release_finished(struct dma_fence *f)
+{
+       struct drm_sched_fence *fence = to_drm_sched_fence(f);
+
+       dma_fence_put(&fence->scheduled);
+}
+
+const struct dma_fence_ops drm_sched_fence_ops_scheduled = {
+       .get_driver_name = drm_sched_fence_get_driver_name,
+       .get_timeline_name = drm_sched_fence_get_timeline_name,
+       .enable_signaling = drm_sched_fence_enable_signaling,
+       .signaled = NULL,
+       .wait = dma_fence_default_wait,
+       .release = drm_sched_fence_release_scheduled,
+};
+
+const struct dma_fence_ops drm_sched_fence_ops_finished = {
+       .get_driver_name = drm_sched_fence_get_driver_name,
+       .get_timeline_name = drm_sched_fence_get_timeline_name,
+       .enable_signaling = drm_sched_fence_enable_signaling,
+       .signaled = NULL,
+       .wait = dma_fence_default_wait,
+       .release = drm_sched_fence_release_finished,
+};
+
+struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f)
+{
+       if (f->ops == &drm_sched_fence_ops_scheduled)
+               return container_of(f, struct drm_sched_fence, scheduled);
+
+       if (f->ops == &drm_sched_fence_ops_finished)
+               return container_of(f, struct drm_sched_fence, finished);
+
+       return NULL;
+}
+EXPORT_SYMBOL(to_drm_sched_fence);
+
+struct drm_sched_fence *drm_sched_fence_create(struct drm_sched_entity *entity,
+                                              void *owner)
+{
+       struct drm_sched_fence *fence = NULL;
+       unsigned seq;
+
+       fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL);
+       if (fence == NULL)
+               return NULL;
+
+       fence->owner = owner;
+       fence->sched = entity->sched;
+       spin_lock_init(&fence->lock);
+
+       seq = atomic_inc_return(&entity->fence_seq);
+       dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled,
+                      &fence->lock, entity->fence_context, seq);
+       dma_fence_init(&fence->finished, &drm_sched_fence_ops_finished,
+                      &fence->lock, entity->fence_context + 1, seq);
+
+       return fence;
+}
diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h
new file mode 100644 (file)
index 0000000..d29da4c
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 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 _DRM_GPU_SCHEDULER_H_
+#define _DRM_GPU_SCHEDULER_H_
+
+#include <drm/spsc_queue.h>
+#include <linux/dma-fence.h>
+
+struct drm_gpu_scheduler;
+struct drm_sched_rq;
+
+enum drm_sched_priority {
+       DRM_SCHED_PRIORITY_MIN,
+       DRM_SCHED_PRIORITY_LOW = DRM_SCHED_PRIORITY_MIN,
+       DRM_SCHED_PRIORITY_NORMAL,
+       DRM_SCHED_PRIORITY_HIGH_SW,
+       DRM_SCHED_PRIORITY_HIGH_HW,
+       DRM_SCHED_PRIORITY_KERNEL,
+       DRM_SCHED_PRIORITY_MAX,
+       DRM_SCHED_PRIORITY_INVALID = -1,
+       DRM_SCHED_PRIORITY_UNSET = -2
+};
+
+/**
+ * A scheduler entity is a wrapper around a job queue or a group
+ * of other entities. Entities take turns emitting jobs from their
+ * job queues to corresponding hardware ring based on scheduling
+ * policy.
+*/
+struct drm_sched_entity {
+       struct list_head                list;
+       struct drm_sched_rq             *rq;
+       spinlock_t                      rq_lock;
+       struct drm_gpu_scheduler        *sched;
+
+       spinlock_t                      queue_lock;
+       struct spsc_queue               job_queue;
+
+       atomic_t                        fence_seq;
+       uint64_t                        fence_context;
+
+       struct dma_fence                *dependency;
+       struct dma_fence_cb             cb;
+       atomic_t                        *guilty; /* points to ctx's guilty */
+};
+
+/**
+ * Run queue is a set of entities scheduling command submissions for
+ * one specific ring. It implements the scheduling policy that selects
+ * the next entity to emit commands from.
+*/
+struct drm_sched_rq {
+       spinlock_t                      lock;
+       struct list_head                entities;
+       struct drm_sched_entity         *current_entity;
+};
+
+struct drm_sched_fence {
+       struct dma_fence                scheduled;
+       struct dma_fence                finished;
+       struct dma_fence_cb             cb;
+       struct dma_fence                *parent;
+       struct drm_gpu_scheduler        *sched;
+       spinlock_t                      lock;
+       void                            *owner;
+};
+
+struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f);
+
+struct drm_sched_job {
+       struct spsc_node                queue_node;
+       struct drm_gpu_scheduler        *sched;
+       struct drm_sched_fence          *s_fence;
+       struct dma_fence_cb             finish_cb;
+       struct work_struct              finish_work;
+       struct list_head                node;
+       struct delayed_work             work_tdr;
+       uint64_t                        id;
+       atomic_t                        karma;
+       enum drm_sched_priority         s_priority;
+};
+
+static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job,
+                                           int threshold)
+{
+       return (s_job && atomic_inc_return(&s_job->karma) > threshold);
+}
+
+/**
+ * Define the backend operations called by the scheduler,
+ * these functions should be implemented in driver side
+*/
+struct drm_sched_backend_ops {
+       struct dma_fence *(*dependency)(struct drm_sched_job *sched_job,
+                                       struct drm_sched_entity *s_entity);
+       struct dma_fence *(*run_job)(struct drm_sched_job *sched_job);
+       void (*timedout_job)(struct drm_sched_job *sched_job);
+       void (*free_job)(struct drm_sched_job *sched_job);
+};
+
+/**
+ * One scheduler is implemented for each hardware ring
+*/
+struct drm_gpu_scheduler {
+       const struct drm_sched_backend_ops      *ops;
+       uint32_t                        hw_submission_limit;
+       long                            timeout;
+       const char                      *name;
+       struct drm_sched_rq             sched_rq[DRM_SCHED_PRIORITY_MAX];
+       wait_queue_head_t               wake_up_worker;
+       wait_queue_head_t               job_scheduled;
+       atomic_t                        hw_rq_count;
+       atomic64_t                      job_id_count;
+       struct task_struct              *thread;
+       struct list_head                ring_mirror_list;
+       spinlock_t                      job_list_lock;
+       int                             hang_limit;
+};
+
+int drm_sched_init(struct drm_gpu_scheduler *sched,
+                  const struct drm_sched_backend_ops *ops,
+                  uint32_t hw_submission, unsigned hang_limit, long timeout,
+                  const char *name);
+void drm_sched_fini(struct drm_gpu_scheduler *sched);
+
+int drm_sched_entity_init(struct drm_gpu_scheduler *sched,
+                         struct drm_sched_entity *entity,
+                         struct drm_sched_rq *rq,
+                         uint32_t jobs, atomic_t *guilty);
+void drm_sched_entity_fini(struct drm_gpu_scheduler *sched,
+                          struct drm_sched_entity *entity);
+void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
+                              struct drm_sched_entity *entity);
+void drm_sched_entity_set_rq(struct drm_sched_entity *entity,
+                            struct drm_sched_rq *rq);
+
+int drm_sched_fence_slab_init(void);
+void drm_sched_fence_slab_fini(void);
+
+struct drm_sched_fence *drm_sched_fence_create(
+       struct drm_sched_entity *s_entity, void *owner);
+void drm_sched_fence_scheduled(struct drm_sched_fence *fence);
+void drm_sched_fence_finished(struct drm_sched_fence *fence);
+int drm_sched_job_init(struct drm_sched_job *job,
+                      struct drm_gpu_scheduler *sched,
+                      struct drm_sched_entity *entity,
+                      void *owner);
+void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched,
+                           struct drm_sched_job *job);
+void drm_sched_job_recovery(struct drm_gpu_scheduler *sched);
+bool drm_sched_dependency_optimized(struct dma_fence* fence,
+                                   struct drm_sched_entity *entity);
+void drm_sched_job_kickout(struct drm_sched_job *s_job);
+
+#endif
diff --git a/include/drm/gpu_scheduler_trace.h b/include/drm/gpu_scheduler_trace.h
new file mode 100644 (file)
index 0000000..0789e8d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 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.
+ *
+ */
+
+#if !defined(_GPU_SCHED_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _GPU_SCHED_TRACE_H_
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include <drm/drmP.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM gpu_scheduler
+#define TRACE_INCLUDE_FILE gpu_scheduler_trace
+
+TRACE_EVENT(drm_sched_job,
+           TP_PROTO(struct drm_sched_job *sched_job, struct drm_sched_entity *entity),
+           TP_ARGS(sched_job, entity),
+           TP_STRUCT__entry(
+                            __field(struct drm_sched_entity *, entity)
+                            __field(struct dma_fence *, fence)
+                            __field(const char *, name)
+                            __field(uint64_t, id)
+                            __field(u32, job_count)
+                            __field(int, hw_job_count)
+                            ),
+
+           TP_fast_assign(
+                          __entry->entity = entity;
+                          __entry->id = sched_job->id;
+                          __entry->fence = &sched_job->s_fence->finished;
+                          __entry->name = sched_job->sched->name;
+                          __entry->job_count = spsc_queue_count(&entity->job_queue);
+                          __entry->hw_job_count = atomic_read(
+                                  &sched_job->sched->hw_rq_count);
+                          ),
+           TP_printk("entity=%p, id=%llu, fence=%p, ring=%s, job count:%u, hw job count:%d",
+                     __entry->entity, __entry->id,
+                     __entry->fence, __entry->name,
+                     __entry->job_count, __entry->hw_job_count)
+);
+
+TRACE_EVENT(drm_sched_process_job,
+           TP_PROTO(struct drm_sched_fence *fence),
+           TP_ARGS(fence),
+           TP_STRUCT__entry(
+                   __field(struct dma_fence *, fence)
+                   ),
+
+           TP_fast_assign(
+                   __entry->fence = &fence->finished;
+                   ),
+           TP_printk("fence=%p signaled", __entry->fence)
+);
+
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/include/drm/spsc_queue.h b/include/drm/spsc_queue.h
new file mode 100644 (file)
index 0000000..125f096
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2017 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 DRM_SCHEDULER_SPSC_QUEUE_H_
+#define DRM_SCHEDULER_SPSC_QUEUE_H_
+
+#include <linux/atomic.h>
+#include <linux/preempt.h>
+
+/** SPSC lockless queue */
+
+struct spsc_node {
+
+       /* Stores spsc_node* */
+       struct spsc_node *next;
+};
+
+struct spsc_queue {
+
+        struct spsc_node *head;
+
+       /* atomic pointer to struct spsc_node* */
+       atomic_long_t tail;
+
+       atomic_t job_count;
+};
+
+static inline void spsc_queue_init(struct spsc_queue *queue)
+{
+       queue->head = NULL;
+       atomic_long_set(&queue->tail, (long)&queue->head);
+       atomic_set(&queue->job_count, 0);
+}
+
+static inline struct spsc_node *spsc_queue_peek(struct spsc_queue *queue)
+{
+       return queue->head;
+}
+
+static inline int spsc_queue_count(struct spsc_queue *queue)
+{
+       return atomic_read(&queue->job_count);
+}
+
+static inline bool spsc_queue_push(struct spsc_queue *queue, struct spsc_node *node)
+{
+       struct spsc_node **tail;
+
+       node->next = NULL;
+
+       preempt_disable();
+
+       tail = (struct spsc_node **)atomic_long_xchg(&queue->tail, (long)&node->next);
+       WRITE_ONCE(*tail, node);
+       atomic_inc(&queue->job_count);
+
+       /*
+        * In case of first element verify new node will be visible to the consumer
+        * thread when we ping the kernel thread that there is new work to do.
+        */
+       smp_wmb();
+
+       preempt_enable();
+
+       return tail == &queue->head;
+}
+
+
+static inline struct spsc_node *spsc_queue_pop(struct spsc_queue *queue)
+{
+       struct spsc_node *next, *node;
+
+       /* Verify reading from memory and not the cache */
+       smp_rmb();
+
+       node = READ_ONCE(queue->head);
+
+       if (!node)
+               return NULL;
+
+       next = READ_ONCE(node->next);
+       WRITE_ONCE(queue->head, next);
+
+       if (unlikely(!next)) {
+               /* slowpath for the last element in the queue */
+
+               if (atomic_long_cmpxchg(&queue->tail,
+                               (long)&node->next, (long) &queue->head) != (long)&node->next) {
+                       /* Updating tail failed wait for new next to appear */
+                       do {
+                               smp_rmb();
+                       } while (unlikely(!(queue->head = READ_ONCE(node->next))));
+               }
+       }
+
+       atomic_dec(&queue->job_count);
+       return node;
+}
+
+
+
+#endif /* DRM_SCHEDULER_SPSC_QUEUE_H_ */