[PATCH 5/8] drm/amdgpu: move priority decay logic into amd_sched_priority_ctr
Andres Rodriguez
andresx7 at gmail.com
Fri Jun 9 17:05:20 UTC 2017
On 2017-06-09 06:54 AM, Christian König wrote:
> Am 09.06.2017 um 00:06 schrieb Andres Rodriguez:
>> So that it can be re-used.
>>
>> A priority counter can be used to track priority requests. If no active
>> requests exists, the counter will default to ()->default_priority.
>>
>> The re-factored version is now allowed to decay below NORMAL. This
>> allows us to create requests that lower the priority. This didn't
>> matter for rings, as no priority below NORMAL has an effect.
>
> NAK to the whole approach. This handling is amdgpu specific and not
> related to the gpu scheduler in any way.
>
Hey Christian,
I moved this to gpu_scheduler.c since it's original purpose is to track
the scheduler's min_priority. Sorry I didn't provide any context for
that in the original cover letter.
In my previous patch, "[PATCH 3/3] drm/amdgpu: add a mechanism to
acquire gpu exclusivity", the functions amd_sched_min_priority_get/put()
are almost identical copies of amdgpu_ring_priority_get/put(). To remove
the duplication I introduced amd_sched_priority_ctr.
I later realized that I could re-use the same mechanism to track the
context priority changes if I added a default_priority parameter. It
also has the added benefit of keeping the requests refcounted.
I agree the usage of amd_sched_priority_ctr seems a little overkill. I
originally used the approach of combining a ctx->init_priority with a
ctx->master_priority, and that was pretty simple. However, re-using a
concept that was already implemented, instead of introducing a new one
had its own arguments for simplicity as well.
There is also one theoretical future scenario where the refcounting
could be useful. Most VR apps output a 'mirror' window to the system
compositor. Therefore they are clients of the system compositor and the
VR compositor simultaneously. If both compositors were to use the
ctx_set_priority() API on this app, the second request would override
the first. With amd_sched_priority_ctr we would honor the highest of the
two requests. In combination with the ability to set a minimum required
priority to schedule gpu work, we can potentially run into undesired
consequences.
Anyways, I only have a slight preference for this approach. So if you'd
like me to go back to the muxing of two priorities I'm happy to go for
it (and move this patch to the followup series for min_priority tracking).
Regards,
Andres
> Christian.
>
>>
>> Signed-off-by: Andres Rodriguez <andresx7 at gmail.com>
>> ---
>> drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c | 69 +++++----------
>> drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h | 6 +-
>> drivers/gpu/drm/amd/scheduler/gpu_scheduler.c | 122
>> ++++++++++++++++++++++++++
>> drivers/gpu/drm/amd/scheduler/gpu_scheduler.h | 21 +++++
>> 4 files changed, 164 insertions(+), 54 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
>> index 2d8b20a7..159ab0e 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
>> @@ -142,115 +142,86 @@ void amdgpu_ring_commit(struct amdgpu_ring *ring)
>> /**
>> * amdgpu_ring_undo - reset the wptr
>> *
>> * @ring: amdgpu_ring structure holding ring information
>> *
>> * Reset the driver's copy of the wptr (all asics).
>> */
>> void amdgpu_ring_undo(struct amdgpu_ring *ring)
>> {
>> ring->wptr = ring->wptr_old;
>> if (ring->funcs->end_use)
>> ring->funcs->end_use(ring);
>> }
>> +static void amdgpu_ring_priority_set(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority)
>> +{
>> + struct amdgpu_ring *ring = container_of(p, struct amdgpu_ring,
>> + priority_ctr);
>> +
>> + if (ring->funcs->set_priority)
>> + ring->funcs->set_priority(ring, priority);
>> +}
>> +
>> /**
>> * amdgpu_ring_priority_put - restore a ring's priority
>> *
>> * @ring: amdgpu_ring structure holding the information
>> * @priority: target priority
>> *
>> * Release a request for executing at @priority
>> */
>> void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
>> enum amd_sched_priority priority)
>> {
>> - int i;
>> -
>> - if (!ring->funcs->set_priority)
>> - return;
>> -
>> - if (atomic_dec_return(&ring->num_jobs[priority]) > 0)
>> - return;
>> -
>> - /* no need to restore if the job is already at the lowest
>> priority */
>> - if (priority == AMD_SCHED_PRIORITY_NORMAL)
>> - return;
>> -
>> - mutex_lock(&ring->priority_mutex);
>> - /* something higher prio is executing, no need to decay */
>> - if (ring->priority > priority)
>> - 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
>> - || atomic_read(&ring->num_jobs[i])) {
>> - ring->priority = i;
>> - ring->funcs->set_priority(ring, i);
>> - break;
>> - }
>> - }
>> -
>> -out_unlock:
>> - mutex_unlock(&ring->priority_mutex);
>> + if (ring->funcs->set_priority)
>> + amd_sched_priority_ctr_put(&ring->priority_ctr, priority);
>> }
>> /**
>> * amdgpu_ring_priority_get - change the ring's priority
>> *
>> * @ring: amdgpu_ring structure holding the information
>> * @priority: target priority
>> *
>> * 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)
>> {
>> - if (!ring->funcs->set_priority)
>> - return;
>> -
>> - atomic_inc(&ring->num_jobs[priority]);
>> -
>> - mutex_lock(&ring->priority_mutex);
>> - if (priority <= ring->priority)
>> - goto out_unlock;
>> -
>> - ring->priority = priority;
>> - ring->funcs->set_priority(ring, priority);
>> -
>> -out_unlock:
>> - mutex_unlock(&ring->priority_mutex);
>> + if (ring->funcs->set_priority)
>> + amd_sched_priority_ctr_get(&ring->priority_ctr, priority);
>> }
>> /**
>> * amdgpu_ring_init - init driver ring struct.
>> *
>> * @adev: amdgpu_device pointer
>> * @ring: amdgpu_ring structure holding ring information
>> * @max_ndw: maximum number of dw for ring alloc
>> * @nop: nop packet for this ring
>> *
>> * Initialize the driver information for the selected ring (all asics).
>> * Returns 0 on success, error on failure.
>> */
>> int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring
>> *ring,
>> unsigned max_dw, struct amdgpu_irq_src *irq_src,
>> unsigned irq_type)
>> {
>> - int r, i;
>> + int r;
>> if (ring->adev == NULL) {
>> if (adev->num_rings >= AMDGPU_MAX_RINGS)
>> return -EINVAL;
>> ring->adev = adev;
>> ring->idx = adev->num_rings++;
>> adev->rings[ring->idx] = ring;
>> r = amdgpu_fence_driver_init_ring(ring,
>> amdgpu_sched_hw_submission);
>> if (r)
>> return r;
>> }
>> if (ring->funcs->support_64bit_ptrs) {
>> @@ -312,55 +283,55 @@ int amdgpu_ring_init(struct amdgpu_device *adev,
>> struct amdgpu_ring *ring,
>> /* Allocate ring buffer */
>> if (ring->ring_obj == NULL) {
>> r = amdgpu_bo_create_kernel(adev, ring->ring_size, PAGE_SIZE,
>> AMDGPU_GEM_DOMAIN_GTT,
>> &ring->ring_obj,
>> &ring->gpu_addr,
>> (void **)&ring->ring);
>> if (r) {
>> dev_err(adev->dev, "(%d) ring create failed\n", r);
>> return r;
>> }
>> amdgpu_ring_clear_ring(ring);
>> }
>> ring->max_dw = max_dw;
>> - ring->priority = AMD_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)
>> - atomic_set(&ring->num_jobs[i], 0);
>> + amd_sched_priority_ctr_init(&ring->priority_ctr,
>> + AMD_SCHED_PRIORITY_NORMAL,
>> + amdgpu_ring_priority_set);
>> if (amdgpu_debugfs_ring_init(adev, ring)) {
>> DRM_ERROR("Failed to register debugfs file for rings !\n");
>> }
>> return 0;
>> }
>> /**
>> * amdgpu_ring_fini - tear down the driver ring struct.
>> *
>> * @adev: amdgpu_device pointer
>> * @ring: amdgpu_ring structure holding ring information
>> *
>> * Tear down the driver information for the selected ring (all asics).
>> */
>> void amdgpu_ring_fini(struct amdgpu_ring *ring)
>> {
>> + amd_sched_priority_ctr_fini(&ring->priority_ctr);
>> +
>> ring->ready = false;
>> if (ring->funcs->support_64bit_ptrs) {
>> amdgpu_wb_free_64bit(ring->adev, ring->cond_exe_offs);
>> amdgpu_wb_free_64bit(ring->adev, ring->fence_offs);
>> amdgpu_wb_free_64bit(ring->adev, ring->rptr_offs);
>> amdgpu_wb_free_64bit(ring->adev, ring->wptr_offs);
>> } else {
>> amdgpu_wb_free(ring->adev, ring->cond_exe_offs);
>> amdgpu_wb_free(ring->adev, ring->fence_offs);
>> amdgpu_wb_free(ring->adev, ring->rptr_offs);
>> amdgpu_wb_free(ring->adev, ring->wptr_offs);
>> }
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
>> index 7348769..2a04c0d 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
>> @@ -147,30 +147,31 @@ struct amdgpu_ring_funcs {
>> void (*emit_switch_buffer) (struct amdgpu_ring *ring);
>> void (*emit_cntxcntl) (struct amdgpu_ring *ring, uint32_t flags);
>> void (*emit_rreg)(struct amdgpu_ring *ring, uint32_t reg);
>> void (*emit_wreg)(struct amdgpu_ring *ring, uint32_t reg,
>> uint32_t val);
>> void (*emit_tmz)(struct amdgpu_ring *ring, bool start);
>> /* priority functions */
>> void (*set_priority) (struct amdgpu_ring *ring,
>> enum amd_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 amd_sched_priority_ctr priority_ctr;
>> struct list_head lru_list;
>> struct amdgpu_bo *ring_obj;
>> volatile uint32_t *ring;
>> unsigned rptr_offs;
>> u64 wptr;
>> u64 wptr_old;
>> unsigned ring_size;
>> unsigned max_dw;
>> int count_dw;
>> uint64_t gpu_addr;
>> uint64_t ptr_mask;
>> uint32_t buf_mask;
>> bool ready;
>> u32 idx;
>> @@ -181,35 +182,30 @@ struct amdgpu_ring {
>> uint64_t mqd_gpu_addr;
>> void *mqd_ptr;
>> uint64_t eop_gpu_addr;
>> u32 doorbell_index;
>> bool use_doorbell;
>> unsigned wptr_offs;
>> unsigned fence_offs;
>> uint64_t current_ctx;
>> char name[16];
>> unsigned cond_exe_offs;
>> u64 cond_exe_gpu_addr;
>> volatile u32 *cond_exe_cpu_addr;
>> unsigned vm_inv_eng;
>> bool has_compute_vm_bug;
>> - atomic_t num_jobs[AMD_SCHED_PRIORITY_MAX];
>> - struct mutex priority_mutex;
>> - /* protected by priority_mutex */
>> - int priority;
>> -
>> #if defined(CONFIG_DEBUG_FS)
>> struct dentry *ent;
>> #endif
>> };
>> int amdgpu_ring_alloc(struct amdgpu_ring *ring, unsigned ndw);
>> void amdgpu_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count);
>> 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);
>> void amdgpu_ring_priority_put(struct amdgpu_ring *ring,
>> enum amd_sched_priority priority);
>> int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring
>> *ring,
>> diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
>> b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
>> index 38cea6f..a203736 100644
>> --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
>> +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
>> @@ -488,30 +488,152 @@ int amd_sched_job_init(struct amd_sched_job *job,
>> job->sched = sched;
>> job->s_entity = entity;
>> 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;
>> }
>> /**
>> + * Initialize a priority_ctr
>> + *
>> + * @p: priority_ctr to initialize
>> + * @default_priority: priority to be used when no active requests exist
>> + * @set_priority: callback for changing the underlying resource's
>> priority
>> + *
>> + * The @set_priority callback will be invoked whenever a transition
>> + * happens into a new priority state, e.g. NORMAL->HIGH or
>> HIGH->NORMAL. It
>> + * is guaranteed to be invoked exactly once per state transition.
>> + *
>> + */
>> +void amd_sched_priority_ctr_init(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority default_priority,
>> + amd_sched_set_priority_func_t set_priority)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < AMD_SCHED_PRIORITY_MAX; ++i)
>> + atomic_set(&p->requests[i], 0);
>> +
>> + p->default_priority = default_priority;
>> + p->priority = p->default_priority;
>> + p->set_priority = set_priority;
>> + mutex_init(&p->mutex);
>> +}
>> +
>> +/**
>> + * Cleanup a priority_ctr
>> + *
>> + * @p: priority_ctr to cleanup
>> + *
>> + * Must be called when the priority_ctr is no longer needed
>> + */
>> +void amd_sched_priority_ctr_fini(struct amd_sched_priority_ctr *p)
>> +{
>> + p->priority = AMD_SCHED_PRIORITY_INVALID;
>> +}
>> +
>> +/**
>> + * Begin a request to set the current priority to @priority
>> + *
>> + * @p: priority_ctr
>> + * @priority: requested priority
>> + *
>> + * Requests are reference counted.
>> + *
>> + * The current priority will be the highest priority for which an
>> + * active request exists.
>> + *
>> + * If no active requests exist, the resource will default to
>> + * p->default_priority
>> + */
>> +void amd_sched_priority_ctr_get(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority)
>> +{
>> + if (WARN_ON(priority > AMD_SCHED_PRIORITY_MAX
>> + || priority < AMD_SCHED_PRIORITY_MIN))
>> + return;
>> +
>> + if (atomic_inc_return(&p->requests[priority]) > 1)
>> + return;
>> +
>> + mutex_lock(&p->mutex);
>> +
>> + /* A request with higher priority already exists */
>> + if (priority <= p->priority)
>> + goto out_unlock;
>> +
>> + p->set_priority(p, priority);
>> + p->priority = priority;
>> +
>> +out_unlock:
>> + mutex_unlock(&p->mutex);
>> +}
>> +
>> +/**
>> + * End a request to set the current priority to @priority
>> + *
>> + * @p: priority_ctr
>> + * @priority: requested priority
>> + *
>> + * Refer to @amd_sched_priority_ctr_get for more details.
>> + */
>> +void amd_sched_priority_ctr_put(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority)
>> +{
>> + int i;
>> + enum amd_sched_priority decayed_priority =
>> AMD_SCHED_PRIORITY_INVALID;
>> +
>> + if (WARN_ON(priority > AMD_SCHED_PRIORITY_MAX
>> + || priority < AMD_SCHED_PRIORITY_MIN))
>> + return;
>> +
>> + if (atomic_dec_return(&p->requests[priority]) > 0)
>> + return;
>> +
>> + mutex_lock(&p->mutex);
>> +
>> + /* something higher prio is executing, no need to decay */
>> + if (p->priority > priority)
>> + goto out_unlock;
>> +
>> + /* decay priority to the next level with an active request */
>> + for (i = priority; i >= AMD_SCHED_PRIORITY_MIN; i--) {
>> + if (atomic_read(&p->requests[i])) {
>> + decayed_priority = i;
>> + break;
>> + }
>> + }
>> +
>> + /* If no requests are active, use the default priority */
>> + if (decayed_priority == AMD_SCHED_PRIORITY_INVALID)
>> + decayed_priority = p->default_priority;
>> +
>> + p->set_priority(p, decayed_priority);
>> + p->priority = decayed_priority;
>> +
>> +out_unlock:
>> + mutex_unlock(&p->mutex);
>> +}
>> +
>> +/**
>> * 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);
>> diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
>> b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
>> index da040bc..b9283b5 100644
>> --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
>> +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
>> @@ -17,30 +17,31 @@
>> * 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>
>> struct amd_gpu_scheduler;
>> struct amd_sched_rq;
>> +struct amd_sched_priority_ctr;
>> /**
>> * 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;
>> struct amd_gpu_scheduler *sched;
>> spinlock_t queue_lock;
>> struct kfifo job_queue;
>> @@ -112,30 +113,41 @@ struct amd_sched_backend_ops {
>> void (*timedout_job)(struct amd_sched_job *sched_job);
>> void (*free_job)(struct amd_sched_job *sched_job);
>> };
>> 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
>> };
>> +typedef void (*amd_sched_set_priority_func_t)(struct
>> amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority);
>> +
>> +struct amd_sched_priority_ctr {
>> + struct mutex mutex;
>> + atomic_t requests[AMD_SCHED_PRIORITY_MAX];
>> + enum amd_sched_priority priority;
>> + enum amd_sched_priority default_priority;
>> + amd_sched_set_priority_func_t set_priority;
>> +};
>> +
>> /**
>> * 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;
>> @@ -166,16 +178,25 @@ 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);
>> 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);
>> static inline enum amd_sched_priority
>> amd_sched_get_job_priority(struct amd_sched_job *job)
>> {
>> return (job->s_entity->rq - job->sched->sched_rq);
>> }
>> +void amd_sched_priority_ctr_init(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority default_priority,
>> + amd_sched_set_priority_func_t set_priority);
>> +void amd_sched_priority_ctr_fini(struct amd_sched_priority_ctr *p);
>> +void amd_sched_priority_ctr_get(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority);
>> +void amd_sched_priority_ctr_put(struct amd_sched_priority_ctr *p,
>> + enum amd_sched_priority priority);
>> +
>> #endif
>
>
More information about the amd-gfx
mailing list