staticinline enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type)
{ if (type == KFD_QUEUE_TYPE_SDMA || type == KFD_QUEUE_TYPE_SDMA_XGMI) return KFD_MQD_TYPE_SDMA; return KFD_MQD_TYPE_CP;
}
staticbool is_pipe_enabled(struct device_queue_manager *dqm, int mec, int pipe)
{ int i; int pipe_offset = (mec * dqm->dev->kfd->shared_resources.num_pipe_per_mec
+ pipe) * dqm->dev->kfd->shared_resources.num_queue_per_pipe;
/* queue is available for KFD usage if bit is 1 */ for (i = 0; i < dqm->dev->kfd->shared_resources.num_queue_per_pipe; ++i) if (test_bit(pipe_offset + i,
dqm->dev->kfd->shared_resources.cp_queue_bitmap)) returntrue; returnfalse;
}
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->add_hw_queue(&adev->mes, &queue_input);
amdgpu_mes_unlock(&adev->mes);
up_read(&adev->reset_domain->sem); if (r) {
dev_err(adev->dev, "failed to add hardware queue to MES, doorbell=0x%x\n",
q->properties.doorbell_off);
dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
kfd_hws_hang(dqm);
}
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input);
amdgpu_mes_unlock(&adev->mes);
up_read(&adev->reset_domain->sem);
if (r) {
dev_err(adev->dev, "failed to remove hardware queue from MES, doorbell=0x%x\n",
q->properties.doorbell_off);
dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
kfd_hws_hang(dqm);
}
list_for_each_entry(cur, &dqm->queues, list) {
qpd = cur->qpd;
list_for_each_entry(q, &qpd->queues_list, list) { if (!q->properties.is_active) continue;
retval = add_queue_mes(dqm, q, qpd); if (retval) {
dev_err(dev, "%s: Failed to add queue %d for dev %d",
__func__,
q->properties.queue_id,
dqm->dev->id); return retval;
}
}
}
return retval;
}
staticint suspend_all_queues_mes(struct device_queue_manager *dqm)
{ struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; int r = 0;
if (!down_read_trylock(&adev->reset_domain->sem)) return -EIO;
r = amdgpu_mes_suspend(adev);
up_read(&adev->reset_domain->sem);
if (r) {
dev_err(adev->dev, "failed to suspend gangs from MES\n");
dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
kfd_hws_hang(dqm);
}
return r;
}
staticint resume_all_queues_mes(struct device_queue_manager *dqm)
{ struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; int r = 0;
if (!down_read_trylock(&adev->reset_domain->sem)) return -EIO;
r = amdgpu_mes_resume(adev);
up_read(&adev->reset_domain->sem);
if (r) {
dev_err(adev->dev, "failed to resume gangs from MES\n");
dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
kfd_hws_hang(dqm);
}
if (restore_id && *restore_id != valid_id) return -EINVAL;
q->doorbell_id = valid_id;
} else { /* For CP queues on SOC15 */ if (restore_id) { /* make sure that ID is free */ if (__test_and_set_bit(*restore_id, qpd->doorbell_bitmap)) return -EINVAL;
q->doorbell_id = *restore_id;
} else { /* or reserve a free doorbell ID */ unsignedint found;
if (KFD_IS_SOC15(dqm->dev) && dqm->dev->kfd->cwsr_enabled)
program_trap_handler_settings(dqm, qpd);
/* qpd->page_table_base is set earlier when register_process() *iscalled,i.e.whenthefirstqueueiscreated.
*/
dqm->dev->kfd2kgd->set_vm_context_page_table_base(dqm->dev->adev,
qpd->vmid,
qpd->page_table_base); /* invalidate the VM context after pasid and vmid mapping is set up */
kfd_flush_tlb(qpd_to_pdd(qpd), TLB_FLUSH_LEGACY);
if (dqm->dev->kfd2kgd->set_scratch_backing_va)
dqm->dev->kfd2kgd->set_scratch_backing_va(dqm->dev->adev,
qpd->sh_hidden_private_base, qpd->vmid);
/* On GFX v7, CP doesn't flush TC at dequeue */ if (q->device->adev->asic_type == CHIP_HAWAII) if (flush_texture_cache_nocpsch(q->device, qpd))
dev_err(dev, "Failed to flush TC\n");
if (q->properties.is_active) { if (!dqm->sched_running) {
WARN_ONCE(1, "Load non-HWS mqd while stopped\n"); goto add_queue_to_list;
}
if (WARN(q->process->mm != current->mm, "should only run in user thread"))
retval = -EFAULT; else
retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe,
q->queue, &q->properties, current->mm); if (retval) goto out_free_mqd;
}
add_queue_to_list:
list_add(&q->list, &qpd->queues_list);
qpd->queue_count++; if (q->properties.is_active)
increment_queue_count(dqm, qpd, q);
/* *Unconditionallyincrementthiscounter,regardlessofthequeue's *typeorwhetherthequeueisactive.
*/
dqm->total_queue_count++;
pr_debug("Total of %d queues are accountable so far\n",
dqm->total_queue_count); goto out_unlock;
/* Access to DQM has to be locked before calling destroy_queue_nocpsch_locked *toavoidasynchronizedaccess
*/ staticint destroy_queue_nocpsch_locked(struct device_queue_manager *dqm, struct qcm_process_device *qpd, struct queue *q)
{ int retval; struct mqd_manager *mqd_mgr;
list_del(&q->list); if (list_empty(&qpd->queues_list)) { if (qpd->reset_wavefronts) {
pr_warn("Resetting wave fronts (nocpsch) on dev %p\n",
dqm->dev); /* dbgdev_wave_reset_wavefronts has to be called before *deallocate_vmid(),i.e.whenvmidisstillinuse.
*/
dbgdev_wave_reset_wavefronts(dqm->dev,
qpd->pqm->process);
qpd->reset_wavefronts = false;
}
deallocate_vmid(dqm, qpd, q);
}
qpd->queue_count--; if (q->properties.is_active)
decrement_queue_count(dqm, qpd, q);
/* Save previous activity state for counters */
prev_active = q->properties.is_active;
/* Make sure the queue is unmapped before updating the MQD */ if (dqm->sched_policy != KFD_SCHED_POLICY_NO_HWS) { if (!dqm->dev->kfd->shared_resources.enable_mes)
retval = unmap_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD, false); elseif (prev_active)
retval = remove_queue_mes(dqm, q, &pdd->qpd);
/* queue is reset so inaccessable */ if (pdd->has_reset_queue) {
retval = -EACCES; goto out_unlock;
}
if (is_new || q->properties.is_being_destroyed) {
pr_debug("Suspend: skip %s queue id %i\n",
is_new ? "new" : "destroyed",
q->properties.queue_id); return -EBUSY;
}
q->properties.is_suspended = true; if (q->properties.is_active) { if (dqm->dev->kfd->shared_resources.enable_mes) { int r = remove_queue_mes(dqm, q, &pdd->qpd);
dqm_lock(dqm); if (qpd->evicted++ > 0) /* already evicted, do nothing */ goto out;
pdd = qpd_to_pdd(qpd);
pr_debug_ratelimited("Evicting process pid %d queues\n",
pdd->process->lead_thread->pid);
pdd->last_evict_timestamp = get_jiffies_64(); /* Mark all queues as evicted. Deactivate all active queues on *theqpd.
*/
list_for_each_entry(q, &qpd->queues_list, list) {
q->properties.is_evicted = true; if (!q->properties.is_active) continue;
dqm_lock(dqm); if (qpd->evicted++ > 0) /* already evicted, do nothing */ goto out;
pdd = qpd_to_pdd(qpd);
/* The debugger creates processes that temporarily have not acquired *allVMsforalldevicesandhasnoVMsitself. *Skipqueueevictiononprocesseviction.
*/ if (!pdd->drm_priv) goto out;
pr_debug_ratelimited("Evicting process pid %d queues\n",
pdd->process->lead_thread->pid);
if (dqm->dev->kfd->shared_resources.enable_mes) {
pdd->last_evict_timestamp = get_jiffies_64();
retval = suspend_all_queues_mes(dqm); if (retval) {
dev_err(dev, "Suspending all queues failed"); goto out;
}
}
/* Mark all queues as evicted. Deactivate all active queues on *theqpd.
*/
list_for_each_entry(q, &qpd->queues_list, list) {
q->properties.is_evicted = true; if (!q->properties.is_active) continue;
dqm_lock(dqm); if (WARN_ON_ONCE(!qpd->evicted)) /* already restored, do nothing */ goto out; if (qpd->evicted > 1) { /* ref count still > 0, decrement & quit */
qpd->evicted--; goto out;
}
pr_debug_ratelimited("Restoring process pid %d queues\n",
pdd->process->lead_thread->pid);
/* Update PD Base in QPD */
qpd->page_table_base = pd_base;
pr_debug("Updated PD address to 0x%llx\n", pd_base);
if (!list_empty(&qpd->queues_list)) {
dqm->dev->kfd2kgd->set_vm_context_page_table_base(
dqm->dev->adev,
qpd->vmid,
qpd->page_table_base);
kfd_flush_tlb(pdd, TLB_FLUSH_LEGACY);
}
/* Take a safe reference to the mm_struct, which may otherwise *disappearevenwhilethekfd_processisstillreferenced.
*/
mm = get_task_mm(pdd->process->lead_thread); if (!mm) {
ret = -EFAULT; goto out;
}
/* Remove the eviction flags. Activate queues that are not *inactiveforotherreasons.
*/
list_for_each_entry(q, &qpd->queues_list, list) {
q->properties.is_evicted = false; if (!QUEUE_IS_ACTIVE(q->properties)) continue;
dqm_lock(dqm); if (WARN_ON_ONCE(!qpd->evicted)) /* already restored, do nothing */ goto out; if (qpd->evicted > 1) { /* ref count still > 0, decrement & quit */
qpd->evicted--; goto out;
}
/* The debugger creates processes that temporarily have not acquired *allVMsforalldevicesandhasnoVMsitself. *Skipqueuerestoreonprocessrestore.
*/ if (!pdd->drm_priv) goto vm_not_acquired;
pr_debug_ratelimited("Restoring process pid %d queues\n",
pdd->process->lead_thread->pid);
/* Update PD Base in QPD */
qpd->page_table_base = amdgpu_amdkfd_gpuvm_get_process_page_dir(pdd->drm_priv);
pr_debug("Updated PD address to 0x%llx\n", qpd->page_table_base);
/* activate all active queues on the qpd */
list_for_each_entry(q, &qpd->queues_list, list) {
q->properties.is_evicted = false; if (!QUEUE_IS_ACTIVE(q->properties)) continue;
/* Update PD Base in QPD */
qpd->page_table_base = pd_base;
pr_debug("Updated PD address to 0x%llx\n", pd_base);
retval = dqm->asic_ops.update_qpd(dqm, qpd);
dqm->processes_count++;
dqm_unlock(dqm);
/* Outside the DQM lock because under the DQM lock we can't do *reclaimortakeotherlocksthatothersholdwhilereclaiming.
*/
kfd_inc_compute_active(dqm->dev);
pr_debug("qpd->queues_list is %s\n",
list_empty(&qpd->queues_list) ? "empty" : "not empty");
retval = 0;
dqm_lock(dqm);
list_for_each_entry_safe(cur, next, &dqm->queues, list) { if (qpd == cur->qpd) {
list_del(&cur->list);
kfree(cur);
dqm->processes_count--; goto out;
}
} /* qpd not found in dqm list */
retval = 1;
out:
dqm_unlock(dqm);
/* Outside the DQM lock because under the DQM lock we can't do *reclaimortakeotherlocksthatothersholdwhilereclaiming.
*/ if (!retval)
kfd_dec_compute_active(dqm->dev);
if (q->properties.type == KFD_QUEUE_TYPE_SDMA) { if (bitmap_empty(dqm->sdma_bitmap, get_num_sdma_queues(dqm))) {
dev_warn(dev, "No more SDMA queue to allocate (%d total queues)\n",
get_num_sdma_queues(dqm)); return -ENOMEM;
}
if (restore_sdma_id) { /* Re-use existing sdma_id */ if (!test_bit(*restore_sdma_id, dqm->sdma_bitmap)) {
dev_err(dev, "SDMA queue already in use\n"); return -EBUSY;
}
clear_bit(*restore_sdma_id, dqm->sdma_bitmap);
q->sdma_id = *restore_sdma_id;
} else { /* Find first available sdma_id */
bit = find_first_bit(dqm->sdma_bitmap,
get_num_sdma_queues(dqm));
clear_bit(bit, dqm->sdma_bitmap);
q->sdma_id = bit;
}
q->properties.sdma_engine_id =
q->sdma_id % kfd_get_num_sdma_engines(dqm->dev);
q->properties.sdma_queue_id = q->sdma_id /
kfd_get_num_sdma_engines(dqm->dev);
} elseif (q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI) { if (bitmap_empty(dqm->xgmi_sdma_bitmap, get_num_xgmi_sdma_queues(dqm))) {
dev_warn(dev, "No more XGMI SDMA queue to allocate (%d total queues)\n",
get_num_xgmi_sdma_queues(dqm)); return -ENOMEM;
} if (restore_sdma_id) { /* Re-use existing sdma_id */ if (!test_bit(*restore_sdma_id, dqm->xgmi_sdma_bitmap)) {
dev_err(dev, "SDMA queue already in use\n"); return -EBUSY;
}
clear_bit(*restore_sdma_id, dqm->xgmi_sdma_bitmap);
q->sdma_id = *restore_sdma_id;
} else {
bit = find_first_bit(dqm->xgmi_sdma_bitmap,
get_num_xgmi_sdma_queues(dqm));
clear_bit(bit, dqm->xgmi_sdma_bitmap);
q->sdma_id = bit;
} /* sdma_engine_id is sdma id including *bothPCIe-optimizedSDMAsandXGMI- *optimizedSDMAs.Thecalculationbelow *assumesthefirstNenginesarealways *PCIe-optimizedones
*/
q->properties.sdma_engine_id =
kfd_get_num_sdma_engines(dqm->dev) +
q->sdma_id % kfd_get_num_xgmi_sdma_engines(dqm->dev);
q->properties.sdma_queue_id = q->sdma_id /
kfd_get_num_xgmi_sdma_engines(dqm->dev);
} elseif (q->properties.type == KFD_QUEUE_TYPE_SDMA_BY_ENG_ID) { int i, num_queues, num_engines, eng_offset = 0, start_engine; bool free_bit_found = false, is_xgmi = false;
/* Scan available bit based on target engine ID. */
start_engine = q->properties.sdma_engine_id - eng_offset; for (i = start_engine; i < num_queues; i += num_engines) {
if (!test_bit(i, is_xgmi ? dqm->xgmi_sdma_bitmap : dqm->sdma_bitmap)) continue;
if (!free_bit_found) {
dev_warn(dev, "No more SDMA queue to allocate for target ID %i (%d total queues)\n",
q->properties.sdma_engine_id, num_queues); return -ENOMEM;
}
}
staticint set_sched_resources(struct device_queue_manager *dqm)
{ int i, mec; struct scheduling_resources res; struct device *dev = dqm->dev->adev->dev;
res.vmid_mask = dqm->dev->compute_vmid_bitmap;
res.queue_mask = 0; for (i = 0; i < AMDGPU_MAX_QUEUES; ++i) {
mec = (i / dqm->dev->kfd->shared_resources.num_queue_per_pipe)
/ dqm->dev->kfd->shared_resources.num_pipe_per_mec;
if (!test_bit(i, dqm->dev->kfd->shared_resources.cp_queue_bitmap)) continue;
/* only acquire queues from the first MEC */ if (mec > 0) continue;
/* This situation may be hit in the future if a new HW *generationexposesmorethan64queues.Ifso,the *definitionofres.queue_maskneedsupdating
*/ if (WARN_ON(i >= (sizeof(res.queue_mask)*8))) {
dev_err(dev, "Invalid queue enabled by amdgpu: %d\n", i); break;
}
/* halt_cpsch: *Unmapqueuessothescheduledoesn'tcontinueremainingjobsinthequeue. *Thensetdqm->sched_haltsoqueuesdon'tmaptorunlistuntilunhalt_cpsch *iscalled.
*/ staticint halt_cpsch(struct device_queue_manager *dqm)
{ int ret = 0;
dqm_lock(dqm); if (!dqm->sched_running) {
dqm_unlock(dqm); return0;
}
WARN_ONCE(dqm->sched_halt, "Scheduling is already on halt\n");
if (!dqm->is_hws_hang) { if (!dqm->dev->kfd->shared_resources.enable_mes)
ret = unmap_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES, 0,
USE_DEFAULT_GRACE_PERIOD, false); else
ret = remove_all_kfd_queues_mes(dqm);
}
dqm->sched_halt = true;
dqm_unlock(dqm);
return ret;
}
/* unhalt_cpsch *Unsetdqm->sched_haltandmapqueuesbacktorunlist
*/ staticint unhalt_cpsch(struct device_queue_manager *dqm)
{ int ret = 0;
dqm_lock(dqm); if (!dqm->sched_running || !dqm->sched_halt) {
WARN_ONCE(!dqm->sched_halt, "Scheduling is not on halt.\n");
dqm_unlock(dqm); return0;
}
dqm->sched_halt = false; if (!dqm->dev->kfd->shared_resources.enable_mes)
ret = execute_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD); else
ret = add_all_kfd_queues_mes(dqm);
if (!dqm->dev->kfd->shared_resources.enable_mes)
unmap_queues_cpsch(dqm, KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD, false); else
remove_all_kfd_queues_mes(dqm);
dqm->sched_running = false;
if (!dqm->dev->kfd->shared_resources.enable_mes)
pm_release_ib(&dqm->packet_mgr);
kfd_gtt_sa_free(dqm->dev, dqm->fence_mem); if (!dqm->dev->kfd->shared_resources.enable_mes)
pm_uninit(&dqm->packet_mgr);
kfree(dqm->detect_hang_info);
dqm->detect_hang_info = NULL;
dqm_unlock(dqm);
return0;
}
staticint create_kernel_queue_cpsch(struct device_queue_manager *dqm, struct kernel_queue *kq, struct qcm_process_device *qpd)
{
dqm_lock(dqm); if (dqm->total_queue_count >= max_num_of_queues_per_device) {
pr_warn("Can't create new kernel queue because %d queues were already created\n",
dqm->total_queue_count);
dqm_unlock(dqm); return -EPERM;
}
/* *Unconditionallyincrementthiscounter,regardlessofthequeue's *typeorwhetherthequeueisactive.
*/
dqm->total_queue_count++;
pr_debug("Total of %d queues are accountable so far\n",
dqm->total_queue_count);
while (*fence_addr != fence_value) { /* Fatal err detected, this response won't come */ if (amdgpu_amdkfd_is_fed(dqm->dev->adev)) return -EIO;
if (time_after(jiffies, end_jiffies)) {
dev_err(dev, "qcm fence wait loop timeout expired\n"); /* In HWS case, this is used to halt the driver thread *inordernottomessupCPstatesbeforedoing *scandumpsforFWdebugging.
*/ while (halt_if_hws_hang)
schedule();
return -ETIME;
}
schedule();
}
return0;
}
/* dqm->lock mutex has to be locked before calling this function */ staticint map_queues_cpsch(struct device_queue_manager *dqm)
{ struct device *dev = dqm->dev->adev->dev; int retval;
if (!dqm->sched_running || dqm->sched_halt) return0; if (dqm->active_queue_count <= 0 || dqm->processes_count <= 0) return0; if (dqm->active_runlist) return0;
retval = pm_send_runlist(&dqm->packet_mgr, &dqm->queues);
pr_debug("%s sent runlist\n", __func__); if (retval) {
dev_err(dev, "failed to execute runlist\n"); return retval;
}
dqm->active_runlist = true;
/* either reset failed or we reset an unexpected queue. */ if (queue_addr != q->properties.queue_address) {
r = -ENOTRECOVERABLE; goto reset_fail;
}
set_queue_as_reset(dqm, q, &pdd->qpd);
reset_count++;
}
if (reset_count == dqm->detect_hang_count)
kfd_signal_reset_event(dqm->dev); else
r = -ENOTRECOVERABLE;
reset_fail:
dqm->detect_hang_count = 0;
return r;
}
staticbool sdma_has_hang(struct device_queue_manager *dqm)
{ int engine_start = dqm->dev->node_id * get_num_all_sdma_engines(dqm); int engine_end = engine_start + get_num_all_sdma_engines(dqm); int num_queues_per_eng = dqm->dev->kfd->device_info.num_sdma_queues_per_engine; int i, j;
for (i = engine_start; i < engine_end; i++) { for (j = 0; j < num_queues_per_eng; j++) { if (!dqm->dev->kfd2kgd->hqd_sdma_get_doorbell(dqm->dev->adev, i, j)) continue;
staticint reset_hung_queues_sdma(struct device_queue_manager *dqm)
{ int engine_start = dqm->dev->node_id * get_num_all_sdma_engines(dqm); int engine_end = engine_start + get_num_all_sdma_engines(dqm); int num_queues_per_eng = dqm->dev->kfd->device_info.num_sdma_queues_per_engine; int r = 0, i, j;
if (dqm->is_hws_hang) return -EIO;
/* Scan for hung HW queues and reset engine. */
dqm->detect_hang_count = 0; for (i = engine_start; i < engine_end; i++) { for (j = 0; j < num_queues_per_eng; j++) {
uint32_t doorbell_off =
dqm->dev->kfd2kgd->hqd_sdma_get_doorbell(dqm->dev->adev, i, j);
if (!doorbell_off) continue;
/* Reset engine and check. */ if (amdgpu_sdma_reset_engine(dqm->dev->adev, i, false) ||
dqm->dev->kfd2kgd->hqd_sdma_get_doorbell(dqm->dev->adev, i, j) ||
!set_sdma_queue_as_reset(dqm, doorbell_off)) {
r = -ENOTRECOVERABLE; goto reset_fail;
}
/* Should only expect one queue active per engine */
dqm->detect_hang_count++; break;
}
}
/* Signal process reset */ if (dqm->detect_hang_count)
kfd_signal_reset_event(dqm->dev); else
r = -ENOTRECOVERABLE;
/* dqm->lock mutex has to be locked before calling this function * *@grace_period:IfUSE_DEFAULT_GRACE_PERIODthendefaultwaittime *forcontextswitchlatency.Lowervaluesareusedbydebugger *sincecontextswitchingaretriggeredathighfrequency. *ThisisconfiguredbysettingCP_IQ_WAIT_TIME2.SCH_WAVE *
*/ staticint unmap_queues_cpsch(struct device_queue_manager *dqm, enum kfd_unmap_queues_filter filter,
uint32_t filter_param,
uint32_t grace_period, bool reset)
{ struct device *dev = dqm->dev->adev->dev; struct mqd_manager *mqd_mgr; int retval;
if (!dqm->sched_running) return0; if (!dqm->active_runlist) return0; if (!down_read_trylock(&dqm->dev->adev->reset_domain->sem)) return -EIO;
if (grace_period != USE_DEFAULT_GRACE_PERIOD) {
retval = pm_config_dequeue_wait_counts(&dqm->packet_mgr,
KFD_DEQUEUE_WAIT_SET_SCH_WAVE, grace_period); if (retval) goto out;
}
retval = pm_send_unmap_queue(&dqm->packet_mgr, filter, filter_param, reset); if (retval) goto out;
*dqm->fence_addr = KFD_FENCE_INIT;
mb();
pm_send_query_status(&dqm->packet_mgr, dqm->fence_gpu_addr,
KFD_FENCE_COMPLETED); /* should be timed out */
retval = amdkfd_fence_wait_timeout(dqm, KFD_FENCE_COMPLETED,
queue_preemption_timeout_ms); if (retval) {
dev_err(dev, "The cp might be in an unrecoverable state due to an unsuccessful queues preemption\n");
kfd_hws_hang(dqm); goto out;
}
/* In the current MEC firmware implementation, if compute queue *doesn'tresponsetothepreemptionrequestintime,HIQwill *abandontheunmaprequestwithoutreturninganytimeouterror *todriver.Instead,MECfirmwarewilllogthedoorbellofthe *unrespondingcomputequeuetoHIQ.MQD.queue_doorbell_idfields. *Tomakesurethequeueunmapwassuccessful,driverneedto *checkthosefields
*/
mqd_mgr = dqm->mqd_mgrs[KFD_MQD_TYPE_HIQ]; if (mqd_mgr->check_preemption_failed(mqd_mgr, dqm->packet_mgr.priv_queue->queue->mqd) &&
reset_queues_on_hws_hang(dqm, false)) goto reset_fail;
/* Check for SDMA hang and attempt SDMA reset */ if (sdma_has_hang(dqm) && reset_queues_on_hws_hang(dqm, true)) goto reset_fail;
/* We need to reset the grace period value for this device */ if (grace_period != USE_DEFAULT_GRACE_PERIOD) { if (pm_config_dequeue_wait_counts(&dqm->packet_mgr,
KFD_DEQUEUE_WAIT_RESET, 0/* unused */))
dev_err(dev, "Failed to reset grace period\n");
}
/* dqm->lock mutex has to be locked before calling this function */ staticint execute_queues_cpsch(struct device_queue_manager *dqm, enum kfd_unmap_queues_filter filter,
uint32_t filter_param,
uint32_t grace_period)
{ int retval;
if (!down_read_trylock(&dqm->dev->adev->reset_domain->sem)) return -EIO;
retval = unmap_queues_cpsch(dqm, filter, filter_param, grace_period, false); if (!retval)
retval = map_queues_cpsch(dqm);
up_read(&dqm->dev->adev->reset_domain->sem); return retval;
}
staticint wait_on_destroy_queue(struct device_queue_manager *dqm,
struct queue *q)
{
struct kfd_process_device *pdd = kfd_get_process_device_data(q->device,
q->process); int ret = 0;
if (WARN_ON(!pdd)) return ret;
if (pdd->qpd.is_debug) return ret;
q->properties.is_being_destroyed = true;
if (pdd->process->debug_trap_enabled && q->properties.is_suspended) {
dqm_unlock(dqm);
mutex_unlock(&q->process->mutex);
ret = wait_event_interruptible(dqm->destroy_wait,
!q->properties.is_suspended);
if (q->properties.is_active) {
decrement_queue_count(dqm, qpd, q);
q->properties.is_active = false; if (!dqm->dev->kfd->shared_resources.enable_mes) {
retval = execute_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0,
USE_DEFAULT_GRACE_PERIOD); if (retval == -ETIME)
qpd->reset_wavefronts = true;
} else {
retval = remove_queue_mes(dqm, q, qpd);
}
}
list_del(&q->list);
qpd->queue_count--;
/* *Unconditionallydecrementthiscounter,regardlessofthequeue's *type
*/
dqm->total_queue_count--;
pr_debug("Total of %d queues are accountable so far\n",
dqm->total_queue_count);
/* Unregister process */
list_for_each_entry_safe(cur, next_dpn, &dqm->queues, list) { if (qpd == cur->qpd) {
list_del(&cur->list);
kfree(cur);
dqm->processes_count--;
found = true; break;
}
}
dqm_unlock(dqm);
/* Outside the DQM lock because under the DQM lock we can't do *reclaimortakeotherlocksthatothersholdwhilereclaiming.
*/ if (found)
kfd_dec_compute_active(dqm->dev);
/* Clear all user mode queues */
list_for_each_entry(q, &qpd->queues_list, list) { if (q->properties.type == KFD_QUEUE_TYPE_SDMA)
deallocate_sdma_queue(dqm, q); elseif (q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)
deallocate_sdma_queue(dqm, q);
if (q->properties.is_active) {
decrement_queue_count(dqm, qpd, q);
if (dqm->dev->kfd->shared_resources.enable_mes) {
retval = remove_queue_mes(dqm, q, qpd); if (retval)
dev_err(dev, "Failed to remove queue %d\n",
q->properties.queue_id);
}
}
dqm->total_queue_count--;
}
/* Unregister process */
list_for_each_entry_safe(cur, next_dpn, &dqm->queues, list) { if (qpd == cur->qpd) {
list_del(&cur->list);
kfree(cur);
dqm->processes_count--;
found = true; break;
}
}
if (!dqm->dev->kfd->shared_resources.enable_mes)
retval = execute_queues_cpsch(dqm, filter, 0, USE_DEFAULT_GRACE_PERIOD);
if ((retval || qpd->reset_wavefronts) &&
down_read_trylock(&dqm->dev->adev->reset_domain->sem)) {
pr_warn("Resetting wave fronts (cpsch) on dev %p\n", dqm->dev);
dbgdev_wave_reset_wavefronts(dqm->dev, qpd->pqm->process);
qpd->reset_wavefronts = false;
up_read(&dqm->dev->adev->reset_domain->sem);
}
/* Outside the DQM lock because under the DQM lock we can't do *reclaimortakeotherlocksthatothersholdwhilereclaiming.
*/ if (found)
kfd_dec_compute_active(dqm->dev);
return retval;
}
staticint init_mqd_managers(struct device_queue_manager *dqm)
{ int i, j;
struct device *dev = dqm->dev->adev->dev;
struct mqd_manager *mqd_mgr;
for (i = 0; i < KFD_MQD_TYPE_MAX; i++) {
mqd_mgr = dqm->asic_ops.mqd_manager_init(i, dqm->dev); if (!mqd_mgr) {
dev_err(dev, "mqd manager [%d] initialization failed\n", i); goto out_free;
}
dqm->mqd_mgrs[i] = mqd_mgr;
}
dqm = kzalloc(sizeof(*dqm), GFP_KERNEL); if (!dqm) returnNULL;
switch (dev->adev->asic_type) { /* HWS is not available on Hawaii. */ case CHIP_HAWAII: /* HWS depends on CWSR for timely dequeue. CWSR is not *availableonTonga. * *FIXME:ThisargumentalsoappliestoKaveri.
*/ case CHIP_TONGA:
dqm->sched_policy = KFD_SCHED_POLICY_NO_HWS; break; default:
dqm->sched_policy = sched_policy; break;
}
switch (dev->adev->asic_type) { case CHIP_KAVERI: case CHIP_HAWAII:
device_queue_manager_init_cik(&dqm->asic_ops); break;
case CHIP_CARRIZO: case CHIP_TONGA: case CHIP_FIJI: case CHIP_POLARIS10: case CHIP_POLARIS11: case CHIP_POLARIS12: case CHIP_VEGAM:
device_queue_manager_init_vi(&dqm->asic_ops); break;
dqm->dev->kfd->shared_resources.compute_vmid_bitmap = updated_vmid_mask;
dqm->trap_debug_vmid = dqm->dev->vm_info.last_vmid_kfd;
r = set_sched_resources(dqm); if (r) goto out_unlock;
r = map_queues_cpsch(dqm); if (r) goto out_unlock;
pr_debug("Reserved VMID for trap debug: %i\n", dqm->trap_debug_vmid);
out_unlock:
dqm_unlock(dqm); return r;
}
/* *Releasesvmidforthetrapdebugger
*/ int release_debug_trap_vmid(struct device_queue_manager *dqm,
struct qcm_process_device *qpd)
{
struct device *dev = dqm->dev->adev->dev; int r; int updated_vmid_mask;
uint32_t trap_debug_vmid;
if (dqm->sched_policy == KFD_SCHED_POLICY_NO_HWS) {
dev_err(dev, "Unsupported on sched_policy: %i\n", dqm->sched_policy); return -EINVAL;
}
dqm_lock(dqm);
trap_debug_vmid = dqm->trap_debug_vmid; if (dqm->trap_debug_vmid == 0) {
dev_err(dev, "Trap debug id is not reserved\n");
r = -EINVAL; goto out_unlock;
}
r = unmap_queues_cpsch(dqm, KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES, 0,
USE_DEFAULT_GRACE_PERIOD, false); if (r) goto out_unlock;
int resume_queues(struct kfd_process *p,
uint32_t num_queues,
uint32_t *usr_queue_id_array)
{
uint32_t *queue_ids = NULL; int total_resumed = 0; int i;
if (usr_queue_id_array) {
queue_ids = get_queue_ids(num_queues, usr_queue_id_array);
if (IS_ERR(queue_ids)) return PTR_ERR(queue_ids);
/* mask all queues as invalid. unmask per successful request */
q_array_invalidate(num_queues, queue_ids);
}
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
struct device_queue_manager *dqm = pdd->dev->dqm;
struct device *dev = dqm->dev->adev->dev;
struct qcm_process_device *qpd = &pdd->qpd;
struct queue *q; int r, per_device_resumed = 0;
dqm_lock(dqm);
/* unmask queues that resume or already resumed as valid */
list_for_each_entry(q, &qpd->queues_list, list) { int q_idx = QUEUE_NOT_FOUND;
if (queue_ids)
q_idx = q_array_get_index(
q->properties.queue_id,
num_queues,
queue_ids);
if (!queue_ids || q_idx != QUEUE_NOT_FOUND) { int err = resume_single_queue(dqm, &pdd->qpd, q);
if (queue_ids) { if (!err) {
queue_ids[q_idx] &=
~KFD_DBG_QUEUE_INVALID_MASK;
} else {
queue_ids[q_idx] |=
KFD_DBG_QUEUE_ERROR_MASK; break;
}
}
if (dqm->dev->kfd->shared_resources.enable_mes) {
wake_up_all(&dqm->destroy_wait); if (!err)
total_resumed++;
} else {
per_device_resumed++;
}
}
}
if (!per_device_resumed) {
dqm_unlock(dqm); continue;
}
r = execute_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0,
USE_DEFAULT_GRACE_PERIOD); if (r) {
dev_err(dev, "Failed to resume process queues\n"); if (queue_ids) {
list_for_each_entry(q, &qpd->queues_list, list) { int q_idx = q_array_get_index(
q->properties.queue_id,
num_queues,
queue_ids);
/* mask queue as error on resume fail */ if (q_idx != QUEUE_NOT_FOUND)
queue_ids[q_idx] |=
KFD_DBG_QUEUE_ERROR_MASK;
}
}
} else {
wake_up_all(&dqm->destroy_wait);
total_resumed += per_device_resumed;
}
dqm_unlock(dqm);
}
if (queue_ids) { if (copy_to_user((void __user *)usr_queue_id_array, queue_ids,
num_queues * sizeof(uint32_t)))
pr_err("copy_to_user failed on queue resume\n");
kfree(queue_ids);
}
return total_resumed;
}
int suspend_queues(struct kfd_process *p,
uint32_t num_queues,
uint32_t grace_period,
uint64_t exception_clear_mask,
uint32_t *usr_queue_id_array)
{
uint32_t *queue_ids = get_queue_ids(num_queues, usr_queue_id_array); int total_suspended = 0; int i;
if (IS_ERR(queue_ids)) return PTR_ERR(queue_ids);
/* mask all queues as invalid. umask on successful request */
q_array_invalidate(num_queues, queue_ids);
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
struct device_queue_manager *dqm = pdd->dev->dqm;
struct device *dev = dqm->dev->adev->dev;
struct qcm_process_device *qpd = &pdd->qpd;
struct queue *q; int r, per_device_suspended = 0;
mutex_lock(&p->event_mutex);
dqm_lock(dqm);
/* unmask queues that suspend or already suspended */
list_for_each_entry(q, &qpd->queues_list, list) { int q_idx = q_array_get_index(q->properties.queue_id,
num_queues,
queue_ids);
if (q_idx != QUEUE_NOT_FOUND) { int err = suspend_single_queue(dqm, pdd, q);
bool is_mes = dqm->dev->kfd->shared_resources.enable_mes;
if (!err) {
queue_ids[q_idx] &= ~KFD_DBG_QUEUE_INVALID_MASK; if (exception_clear_mask && is_mes)
q->properties.exception_status &=
~exception_clear_mask;
if (!per_device_suspended) {
dqm_unlock(dqm);
mutex_unlock(&p->event_mutex); if (total_suspended)
amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev); continue;
}
r = execute_queues_cpsch(dqm,
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0,
grace_period);
if (r)
dev_err(dev, "Failed to suspend process queues.\n"); else
total_suspended += per_device_suspended;
list_for_each_entry(q, &qpd->queues_list, list) { int q_idx = q_array_get_index(q->properties.queue_id,
num_queues, queue_ids);
if (q_idx == QUEUE_NOT_FOUND) continue;
/* mask queue as error on suspend fail */ if (r)
queue_ids[q_idx] |= KFD_DBG_QUEUE_ERROR_MASK; elseif (exception_clear_mask)
q->properties.exception_status &=
~exception_clear_mask;
}
int dqm_debugfs_hang_hws(struct device_queue_manager *dqm)
{ int r = 0;
dqm_lock(dqm);
r = pm_debugfs_hang_hws(&dqm->packet_mgr); if (r) {
dqm_unlock(dqm); return r;
}
dqm->active_runlist = true;
r = execute_queues_cpsch(dqm, KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD);
dqm_unlock(dqm);
return r;
}
#endif
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.97Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤