/* * 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. * * Authors: monk liu <monk.liu@amd.com> */ #include <drm/drm_auth.h>#include <drm/drm_drv.h>#include "amdgpu.h"#include "amdgpu_sched.h"#include "amdgpu_ras.h"#include <linux/nospec.h> #define to_amdgpu_ctx_entity(e) \ container_of((e), struct amdgpu_ctx_entity, entity) const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM] = { [AMDGPU_HW_IP_GFX] = 1, [AMDGPU_HW_IP_COMPUTE] = 4, [AMDGPU_HW_IP_DMA] = 2, [AMDGPU_HW_IP_UVD] = 1, [AMDGPU_HW_IP_VCE] = 1, [AMDGPU_HW_IP_UVD_ENC] = 1, [AMDGPU_HW_IP_VCN_DEC] = 1, [AMDGPU_HW_IP_VCN_ENC] = 1, [AMDGPU_HW_IP_VCN_JPEG] = 1, [AMDGPU_HW_IP_VPE] = 1,}; bool amdgpu_ctx_priority_is_valid(int32_t ctx_prio){ switch (ctx_prio) { case AMDGPU_CTX_PRIORITY_VERY_LOW: case AMDGPU_CTX_PRIORITY_LOW: case AMDGPU_CTX_PRIORITY_NORMAL: case AMDGPU_CTX_PRIORITY_HIGH: case AMDGPU_CTX_PRIORITY_VERY_HIGH: return true; default: case AMDGPU_CTX_PRIORITY_UNSET: /* UNSET priority is not valid and we don't carry that * around, but set it to NORMAL in the only place this * function is called, amdgpu_ctx_ioctl(). */ return false; }} static enum drm_sched_priorityamdgpu_ctx_to_drm_sched_prio(int32_t ctx_prio){ switch (ctx_prio) { case AMDGPU_CTX_PRIORITY_UNSET: pr_warn_once("AMD-->DRM context priority value UNSET-->NORMAL"); return DRM_SCHED_PRIORITY_NORMAL; case AMDGPU_CTX_PRIORITY_VERY_LOW: return DRM_SCHED_PRIORITY_LOW; case AMDGPU_CTX_PRIORITY_LOW: return DRM_SCHED_PRIORITY_LOW; case AMDGPU_CTX_PRIORITY_NORMAL: return DRM_SCHED_PRIORITY_NORMAL; case AMDGPU_CTX_PRIORITY_HIGH: return DRM_SCHED_PRIORITY_HIGH; case AMDGPU_CTX_PRIORITY_VERY_HIGH: return DRM_SCHED_PRIORITY_HIGH; /* This should not happen as we sanitized userspace provided priority * already, WARN if this happens. */ default: WARN(1, "Invalid context priority %d\n", ctx_prio); return DRM_SCHED_PRIORITY_NORMAL; } } static int amdgpu_ctx_priority_permit(struct drm_file *filp, int32_t priority){ /* NORMAL and below are accessible by everyone */ if (priority <= AMDGPU_CTX_PRIORITY_NORMAL) return 0; if (capable(CAP_SYS_NICE)) return 0; if (drm_is_current_master(filp)) return 0; return -EACCES;} static enum amdgpu_gfx_pipe_priority amdgpu_ctx_prio_to_gfx_pipe_prio(int32_t prio){ switch (prio) { case AMDGPU_CTX_PRIORITY_HIGH: case AMDGPU_CTX_PRIORITY_VERY_HIGH: return AMDGPU_GFX_PIPE_PRIO_HIGH; default: return AMDGPU_GFX_PIPE_PRIO_NORMAL; }} static enum amdgpu_ring_priority_level amdgpu_ctx_sched_prio_to_ring_prio(int32_t prio){ switch (prio) { case AMDGPU_CTX_PRIORITY_HIGH: return AMDGPU_RING_PRIO_1; case AMDGPU_CTX_PRIORITY_VERY_HIGH: return AMDGPU_RING_PRIO_2; default: return AMDGPU_RING_PRIO_0; }} static unsigned int amdgpu_ctx_get_hw_prio(struct amdgpu_ctx *ctx, u32 hw_ip){ struct amdgpu_device *adev = ctx->mgr->adev; unsigned int hw_prio; int32_t ctx_prio; ctx_prio = (ctx->override_priority == AMDGPU_CTX_PRIORITY_UNSET) ? ctx->init_priority : ctx->override_priority; switch (hw_ip) { case AMDGPU_HW_IP_GFX: case AMDGPU_HW_IP_COMPUTE: hw_prio = amdgpu_ctx_prio_to_gfx_pipe_prio(ctx_prio); break; case AMDGPU_HW_IP_VCE: case AMDGPU_HW_IP_VCN_ENC: hw_prio = amdgpu_ctx_sched_prio_to_ring_prio(ctx_prio); break; default: hw_prio = AMDGPU_RING_PRIO_DEFAULT; break; } hw_ip = array_index_nospec(hw_ip, AMDGPU_HW_IP_NUM); if (adev->gpu_sched[hw_ip][hw_prio].num_scheds == 0) hw_prio = AMDGPU_RING_PRIO_DEFAULT; return hw_prio;} /* Calculate the time spend on the hw */static ktime_t amdgpu_ctx_fence_time(struct dma_fence *fence){ struct drm_sched_fence *s_fence; if (!fence) return ns_to_ktime(0); /* When the fence is not even scheduled it can't have spend time */ s_fence = to_drm_sched_fence(fence); if (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &s_fence->scheduled.flags)) return ns_to_ktime(0); /* When it is still running account how much already spend */ if (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &s_fence->finished.flags)) return ktime_sub(ktime_get(), s_fence->scheduled.timestamp); return ktime_sub(s_fence->finished.timestamp, s_fence->scheduled.timestamp);} static ktime_t amdgpu_ctx_entity_time(struct amdgpu_ctx *ctx, struct amdgpu_ctx_entity *centity){ ktime_t res = ns_to_ktime(0); uint32_t i; spin_lock(&ctx->ring_lock); for (i = 0; i < amdgpu_sched_jobs; i++) { res = ktime_add(res, amdgpu_ctx_fence_time(centity->fences[i])); } spin_unlock(&ctx->ring_lock); return res;} static int amdgpu_ctx_init_entity(struct amdgpu_ctx *ctx, u32 hw_ip, const u32 ring){ struct drm_gpu_scheduler **scheds = NULL, *sched = NULL; struct amdgpu_device *adev = ctx->mgr->adev; struct amdgpu_ctx_entity *entity; enum drm_sched_priority drm_prio; unsigned int hw_prio, num_scheds; int32_t ctx_prio; int r; entity = kzalloc_flex(*entity, fences, amdgpu_sched_jobs); if (!entity) return -ENOMEM; ctx_prio = (ctx->override_priority == AMDGPU_CTX_PRIORITY_UNSET) ? ctx->init_priority : ctx->override_priority; entity->hw_ip = hw_ip; entity->sequence = 1; hw_prio = amdgpu_ctx_get_hw_prio(ctx, hw_ip); drm_prio = amdgpu_ctx_to_drm_sched_prio(ctx_prio); hw_ip = array_index_nospec(hw_ip, AMDGPU_HW_IP_NUM); if (!(adev)->xcp_mgr) { scheds = adev->gpu_sched[hw_ip][hw_prio].sched; num_scheds = adev->gpu_sched[hw_ip][hw_prio].num_scheds; } else { struct amdgpu_fpriv *fpriv; /* TODO: Stop using fpriv here, we only need the xcp_id. */ fpriv = container_of(ctx->mgr, struct amdgpu_fpriv, ctx_mgr); r = amdgpu_xcp_select_scheds(adev, hw_ip, hw_prio, fpriv, &num_scheds, &scheds); if (r) goto error_free_entity; } if (num_scheds == 0) { r = -EINVAL; goto error_free_entity; } /* disable load balance if the hw engine retains context among dependent jobs */ if (hw_ip == AMDGPU_HW_IP_VCN_ENC || hw_ip == AMDGPU_HW_IP_VCN_DEC || hw_ip == AMDGPU_HW_IP_UVD_ENC || hw_ip == AMDGPU_HW_IP_UVD) { sched = drm_sched_pick_best(scheds, num_scheds); scheds = &sched; num_scheds = 1; } r = drm_sched_entity_init(&entity->entity, drm_prio, scheds, num_scheds, NULL); if (r) goto error_free_entity; /* It's not an error if we fail to install the new entity */ if (cmpxchg(&ctx->entities[hw_ip][ring], NULL, entity)) goto cleanup_entity; return 0; cleanup_entity: drm_sched_entity_fini(&entity->entity); error_free_entity: kfree(entity); return r;} static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev, struct amdgpu_ctx_entity *entity){ ktime_t res = ns_to_ktime(0); int i; if (!entity) return res; drm_sched_entity_destroy(&entity->entity); for (i = 0; i < amdgpu_sched_jobs; ++i) { res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i])); dma_fence_put(entity->fences[i]); } amdgpu_xcp_release_sched(adev, entity); kfree(entity); return res;} static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev){ switch (amdgpu_dpm_get_performance_level(adev)) { case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD: return AMDGPU_CTX_STABLE_PSTATE_STANDARD; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK: return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK: return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK: return AMDGPU_CTX_STABLE_PSTATE_PEAK; default: return AMDGPU_CTX_STABLE_PSTATE_NONE; }} static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, struct drm_file *filp, struct amdgpu_ctx *ctx){ struct amdgpu_fpriv *fpriv = filp->driver_priv; int r; r = amdgpu_ctx_priority_permit(filp, priority); if (r) return r; memset(ctx, 0, sizeof(*ctx)); kref_init(&ctx->refcount); ctx->mgr = mgr; spin_lock_init(&ctx->ring_lock); ctx->reset_counter = atomic_read(&mgr->adev->gpu_reset_counter); ctx->reset_counter_query = ctx->reset_counter; ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm); ctx->init_priority = priority; ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET; ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; return 0;} static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, u32 stable_pstate){ struct amdgpu_device *adev = ctx->mgr->adev; enum amd_dpm_forced_level level; struct amdgpu_ctx *current_ctx; u32 current_stable_pstate; int r = 0; lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock); switch (stable_pstate) { case AMDGPU_CTX_STABLE_PSTATE_NONE: level = AMD_DPM_FORCED_LEVEL_AUTO; break; case AMDGPU_CTX_STABLE_PSTATE_STANDARD: level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD; break; case AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK: level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK; break; case AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK: level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK; break; case AMDGPU_CTX_STABLE_PSTATE_PEAK: level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; break; default: return -EINVAL; } current_ctx = adev->pm.stable_pstate_ctx; if (current_ctx && current_ctx != ctx) return -EBUSY; current_stable_pstate = amdgpu_get_stable_pstate(adev); if (current_stable_pstate == stable_pstate) return 0; r = amdgpu_dpm_force_performance_level(adev, level); if (r) return r; if (!current_ctx) { adev->pm.stable_pstate_ctx = ctx; /* * Serialized by context taking ownership for the first time * while holding adev->pm.stable_pstate_ctx_lock). */ WRITE_ONCE(ctx->stable_pstate, current_stable_pstate); } return 0;} static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, u32 stable_pstate){ struct amdgpu_device *adev = ctx->mgr->adev; int r; mutex_lock(&adev->pm.stable_pstate_ctx_lock); r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return r;} void amdgpu_ctx_fini(struct kref *ref){ struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount); struct amdgpu_ctx_mgr *mgr = ctx->mgr; struct amdgpu_device *adev = mgr->adev; unsigned i, j, idx; if (!adev) return; for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) { for (j = 0; j < AMDGPU_MAX_ENTITY_NUM; ++j) { ktime_t spend; spend = amdgpu_ctx_fini_entity(adev, ctx->entities[i][j]); atomic64_add(ktime_to_ns(spend), &mgr->time_spend[i]); } } if (drm_dev_enter(adev_to_drm(adev), &idx)) { mutex_lock(&adev->pm.stable_pstate_ctx_lock); if (adev->pm.stable_pstate_ctx == ctx) { __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); adev->pm.stable_pstate_ctx = NULL; } mutex_unlock(&adev->pm.stable_pstate_ctx_lock); drm_dev_exit(idx); } kfree(ctx);} int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance, u32 ring, struct drm_sched_entity **entity){ int r; struct drm_sched_entity *ctx_entity; if (hw_ip >= AMDGPU_HW_IP_NUM) { drm_err(adev_to_drm(ctx->mgr->adev), "unknown HW IP type: %d\n", hw_ip); return -EINVAL; } /* Right now all IPs have only one instance - multiple rings. */ if (instance != 0) { drm_dbg(adev_to_drm(ctx->mgr->adev), "invalid ip instance: %d\n", instance); return -EINVAL; } if (ring >= amdgpu_ctx_num_entities[hw_ip]) { drm_dbg(adev_to_drm(ctx->mgr->adev), "invalid ring: %d %d\n", hw_ip, ring); return -EINVAL; } if (ctx->entities[hw_ip][ring] == NULL) { r = amdgpu_ctx_init_entity(ctx, hw_ip, ring); if (r) return r; } ctx_entity = &ctx->entities[hw_ip][ring]->entity; r = drm_sched_entity_error(ctx_entity); if (r) { DRM_DEBUG("error entity %p\n", ctx_entity); return r; } *entity = ctx_entity; return 0;} static int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, struct drm_file *filp, int32_t priority, uint32_t *id){ struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; struct amdgpu_ctx *ctx; int r; ctx = kmalloc_obj(*ctx); if (!ctx) return -ENOMEM; r = amdgpu_ctx_init(mgr, priority, filp, ctx); if (r) { kfree(ctx); return r;Lines 1–500 of 949. Load more
/* * 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. * * Authors: monk liu <monk.liu@amd.com> */ #include <drm/drm_auth.h> #include <drm/drm_drv.h> #include "amdgpu.h" #include "amdgpu_sched.h" #include "amdgpu_ras.h" #include <linux/nospec.h> #define to_amdgpu_ctx_entity(e) \ container_of((e), struct amdgpu_ctx_entity, entity) const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM] = { [AMDGPU_HW_IP_GFX] = 1, [AMDGPU_HW_IP_COMPUTE] = 4, [AMDGPU_HW_IP_DMA] = 2, [AMDGPU_HW_IP_UVD] = 1, [AMDGPU_HW_IP_VCE] = 1, [AMDGPU_HW_IP_UVD_ENC] = 1, [AMDGPU_HW_IP_VCN_DEC] = 1, [AMDGPU_HW_IP_VCN_ENC] = 1, [AMDGPU_HW_IP_VCN_JPEG] = 1, [AMDGPU_HW_IP_VPE] = 1, }; bool amdgpu_ctx_priority_is_valid(int32_t ctx_prio) { switch (ctx_prio) { case AMDGPU_CTX_PRIORITY_VERY_LOW: case AMDGPU_CTX_PRIORITY_LOW: case AMDGPU_CTX_PRIORITY_NORMAL: case AMDGPU_CTX_PRIORITY_HIGH: case AMDGPU_CTX_PRIORITY_VERY_HIGH: return true; default: case AMDGPU_CTX_PRIORITY_UNSET: /* UNSET priority is not valid and we don't carry that * around, but set it to NORMAL in the only place this * function is called, amdgpu_ctx_ioctl(). */ return false; } } static enum drm_sched_priority amdgpu_ctx_to_drm_sched_prio(int32_t ctx_prio) { switch (ctx_prio) { case AMDGPU_CTX_PRIORITY_UNSET: pr_warn_once("AMD-->DRM context priority value UNSET-->NORMAL"); return DRM_SCHED_PRIORITY_NORMAL; case AMDGPU_CTX_PRIORITY_VERY_LOW: return DRM_SCHED_PRIORITY_LOW; case AMDGPU_CTX_PRIORITY_LOW: return DRM_SCHED_PRIORITY_LOW; case AMDGPU_CTX_PRIORITY_NORMAL: return DRM_SCHED_PRIORITY_NORMAL; case AMDGPU_CTX_PRIORITY_HIGH: return DRM_SCHED_PRIORITY_HIGH; case AMDGPU_CTX_PRIORITY_VERY_HIGH: return DRM_SCHED_PRIORITY_HIGH; /* This should not happen as we sanitized userspace provided priority * already, WARN if this happens. */ default: WARN(1, "Invalid context priority %d\n", ctx_prio); return DRM_SCHED_PRIORITY_NORMAL; } } static int amdgpu_ctx_priority_permit(struct drm_file *filp, int32_t priority) { /* NORMAL and below are accessible by everyone */ if (priority <= AMDGPU_CTX_PRIORITY_NORMAL) return 0; if (capable(CAP_SYS_NICE)) return 0; if (drm_is_current_master(filp)) return 0; return -EACCES; } static enum amdgpu_gfx_pipe_priority amdgpu_ctx_prio_to_gfx_pipe_prio(int32_t prio) { switch (prio) { case AMDGPU_CTX_PRIORITY_HIGH: case AMDGPU_CTX_PRIORITY_VERY_HIGH: return AMDGPU_GFX_PIPE_PRIO_HIGH; default: return AMDGPU_GFX_PIPE_PRIO_NORMAL; } } static enum amdgpu_ring_priority_level amdgpu_ctx_sched_prio_to_ring_prio(int32_t prio) { switch (prio) { case AMDGPU_CTX_PRIORITY_HIGH: return AMDGPU_RING_PRIO_1; case AMDGPU_CTX_PRIORITY_VERY_HIGH: return AMDGPU_RING_PRIO_2; default: return AMDGPU_RING_PRIO_0; } } static unsigned int amdgpu_ctx_get_hw_prio(struct amdgpu_ctx *ctx, u32 hw_ip) { struct amdgpu_device *adev = ctx->mgr->adev; unsigned int hw_prio; int32_t ctx_prio; ctx_prio = (ctx->override_priority == AMDGPU_CTX_PRIORITY_UNSET) ? ctx->init_priority : ctx->override_priority; switch (hw_ip) { case AMDGPU_HW_IP_GFX: case AMDGPU_HW_IP_COMPUTE: hw_prio = amdgpu_ctx_prio_to_gfx_pipe_prio(ctx_prio); break; case AMDGPU_HW_IP_VCE: case AMDGPU_HW_IP_VCN_ENC: hw_prio = amdgpu_ctx_sched_prio_to_ring_prio(ctx_prio); break; default: hw_prio = AMDGPU_RING_PRIO_DEFAULT; break; } hw_ip = array_index_nospec(hw_ip, AMDGPU_HW_IP_NUM); if (adev->gpu_sched[hw_ip][hw_prio].num_scheds == 0) hw_prio = AMDGPU_RING_PRIO_DEFAULT; return hw_prio; } /* Calculate the time spend on the hw */ static ktime_t amdgpu_ctx_fence_time(struct dma_fence *fence) { struct drm_sched_fence *s_fence; if (!fence) return ns_to_ktime(0); /* When the fence is not even scheduled it can't have spend time */ s_fence = to_drm_sched_fence(fence); if (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &s_fence->scheduled.flags)) return ns_to_ktime(0); /* When it is still running account how much already spend */ if (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &s_fence->finished.flags)) return ktime_sub(ktime_get(), s_fence->scheduled.timestamp); return ktime_sub(s_fence->finished.timestamp, s_fence->scheduled.timestamp); } static ktime_t amdgpu_ctx_entity_time(struct amdgpu_ctx *ctx, struct amdgpu_ctx_entity *centity) { ktime_t res = ns_to_ktime(0); uint32_t i; spin_lock(&ctx->ring_lock); for (i = 0; i < amdgpu_sched_jobs; i++) { res = ktime_add(res, amdgpu_ctx_fence_time(centity->fences[i])); } spin_unlock(&ctx->ring_lock); return res; } static int amdgpu_ctx_init_entity(struct amdgpu_ctx *ctx, u32 hw_ip, const u32 ring) { struct drm_gpu_scheduler **scheds = NULL, *sched = NULL; struct amdgpu_device *adev = ctx->mgr->adev; struct amdgpu_ctx_entity *entity; enum drm_sched_priority drm_prio; unsigned int hw_prio, num_scheds; int32_t ctx_prio; int r; entity = kzalloc_flex(*entity, fences, amdgpu_sched_jobs); if (!entity) return -ENOMEM; ctx_prio = (ctx->override_priority == AMDGPU_CTX_PRIORITY_UNSET) ? ctx->init_priority : ctx->override_priority; entity->hw_ip = hw_ip; entity->sequence = 1; hw_prio = amdgpu_ctx_get_hw_prio(ctx, hw_ip); drm_prio = amdgpu_ctx_to_drm_sched_prio(ctx_prio); hw_ip = array_index_nospec(hw_ip, AMDGPU_HW_IP_NUM); if (!(adev)->xcp_mgr) { scheds = adev->gpu_sched[hw_ip][hw_prio].sched; num_scheds = adev->gpu_sched[hw_ip][hw_prio].num_scheds; } else { struct amdgpu_fpriv *fpriv; /* TODO: Stop using fpriv here, we only need the xcp_id. */ fpriv = container_of(ctx->mgr, struct amdgpu_fpriv, ctx_mgr); r = amdgpu_xcp_select_scheds(adev, hw_ip, hw_prio, fpriv, &num_scheds, &scheds); if (r) goto error_free_entity; } if (num_scheds == 0) { r = -EINVAL; goto error_free_entity; } /* disable load balance if the hw engine retains context among dependent jobs */ if (hw_ip == AMDGPU_HW_IP_VCN_ENC || hw_ip == AMDGPU_HW_IP_VCN_DEC || hw_ip == AMDGPU_HW_IP_UVD_ENC || hw_ip == AMDGPU_HW_IP_UVD) { sched = drm_sched_pick_best(scheds, num_scheds); scheds = &sched; num_scheds = 1; } r = drm_sched_entity_init(&entity->entity, drm_prio, scheds, num_scheds, NULL); if (r) goto error_free_entity; /* It's not an error if we fail to install the new entity */ if (cmpxchg(&ctx->entities[hw_ip][ring], NULL, entity)) goto cleanup_entity; return 0; cleanup_entity: drm_sched_entity_fini(&entity->entity); error_free_entity: kfree(entity); return r; } static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev, struct amdgpu_ctx_entity *entity) { ktime_t res = ns_to_ktime(0); int i; if (!entity) return res; drm_sched_entity_destroy(&entity->entity); for (i = 0; i < amdgpu_sched_jobs; ++i) { res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i])); dma_fence_put(entity->fences[i]); } amdgpu_xcp_release_sched(adev, entity); kfree(entity); return res; } static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev) { switch (amdgpu_dpm_get_performance_level(adev)) { case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD: return AMDGPU_CTX_STABLE_PSTATE_STANDARD; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK: return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK: return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK: return AMDGPU_CTX_STABLE_PSTATE_PEAK; default: return AMDGPU_CTX_STABLE_PSTATE_NONE; } } static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, struct drm_file *filp, struct amdgpu_ctx *ctx) { struct amdgpu_fpriv *fpriv = filp->driver_priv; int r; r = amdgpu_ctx_priority_permit(filp, priority); if (r) return r; memset(ctx, 0, sizeof(*ctx)); kref_init(&ctx->refcount); ctx->mgr = mgr; spin_lock_init(&ctx->ring_lock); ctx->reset_counter = atomic_read(&mgr->adev->gpu_reset_counter); ctx->reset_counter_query = ctx->reset_counter; ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm); ctx->init_priority = priority; ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET; ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; return 0; } static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, u32 stable_pstate) { struct amdgpu_device *adev = ctx->mgr->adev; enum amd_dpm_forced_level level; struct amdgpu_ctx *current_ctx; u32 current_stable_pstate; int r = 0; lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock); switch (stable_pstate) { case AMDGPU_CTX_STABLE_PSTATE_NONE: level = AMD_DPM_FORCED_LEVEL_AUTO; break; case AMDGPU_CTX_STABLE_PSTATE_STANDARD: level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD; break; case AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK: level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK; break; case AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK: level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK; break; case AMDGPU_CTX_STABLE_PSTATE_PEAK: level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; break; default: return -EINVAL; } current_ctx = adev->pm.stable_pstate_ctx; if (current_ctx && current_ctx != ctx) return -EBUSY; current_stable_pstate = amdgpu_get_stable_pstate(adev); if (current_stable_pstate == stable_pstate) return 0; r = amdgpu_dpm_force_performance_level(adev, level); if (r) return r; if (!current_ctx) { adev->pm.stable_pstate_ctx = ctx; /* * Serialized by context taking ownership for the first time * while holding adev->pm.stable_pstate_ctx_lock). */ WRITE_ONCE(ctx->stable_pstate, current_stable_pstate); } return 0; } static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, u32 stable_pstate) { struct amdgpu_device *adev = ctx->mgr->adev; int r; mutex_lock(&adev->pm.stable_pstate_ctx_lock); r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return r; } void amdgpu_ctx_fini(struct kref *ref) { struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount); struct amdgpu_ctx_mgr *mgr = ctx->mgr; struct amdgpu_device *adev = mgr->adev; unsigned i, j, idx; if (!adev) return; for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) { for (j = 0; j < AMDGPU_MAX_ENTITY_NUM; ++j) { ktime_t spend; spend = amdgpu_ctx_fini_entity(adev, ctx->entities[i][j]); atomic64_add(ktime_to_ns(spend), &mgr->time_spend[i]); } } if (drm_dev_enter(adev_to_drm(adev), &idx)) { mutex_lock(&adev->pm.stable_pstate_ctx_lock); if (adev->pm.stable_pstate_ctx == ctx) { __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); adev->pm.stable_pstate_ctx = NULL; } mutex_unlock(&adev->pm.stable_pstate_ctx_lock); drm_dev_exit(idx); } kfree(ctx); } int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance, u32 ring, struct drm_sched_entity **entity) { int r; struct drm_sched_entity *ctx_entity; if (hw_ip >= AMDGPU_HW_IP_NUM) { drm_err(adev_to_drm(ctx->mgr->adev), "unknown HW IP type: %d\n", hw_ip); return -EINVAL; } /* Right now all IPs have only one instance - multiple rings. */ if (instance != 0) { drm_dbg(adev_to_drm(ctx->mgr->adev), "invalid ip instance: %d\n", instance); return -EINVAL; } if (ring >= amdgpu_ctx_num_entities[hw_ip]) { drm_dbg(adev_to_drm(ctx->mgr->adev), "invalid ring: %d %d\n", hw_ip, ring); return -EINVAL; } if (ctx->entities[hw_ip][ring] == NULL) { r = amdgpu_ctx_init_entity(ctx, hw_ip, ring); if (r) return r; } ctx_entity = &ctx->entities[hw_ip][ring]->entity; r = drm_sched_entity_error(ctx_entity); if (r) { DRM_DEBUG("error entity %p\n", ctx_entity); return r; } *entity = ctx_entity; return 0; } static int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, struct drm_file *filp, int32_t priority, uint32_t *id) { struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; struct amdgpu_ctx *ctx; int r; ctx = kmalloc_obj(*ctx); if (!ctx) return -ENOMEM; r = amdgpu_ctx_init(mgr, priority, filp, ctx); if (r) { kfree(ctx); return r;