18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Etnaviv Project 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "etnaviv_drv.h" 98c2ecf20Sopenharmony_ci#include "etnaviv_dump.h" 108c2ecf20Sopenharmony_ci#include "etnaviv_gem.h" 118c2ecf20Sopenharmony_ci#include "etnaviv_gpu.h" 128c2ecf20Sopenharmony_ci#include "etnaviv_sched.h" 138c2ecf20Sopenharmony_ci#include "state.xml.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic int etnaviv_job_hang_limit = 0; 168c2ecf20Sopenharmony_cimodule_param_named(job_hang_limit, etnaviv_job_hang_limit, int , 0444); 178c2ecf20Sopenharmony_cistatic int etnaviv_hw_jobs_limit = 4; 188c2ecf20Sopenharmony_cimodule_param_named(hw_job_limit, etnaviv_hw_jobs_limit, int , 0444); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct dma_fence * 218c2ecf20Sopenharmony_cietnaviv_sched_dependency(struct drm_sched_job *sched_job, 228c2ecf20Sopenharmony_ci struct drm_sched_entity *entity) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job); 258c2ecf20Sopenharmony_ci struct dma_fence *fence; 268c2ecf20Sopenharmony_ci int i; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (unlikely(submit->in_fence)) { 298c2ecf20Sopenharmony_ci fence = submit->in_fence; 308c2ecf20Sopenharmony_ci submit->in_fence = NULL; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (!dma_fence_is_signaled(fence)) 338c2ecf20Sopenharmony_ci return fence; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci dma_fence_put(fence); 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 398c2ecf20Sopenharmony_ci struct etnaviv_gem_submit_bo *bo = &submit->bos[i]; 408c2ecf20Sopenharmony_ci int j; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (bo->excl) { 438c2ecf20Sopenharmony_ci fence = bo->excl; 448c2ecf20Sopenharmony_ci bo->excl = NULL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (!dma_fence_is_signaled(fence)) 478c2ecf20Sopenharmony_ci return fence; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci dma_fence_put(fence); 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci for (j = 0; j < bo->nr_shared; j++) { 538c2ecf20Sopenharmony_ci if (!bo->shared[j]) 548c2ecf20Sopenharmony_ci continue; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci fence = bo->shared[j]; 578c2ecf20Sopenharmony_ci bo->shared[j] = NULL; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (!dma_fence_is_signaled(fence)) 608c2ecf20Sopenharmony_ci return fence; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci dma_fence_put(fence); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci kfree(bo->shared); 658c2ecf20Sopenharmony_ci bo->nr_shared = 0; 668c2ecf20Sopenharmony_ci bo->shared = NULL; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return NULL; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct dma_fence *etnaviv_sched_run_job(struct drm_sched_job *sched_job) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job); 758c2ecf20Sopenharmony_ci struct dma_fence *fence = NULL; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (likely(!sched_job->s_fence->finished.error)) 788c2ecf20Sopenharmony_ci fence = etnaviv_gpu_submit(submit); 798c2ecf20Sopenharmony_ci else 808c2ecf20Sopenharmony_ci dev_dbg(submit->gpu->dev, "skipping bad job\n"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return fence; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void etnaviv_sched_timedout_job(struct drm_sched_job *sched_job) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job); 888c2ecf20Sopenharmony_ci struct etnaviv_gpu *gpu = submit->gpu; 898c2ecf20Sopenharmony_ci u32 dma_addr; 908c2ecf20Sopenharmony_ci int change; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* block scheduler */ 938c2ecf20Sopenharmony_ci drm_sched_stop(&gpu->sched, sched_job); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * If the GPU managed to complete this jobs fence, the timout is 978c2ecf20Sopenharmony_ci * spurious. Bail out. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci if (dma_fence_is_signaled(submit->out_fence)) 1008c2ecf20Sopenharmony_ci goto out_no_timeout; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * If the GPU is still making forward progress on the front-end (which 1048c2ecf20Sopenharmony_ci * should never loop) we shift out the timeout to give it a chance to 1058c2ecf20Sopenharmony_ci * finish the job. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); 1088c2ecf20Sopenharmony_ci change = dma_addr - gpu->hangcheck_dma_addr; 1098c2ecf20Sopenharmony_ci if (gpu->completed_fence != gpu->hangcheck_fence || 1108c2ecf20Sopenharmony_ci change < 0 || change > 16) { 1118c2ecf20Sopenharmony_ci gpu->hangcheck_dma_addr = dma_addr; 1128c2ecf20Sopenharmony_ci gpu->hangcheck_fence = gpu->completed_fence; 1138c2ecf20Sopenharmony_ci goto out_no_timeout; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if(sched_job) 1178c2ecf20Sopenharmony_ci drm_sched_increase_karma(sched_job); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* get the GPU back into the init state */ 1208c2ecf20Sopenharmony_ci etnaviv_core_dump(submit); 1218c2ecf20Sopenharmony_ci etnaviv_gpu_recover_hang(gpu); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci drm_sched_resubmit_jobs(&gpu->sched); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciout_no_timeout: 1268c2ecf20Sopenharmony_ci /* restart scheduler after GPU is usable again */ 1278c2ecf20Sopenharmony_ci drm_sched_start(&gpu->sched, true); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void etnaviv_sched_free_job(struct drm_sched_job *sched_job) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci drm_sched_job_cleanup(sched_job); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci etnaviv_submit_put(submit); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct drm_sched_backend_ops etnaviv_sched_ops = { 1408c2ecf20Sopenharmony_ci .dependency = etnaviv_sched_dependency, 1418c2ecf20Sopenharmony_ci .run_job = etnaviv_sched_run_job, 1428c2ecf20Sopenharmony_ci .timedout_job = etnaviv_sched_timedout_job, 1438c2ecf20Sopenharmony_ci .free_job = etnaviv_sched_free_job, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ciint etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, 1478c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int ret = 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * Hold the fence lock across the whole operation to avoid jobs being 1538c2ecf20Sopenharmony_ci * pushed out of order with regard to their sched fence seqnos as 1548c2ecf20Sopenharmony_ci * allocated in drm_sched_job_init. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci mutex_lock(&submit->gpu->fence_lock); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ret = drm_sched_job_init(&submit->sched_job, sched_entity, 1598c2ecf20Sopenharmony_ci submit->ctx); 1608c2ecf20Sopenharmony_ci if (ret) 1618c2ecf20Sopenharmony_ci goto out_unlock; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished); 1648c2ecf20Sopenharmony_ci submit->out_fence_id = idr_alloc_cyclic(&submit->gpu->fence_idr, 1658c2ecf20Sopenharmony_ci submit->out_fence, 0, 1668c2ecf20Sopenharmony_ci INT_MAX, GFP_KERNEL); 1678c2ecf20Sopenharmony_ci if (submit->out_fence_id < 0) { 1688c2ecf20Sopenharmony_ci drm_sched_job_cleanup(&submit->sched_job); 1698c2ecf20Sopenharmony_ci ret = -ENOMEM; 1708c2ecf20Sopenharmony_ci goto out_unlock; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* the scheduler holds on to the job now */ 1748c2ecf20Sopenharmony_ci kref_get(&submit->refcount); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci drm_sched_entity_push_job(&submit->sched_job, sched_entity); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciout_unlock: 1798c2ecf20Sopenharmony_ci mutex_unlock(&submit->gpu->fence_lock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciint etnaviv_sched_init(struct etnaviv_gpu *gpu) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops, 1898c2ecf20Sopenharmony_ci etnaviv_hw_jobs_limit, etnaviv_job_hang_limit, 1908c2ecf20Sopenharmony_ci msecs_to_jiffies(500), dev_name(gpu->dev)); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_civoid etnaviv_sched_fini(struct etnaviv_gpu *gpu) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci drm_sched_fini(&gpu->sched); 2008c2ecf20Sopenharmony_ci} 201