summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-08-20 10:58:44 +1000
committerDave Airlie <airlied@redhat.com>2026-08-20 10:58:44 +1000
commitc44e278ce02efd0c4be79a8eda1ea6885c1ce5ec (patch)
treeb6b7324a56fb1869a094da177d6a41f4d2efe8e0 /drivers/gpu/drm
parent65b92bc671db7c222f8de1146c88e40fe8764d95 (diff)
parent8d3ae59288f1e7d58d76558a6ee96d533bc5019f (diff)
BackMerge tag 'v7.2' into drm-next
Linux 7.2 There was a lot of conflicts this round between fixes and next, and I'd like to get the merge resolutions that we have in drm-tip. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c60
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c16
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c4
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c11
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c27
-rw-r--r--drivers/gpu/drm/clients/drm_log.c12
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c12
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_state_helper.c3
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c92
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c6
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c59
-rw-r--r--drivers/gpu/drm/drm_ioctl.c14
-rw-r--r--drivers/gpu/drm/drm_pagemap.c11
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_sched.c1
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c21
-rw-r--r--drivers/gpu/drm/imagination/pvr_context.c4
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c8
-rw-r--r--drivers/gpu/drm/imagination/pvr_trace.h16
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm.c2
-rw-r--r--drivers/gpu/drm/lima/lima_sched.c1
-rw-r--r--drivers/gpu/drm/mediatek/mtk_crtc.c6
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c7
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c5
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.c11
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c8
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c1
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.c1
-rw-r--r--drivers/gpu/drm/panel/Kconfig3
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c7
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c1
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.c5
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c51
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c6
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c1
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c1
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c54
-rw-r--r--drivers/gpu/drm/scheduler/sched_fence.c2
-rw-r--r--drivers/gpu/drm/scheduler/sched_internal.h15
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c125
-rw-r--r--drivers/gpu/drm/scheduler/sched_rq.c87
-rw-r--r--drivers/gpu/drm/scheduler/tests/mock_scheduler.c1
-rw-r--r--drivers/gpu/drm/tegra/sor.c6
-rw-r--r--drivers/gpu/drm/tests/drm_exec_test.c24
-rw-r--r--drivers/gpu/drm/tests/drm_gem_shmem_test.c16
-rw-r--r--drivers/gpu/drm/tiny/appletbdrm.c53
-rw-r--r--drivers/gpu/drm/ttm/ttm_backup.c129
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool.c123
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool_internal.h8
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h8
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c16
-rw-r--r--drivers/gpu/drm/v3d/v3d_power.c7
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c10
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c12
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c29
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_gem.c14
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c5
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_blit.c39
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c49
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c20
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c13
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c13
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c22
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c17
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h14
-rw-r--r--drivers/gpu/drm/xe/xe_dep_scheduler.c1
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c3
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c1
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c1
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c93
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c29
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h7
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c13
-rw-r--r--drivers/gpu/drm/xe/xe_pxp.c162
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_types.h27
-rw-r--r--drivers/gpu/drm/xe/xe_uc.c7
95 files changed, 1391 insertions, 478 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 67b4ea772030..03b41f803520 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -1120,8 +1120,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
if (p->gang_size > 1 && !adev->vm_manager.concurrent_flush) {
for (i = 0; i < p->gang_size; ++i) {
struct drm_sched_entity *entity = p->entities[i];
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
+ struct drm_gpu_scheduler *sched = entity->rq->sched;
struct amdgpu_ring *ring = to_amdgpu_ring(sched);
if (amdgpu_vmid_uses_reserved(vm, ring->vm_hub))
@@ -1239,8 +1238,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
return r;
}
- sched = container_of(p->gang_leader->base.entity->rq, typeof(*sched),
- rq);
+ sched = p->gang_leader->base.entity->rq->sched;
while ((fence = amdgpu_sync_get_fence(&p->sync))) {
struct drm_sched_fence *s_fence = to_drm_sched_fence(fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 520fd59036d5..53539ceeda16 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -2284,6 +2284,7 @@ static int amdgpu_device_init_schedulers(struct amdgpu_device *adev)
{
struct drm_sched_init_args args = {
.ops = &amdgpu_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.timeout_wq = adev->reset_domain->wq,
.dev = adev->dev,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index d97cf1212e0f..1dedaf1698dc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -400,9 +400,7 @@ static struct dma_fence *
amdgpu_job_prepare_job(struct drm_sched_job *sched_job,
struct drm_sched_entity *s_entity)
{
- struct drm_gpu_scheduler *sched =
- container_of(s_entity->rq, typeof(*sched), rq);
- struct amdgpu_ring *ring = to_amdgpu_ring(sched);
+ struct amdgpu_ring *ring = to_amdgpu_ring(s_entity->rq->sched);
struct amdgpu_job *job = to_amdgpu_job(sched_job);
struct dma_fence *fence;
int r;
@@ -495,22 +493,25 @@ drm_sched_entity_queue_pop(struct drm_sched_entity *entity)
void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched)
{
- struct drm_sched_rq *rq = &sched->rq;
- struct drm_sched_entity *s_entity;
struct drm_sched_job *s_job;
+ struct drm_sched_entity *s_entity = NULL;
+ int i;
/* Signal all jobs not yet scheduled */
- spin_lock(&rq->lock);
- list_for_each_entry(s_entity, &rq->entities, list) {
- while ((s_job = drm_sched_entity_queue_pop(s_entity))) {
- struct drm_sched_fence *s_fence = s_job->s_fence;
-
- dma_fence_signal(&s_fence->scheduled);
- dma_fence_set_error(&s_fence->finished, -EHWPOISON);
- dma_fence_signal(&s_fence->finished);
+ for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
+ struct drm_sched_rq *rq = sched->sched_rq[i];
+ spin_lock(&rq->lock);
+ list_for_each_entry(s_entity, &rq->entities, list) {
+ while ((s_job = drm_sched_entity_queue_pop(s_entity))) {
+ struct drm_sched_fence *s_fence = s_job->s_fence;
+
+ dma_fence_signal(&s_fence->scheduled);
+ dma_fence_set_error(&s_fence->finished, -EHWPOISON);
+ dma_fence_signal(&s_fence->finished);
+ }
}
+ spin_unlock(&rq->lock);
}
- spin_unlock(&rq->lock);
/* Signal all jobs already scheduled to HW */
list_for_each_entry(s_job, &sched->pending_list, list) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
index 7e03bd2ff639..44fe40f9e8df 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
@@ -107,10 +107,7 @@ struct amdgpu_job {
static inline struct amdgpu_ring *amdgpu_job_ring(struct amdgpu_job *job)
{
- struct drm_gpu_scheduler *sched =
- container_of(job->base.entity->rq, typeof(*sched), rq);
-
- return to_amdgpu_ring(sched);
+ return to_amdgpu_ring(job->base.entity->rq->sched);
}
int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
index 5324030a13f5..fcd4cf2f4cd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
@@ -147,7 +147,6 @@ TRACE_EVENT(amdgpu_cs,
struct amdgpu_ib *ib),
TP_ARGS(p, job, ib),
TP_STRUCT__entry(
- __field(struct drm_gpu_scheduler *, sched)
__field(struct amdgpu_bo_list *, bo_list)
__field(u32, ring)
__field(u32, dw)
@@ -155,14 +154,11 @@ TRACE_EVENT(amdgpu_cs,
),
TP_fast_assign(
- __entry->sched = container_of(job->base.entity->rq,
- typeof(*__entry->sched),
- rq);
__entry->bo_list = p->bo_list;
- __entry->ring = to_amdgpu_ring(__entry->sched)->idx;
+ __entry->ring = to_amdgpu_ring(job->base.entity->rq->sched)->idx;
__entry->dw = ib->length_dw;
__entry->fences = amdgpu_fence_count_emitted(
- to_amdgpu_ring(__entry->sched));
+ to_amdgpu_ring(job->base.entity->rq->sched));
),
TP_printk("bo_list=%p, ring=%u, dw=%u, fences=%u",
__entry->bo_list, __entry->ring, __entry->dw,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 71050a86bcc3..d2ad5b0e8759 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -3023,6 +3023,8 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
is_compute_context = vm->is_compute_context;
if (is_compute_context) {
+ __label__ drm_exec_retry;
+
/* Release the root PD lock since svm_range_restore_pages
* might try to take it.
* TODO: rework svm_range_restore_pages so that this isn't
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
index 430274237689..50cc0779c340 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
@@ -106,13 +106,13 @@ static int amdgpu_vm_sdma_prepare(struct amdgpu_vm_update_params *p,
static int amdgpu_vm_sdma_commit(struct amdgpu_vm_update_params *p,
struct dma_fence **fence)
{
- struct drm_gpu_scheduler *sched =
- container_of(p->vm->delayed.rq, typeof(*sched), rq);
- struct amdgpu_ring *ring =
- container_of(sched, struct amdgpu_ring, sched);
struct amdgpu_ib *ib = p->job->ibs;
+ struct amdgpu_ring *ring;
struct dma_fence *f;
+ ring = container_of(p->vm->delayed.rq->sched, struct amdgpu_ring,
+ sched);
+
WARN_ON(ib->length_dw == 0);
amdgpu_ring_pad_ib(ring, ib);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
index 35faea0ff17f..b15af318784b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
@@ -467,13 +467,13 @@ int amdgpu_xcp_open_device(struct amdgpu_device *adev,
void amdgpu_xcp_release_sched(struct amdgpu_device *adev,
struct amdgpu_ctx_entity *entity)
{
- struct drm_gpu_scheduler *sched =
- container_of(entity->entity.rq, typeof(*sched), rq);
struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
+ struct drm_gpu_scheduler *sched;
if (!xcp_mgr)
return;
+ sched = entity->entity.rq->sched;
if (drm_sched_wqueue_ready(sched)) {
struct amdgpu_ring *ring = to_amdgpu_ring(sched);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index 1054c4b4b03e..56a5c11bc196 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -1646,36 +1646,36 @@ static int smu_v14_0_2_init_ppt_limits(struct smu_context *smu)
PPTable_t *pptable = table_context->driver_pptable;
CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
int16_t od_percent_upper = 0, od_percent_lower = 0;
- uint32_t pp_limit, msg_limit, min_limit, max_limit;
- int i;
-
- if (powerplay_table &&
- smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) {
- od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt;
- od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt;
- }
-
- for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) {
- pp_limit = i == SMU_POWER_SOURCE_AC ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
- msg_limit = pptable->SkuTable.MsgLimits.Power
- [PPT_THROTTLER_PPT0][i];
- min_limit = min(pp_limit, msg_limit);
- max_limit = max(pp_limit, msg_limit);
-
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value =
- pp_limit;
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = max_limit;
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min =
- min_limit * (100 + od_percent_lower) / 100;
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max =
- max_limit * (100 + od_percent_upper) / 100;
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min =
- smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min;
- }
-
- smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0);
+ uint32_t pp_limit, msg_limit, min_limit, max_limit;
+ int i;
+
+ if (powerplay_table &&
+ smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) {
+ od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt;
+ od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt;
+ }
+
+ for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) {
+ pp_limit = i == SMU_POWER_SOURCE_AC ?
+ skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
+ skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
+ msg_limit = pptable->SkuTable.MsgLimits.Power
+ [PPT_THROTTLER_PPT0][i];
+ min_limit = min(pp_limit, msg_limit);
+ max_limit = max(pp_limit, msg_limit);
+
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value =
+ pp_limit;
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = max_limit;
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min =
+ min_limit * (100 + od_percent_lower) / 100;
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max =
+ max_limit * (100 + od_percent_upper) / 100;
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min =
+ smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min;
+ }
+
+ smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0);
return 0;
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
index 12fe2bc0488e..cbfeb9736e6c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
@@ -521,14 +521,14 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu,
if (ret < 0)
return ret;
- if (ret == 0 &&
- metrics_info->metrics[metrics_info->active_idx].IOD.AccumulationCounter !=
- metrics_info->metrics[!metrics_info->active_idx].IOD.AccumulationCounter) {
- /* New sample: active_idx already points to the latest sample. */
- smu_v15_0_0_compute_all_metrics(metrics_info->avg_metric,
- &metrics_info->metrics[!metrics_info->active_idx],
- &metrics_info->metrics[metrics_info->active_idx]);
- }
+ if (ret == 0 &&
+ metrics_info->metrics[metrics_info->active_idx].IOD.AccumulationCounter !=
+ metrics_info->metrics[!metrics_info->active_idx].IOD.AccumulationCounter) {
+ /* New sample: active_idx already points to the latest sample. */
+ smu_v15_0_0_compute_all_metrics(metrics_info->avg_metric,
+ &metrics_info->metrics[!metrics_info->active_idx],
+ &metrics_info->metrics[metrics_info->active_idx]);
+ }
*value = metrics_info->avg_metric[member];
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 8d3d4a6e6ca2..30c0c0b41d5a 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -309,7 +309,9 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
lane_count = dp->link_train.lane_count;
for (lane = 0; lane < lane_count; lane++) {
voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane);
+ voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT;
pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
+ pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
DPCD_PRE_EMPHASIS_SET(pre_emphasis);
@@ -355,7 +357,9 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
for (lane = 0; lane < lane_count; lane++) {
training_lane = analogix_dp_get_lane_link_training(dp, lane);
voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane);
+ voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT;
pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
+ pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing &&
DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis)
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index d1e74dd2e051..eb3412ec30a0 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -43,6 +43,13 @@ static int display_connector_attach(struct drm_bridge *bridge,
return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
}
+static void display_connector_destroy(struct drm_bridge *bridge)
+{
+ struct display_connector *conn = to_display_connector(bridge);
+
+ i2c_put_adapter(conn->bridge.ddc);
+}
+
static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge)
{
struct display_connector *conn = to_display_connector(bridge);
@@ -208,6 +215,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
.attach = display_connector_attach,
+ .destroy = display_connector_destroy,
.detect = display_connector_bridge_detect,
.hpd_enable = display_connector_hpd_enable,
.hpd_disable = display_connector_hpd_disable,
@@ -439,9 +447,6 @@ static void display_connector_remove(struct platform_device *pdev)
regulator_disable(conn->supply);
drm_bridge_remove(&conn->bridge);
-
- if (!IS_ERR(conn->bridge.ddc))
- i2c_put_adapter(conn->bridge.ddc);
}
static const struct of_device_id display_connector_match[] = {
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index 96332721cb69..56b48f8feab6 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -257,8 +257,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
addr_len[PAGE0_SWAUX_LENGTH - base] = (len == 0) ? SWAUX_NO_PAYLOAD :
((len - 1) & SWAUX_LENGTH_MASK);
- regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
- ARRAY_SIZE(addr_len));
+ ret = regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
+ ARRAY_SIZE(addr_len));
+ if (ret) {
+ DRM_DEV_ERROR(dev,
+ "failed to write AUX address %#x, len %zu: %d\n",
+ msg->address, len, ret);
+ return ret;
+ }
if (len && (request == DP_AUX_NATIVE_WRITE ||
request == DP_AUX_I2C_WRITE)) {
@@ -274,13 +280,22 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
}
}
- regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ ret = regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to start AUX transfer: %d\n", ret);
+ return ret;
+ }
/* Zero delay loop because i2c transactions are slow already */
- regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
- !(data & SWAUX_SEND), 0, 50 * 1000);
+ ret = regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
+ !(data & SWAUX_SEND), 0, 50 * 1000);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to complete AUX transfer: %d\n",
+ ret);
+ return ret;
+ }
- regmap_read(map, PAGE0_SWAUX_STATUS, &data);
+ ret = regmap_read(map, PAGE0_SWAUX_STATUS, &data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to read PAGE0_SWAUX_STATUS: %d\n",
ret);
diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c
index e3e02c84a4cf..99062609d9a6 100644
--- a/drivers/gpu/drm/clients/drm_log.c
+++ b/drivers/gpu/drm/clients/drm_log.c
@@ -162,6 +162,9 @@ static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout,
{
u32 prefix_len = 0;
+ if (!len)
+ return;
+
if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']')
prefix_len = TS_PREFIX_LEN + 1;
@@ -217,6 +220,12 @@ static int drm_log_setup_modeset(struct drm_client_dev *client,
scanout->scaled_font_w = scanout->font->width * scale;
scanout->rows = height / scanout->scaled_font_h;
scanout->columns = width / scanout->scaled_font_w;
+ if (!scanout->rows || !scanout->columns) {
+ drm_client_buffer_delete(scanout->buffer);
+ scanout->buffer = NULL;
+ mode_set->fb = NULL;
+ return -EINVAL;
+ }
scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format);
scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format);
return 0;
@@ -421,6 +430,9 @@ void drm_log_register(struct drm_device *dev)
{
struct drm_log *new;
+ if (!scale)
+ scale = 1;
+
new = kzalloc_obj(*new);
if (!new)
goto err_warn;
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index 9c31e14cc413..e623ccb4c1d8 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -3686,6 +3686,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
int bw;
u8 buf;
+ if (!drm_dp_is_branch(dpcd))
+ return 0;
+
+ if (dpcd[DP_DPCD_REV] < 0x11)
+ return 0;
+
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 0;
+
+ if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI)
+ return 0;
+
buf = port_cap[2];
bw = buf & DP_PCON_MAX_FRL_BW;
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index ce17eeefc2da..d6669e36ca1b 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -1142,7 +1142,8 @@ drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *co
mutex_lock(&connector->hdmi.infoframes.lock);
- memcpy(&infoframe->data, frame, sizeof(infoframe->data));
+ BUILD_BUG_ON(sizeof(*frame) > sizeof(infoframe->data));
+ memcpy(&infoframe->data, frame, sizeof(*frame));
infoframe->set = true;
ret = write_infoframe(connector, &funcs->audio, "Audio", infoframe);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3316bb00a662..d4664ed468b2 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
console_unlock();
}
+static int find_crtc_index_atomic(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ int crtc_index = -EINVAL;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_plane *plane;
+ int ret = 0;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry:
+ drm_for_each_plane(plane, dev) {
+ const struct drm_plane_state *plane_state;
+
+ if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ ret = drm_modeset_lock(&plane->mutex, &ctx);
+ if (ret)
+ goto err_drm_modeset_lock;
+ plane_state = plane->state;
+
+ if (plane_state->fb == helper->fb && plane_state->crtc) {
+ struct drm_crtc *crtc = plane_state->crtc;
+
+ ret = drm_modeset_lock(&crtc->mutex, &ctx);
+ if (ret)
+ goto err_drm_modeset_lock;
+ if (crtc->state->active)
+ crtc_index = crtc->index;
+ drm_modeset_unlock(&crtc->mutex);
+ }
+ drm_modeset_unlock(&plane->mutex);
+
+ if (crtc_index >= 0)
+ break;
+ }
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ return crtc_index;
+
+err_drm_modeset_lock:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry;
+ }
+ return ret;
+}
+
+static int find_crtc_index_legacy(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_plane *plane = crtc->primary;
+
+ if (!crtc->enabled)
+ continue;
+ if (!plane || plane->fb != helper->fb)
+ continue; /* CRTC doesn't display fbdev emulation */
+
+ return crtc->index;
+ }
+
+ return -EINVAL;
+}
+
+static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ int crtc_index;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ if (drm_drv_uses_atomic_modeset(dev))
+ crtc_index = find_crtc_index_atomic(helper);
+ else
+ crtc_index = find_crtc_index_legacy(helper);
+
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return crtc_index;
+}
+
static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
{
struct drm_device *dev = helper->dev;
struct drm_clip_rect *clip = &helper->damage_clip;
struct drm_clip_rect clip_copy;
+ int crtc_index;
unsigned long flags;
int ret;
mutex_lock(&helper->lock);
- drm_client_modeset_wait_for_vblank(&helper->client, 0);
+ crtc_index = drm_fb_helper_find_crtc_index(helper);
+ if (crtc_index >= 0)
+ drm_client_modeset_wait_for_vblank(&helper->client, crtc_index);
mutex_unlock(&helper->lock);
if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 22ec52e2ffb8..7bca4324f94f 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -611,9 +611,13 @@ static vm_fault_t try_insert_pfn(struct vm_fault *vmf, unsigned int order,
#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
} else if (order == PMD_ORDER) {
unsigned long paddr = pfn << PAGE_SHIFT;
+ struct vm_area_struct *vma = vmf->vma;
+ unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE);
+ unsigned long end = start + PMD_SIZE;
+ bool in_range = vma->vm_start <= start && end <= vma->vm_end;
bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK);
- if (aligned &&
+ if (aligned && in_range &&
folio_test_pmd_mappable(page_folio(pfn_to_page(pfn)))) {
vm_fault_t ret;
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index fcfe635bc195..dc184fd113ff 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -860,7 +860,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range,
const struct dev_pagemap *other = NULL;
int err, i;
- pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
+ pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
return DRM_GPUSVM_SCAN_UNPOPULATED;
@@ -1230,20 +1230,35 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm,
};
bool use_iova = dma_use_iova(&svm_pages->state);
- if (use_iova)
- dma_iova_destroy(dev, &svm_pages->state,
- svm_pages->state_offset,
- svm_pages->dma_addr[0].dir, 0);
+ /*
+ * IOVA is reserved for the whole range but only the linked
+ * system pages (state_offset bytes) need unlinking; free the
+ * entire reservation to avoid leaking the device-page part.
+ * On the error path state_offset is 0, so just free it.
+ */
+ if (use_iova) {
+ if (svm_pages->state_offset)
+ dma_iova_unlink(dev, &svm_pages->state, 0,
+ svm_pages->state_offset,
+ svm_pages->dma_addr[0].dir, 0);
+ dma_iova_free(dev, &svm_pages->state);
+ }
for (i = 0, j = 0; i < npages; j++) {
struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j];
- if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM)
- dma_unmap_page(dev,
- addr->addr,
- PAGE_SIZE << addr->order,
- addr->dir);
- else if (dpagemap && dpagemap->ops->device_unmap)
+ if (addr->proto == DRM_INTERCONNECT_SYSTEM) {
+ /*
+ * Linked IOVA pages were already torn down by
+ * the dma_iova_unlink()/dma_iova_free() above;
+ * only the non-IOVA mappings need unmap here.
+ */
+ if (!use_iova)
+ dma_unmap_page(dev,
+ addr->addr,
+ PAGE_SIZE << addr->order,
+ addr->dir);
+ } else if (dpagemap && dpagemap->ops->device_unmap)
dpagemap->ops->device_unmap(dpagemap,
dev, addr);
i += 1 << addr->order;
@@ -1556,7 +1571,7 @@ map_pages:
/* Unlock and restart mapping to allocate memory. */
drm_gpusvm_notifier_unlock(gpusvm);
svm_pages->dma_addr =
- kvmalloc_objs(*svm_pages->dma_addr, npages);
+ kvzalloc_objs(*svm_pages->dma_addr, npages);
if (!svm_pages->dma_addr) {
err = -ENOMEM;
goto err_free;
@@ -1599,6 +1614,16 @@ map_pages:
err = -EAGAIN;
goto err_unmap;
}
+
+ /*
+ * Set the dpagemap as soon as the first
+ * device page is mapped so the err_unmap path
+ * can device_unmap() the device mappings that
+ * have already been created.
+ */
+ drm_pagemap_get(dpagemap);
+ drm_pagemap_put(svm_pages->dpagemap);
+ svm_pages->dpagemap = dpagemap;
}
svm_pages->dma_addr[j] =
dpagemap->ops->device_map(dpagemap,
@@ -1666,12 +1691,8 @@ map_pages:
goto err_unmap;
}
- if (pagemap) {
+ if (pagemap)
flags.has_devmem_pages = true;
- drm_pagemap_get(dpagemap);
- drm_pagemap_put(svm_pages->dpagemap);
- svm_pages->dpagemap = dpagemap;
- }
/* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */
WRITE_ONCE(svm_pages->flags.__flags, flags.__flags);
@@ -1757,8 +1778,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
return -EFAULT;
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
- if (!pfns)
+ if (!pfns) {
+ mmput(mm);
return -ENOMEM;
+ }
hmm_range.hmm_pfns = pfns;
while (!time_after(jiffies, timeout)) {
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index e2df4becce62..9039a39c4324 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -373,13 +373,25 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
return -EINVAL;
file_priv->supports_virtualized_cursor_plane = req->value;
break;
- case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE:
+ case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: {
+ struct drm_plane *plane;
+ bool has_plane_with_color_pipeline = false;
+
if (!file_priv->atomic)
return -EINVAL;
if (req->value > 1)
return -EINVAL;
+ drm_for_each_plane(plane, dev) {
+ if (plane->color_pipeline_property) {
+ has_plane_with_color_pipeline = true;
+ break;
+ }
+ }
+ if (!has_plane_with_color_pipeline)
+ return -EOPNOTSUPP;
file_priv->plane_color_pipeline = req->value;
break;
+ }
default:
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 15c78eca180b..892b325fa99b 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -727,8 +727,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
}
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
- if (err)
- goto err_aborted_migration;
+ if (err) {
+ npages = 0;
+ goto err_finalize;
+ }
own_pages = 0;
@@ -807,8 +809,11 @@ next:
msecs_to_jiffies(mdetails->timeslice_ms);
err_finalize:
- if (err)
+ if (err) {
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
+ for (i = npages; i < npages_in_range(start, end); ++i)
+ migrate.dst[i] = 0;
+ }
err_aborted_migration:
migrate_vma_pages(&migrate);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
index 139e6e38784b..3cc50d697c89 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
@@ -144,6 +144,7 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu)
{
const struct drm_sched_init_args args = {
.ops = &etnaviv_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = etnaviv_hw_jobs_limit,
.hang_limit = etnaviv_job_hang_limit,
.timeout = msecs_to_jiffies(500),
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
index 2a7916ca5907..a780643f25aa 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
@@ -98,6 +98,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
u32 temp;
+ int ret;
i2c_dev->status = I2C_STAT_INIT;
i2c_dev->msg = pmsg;
@@ -109,9 +110,14 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
HDMI_WRITE(HDMI_HI2CHCR, temp);
HDMI_READ(HDMI_HI2CHCR);
- while (i2c_dev->status != I2C_TRANSACTION_DONE)
- wait_for_completion_interruptible_timeout(&i2c_dev->complete,
+ while (i2c_dev->status != I2C_TRANSACTION_DONE) {
+ ret = wait_for_completion_interruptible_timeout(&i2c_dev->complete,
10 * HZ);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return -ETIMEDOUT;
+ }
return 0;
}
@@ -130,7 +136,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
{
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
- int i;
+ int i, ret = 0;
mutex_lock(&i2c_dev->i2c_lock);
@@ -142,9 +148,11 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
for (i = 0; i < num; i++) {
if (pmsg->len && pmsg->buf) {
if (pmsg->flags & I2C_M_RD)
- xfer_read(adap, pmsg);
+ ret = xfer_read(adap, pmsg);
else
- xfer_write(adap, pmsg);
+ ret = xfer_write(adap, pmsg);
+ if (ret)
+ break;
}
pmsg++; /* next message */
}
@@ -154,6 +162,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
mutex_unlock(&i2c_dev->i2c_lock);
+ if (ret)
+ return ret;
+
return i;
}
diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c
index 52e16c1e7af0..406e0758e860 100644
--- a/drivers/gpu/drm/imagination/pvr_context.c
+++ b/drivers/gpu/drm/imagination/pvr_context.c
@@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
goto err_free_ctx;
ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
- if (IS_ERR(ctx->vm_ctx)) {
- err = PTR_ERR(ctx->vm_ctx);
+ if (!ctx->vm_ctx) {
+ err = -EINVAL;
goto err_free_ctx;
}
diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
index 6bb5baa6c41b..805d9f9bc1dd 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
@@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp)
return 0;
}
-const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
+static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
.set = pvr_fw_trace_init_mask_set,
.get = param_get_hexint,
};
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index 09993e858df8..24a8645aa913 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -726,8 +726,6 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1;
}
- trace_pvr_job_submit_fw(job);
-
/* Submit job to FW */
pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
job->id, job->id);
@@ -802,6 +800,9 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
job->hwrt,
frag_job->fw_ccb_cmd_type ==
ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR);
+
+ trace_pvr_job_submit_fw(geom_job);
+ trace_pvr_job_submit_fw(frag_job);
} else {
struct pvr_queue *queue = container_of(job->base.sched,
struct pvr_queue, scheduler);
@@ -809,6 +810,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb,
pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset,
job->hwrt);
+
+ trace_pvr_job_submit_fw(job);
}
return dma_fence_get(job->done_fence);
@@ -1282,6 +1285,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
const struct drm_sched_init_args sched_args = {
.ops = &pvr_queue_sched_ops,
.submit_wq = pvr_dev->sched_wq,
+ .num_rqs = 1,
.credit_limit = 64 * 1024,
.hang_limit = 1,
.timeout = msecs_to_jiffies(SCHED_TIMEOUT_PERIOD),
diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h
index ffbbde3b2bcc..943d6b2418cb 100644
--- a/drivers/gpu/drm/imagination/pvr_trace.h
+++ b/drivers/gpu/drm/imagination/pvr_trace.h
@@ -41,6 +41,17 @@ TRACE_EVENT(pvr_job_submit_ioctl,
__entry->count)
);
+#define PVR_JOB_GET_HWRT_FW_ADDR(job) \
+ ({ \
+ struct pvr_job *_job = (job); \
+ u32 _hwrt_fw_addr = 0; \
+ \
+ if (_job && _job->hwrt) \
+ pvr_fw_object_get_fw_addr(_job->hwrt->fw_obj, &_hwrt_fw_addr); \
+ \
+ _hwrt_fw_addr; \
+ })
+
#define PVR_JOB_TYPE_TO_STR(val) \
__print_symbolic(val, \
{ DRM_PVR_JOB_TYPE_GEOMETRY, "geometry" }, \
@@ -64,9 +75,7 @@ TRACE_EVENT(pvr_job_create,
__entry->ctx = job->ctx;
__entry->fw_obj = job->ctx->fw_obj;
pvr_fw_object_get_fw_addr(job->ctx->fw_obj, &__entry->fw_addr);
- __entry->hwrt_addr = job->hwrt ?
- job->hwrt->fw_obj->fw_addr_offset :
- 0;
+ __entry->hwrt_addr = PVR_JOB_GET_HWRT_FW_ADDR(job);
__entry->job = job;
__entry->job_type = job->type;
__entry->sync_op_count = sync_op_count;),
@@ -82,6 +91,7 @@ TRACE_EVENT(pvr_job_create,
);
#undef PVR_JOB_TYPE_TO_STR
+#undef PVR_JOB_GET_HWRT_FW_ADDR
TRACE_EVENT(pvr_job_submit_fw,
TP_PROTO(struct pvr_job *job),
diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c
index 396d349fb6ce..ceb78694cd98 100644
--- a/drivers/gpu/drm/imagination/pvr_vm.c
+++ b/drivers/gpu/drm/imagination/pvr_vm.c
@@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
pvr_gem_object_get(pvr_obj);
+ mutex_lock(&vm_ctx->lock);
err = drm_gpuvm_exec_lock(&vm_exec);
if (err)
goto err_cleanup;
@@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
drm_gpuvm_exec_unlock(&vm_exec);
err_cleanup:
+ mutex_unlock(&vm_ctx->lock);
pvr_vm_bind_op_fini(&bind_op);
return err;
diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
index 0a01213c4878..9a1e6b9ecbe5 100644
--- a/drivers/gpu/drm/lima/lima_sched.c
+++ b/drivers/gpu/drm/lima/lima_sched.c
@@ -521,6 +521,7 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name)
lima_sched_timeout_ms : 10000;
const struct drm_sched_init_args args = {
.ops = &lima_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.hang_limit = lima_job_hang_limit,
.timeout = msecs_to_jiffies(timeout),
diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 8e552cdc3b53..97e3ff412e6e 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -154,10 +154,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc)
{
struct mtk_crtc_state *state;
- if (crtc->state)
+ if (crtc->state) {
__drm_atomic_helper_crtc_destroy_state(crtc->state);
-
- kfree(to_mtk_crtc_state(crtc->state));
+ kfree(to_mtk_crtc_state(crtc->state));
+ }
crtc->state = NULL;
state = kzalloc_obj(*state);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index c0af3e3b51d5..6a259872b0a9 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev)
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
priv->children_bound = false;
+ component_unbind_all(dev, priv->mmsys_dev);
}
static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
@@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
priv->mmsys_dev = pdev->dev.platform_data;
- component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+ ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add component master\n");
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops);
if (ret != 0) {
+ component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
@@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
{
+ component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);
component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 3f3f56eed3f9..7961139333ad 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -743,8 +743,6 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
mtk_dsi_set_vm_cmd(dsi);
mtk_dsi_config_vdo_timing(dsi);
mtk_dsi_set_interrupt_enable(dsi);
- mtk_dsi_lane_ready(dsi);
- mtk_dsi_clk_hs_mode(dsi, 1);
return 0;
err_disable_engine_clk:
@@ -858,6 +856,9 @@ static void mtk_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
ret = mtk_dsi_poweron(dsi);
if (ret < 0)
drm_err(drm, "failed to power on dsi\n");
+
+ mtk_dsi_lane_ready(dsi);
+ mtk_dsi_clk_hs_mode(dsi, 1);
}
static void mtk_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
index a049489daa2d..042b98e5a519 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
@@ -290,6 +290,13 @@ static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struc
return 0;
}
+static void mtk_hdmi_put_adapter(void *_adap)
+{
+ struct i2c_adapter *adap = _adap;
+
+ i2c_put_adapter(adap);
+}
+
static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev,
const char * const *clk_names, size_t num_clocks)
{
@@ -328,12 +335,12 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device
if (!i2c_np)
return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n");
- hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
+ hdmi->ddc_adpt = of_get_i2c_adapter_by_node(i2c_np);
of_node_put(i2c_np);
if (!hdmi->ddc_adpt)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n");
- ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &hdmi->ddc_adpt->dev);
+ ret = devm_add_action_or_reset(dev, mtk_hdmi_put_adapter, hdmi->ddc_adpt);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
index d9316d30fd5f..f0277fcf3345 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -1499,13 +1499,6 @@ static int mtk_hdmi_v2_probe(struct platform_device *pdev)
return 0;
}
-static void mtk_hdmi_v2_remove(struct platform_device *pdev)
-{
- struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
-
- i2c_put_adapter(hdmi->ddc_adpt);
-}
-
static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = {
{ .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 },
{ .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 },
@@ -1515,7 +1508,6 @@ MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids);
static struct platform_driver mtk_hdmi_v2_driver = {
.probe = mtk_hdmi_v2_probe,
- .remove = mtk_hdmi_v2_remove,
.driver = {
.name = "mediatek-drm-hdmi-v2",
.of_match_table = mtk_drm_hdmi_v2_of_ids,
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 3ed05ab0eeef..c4cfe036066b 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -841,6 +841,7 @@ msm_gem_vm_create(struct drm_device *drm, struct msm_mmu *mmu, const char *name,
if (!managed) {
struct drm_sched_init_args args = {
.ops = &msm_vm_bind_ops,
+ .num_rqs = 1,
.credit_limit = 1,
.timeout = MAX_SCHEDULE_TIMEOUT,
.name = "msm-vm-bind",
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 0d14c31bd4e4..2d6b930b766e 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -67,6 +67,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
{
struct drm_sched_init_args args = {
.ops = &msm_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = num_hw_submissions,
.timeout = MAX_SCHEDULE_TIMEOUT,
.dev = gpu->dev->dev,
diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c
index 2cbae003d6de..8b9f935afe09 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sched.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sched.c
@@ -405,6 +405,7 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm,
struct drm_sched_entity *entity = &sched->entity;
struct drm_sched_init_args args = {
.ops = &nouveau_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = credit_limit,
.timeout = msecs_to_jiffies(NOUVEAU_SCHED_JOB_TIMEOUT_MS),
.name = "nouveau_sched",
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 53c0b95c26ca..cbdf7b8f7f7a 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -253,6 +253,7 @@ config DRM_PANEL_HIMAX_HX83121A
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_KMS_HELPER
help
@@ -374,6 +375,7 @@ config DRM_PANEL_ILITEK_ILI9882T
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y if you want to enable support for panels based on the
@@ -1019,6 +1021,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y or M here if you want to enable support for the
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
index 3abdd0870e83..c3d5ab67121d 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
@@ -2563,9 +2563,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
"Couldn't get our power regulator\n");
ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc");
- if (IS_ERR(ctx->iovcc))
- return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
+ if (IS_ERR(ctx->iovcc)) {
+ if (PTR_ERR(ctx->iovcc) != -ENODEV)
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
"Couldn't get our iovcc regulator\n");
+ ctx->iovcc = NULL;
+ }
ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 2d12b83e900a..d59b4863b8ad 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -850,6 +850,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
{
struct drm_sched_init_args args = {
.ops = &panfrost_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 2,
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
.dev = pfdev->base.dev,
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 0b25abebb803..9687c59de350 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev)
return ret;
#ifdef CONFIG_DEBUG_FS
- drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+ ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+ if (ret)
+ return ret;
+
INIT_LIST_HEAD(&ptdev->gems.node);
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index fc1a423e48a8..10b7e12c1f1e 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -545,6 +545,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
struct panthor_fw_binary_section_entry_hdr hdr;
struct panthor_fw_section *section;
u32 section_size;
+ u32 data_size;
u32 name_len;
int ret;
@@ -595,6 +596,16 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
return -EINVAL;
}
+ section_size = hdr.va.end - hdr.va.start;
+ data_size = hdr.data.end - hdr.data.start;
+ if (data_size > section_size) {
+ drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n");
+ return -EINVAL;
+ }
+
+ if (!section_size)
+ return 0;
+
name_len = iter->size - iter->offset;
section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL);
@@ -603,7 +614,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
list_add_tail(&section->node, &ptdev->fw->sections);
section->flags = hdr.flags;
- section->data.size = hdr.data.end - hdr.data.start;
+ section->data.size = data_size;
if (section->data.size > 0) {
void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL);
@@ -626,7 +637,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
section->name = name;
}
- section_size = hdr.va.end - hdr.va.start;
if (section_size) {
u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
struct panthor_gem_object *bo;
@@ -830,6 +840,7 @@ static int panthor_fw_load(struct panthor_device *ptdev)
}
if (hdr.size > iter.size) {
+ ret = -EINVAL;
drm_err(&ptdev->base, "Firmware image is truncated\n");
goto out;
}
@@ -857,18 +868,24 @@ out:
* iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address
* @ptdev: Device.
* @mcu_va: MCU address.
+ * @size: Size of the object pointed to by @mcu_va.
*
- * Return: NULL if the address is not part of the shared section, non-NULL otherwise.
+ * Return: NULL if the object is not part of the shared section, non-NULL otherwise.
*/
-static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va)
+static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size)
{
u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem);
- u64 shared_mem_end = shared_mem_start +
- panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
- if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end)
+ size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
+ u64 offset;
+
+ if (mcu_va < shared_mem_start)
+ return NULL;
+
+ offset = mcu_va - shared_mem_start;
+ if (offset > shared_mem_size || size > shared_mem_size - offset)
return NULL;
- return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start);
+ return ptdev->fw->shared_section->mem->kmap + offset;
}
static int panthor_init_cs_iface(struct panthor_device *ptdev,
@@ -891,8 +908,10 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev,
spin_lock_init(&cs_iface->lock);
cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
- cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va);
- cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va);
+ cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va,
+ sizeof(*cs_iface->input));
+ cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va,
+ sizeof(*cs_iface->output));
if (!cs_iface->input || !cs_iface->output) {
drm_err(&ptdev->base, "Invalid stream control interface input/output VA");
@@ -944,8 +963,10 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev,
spin_lock_init(&csg_iface->lock);
csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
- csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va);
- csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va);
+ csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va,
+ sizeof(*csg_iface->input));
+ csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va,
+ sizeof(*csg_iface->output));
if (csg_iface->control->stream_num < MIN_CS_PER_CSG ||
csg_iface->control->stream_num > MAX_CS_PER_CSG)
@@ -1006,8 +1027,10 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
return -EINVAL;
}
- glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va);
- glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va);
+ glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va,
+ sizeof(*glb_iface->input));
+ glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va,
+ sizeof(*glb_iface->output));
if (!glb_iface->input || !glb_iface->output) {
drm_err(&ptdev->base, "Invalid global control interface input/output VA");
return -EINVAL;
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index 9855df738194..772121ece318 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -802,9 +802,13 @@ static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct p
} else if (order == PMD_ORDER) {
unsigned long pfn = page_to_pfn(page);
unsigned long paddr = pfn << PAGE_SHIFT;
+ struct vm_area_struct *vma = vmf->vma;
+ unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE);
+ unsigned long end = start + PMD_SIZE;
+ bool in_range = vma->vm_start <= start && end <= vma->vm_end;
bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK);
- if (aligned &&
+ if (aligned && in_range &&
folio_test_pmd_mappable(page_folio(page))) {
pfn &= PMD_MASK >> PAGE_SHIFT;
return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 0182b72f1932..9f63a048df61 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -2859,6 +2859,7 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
const struct drm_sched_init_args sched_args = {
.ops = &panthor_vm_bind_ops,
.submit_wq = ptdev->mmu->vm.wq,
+ .num_rqs = 1,
.credit_limit = 1,
/* Bind operations are synchronous for now, no timeout needed. */
.timeout = MAX_SCHEDULE_TIMEOUT,
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 5832dccfc093..5778e23e20a0 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3501,6 +3501,7 @@ group_create_queue(struct panthor_group *group,
struct drm_sched_init_args sched_args = {
.ops = &panthor_queue_sched_ops,
.submit_wq = group->ptdev->scheduler->wq,
+ .num_rqs = 1,
/*
* The credit limit argument tells us the total number of
* instructions across all CS slots in the ringbuffer, with
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index c51101ec70c1..a4a7efdbf229 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -132,11 +132,39 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
entity->guilty = guilty;
entity->priority = priority;
entity->last_user = current->group_leader;
+ entity->rq_priority = drm_sched_policy == DRM_SCHED_POLICY_FAIR ?
+ DRM_SCHED_PRIORITY_KERNEL : priority;
entity->num_sched_list = num_sched_list;
entity->sched_list = num_sched_list > 1 ? sched_list : NULL;
- entity->rq = &sched_list[0]->rq;
RCU_INIT_POINTER(entity->last_scheduled, NULL);
RB_CLEAR_NODE(&entity->rb_tree_node);
+
+ if (!sched_list[0]->sched_rq) {
+ /* Since every entry covered by num_sched_list
+ * should be non-NULL and therefore we warn drivers
+ * not to do this and to fix their DRM calling order.
+ */
+ pr_warn("%s: called with uninitialized scheduler\n", __func__);
+ } else {
+ enum drm_sched_priority p = entity->priority;
+
+ /*
+ * The "priority" of an entity cannot exceed the number of
+ * run-queues of a scheduler. Protect against num_rqs being 0,
+ * by converting to signed. Choose the lowest priority
+ * available.
+ */
+ if (p >= sched_list[0]->num_user_rqs) {
+ dev_err(sched_list[0]->dev, "entity with out-of-bounds priority:%u num_user_rqs:%u\n",
+ p, sched_list[0]->num_user_rqs);
+ p = max_t(s32,
+ (s32)sched_list[0]->num_user_rqs - 1,
+ (s32)DRM_SCHED_PRIORITY_KERNEL);
+ entity->priority = p;
+ }
+ entity->rq = sched_list[0]->sched_rq[entity->rq_priority];
+ }
+
init_completion(&entity->entity_idle);
/* We start in an idle state. */
@@ -325,11 +353,11 @@ EXPORT_SYMBOL(drm_sched_entity_kill);
*/
long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
{
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
+ struct drm_gpu_scheduler *sched;
struct task_struct *last_user;
long ret = timeout;
+ sched = entity->rq->sched;
/*
* The client will not queue more jobs during this fini - consume
* existing queued ones, or discard them on SIGKILL.
@@ -410,12 +438,10 @@ static void drm_sched_entity_wakeup(struct dma_fence *f,
{
struct drm_sched_entity *entity =
container_of(cb, struct drm_sched_entity, cb);
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
entity->dependency = NULL;
dma_fence_put(f);
- drm_sched_wakeup(sched);
+ drm_sched_wakeup(entity->rq->sched);
}
/**
@@ -442,8 +468,7 @@ EXPORT_SYMBOL(drm_sched_entity_set_priority);
static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity,
struct drm_sched_job *sched_job)
{
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
+ struct drm_gpu_scheduler *sched = entity->rq->sched;
struct dma_fence *fence = entity->dependency;
struct drm_sched_fence *s_fence;
@@ -577,7 +602,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
spin_lock(&entity->lock);
sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list);
- rq = sched ? &sched->rq : NULL;
+ rq = sched ? sched->sched_rq[entity->rq_priority] : NULL;
if (rq != entity->rq) {
drm_sched_rq_remove_entity(entity->rq, entity);
entity->rq = rq;
@@ -601,9 +626,8 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
{
struct drm_sched_entity *entity = sched_job->entity;
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
bool first;
+ ktime_t submit_ts;
trace_drm_sched_job_queue(sched_job, entity);
@@ -614,18 +638,22 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
xa_for_each(&sched_job->dependencies, index, entry)
trace_drm_sched_job_add_dep(sched_job, entry);
}
- atomic_inc(sched->score);
+ atomic_inc(entity->rq->sched->score);
WRITE_ONCE(entity->last_user, current->group_leader);
/*
* After the sched_job is pushed into the entity queue, it may be
* completed and freed up at any time. We can no longer access it.
+ * Make sure to set the submit_ts first, to avoid a race.
*/
+ sched_job->submit_ts = submit_ts = ktime_get();
first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
/* first job wakes up scheduler */
if (first) {
- sched = drm_sched_rq_add_entity(entity);
+ struct drm_gpu_scheduler *sched;
+
+ sched = drm_sched_rq_add_entity(entity, submit_ts);
if (sched)
drm_sched_wakeup(sched);
}
diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c
index 85ee3d694dc9..096fe28aa9c9 100644
--- a/drivers/gpu/drm/scheduler/sched_fence.c
+++ b/drivers/gpu/drm/scheduler/sched_fence.c
@@ -227,7 +227,7 @@ void drm_sched_fence_init(struct drm_sched_fence *fence,
{
unsigned seq;
- fence->sched = container_of(entity->rq, typeof(*fence->sched), rq);
+ fence->sched = entity->rq->sched;
seq = atomic_inc_return(&entity->fence_seq);
dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled,
&fence->lock, entity->fence_context, seq);
diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h
index 13ecb771d7a2..a901801fce85 100644
--- a/drivers/gpu/drm/scheduler/sched_internal.h
+++ b/drivers/gpu/drm/scheduler/sched_internal.h
@@ -31,20 +31,29 @@ struct drm_sched_entity_stats {
struct ewma_drm_sched_avgtime avg_job_us;
};
+/* Used to choose between FIFO and RR job-scheduling */
+extern int drm_sched_policy;
+
+#define DRM_SCHED_POLICY_RR 0
+#define DRM_SCHED_POLICY_FIFO 1
+#define DRM_SCHED_POLICY_FAIR 2
+
bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
struct drm_sched_entity *entity);
void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
-void drm_sched_rq_init(struct drm_sched_rq *rq);
+void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
+ struct drm_sched_rq *rq);
struct drm_gpu_scheduler *
-drm_sched_rq_add_entity(struct drm_sched_entity *entity);
+drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts);
void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
struct drm_sched_entity *entity);
void drm_sched_rq_pop_entity(struct drm_sched_entity *entity);
struct drm_sched_entity *
-drm_sched_select_entity(struct drm_gpu_scheduler *sched);
+drm_sched_rq_select_entity(struct drm_gpu_scheduler *sched,
+ struct drm_sched_rq *rq);
void drm_sched_entity_select_rq(struct drm_sched_entity *entity);
struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity);
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index d2ca01b31ee4..6cb6f9546493 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -84,6 +84,15 @@
#define CREATE_TRACE_POINTS
#include "gpu_scheduler_trace.h"
+int drm_sched_policy = DRM_SCHED_POLICY_FIFO;
+
+/**
+ * DOC: sched_policy (int)
+ * Used to override default entities scheduling policy in a run queue.
+ */
+MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default), " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair (experimental).");
+module_param_named(sched_policy, drm_sched_policy, int, 0444);
+
static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched)
{
u32 credits;
@@ -639,7 +648,7 @@ void drm_sched_job_arm(struct drm_sched_job *job)
BUG_ON(!entity);
drm_sched_entity_select_rq(entity);
- sched = container_of(entity->rq, typeof(*sched), rq);
+ sched = entity->rq->sched;
job->sched = sched;
job->s_priority = entity->priority;
@@ -863,6 +872,34 @@ void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
}
/**
+ * drm_sched_select_entity - Select next entity to process
+ *
+ * @sched: scheduler instance
+ *
+ * Return an entity to process or NULL if none are found.
+ *
+ * Note, that we break out of the for-loop when "entity" is non-null, which can
+ * also be an error-pointer--this assures we don't process lower priority
+ * run-queues. See comments in the respectively called functions.
+ */
+static struct drm_sched_entity *
+drm_sched_select_entity(struct drm_gpu_scheduler *sched)
+{
+ struct drm_sched_entity *entity = NULL;
+ int i;
+
+ /* Start with the highest priority.
+ */
+ for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
+ entity = drm_sched_rq_select_entity(sched, sched->sched_rq[i]);
+ if (entity)
+ break;
+ }
+
+ return IS_ERR(entity) ? NULL : entity;
+}
+
+/**
* drm_sched_get_finished_job - fetch the next finished job to be destroyed
*
* @sched: scheduler instance
@@ -987,7 +1024,7 @@ static void drm_sched_run_job_work(struct work_struct *w)
/* Find entity with a ready job */
entity = drm_sched_select_entity(sched);
- if (IS_ERR_OR_NULL(entity)) {
+ if (!entity) {
/*
* Either no more work to do, or the next ready job needs more
* credits than the scheduler has currently available.
@@ -1063,6 +1100,8 @@ static struct workqueue_struct *drm_sched_alloc_wq(const char *name)
*/
int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args)
{
+ int i;
+
sched->ops = args->ops;
sched->credit_limit = args->credit_limit;
sched->name = args->name;
@@ -1072,6 +1111,21 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->score = args->score ? args->score : &sched->_score;
sched->dev = args->dev;
+ if (args->num_rqs > DRM_SCHED_PRIORITY_COUNT) {
+ /* This is a gross violation--tell drivers what the problem is.
+ */
+ dev_err(sched->dev, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n",
+ __func__);
+ return -EINVAL;
+ } else if (sched->sched_rq) {
+ /* Not an error, but warn anyway so drivers can
+ * fine-tune their DRM calling order, and return all
+ * is good.
+ */
+ dev_warn(sched->dev, "%s: scheduler already initialized!\n", __func__);
+ return 0;
+ }
+
if (args->submit_wq) {
sched->submit_wq = args->submit_wq;
sched->own_submit_wq = false;
@@ -1083,7 +1137,19 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->own_submit_wq = true;
}
- drm_sched_rq_init(&sched->rq);
+ sched->num_user_rqs = args->num_rqs;
+ sched->num_rqs = drm_sched_policy != DRM_SCHED_POLICY_FAIR ?
+ args->num_rqs : 1;
+ sched->sched_rq = kzalloc_objs(*sched->sched_rq, args->num_rqs);
+ if (!sched->sched_rq)
+ goto Out_check_own;
+
+ for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
+ sched->sched_rq[i] = kzalloc_obj(*sched->sched_rq[i]);
+ if (!sched->sched_rq[i])
+ goto Out_unroll;
+ drm_sched_rq_init(sched, sched->sched_rq[i]);
+ }
init_waitqueue_head(&sched->job_scheduled);
INIT_LIST_HEAD(&sched->pending_list);
@@ -1099,6 +1165,17 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->ready = true;
return 0;
+Out_unroll:
+ for (--i ; i >= DRM_SCHED_PRIORITY_KERNEL; i--)
+ kfree(sched->sched_rq[i]);
+
+ kfree(sched->sched_rq);
+ sched->sched_rq = NULL;
+Out_check_own:
+ if (sched->own_submit_wq)
+ destroy_workqueue(sched->submit_wq);
+ dev_err(sched->dev, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
+ return -ENOMEM;
}
EXPORT_SYMBOL(drm_sched_init);
@@ -1129,8 +1206,13 @@ static void drm_sched_cancel_remaining_jobs(struct drm_gpu_scheduler *sched)
*/
void drm_sched_fini(struct drm_gpu_scheduler *sched)
{
+ int i;
+
drm_sched_wqueue_stop(sched);
+ for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++)
+ kfree(sched->sched_rq[i]);
+
/* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */
wake_up_all(&sched->job_scheduled);
@@ -1144,6 +1226,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
if (sched->own_submit_wq)
destroy_workqueue(sched->submit_wq);
sched->ready = false;
+ kfree(sched->sched_rq);
+ sched->sched_rq = NULL;
if (!list_empty(&sched->pending_list))
dev_warn(sched->dev, "Tearing down scheduler while jobs are pending!\n");
@@ -1161,28 +1245,35 @@ EXPORT_SYMBOL(drm_sched_fini);
*/
void drm_sched_increase_karma(struct drm_sched_job *bad)
{
+ int i;
+ struct drm_sched_entity *tmp;
+ struct drm_sched_entity *entity;
struct drm_gpu_scheduler *sched = bad->sched;
- struct drm_sched_entity *entity, *tmp;
- struct drm_sched_rq *rq = &sched->rq;
/* don't change @bad's karma if it's from KERNEL RQ,
* because sometimes GPU hang would cause kernel jobs (like VM updating jobs)
* corrupt but keep in mind that kernel jobs always considered good.
*/
- if (bad->s_priority == DRM_SCHED_PRIORITY_KERNEL)
- return;
-
- atomic_inc(&bad->karma);
-
- spin_lock(&rq->lock);
- list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
- if (bad->s_fence->scheduled.context == entity->fence_context) {
- if (entity->guilty)
- atomic_set(entity->guilty, 1);
- break;
+ if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
+ atomic_inc(&bad->karma);
+
+ for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; 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 (entity->guilty)
+ atomic_set(entity->guilty, 1);
+ break;
+ }
+ }
+ spin_unlock(&rq->lock);
+ if (&entity->list != &rq->entities)
+ break;
}
}
- spin_unlock(&rq->lock);
}
EXPORT_SYMBOL(drm_sched_increase_karma);
diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c
index 044546bcb5f8..0464d324d98d 100644
--- a/drivers/gpu/drm/scheduler/sched_rq.c
+++ b/drivers/gpu/drm/scheduler/sched_rq.c
@@ -49,7 +49,7 @@ static void drm_sched_rq_update_prio(struct drm_sched_rq *rq)
rq->head_prio = prio;
}
-static void drm_sched_rq_remove_tree_locked(struct drm_sched_entity *entity,
+static void drm_sched_rq_remove_fifo_locked(struct drm_sched_entity *entity,
struct drm_sched_rq *rq)
{
lockdep_assert_held(&entity->lock);
@@ -62,7 +62,7 @@ static void drm_sched_rq_remove_tree_locked(struct drm_sched_entity *entity,
}
}
-static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity,
+static void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity,
struct drm_sched_rq *rq,
ktime_t ts)
{
@@ -74,7 +74,7 @@ static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity,
lockdep_assert_held(&entity->lock);
lockdep_assert_held(&rq->lock);
- drm_sched_rq_remove_tree_locked(entity, rq);
+ drm_sched_rq_remove_fifo_locked(entity, rq);
entity->oldest_job_waiting = ts;
@@ -85,15 +85,18 @@ static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity,
/**
* drm_sched_rq_init - initialize a given run queue struct
+ * @sched: scheduler instance to associate with this run queue
* @rq: scheduler run queue
*
* Initializes a scheduler runqueue.
*/
-void drm_sched_rq_init(struct drm_sched_rq *rq)
+void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
+ struct drm_sched_rq *rq)
{
spin_lock_init(&rq->lock);
INIT_LIST_HEAD(&rq->entities);
rq->rb_tree_root = RB_ROOT_CACHED;
+ rq->sched = sched;
rq->head_prio = DRM_SCHED_PRIORITY_INVALID;
}
@@ -162,8 +165,7 @@ drm_sched_entity_restore_vruntime(struct drm_sched_entity *entity,
enum drm_sched_priority rq_prio)
{
struct drm_sched_entity_stats *stats = entity->stats;
- struct drm_gpu_scheduler *sched =
- container_of(entity->rq, typeof(*sched), rq);
+ struct drm_gpu_scheduler *sched = entity->rq->sched;
enum drm_sched_priority prio = entity->priority;
unsigned long avg_us, sched_avg_us;
ktime_t vruntime;
@@ -237,9 +239,15 @@ static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity)
return runtime;
}
+static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity)
+{
+ return drm_sched_entity_update_vruntime(entity);
+}
+
/**
* drm_sched_rq_add_entity - add an entity
* @entity: scheduler entity
+ * @ts: submission timestamp
*
* Adds a scheduler entity to the run queue.
*
@@ -247,11 +255,10 @@ static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity)
* been stopped and cannot be submitted to.
*/
struct drm_gpu_scheduler *
-drm_sched_rq_add_entity(struct drm_sched_entity *entity)
+drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts)
{
struct drm_gpu_scheduler *sched;
struct drm_sched_rq *rq;
- ktime_t ts;
/* Add the entity to the run queue */
spin_lock(&entity->lock);
@@ -263,17 +270,23 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity)
}
rq = entity->rq;
- sched = container_of(rq, typeof(*sched), rq);
spin_lock(&rq->lock);
+ sched = rq->sched;
if (list_empty(&entity->list)) {
atomic_inc(sched->score);
list_add_tail(&entity->list, &rq->entities);
}
- ts = drm_sched_rq_get_min_vruntime(rq);
- ts = drm_sched_entity_restore_vruntime(entity, ts, rq->head_prio);
- drm_sched_rq_update_tree_locked(entity, rq, ts);
+ if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) {
+ ts = drm_sched_rq_get_min_vruntime(rq);
+ ts = drm_sched_entity_restore_vruntime(entity, ts,
+ rq->head_prio);
+ } else if (drm_sched_policy == DRM_SCHED_POLICY_RR) {
+ ts = entity->rr_ts;
+ }
+
+ drm_sched_rq_update_fifo_locked(entity, rq, ts);
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
@@ -291,8 +304,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity)
void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
struct drm_sched_entity *entity)
{
- struct drm_gpu_scheduler *sched = container_of(rq, typeof(*sched), rq);
-
lockdep_assert_held(&entity->lock);
if (list_empty(&entity->list))
@@ -300,14 +311,30 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
spin_lock(&rq->lock);
- atomic_dec(sched->score);
+ atomic_dec(rq->sched->score);
list_del_init(&entity->list);
- drm_sched_rq_remove_tree_locked(entity, rq);
+ drm_sched_rq_remove_fifo_locked(entity, rq);
spin_unlock(&rq->lock);
}
+static ktime_t
+drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq,
+ struct drm_sched_entity *entity)
+{
+ ktime_t ts;
+
+ lockdep_assert_held(&entity->lock);
+ lockdep_assert_held(&rq->lock);
+
+ ts = ktime_add_ns(rq->rr_ts, 1);
+ entity->rr_ts = ts;
+ rq->rr_ts = ts;
+
+ return ts;
+}
+
/**
* drm_sched_rq_pop_entity - pops an entity
* @entity: scheduler entity
@@ -330,22 +357,32 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
if (next_job) {
ktime_t ts;
- ts = drm_sched_entity_update_vruntime(entity);
- drm_sched_rq_update_tree_locked(entity, rq, ts);
+ if (drm_sched_policy == DRM_SCHED_POLICY_FAIR)
+ ts = drm_sched_entity_get_job_ts(entity);
+ else if (drm_sched_policy == DRM_SCHED_POLICY_FIFO)
+ ts = next_job->submit_ts;
+ else
+ ts = drm_sched_rq_next_rr_ts(rq, entity);
+
+ drm_sched_rq_update_fifo_locked(entity, rq, ts);
} else {
- ktime_t min_vruntime;
+ drm_sched_rq_remove_fifo_locked(entity, rq);
- drm_sched_rq_remove_tree_locked(entity, rq);
- min_vruntime = drm_sched_rq_get_min_vruntime(rq);
- drm_sched_entity_save_vruntime(entity, min_vruntime);
+ if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) {
+ ktime_t min_vruntime;
+
+ min_vruntime = drm_sched_rq_get_min_vruntime(rq);
+ drm_sched_entity_save_vruntime(entity, min_vruntime);
+ }
}
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
}
/**
- * drm_sched_select_entity - Select an entity which provides a job to run
+ * drm_sched_rq_select_entity - Select an entity which provides a job to run
* @sched: the gpu scheduler
+ * @rq: scheduler run queue to check.
*
* Find oldest waiting ready entity.
*
@@ -354,9 +391,9 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
* its job; return NULL, if no ready entity was found.
*/
struct drm_sched_entity *
-drm_sched_select_entity(struct drm_gpu_scheduler *sched)
+drm_sched_rq_select_entity(struct drm_gpu_scheduler *sched,
+ struct drm_sched_rq *rq)
{
- struct drm_sched_rq *rq = &sched->rq;
struct rb_node *rb;
spin_lock(&rq->lock);
diff --git a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
index 14403a762335..8e9ae7d980eb 100644
--- a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
+++ b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
@@ -290,6 +290,7 @@ struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout)
{
struct drm_sched_init_args args = {
.ops = &drm_mock_scheduler_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = U32_MAX,
.hang_limit = 1,
.timeout = timeout,
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 4032c6ad45bc..e384cbd0cbf7 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -3764,10 +3764,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
sor->num_settings = sor->soc->num_settings;
sor->pmc = devm_tegra_pmc_get(&pdev->dev);
- if (IS_ERR(sor->pmc)) {
- err = PTR_ERR(sor->pmc);
- goto put_aux;
- }
+ if (IS_ERR(sor->pmc))
+ return PTR_ERR(sor->pmc);
np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
if (np) {
diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c
index 2fc47f3b463b..7a374e462348 100644
--- a/drivers/gpu/drm/tests/drm_exec_test.c
+++ b/drivers/gpu/drm/tests/drm_exec_test.c
@@ -180,19 +180,27 @@ static void test_multiple_loops(struct kunit *test)
{
struct drm_exec exec;
- drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
- drm_exec_until_all_locked(&exec)
{
- break;
+ __label__ drm_exec_retry;
+
+ drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
+ drm_exec_until_all_locked(&exec)
+ {
+ break;
+ }
+ drm_exec_fini(&exec);
}
- drm_exec_fini(&exec);
- drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
- drm_exec_until_all_locked(&exec)
{
- break;
+ __label__ drm_exec_retry;
+
+ drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
+ drm_exec_until_all_locked(&exec)
+ {
+ break;
+ }
+ drm_exec_fini(&exec);
}
- drm_exec_fini(&exec);
KUNIT_SUCCEED(test);
}
diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c
index 44a190109249..5e69ff1d1ad6 100644
--- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c
+++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c
@@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test)
sg_init_one(sgt->sgl, buf, TEST_SIZE);
/*
- * Set the DMA mask to 64-bits and map the sgtables
- * otherwise drm_gem_shmem_free will cause a warning
- * on debug kernels.
+ * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on
+ * debug kernels.
*/
- ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64));
- KUNIT_ASSERT_EQ(test, ret, 0);
-
ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
@@ -352,12 +348,20 @@ static int drm_gem_shmem_test_init(struct kunit *test)
{
struct device *dev;
struct drm_device *drm_dev;
+ int ret;
/* Allocate a parent device */
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/*
+ * Set the DMA mask to 64-bits to avoid intermittent failures calling
+ * drm_gem_shmem_get_pages_sgt().
+ */
+ ret = dma_set_mask(dev, DMA_BIT_MASK(64));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /*
* The DRM core will automatically initialize the GEM core and create
* a DRM Memory Manager object which provides an address space pool
* for GEM objects allocation.
diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
index cdd35af49892..ef71b9957961 100644
--- a/drivers/gpu/drm/tiny/appletbdrm.c
+++ b/drivers/gpu/drm/tiny/appletbdrm.c
@@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888, /* emulated */
};
-static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
- struct drm_atomic_commit *state)
+static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane,
+ struct drm_plane_state *new_plane_state)
{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
- struct drm_crtc *new_crtc = new_plane_state->crtc;
- struct drm_crtc_state *new_crtc_state = NULL;
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state);
+ size_t frames_size = 0;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
- size_t frames_size = 0;
size_t request_size;
- int ret;
-
- if (new_crtc)
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
- ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
- DRM_PLANE_NO_SCALING,
- DRM_PLANE_NO_SCALING,
- false, false);
- if (ret)
- return ret;
- else if (!new_plane_state->visible)
- return 0;
-
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage));
}
@@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
appletbdrm_state->request_size = request_size;
appletbdrm_state->frames_size = frames_size;
+ return drm_gem_begin_shadow_fb_access(plane, new_plane_state);
+}
+
+static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *new_crtc = new_plane_state->crtc;
+ struct drm_crtc_state *new_crtc_state = NULL;
+ int ret;
+
+ if (new_crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
+
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+ else if (!new_plane_state->visible)
+ return 0;
+
return 0;
}
@@ -468,7 +474,7 @@ end_fb_cpu_access:
}
static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
- struct drm_atomic_commit *old_state)
+ struct drm_atomic_commit *old_state)
{
struct appletbdrm_device *adev = drm_to_adev(plane->dev);
struct drm_device *drm = plane->dev;
@@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = {
- DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access,
+ .end_fb_access = drm_gem_end_shadow_fb_access,
.atomic_check = appletbdrm_primary_plane_helper_atomic_check,
.atomic_update = appletbdrm_primary_plane_helper_atomic_update,
.atomic_disable = appletbdrm_primary_plane_helper_atomic_disable,
diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c
index 81df4cb5606b..3c067aadc52d 100644
--- a/drivers/gpu/drm/ttm/ttm_backup.c
+++ b/drivers/gpu/drm/ttm/ttm_backup.c
@@ -6,9 +6,10 @@
#include <drm/ttm/ttm_backup.h>
#include <linux/export.h>
-#include <linux/page-flags.h>
#include <linux/swap.h>
+#include "ttm_pool_internal.h"
+
/*
* Need to map shmem indices to handle since a handle value
* of 0 means error, following the swp_entry_t convention.
@@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
}
/**
- * ttm_backup_backup_page() - Backup a page
+ * ttm_backup_backup_folio() - Backup a folio
* @backup: The struct backup pointer to use.
- * @page: The page to back up.
- * @writeback: Whether to perform immediate writeback of the page.
+ * @folio: The folio to back up.
+ * @order: The allocation order of @folio. Since TTM allocates higher-order
+ * pages without __GFP_COMP, folio_nr_pages(@folio) would always
+ * return 1; the caller must pass the true order explicitly.
+ * @writeback: Whether to perform immediate writeback of the folio's pages.
* This may have performance implications.
- * @idx: A unique integer for each page and each struct backup.
+ * @idx: A unique integer for the first page of the folio and each struct backup.
* This allows the backup implementation to avoid managing
* its address space separately.
- * @page_gfp: The gfp value used when the page was allocated.
- * This is used for accounting purposes.
+ * @folio_gfp: The gfp value used when the folio was allocated.
+ * Currently unused.
* @alloc_gfp: The gfp to be used when allocating memory.
+ * @nr_pages_backed: Output. On a successful return, set to the number of
+ * pages actually backed up, which may be less than (1 << @order)
+ * if an -ENOMEM was encountered mid-folio.
*
* Context: If called from reclaim context, the caller needs to
* assert that the shrinker gfp has __GFP_FS set, to avoid
@@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
* that the shrinker gfp has __GFP_IO set, since without it,
* we're not allowed to start backup IO.
*
- * Return: A handle on success. Negative error code on failure.
- *
- * Note: This function could be extended to back up a folio and
- * implementations would then split the folio internally if needed.
- * Drawback is that the caller would then have to keep track of
- * the folio size- and usage.
+ * Return: A handle for the first backed-up page on success (handles for
+ * subsequent pages follow sequentially). -ENOMEM if no pages could be backed
+ * up. Any other negative error code if a non-ENOMEM failure occurred; in that
+ * case any pages backed up so far are truncated before returning.
*/
s64
-ttm_backup_backup_page(struct file *backup, struct page *page,
- bool writeback, pgoff_t idx, gfp_t page_gfp,
- gfp_t alloc_gfp)
+ttm_backup_backup_folio(struct file *backup, struct folio *folio,
+ unsigned int order, bool writeback, pgoff_t idx,
+ gfp_t folio_gfp, gfp_t alloc_gfp,
+ pgoff_t *nr_pages_backed)
{
struct address_space *mapping = backup->f_mapping;
- unsigned long handle = 0;
+ int nr_pages = 1 << order;
struct folio *to_folio;
- int ret;
-
- to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
- if (IS_ERR(to_folio))
- return PTR_ERR(to_folio);
-
- folio_mark_accessed(to_folio);
- folio_lock(to_folio);
- folio_mark_dirty(to_folio);
- copy_highpage(folio_file_page(to_folio, idx), page);
- handle = ttm_backup_shmem_idx_to_handle(idx);
-
- if (writeback && !folio_mapped(to_folio) &&
- folio_clear_dirty_for_io(to_folio)) {
- folio_set_reclaim(to_folio);
- ret = shmem_writeout(to_folio, NULL, NULL);
- if (!folio_test_writeback(to_folio))
- folio_clear_reclaim(to_folio);
+ int ret, i;
+
+ *nr_pages_backed = 0;
+
+ for (i = 0; i < nr_pages; ) {
+ int to_nr, j;
+
/*
- * If writeout succeeds, it unlocks the folio. errors
- * are otherwise dropped, since writeout is only best
- * effort here.
+ * Only inject past the first subpage so *nr_pages_backed is
+ * always > 0 here, matching a genuine mid-compound -ENOMEM
+ * and driving the caller's reactive split fallback instead
+ * of an early, no-progress failure.
*/
- if (ret)
+ if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i &&
+ ttm_backup_fault_inject_folio())
+ to_folio = ERR_PTR(-ENOMEM);
+ else
+ to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp);
+ if (IS_ERR(to_folio)) {
+ int err = PTR_ERR(to_folio);
+
+ if (err == -ENOMEM && *nr_pages_backed)
+ return ttm_backup_shmem_idx_to_handle(idx);
+
+ if (*nr_pages_backed) {
+ shmem_truncate_range(file_inode(backup),
+ (loff_t)idx << PAGE_SHIFT,
+ ((loff_t)(idx + i) << PAGE_SHIFT) - 1);
+ /*
+ * The pages just truncated are no longer
+ * backed up; don't let the caller mistake
+ * them for valid handles.
+ */
+ *nr_pages_backed = 0;
+ }
+ return err;
+ }
+
+ to_nr = min_t(int, nr_pages - i,
+ folio_next_index(to_folio) - (idx + i));
+
+ folio_mark_accessed(to_folio);
+ folio_lock(to_folio);
+ folio_mark_dirty(to_folio);
+
+ for (j = 0; j < to_nr; j++)
+ copy_highpage(folio_file_page(to_folio, idx + i + j),
+ folio_page(folio, i + j));
+
+ if (writeback && !folio_mapped(to_folio) &&
+ folio_clear_dirty_for_io(to_folio)) {
+ folio_set_reclaim(to_folio);
+ ret = shmem_writeout(to_folio, NULL, NULL);
+ if (!folio_test_writeback(to_folio))
+ folio_clear_reclaim(to_folio);
+ if (ret == AOP_WRITEPAGE_ACTIVATE)
+ folio_unlock(to_folio);
+ } else {
folio_unlock(to_folio);
- } else {
- folio_unlock(to_folio);
- }
+ }
- folio_put(to_folio);
+ folio_put(to_folio);
+ i += to_nr;
+ *nr_pages_backed = i;
+ }
- return handle;
+ return ttm_backup_shmem_idx_to_handle(idx);
}
/**
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 3d5f2ae0a456..1bf37023fed6 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -53,8 +53,23 @@
#ifdef CONFIG_FAULT_INJECTION
#include <linux/fault-inject.h>
static DECLARE_FAULT_ATTR(backup_fault_inject);
+
+/*
+ * Exposed to ttm_backup.c so a mid-compound subpage can be made to fail
+ * with -ENOMEM, exercising the reactive split-and-retry fallback in
+ * ttm_pool_backup() for high-order backups.
+ */
+bool ttm_backup_fault_inject_folio(void)
+{
+ return should_fail(&backup_fault_inject, 1);
+}
#else
#define should_fail(...) false
+
+bool ttm_backup_fault_inject_folio(void)
+{
+ return false;
+}
#endif
/**
@@ -492,7 +507,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p)
/**
* DOC: Partial backup and restoration of a struct ttm_tt.
*
- * Swapout using ttm_backup_backup_page() and swapin using
+ * Swapout using ttm_backup_backup_folio() and swapin using
* ttm_backup_copy_page() may fail.
* The former most likely due to lack of swap-space or memory, the latter due
* to lack of memory or because of signal interruption during waits.
@@ -1050,12 +1065,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
{
struct file *backup = tt->backup;
struct page *page;
- unsigned long handle;
gfp_t alloc_gfp;
gfp_t gfp;
int ret = 0;
pgoff_t shrunken = 0;
- pgoff_t i, num_pages;
+ pgoff_t i, j, num_pages, npages;
+ pgoff_t nr_backed;
if (WARN_ON(ttm_tt_is_backed_up(tt)))
return -EINVAL;
@@ -1065,9 +1080,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
return -EBUSY;
#ifdef CONFIG_X86
- /* Anything returned to the system needs to be cached. */
- if (tt->caching != ttm_cached)
- set_pages_array_wb(tt->pages, tt->num_pages);
+ /* Anything returned to the system needs to be cached. Walk allocations
+ * skipping NULL pages and issue set_pages_array_wb() per contiguous run.
+ */
+ if (tt->caching != ttm_cached) {
+ pgoff_t run_start = 0, run_count = 0;
+
+ for (i = 0; i < tt->num_pages; i += num_pages) {
+ page = tt->pages[i];
+ if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
+ if (run_count) {
+ set_pages_array_wb(&tt->pages[run_start],
+ run_count);
+ run_count = 0;
+ }
+ num_pages = 1;
+ continue;
+ }
+ num_pages = 1UL << ttm_pool_page_order(pool, page);
+ if (!run_count)
+ run_start = i;
+ run_count += num_pages;
+ }
+ if (run_count)
+ set_pages_array_wb(&tt->pages[run_start], run_count);
+ }
#endif
if (tt->dma_address || flags->purge) {
@@ -1075,7 +1112,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
unsigned int order;
page = tt->pages[i];
- if (unlikely(!page)) {
+ if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
num_pages = 1;
continue;
}
@@ -1111,26 +1148,74 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1))
num_pages = DIV_ROUND_UP(num_pages, 2);
- for (i = 0; i < num_pages; ++i) {
- s64 shandle;
+ for (i = 0; i < num_pages; i += npages) {
+ unsigned int order;
+ s64 handle;
+ npages = 1;
page = tt->pages[i];
if (unlikely(!page))
continue;
- ttm_pool_split_for_swap(pool, page);
+ /* Already-handled entry from a previous attempt. */
+ if (unlikely(ttm_backup_page_ptr_is_handle(page)))
+ continue;
- shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
- gfp, alloc_gfp);
- if (shandle < 0) {
- /* We allow partially shrunken tts */
- ret = shandle;
+ order = ttm_pool_page_order(pool, page);
+ npages = 1UL << order;
+
+ /*
+ * We don't allow dipping kernel reserves for high order backup
+ */
+ if (order)
+ alloc_gfp |= __GFP_NOMEMALLOC;
+ else
+ alloc_gfp &= ~__GFP_NOMEMALLOC;
+
+ /*
+ * Back up the compound atomically at its native order. If
+ * fault injection truncated num_pages mid-compound, skip
+ * the partial tail rather than splitting.
+ */
+ if (unlikely(i + npages > num_pages))
+ break;
+
+ handle = ttm_backup_backup_folio(backup, page_folio(page),
+ order, flags->writeback, i,
+ gfp, alloc_gfp,
+ &nr_backed);
+ /*
+ * Zero progress on this compound (whether order 0 or a
+ * high-order compound that failed before backing up even
+ * its first subpage) is unrecoverable: bail out rather than
+ * looping forever with npages == nr_backed == 0 below.
+ */
+ if (unlikely(handle < 0 && !nr_backed)) {
+ ret = handle;
break;
}
- handle = shandle;
- tt->pages[i] = ttm_backup_handle_to_page_ptr(handle);
- __free_pages_gpu_account(page, 0, false);
- shrunken++;
+
+ for (j = 0; j < nr_backed; j++)
+ tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j);
+
+ shrunken += nr_backed;
+
+ if (unlikely(nr_backed < npages)) {
+ /*
+ * Partial OOM backup: split the compound and free the
+ * subpages whose content is now in shmem. Continue the
+ * loop from the first un-backed order-0 page.
+ */
+ ttm_pool_split_for_swap(pool, page);
+ for (j = 0; j < nr_backed; j++)
+ __free_pages_gpu_account(page + j, 0, false);
+ npages = nr_backed;
+ continue;
+ }
+
+ /* Fully backed up: free at native order. */
+ page->private = 0;
+ __free_pages_gpu_account(page, order, false);
}
return shrunken ? shrunken : ret;
diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h
index 24c179fd69d1..cbb17a2129fe 100644
--- a/drivers/gpu/drm/ttm/ttm_pool_internal.h
+++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h
@@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool)
return pool->alloc_flags & 0xff;
}
+/*
+ * Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault
+ * should be injected mid-compound to test the reactive split-and-retry
+ * fallback in ttm_pool_backup(). Always returns false when
+ * CONFIG_FAULT_INJECTION is disabled.
+ */
+bool ttm_backup_fault_inject_folio(void);
+
#endif
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 8c46ed09f5c4..29fa14be130b 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -206,6 +206,12 @@ struct v3d_dev {
*/
struct mutex reset_lock;
+ /* Ordered workqueue shared by every queue's scheduler timeout work.
+ * V3D reset is global to all queues, so the timeout handlers must not
+ * run concurrently.
+ */
+ struct workqueue_struct *reset_wq;
+
/* Lock taken when creating and pushing the GPU scheduler
* jobs, to keep the sched-fence seqnos in order.
*/
@@ -623,6 +629,8 @@ extern bool super_pages;
void v3d_init_hw_state(struct v3d_dev *v3d);
int v3d_gem_init(struct drm_device *dev);
void v3d_gem_destroy(struct drm_device *dev);
+void v3d_idle_axi(struct v3d_dev *v3d, int core);
+void v3d_idle_gca(struct v3d_dev *v3d);
void v3d_reset_sms(struct v3d_dev *v3d);
void v3d_reset(struct v3d_dev *v3d);
void v3d_invalidate_caches(struct v3d_dev *v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index 18c941f15e2e..bc88ebdcdbea 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -36,9 +36,21 @@ v3d_init_core(struct v3d_dev *v3d, int core)
V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0);
}
-static void
+void
v3d_idle_axi(struct v3d_dev *v3d, int core)
{
+ if (v3d->ver >= V3D_GEN_71) {
+ V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
+
+ if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) &
+ (V3D_GMP_STATUS_RD_COUNT_MASK |
+ V3D_GMP_STATUS_WR_COUNT_MASK |
+ V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) {
+ drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n");
+ }
+ return;
+ }
+
V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) &
@@ -49,7 +61,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core)
}
}
-static void
+void
v3d_idle_gca(struct v3d_dev *v3d)
{
if (v3d->ver >= V3D_GEN_41)
diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c
index ade8e932fb9c..f608e6d5516a 100644
--- a/drivers/gpu/drm/v3d/v3d_power.c
+++ b/drivers/gpu/drm/v3d/v3d_power.c
@@ -56,8 +56,15 @@ int v3d_power_suspend(struct device *dev)
v3d_clean_caches(v3d);
+ /* Wait until V3D has no active or pending AXI transactions. */
+ v3d_idle_axi(v3d, 0);
+ v3d_idle_gca(v3d);
+
ret = v3d_suspend_sms(v3d);
if (ret) {
+ /* Staying active: undo the GMP STOP_REQ from v3d_idle_axi(). */
+ V3D_WRITE(V3D_GMP_CFG(v3d->ver),
+ V3D_READ(V3D_GMP_CFG(v3d->ver)) & ~V3D_GMP_CFG_STOP_REQ);
v3d_irq_enable(v3d);
return ret;
}
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index c49b71b6e6ee..e6283a75bcdf 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -826,8 +826,10 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op
enum v3d_queue queue, const char *name)
{
struct drm_sched_init_args args = {
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.timeout = msecs_to_jiffies(500),
+ .timeout_wq = v3d->reset_wq,
.dev = v3d->drm.dev,
};
@@ -842,9 +844,13 @@ v3d_sched_init(struct v3d_dev *v3d)
{
int ret;
+ v3d->reset_wq = alloc_ordered_workqueue("v3d_reset", 0);
+ if (!v3d->reset_wq)
+ return -ENOMEM;
+
ret = v3d_queue_sched_init(v3d, &v3d_bin_sched_ops, V3D_BIN, "v3d_bin");
if (ret)
- return ret;
+ goto fail;
ret = v3d_queue_sched_init(v3d, &v3d_render_sched_ops, V3D_RENDER,
"v3d_render");
@@ -887,4 +893,6 @@ v3d_sched_fini(struct v3d_dev *v3d)
if (v3d->queue[q].sched.ready)
drm_sched_fini(&v3d->queue[q].sched);
}
+
+ destroy_workqueue(v3d->reset_wq);
}
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index 623df9d5bbee..834d52030979 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -713,6 +713,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
sizeof(indirect_csd.wg_uniform_offsets));
info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect);
+ if (!info->indirect)
+ return -ENOENT;
return 0;
}
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 2161761b1f22..49ea2ed0996b 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -732,9 +732,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct
{
struct vc4_bo *bo = to_vc4_bo(obj);
- if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
- DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
- return -EINVAL;
+ if (bo->validated_shader) {
+ if (vma->vm_flags & VM_WRITE) {
+ DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
+ return -EINVAL;
+ }
+
+ vm_flags_clear(vma, VM_MAYWRITE);
}
mutex_lock(&bo->madv_lock);
@@ -1044,7 +1048,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)
struct vc4_dev *vc4 = to_vc4_dev(dev);
int i;
- timer_delete(&vc4->bo_cache.time_timer);
+ timer_shutdown_sync(&vc4->bo_cache.time_timer);
cancel_work_sync(&vc4->bo_cache.time_work);
vc4_bo_cache_purge(dev);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 184d51ea3fa5..e715147d091c 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
static void vc4_hvs_unbind(struct device *dev, struct device *master,
void *data)
{
- struct drm_device *drm = dev_get_drvdata(master);
+ struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_hvs *hvs = vc4->hvs;
struct drm_mm_node *node, *next;
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 8e5141bb5075..7877d493d80e 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct *work)
vc4->bin_alloc_overflow = BIT(bin_bo_slot);
V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size);
- V3D_WRITE(V3D_BPOS, bo->base.base.size);
+ V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size);
V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index d31b906cb8e7..f32410420d3e 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -494,7 +494,7 @@ err_put_runtime_pm:
static void vc4_v3d_unbind(struct device *dev, struct device *master,
void *data)
{
- struct drm_device *drm = dev_get_drvdata(master);
+ struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
vc4_irq_uninstall(drm);
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
index 7f2fadfde7a8..d2a65c968b1f 100644
--- a/drivers/gpu/drm/vc4/vc4_validate.c
+++ b/drivers/gpu/drm/vc4/vc4_validate.c
@@ -385,6 +385,23 @@ validate_tile_binning_config(VALIDATE_ARGS)
return -EINVAL;
}
+ /* The tile state data array is 48 bytes per tile, and we put it at
+ * the start of a BO containing both it and the tile alloc.
+ */
+ tile_state_size = 48 * tile_count;
+
+ /* Since the tile alloc array will follow us, align. */
+ tile_state_size = roundup(tile_state_size, 4096);
+
+ /* Reject configurations whose tile state would leave no room for
+ * the tile alloc pool that follows it in the slot.
+ */
+ if (tile_state_size >= vc4->bin_alloc_size) {
+ DRM_DEBUG("Tile binning config of %dx%d too large\n",
+ exec->bin_tiles_x, exec->bin_tiles_y);
+ return -EINVAL;
+ }
+
bin_slot = vc4_v3d_get_bin_slot(vc4);
if (bin_slot < 0) {
if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
@@ -400,13 +417,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
exec->bin_slots |= BIT(bin_slot);
bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size;
- /* The tile state data array is 48 bytes per tile, and we put it at
- * the start of a BO containing both it and the tile alloc.
- */
- tile_state_size = 48 * tile_count;
+ exec->tile_alloc_offset = bin_addr + tile_state_size;
- /* Since the tile alloc array will follow us, align. */
- exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
+ /* The TSDA area must be zeroed out before use, otherwise the PTB might
+ * consume a stale tile state.
+ */
+ memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0,
+ tile_state_size);
*(uint8_t *)(validated + 14) =
((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 435d37d36034..66c3f6f74e9c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
if (!vgdev->has_virgl_3d)
return;
- objs = virtio_gpu_array_alloc(1);
- if (!objs)
- return;
- virtio_gpu_array_add_obj(objs, obj);
+ if (vfpriv->context_created) {
+ objs = virtio_gpu_array_alloc(1);
+ if (!objs)
+ return;
+ virtio_gpu_array_add_obj(objs, obj);
- virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
- objs);
+ virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
+ objs);
+ }
virtio_gpu_notify(vgdev);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 223d5d513b5a..1ffe1e431f65 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -50,7 +50,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
virtio_gpu_cmd_get_edids(vgdev);
virtio_gpu_cmd_get_display_info(vgdev);
virtio_gpu_notify(vgdev);
- drm_helper_hpd_irq_event(vgdev->ddev);
+ wait_event_timeout(vgdev->resp_wq,
+ !vgdev->display_info_pending,
+ 5 * HZ);
+ drm_kms_helper_hotplug_event(vgdev->ddev);
}
events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 905b1f42cd98..f2b0ab0f610e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -880,9 +880,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
vgdev->display_info_pending = false;
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
-
- if (!drm_helper_hpd_irq_event(vgdev->ddev))
- drm_kms_helper_hotplug_event(vgdev->ddev);
}
static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c
index 2421b0dd057c..f9042bafdc93 100644
--- a/drivers/gpu/drm/vmwgfx/ttm_object.c
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.c
@@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
if (IS_ERR(dma_buf))
return PTR_ERR(dma_buf);
- if (dma_buf->ops != &tdev->ops)
- return -ENOSYS;
+ if (dma_buf->ops != &tdev->ops) {
+ ret = -ENOSYS;
+ goto out;
+ }
prime = (struct ttm_prime_object *) dma_buf->priv;
base = &prime->base;
*handle = base->handle;
ret = ttm_ref_object_add(tfile, base, NULL, false);
+out:
dma_buf_put(dma_buf);
return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
index 135b75a3e013..56f965ec99dc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -30,6 +30,7 @@
#include "vmwgfx_bo.h"
#include <linux/highmem.h>
+#include <linux/overflow.h>
/*
* Template that implements find_first_diff() for a generic
@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
container_of(dst->tbo.bdev, struct vmw_private, bdev);
size_t dst_size = dst->tbo.resource->size;
size_t src_size = src->tbo.resource->size;
+ size_t dst_end, src_end;
struct iosys_map dst_map = {0};
struct iosys_map src_map = {0};
+ bool dst_mapped = false;
+ bool src_mapped = false;
int ret, i;
int x_in_bytes;
u8 *vsrc;
u8 *vdst;
+ if (!height || !width_in_bytes)
+ return 0;
+
+ if (!dst_stride || !src_stride)
+ return -EINVAL;
+ if (dst_stride < width_in_bytes || src_stride < width_in_bytes)
+ return -EINVAL;
+ if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) ||
+ check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) ||
+ check_add_overflow((size_t)dst_offset, dst_end, &dst_end) ||
+ dst_end > dst_size ||
+ check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) ||
+ check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) ||
+ check_add_overflow((size_t)src_offset, src_end, &src_end) ||
+ src_end > src_size) {
+ drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n");
+ return -EINVAL;
+ }
+
vsrc = map_external(src, &src_map);
if (!vsrc) {
drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n");
ret = -ENOMEM;
goto out;
}
+ src_mapped = true;
vdst = map_external(dst, &dst_map);
if (!vdst) {
@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
ret = -ENOMEM;
goto out;
}
+ dst_mapped = true;
vsrc += src_offset;
vdst += dst_offset;
- if (src_stride == dst_stride) {
- dst_size -= dst_offset;
- src_size -= src_offset;
- memcpy(vdst, vsrc,
- min(dst_stride * height, min(dst_size, src_size)));
+ if (src_stride == dst_stride && width_in_bytes == dst_stride) {
+ memcpy(vdst, vsrc, dst_stride * (size_t)height);
} else {
- WARN_ON(dst_stride < width_in_bytes);
for (i = 0; i < height; ++i) {
memcpy(vdst, vsrc, width_in_bytes);
vsrc += src_stride;
@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
ret = 0;
out:
- unmap_external(src, &src_map);
- unmap_external(dst, &dst_map);
+ if (src_mapped)
+ unmap_external(src, &src_map);
+ if (dst_mapped)
+ unmap_external(dst, &dst_map);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
index b010fc7ca68e..d1e7df500190 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
@@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
vps->base.crtc_w, vps->base.crtc_h);
struct vmw_bo *vbo = vps->cursor.mob;
+ void *map;
if (!vbo)
return -EINVAL;
@@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
if (unlikely(ret != 0))
return -ENOMEM;
- vmw_bo_map_and_cache(vbo);
+ map = vmw_bo_map_and_cache(vbo);
+ if (!map) {
+ vmw_bo_unmap(vbo);
+ ret = -ENOMEM;
+ }
ttm_bo_unreserve(&vbo->tbo);
- return 0;
+ return ret;
}
/**
@@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane,
!vmw_cursor_buffer_changed(vps, old_vps)) {
vps->cursor.update_type =
VMW_CURSOR_UPDATE_NONE;
- } else {
- vmw_cursor_mob_get(vcp, vps);
- vmw_cursor_mob_map(vps);
+ } else if (vps->cursor.update_type ==
+ VMW_CURSOR_UPDATE_MOB &&
+ (vmw_cursor_mob_get(vcp, vps) ||
+ vmw_cursor_mob_map(vps))) {
+ /*
+ * Reset the cursor to avoid crashes later.
+ */
+ vps->cursor.update_type =
+ VMW_CURSOR_UPDATE_NONE;
}
}
}
@@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane,
"surface not suitable for cursor\n");
return -EINVAL;
}
+ } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY ||
+ update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 cursor_max_dim =
+ vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION);
+
+ if (new_state->crtc_w > cursor_max_dim ||
+ new_state->crtc_h > cursor_max_dim) {
+ drm_warn(&vmw->drm,
+ "Cursor dimensions (%d, %d) exceed device max %u\n",
+ new_state->crtc_w, new_state->crtc_h,
+ cursor_max_dim);
+ return -EINVAL;
+ }
+
+ if (update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 mob_max_size =
+ vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE);
+ u64 mob_size = (u64)new_state->crtc_w *
+ new_state->crtc_h * sizeof(u32) +
+ sizeof(SVGAGBCursorHeader);
+
+ if (mob_size > mob_max_size) {
+ drm_warn(&vmw->drm,
+ "Cursor MOB size %llu exceeds device max %u\n",
+ mob_size, mob_max_size);
+ return -EINVAL;
+ }
+ }
}
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index e1f18020170a..a9136a6523cb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv,
SVGA3dCmdHeader *header)
{
VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery);
+ struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context);
struct vmw_bo *vmw_bo;
int ret;
+ if (!ctx_node)
+ return -EINVAL;
+
cmd = container_of(header, typeof(*cmd), header);
/*
@@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv,
return ret;
sw_context->dx_query_mob = vmw_bo;
- sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx;
+ sw_context->dx_query_ctx = ctx_node->ctx;
return 0;
}
@@ -1506,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
bool dirty;
cmd = container_of(header, typeof(*cmd), header);
+
+ if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
+ VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n");
+ return -EINVAL;
+ }
+
suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body +
header->size - sizeof(*suffix));
@@ -1567,11 +1577,17 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,
uint32_t maxnum;
int ret;
+ cmd = container_of(header, typeof(*cmd), header);
+
+ if (unlikely(header->size < sizeof(cmd->body))) {
+ VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n");
+ return -EINVAL;
+ }
+
ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
if (unlikely(ret != 0))
return ret;
- cmd = container_of(header, typeof(*cmd), header);
maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
if (unlikely(cmd->body.numVertexDecls > maxnum)) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 4ef84ff9b638..384c6736cf6b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -367,13 +367,24 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
ret = vmw_fence_obj_wait(fence, false, false,
VMW_FENCE_WAIT_TIMEOUT);
+ spin_lock(&fman->lock);
if (unlikely(ret != 0)) {
+ bool cookie = dma_fence_begin_signalling();
+
list_del_init(&fence->head);
- dma_fence_signal(&fence->base);
+ if (fence->waiter_added) {
+ vmw_seqno_waiter_remove(fman->dev_priv);
+ fence->waiter_added = false;
+ }
+ dma_fence_signal_locked(&fence->base);
+ dma_fence_end_signalling(cookie);
}
BUG_ON(!list_empty(&fence->head));
+ spin_unlock(&fman->lock);
+
dma_fence_put(&fence->base);
+
spin_lock(&fman->lock);
}
spin_unlock(&fman->lock);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
index 45561bc1c9ef..8ab88f388652 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
@@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res)
return;
cur = max(res_start, dirty->start);
- res_end = max(res_end, dirty->end);
+ res_end = min(res_end, dirty->end);
while (cur < res_end) {
unsigned long num;
@@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *vbo)
return;
cur = max(res_start, dirty->start);
- res_end = max(res_end, dirty->end);
+ res_end = min(res_end, dirty->end);
while (cur < res_end) {
unsigned long num;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 388011696941..e3a187a2c7a1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -136,7 +136,7 @@ static void vmw_resource_release(struct kref *kref)
val_buf.num_shared = 0;
res->func->unbind(res, false, &val_buf);
}
- res->guest_memory_size = false;
+ res->guest_memory_dirty = false;
vmw_resource_mob_detach(res);
if (res->dirty)
res->func->dirty_free(res);
@@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw_bo *vbo)
if (!WARN_ON_ONCE(!res->func->unbind))
(void) res->func->unbind(res, res->res_dirty, &val_buf);
- res->guest_memory_size = true;
+ res->guest_memory_dirty = true;
res->res_dirty = false;
vmw_resource_mob_detach(res);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index eca4e3e97eb4..39811cf19db1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -25,6 +25,8 @@
*
**************************************************************************/
+#include <linux/overflow.h>
+
#include <drm/ttm/ttm_placement.h>
#include "vmwgfx_binding.h"
@@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data,
static int vmw_user_shader_alloc(struct vmw_private *dev_priv,
struct vmw_bo *buffer,
size_t shader_size,
- size_t offset,
+ u64 offset,
SVGA3dShaderType shader_type,
uint8_t num_input_sig,
uint8_t num_output_sig,
@@ -739,7 +741,7 @@ out:
static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv,
struct vmw_bo *buffer,
size_t shader_size,
- size_t offset,
+ u64 offset,
SVGA3dShaderType shader_type)
{
struct vmw_shader *shader;
@@ -768,7 +770,7 @@ out_err:
static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
enum drm_vmw_shader_type shader_type_drm,
- u32 buffer_handle, size_t size, size_t offset,
+ u32 buffer_handle, size_t size, u64 offset,
uint8_t num_input_sig, uint8_t num_output_sig,
uint32_t *shader_handle)
{
@@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
int ret;
if (buffer_handle != SVGA3D_INVALID_ID) {
+ u64 end;
+
ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer);
if (unlikely(ret != 0)) {
VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n");
return ret;
}
- if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) {
+ if (check_add_overflow((u64)size, (u64)offset, &end) ||
+ end > buffer->tbo.base.size) {
VMW_DEBUG_USER("Illegal buffer- or shader size.\n");
ret = -EINVAL;
goto out_bad_arg;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b2d3927b5567..bd0563741e89 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res,
static int vmw_gb_surface_destroy(struct vmw_resource *res);
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
- struct drm_vmw_gb_surface_create_ext_req *req,
+ const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv);
static int
@@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data,
*/
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
- struct drm_vmw_gb_surface_create_ext_req *req,
+ const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv)
{
@@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
req->base.svga3d_flags);
/* array_size must be null for non-GL3 host. */
- if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) {
- VMW_DEBUG_USER("SM4 surface not supported.\n");
- return -EINVAL;
+ if (req->base.array_size > 0) {
+ if (has_sm5_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else if (has_sm4_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else {
+ VMW_DEBUG_USER("SM4+ surface not supported.\n");
+ return -EINVAL;
+ }
}
if (!has_sm4_1_context(dev_priv)) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
index 35dc94c3db39..45fde7ec514f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
@@ -309,7 +309,8 @@ int vmw_validation_add_resource(struct vmw_validation_context *ctx,
}
node->res = vmw_resource_reference_unless_doomed(res);
if (!node->res) {
- hash_del_rcu(&node->hash.head);
+ if (ctx->sw_context)
+ hash_del_rcu(&node->hash.head);
return -ESRCH;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
index 7b8163b5e501..3d0d5dfa869f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
@@ -206,14 +206,14 @@ vmw_vkms_init(struct vmw_private *vmw)
vmw->vkms_enabled = false;
ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len);
- if (ret || buf_len > max_buf_len)
- return;
- buffer[buf_len] = '\0';
+ if (!ret && buf_len <= max_buf_len) {
+ buffer[buf_len] = '\0';
- ret = kstrtobool(buffer, &vmw->vkms_enabled);
- if (!ret && vmw->vkms_enabled) {
- ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
- vmw->vkms_enabled = (ret == 0);
+ ret = kstrtobool(buffer, &vmw->vkms_enabled);
+ if (!ret && vmw->vkms_enabled) {
+ ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
+ vmw->vkms_enabled = (ret == 0);
+ }
}
vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0);
@@ -228,7 +228,8 @@ vmw_vkms_init(struct vmw_private *vmw)
void
vmw_vkms_cleanup(struct vmw_private *vmw)
{
- destroy_workqueue(vmw->crc_workq);
+ if (vmw->crc_workq)
+ destroy_workqueue(vmw->crc_workq);
}
bool
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index c6d80e1bd6e7..e8081af5bfc1 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -6,6 +6,7 @@
#ifndef _XE_BO_H_
#define _XE_BO_H_
+#include <drm/drm_prime.h>
#include <drm/ttm/ttm_tt.h>
#include "xe_bo_types.h"
@@ -549,6 +550,19 @@ void xe_bo_dev_fini(struct xe_bo_dev *bo_device);
struct sg_table *xe_bo_sg(struct xe_bo *bo);
+/**
+ * xe_bo_sg_is_contiguous() - Check if a BO's DMA address space is contiguous.
+ * @bo: the BO to check (must have a valid sg table, i.e. !xe_bo_is_vram())
+ * @len: required contiguous length in bytes
+ *
+ * Returns true if the first @len bytes of the BO are mapped to a contiguous
+ * DMA address range.
+ */
+static inline bool xe_bo_sg_is_contiguous(struct xe_bo *bo, size_t len)
+{
+ return drm_prime_get_contiguous_size(xe_bo_sg(bo)) >= len;
+}
+
/*
* xe_sg_segment_size() - Provides upper limit for sg segment size.
* @dev: device pointer
diff --git a/drivers/gpu/drm/xe/xe_dep_scheduler.c b/drivers/gpu/drm/xe/xe_dep_scheduler.c
index 004aac8b89e6..51d99fee9aa5 100644
--- a/drivers/gpu/drm/xe/xe_dep_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_dep_scheduler.c
@@ -78,6 +78,7 @@ xe_dep_scheduler_create(struct xe_device *xe,
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.submit_wq = submit_wq,
+ .num_rqs = 1,
.credit_limit = job_limit,
.timeout = MAX_SCHEDULE_TIMEOUT,
.name = name,
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 38972b6e6d37..cd053de3c6b5 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -802,6 +802,9 @@ static int exec_queue_set_hang_replay_state(struct xe_device *xe,
u64 __user *address = u64_to_user_ptr(value);
void *ptr;
+ if (q->replay_state)
+ return -EINVAL;
+
ptr = vmemdup_user(address, size);
if (XE_IOCTL_DBG(xe, IS_ERR(ptr)))
return PTR_ERR(ptr);
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index cc33ae80e8cf..0d0db66c6ea2 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -328,6 +328,7 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q)
struct drm_gpu_scheduler *sched;
const struct drm_sched_init_args args = {
.ops = &drm_sched_ops,
+ .num_rqs = 1,
.credit_limit = xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES,
.hang_limit = XE_SCHED_HANG_LIMIT,
.timeout = XE_SCHED_JOB_TIMEOUT,
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index 67d8ce368486..9c8004d5dd91 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -66,6 +66,7 @@ int xe_sched_init(struct xe_gpu_scheduler *sched,
const struct drm_sched_init_args args = {
.ops = ops,
.submit_wq = submit_wq,
+ .num_rqs = 1,
.credit_limit = hw_submission,
.hang_limit = hang_limit,
.timeout = timeout,
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index f0ac00586d3a..ff8eee3831aa 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -64,10 +64,14 @@ ads_to_map(struct xe_guc_ads *ads)
/*
* The Additional Data Struct (ADS) has pointers for different buffers used by
- * the GuC. One single gem object contains the ADS struct itself (guc_ads) and
- * all the extra buffers indirectly linked via the ADS struct's entries.
+ * the GuC. One gem object (ads->bo) contains the ADS struct itself (guc_ads)
+ * and most of the extra buffers linked via the ADS struct's entries. The UM
+ * fault queues (PAGE_FAULT, PAGE_FAULT_RESPONSE, ACCESS_COUNTER rings) are
+ * kept in a separate BO (ads->um_queue_bo) so that the full memset of ads->bo
+ * performed on every GT reset does not discard fault descriptors already
+ * written into the rings by the GPU.
*
- * Layout of the ADS blob allocated for the GuC:
+ * Layout of the ADS blob (ads->bo):
*
* +---------------------------------------+ <== base
* | guc_ads |
@@ -99,10 +103,6 @@ ads_to_map(struct xe_guc_ads *ads)
* +---------------------------------------+
* | padding |
* +---------------------------------------+ <== 4K aligned
- * | UM queues |
- * +---------------------------------------+
- * | padding |
- * +---------------------------------------+ <== 4K aligned
* | private data |
* +---------------------------------------+
* | padding |
@@ -156,16 +156,6 @@ static size_t guc_ads_capture_size(struct xe_guc_ads *ads)
return PAGE_ALIGN(ads->capture_size);
}
-static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads)
-{
- struct xe_device *xe = ads_to_xe(ads);
-
- if (!xe->info.has_usm)
- return 0;
-
- return GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX;
-}
-
static size_t guc_ads_private_data_size(struct xe_guc_ads *ads)
{
return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size);
@@ -206,22 +196,12 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads)
return PAGE_ALIGN(offset);
}
-static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads)
-{
- u32 offset;
-
- offset = guc_ads_capture_offset(ads) +
- guc_ads_capture_size(ads);
-
- return PAGE_ALIGN(offset);
-}
-
static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads)
{
size_t offset;
- offset = guc_ads_um_queues_offset(ads) +
- guc_ads_um_queues_size(ads);
+ offset = guc_ads_capture_offset(ads) +
+ guc_ads_capture_size(ads);
return PAGE_ALIGN(offset);
}
@@ -461,6 +441,49 @@ int xe_guc_ads_init(struct xe_guc_ads *ads)
ads->bo = bo;
+ if (xe->info.has_usm) {
+ /*
+ * Allocate a separate BO for the HW fault ring (UM queues).
+ *
+ * Round the size up to the next power of two so that on iGPU
+ * (system memory, no IOMMU) the TTM pool issues a single
+ * alloc_pages(order=N) call, maximising the chance of getting
+ * a physically contiguous block. GuC requires contiguous DPA.
+ */
+ size_t um_size = IS_DGFX(xe) ?
+ GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX :
+ roundup_pow_of_two(GUC_UM_QUEUE_SIZE *
+ GUC_UM_HW_QUEUE_MAX);
+
+ u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_GGTT |
+ XE_BO_FLAG_GGTT_INVALIDATE |
+ XE_BO_FLAG_PINNED_NORESTORE |
+ XE_BO_FLAG_NEEDS_UC;
+
+ bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags);
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
+
+ /*
+ * On pre-Xe3p platforms, GAM (not GuC) accesses the UM queue
+ * ring via base_dpa, which must be a contiguous DMA address
+ * range. Verify that the allocated pages are contiguous in
+ * DMA address space.
+ */
+ if (!xe_bo_is_vram(bo) &&
+ !xe_guc_using_main_gamctrl_queues(ads_to_guc(ads)) &&
+ unlikely(!xe_bo_sg_is_contiguous(bo,
+ GUC_UM_QUEUE_SIZE *
+ GUC_UM_HW_QUEUE_MAX))) {
+ drm_err(&xe->drm,
+ "UM fault queue memory is not contiguous in DMA address space; GAM requires contiguous DPA\n");
+ return -ENOMEM;
+ }
+
+ ads->um_queue_bo = bo;
+ }
+
return 0;
}
ALLOW_ERROR_INJECTION(xe_guc_ads_init, ERRNO); /* See xe_pci_probe() */
@@ -893,7 +916,7 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads)
static void guc_um_init_params(struct xe_guc_ads *ads)
{
- u32 um_queue_offset = guc_ads_um_queues_offset(ads);
+ struct xe_bo *um_bo = ads->um_queue_bo;
struct xe_guc *guc = ads_to_guc(ads);
struct xe_device *xe = ads_to_xe(ads);
u64 base_dpa;
@@ -903,8 +926,14 @@ static void guc_um_init_params(struct xe_guc_ads *ads)
with_dpa = !xe_guc_using_main_gamctrl_queues(guc);
- base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset;
- base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset;
+ if (um_bo) {
+ /* All USM platforms: UM queues in dedicated um_queue_bo */
+ base_ggtt = xe_bo_ggtt_addr(um_bo);
+ base_dpa = xe_bo_main_addr(um_bo, PAGE_SIZE);
+ } else {
+ /* Platform does not support USM: no UM queues, nothing to do */
+ return;
+ }
for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) {
/*
diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h
index 48a8e092023f..845c1fbd93a4 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h
@@ -16,6 +16,11 @@ struct xe_bo;
struct xe_guc_ads {
/** @bo: Xe BO for GuC ads blob */
struct xe_bo *bo;
+ /**
+ * @um_queue_bo: Dedicated BO for the HW fault ring (UM queues).
+ * NULL if the platform does not support USM.
+ */
+ struct xe_bo *um_queue_bo;
/** @golden_lrc_size: golden LRC size */
size_t golden_lrc_size;
/** @regset_size: size of register set passed to GuC for save/restore */
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 3e7c995085d0..35b4e8289b5f 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1096,7 +1096,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc)
* on until it is scheduled, we also read the ENGINE_ID MMIO in the WA BB and
* store it in the PPHSWP.
*/
-#define CONTEXT_ACTIVE 1ULL
+#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVE
static ssize_t setup_utilization_wa(struct xe_lrc *lrc,
struct xe_hw_engine *hwe,
u32 *batch,
@@ -1849,6 +1849,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size)
__xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop));
}
+
+ /*
+ * The ring and the LRC context image are both WC, so the ring tail
+ * update which publishes these writes can become visible to the device
+ * first. Ensure the ring contents are visible before returning.
+ */
+ xe_device_wmb(xe);
}
u64 xe_lrc_descriptor(struct xe_lrc *lrc)
@@ -2726,21 +2733,27 @@ static u64 xe_lrc_update_multi_queue_timestamp(struct xe_lrc *lrc, u64 *old_ts)
static u64 xe_lrc_context_timestamp(struct xe_lrc *lrc)
{
u64 reg_ts, new_ts = lrc->ctx_timestamp;
+ u64 stored;
/* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */
if (IS_SRIOV_VF(lrc_to_xe(lrc)))
return xe_lrc_ctx_timestamp(lrc);
- if (context_active(lrc) &&
- !get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), &reg_ts))
+ /* Safely read CTX_TIMESTAMP: Avoid TOCTOU on LRC-stored CONTEXT_ACTIVE sentinel */
+ stored = xe_lrc_ctx_timestamp(lrc);
+ if (stored != CONTEXT_ACTIVE)
+ return stored;
+
+ /* Context is active: read the live timestamp from the engine's MMIO register */
+ if (!get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), &reg_ts))
new_ts = reg_ts;
- /*
- * If context swicthed out while we were here, just return the latest
- * LRC CTX TIMESTAMP value.
+ /* If the context switched out prefer using the value
+ * from context-save over the stale MMIO read.
*/
- if (!context_active(lrc))
- return xe_lrc_ctx_timestamp(lrc);
+ stored = xe_lrc_ctx_timestamp(lrc);
+ if (stored != CONTEXT_ACTIVE)
+ return stored;
return new_ts;
}
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index 0a3a611391ee..7be5e3da8bc8 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -9,6 +9,13 @@
#include "xe_lrc_types.h"
+/*
+ * Sentinel value stored in lrc->ctx_timestamp while a context is starting.
+ * The hardware hasn't yet written the real CTX_TIMESTAMP, so this is not a
+ * valid elapsed-time sample and must not be used as one.
+ */
+#define XE_LRC_CTX_TIMESTAMP_ACTIVE 1ULL
+
struct drm_printer;
struct xe_bb;
struct xe_device;
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index b3acbcd678b7..9c5384b95c63 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -1594,6 +1594,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg)
config = xchg(&stream->oa_config, config);
drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n",
stream->oa_config->uuid);
+ } else {
+ while (param.num_syncs--)
+ xe_sync_entry_cleanup(&param.syncs[param.num_syncs]);
+ kfree(param.syncs);
}
err_config_put:
@@ -2718,9 +2722,7 @@ static int xe_oa_init_gt(struct xe_gt *gt)
__xe_oa_init_oa_units(gt);
- drmm_mutex_init(&gt_to_xe(gt)->drm, &gt->oa.gt_lock);
-
- return 0;
+ return drmm_mutex_init(&gt_to_xe(gt)->drm, &gt->oa.gt_lock);
}
static void xe_oa_print_gt_oa_units(struct xe_gt *gt)
@@ -2860,7 +2862,10 @@ int xe_oa_init(struct xe_device *xe)
oa->xe = xe;
oa->oa_formats = oa_formats;
- drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock);
+ ret = drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock);
+ if (ret)
+ goto exit;
+
idr_init_base(&oa->metrics_idr, 1);
ret = xe_oa_init_oa_units(oa);
diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c
index fea3d8ceeddb..2fef274a585e 100644
--- a/drivers/gpu/drm/xe/xe_pxp.c
+++ b/drivers/gpu/drm/xe/xe_pxp.c
@@ -8,6 +8,8 @@
#include <drm/drm_managed.h>
#include <uapi/drm/xe_drm.h>
+#include <linux/device.h>
+
#include "xe_bo.h"
#include "xe_bo_types.h"
#include "xe_device_types.h"
@@ -174,16 +176,9 @@ static void mark_termination_in_progress(struct xe_pxp *pxp)
pxp->status = XE_PXP_TERMINATION_IN_PROGRESS;
}
-static void pxp_terminate(struct xe_pxp *pxp)
+static bool pxp_prep_for_termination(struct xe_pxp *pxp)
{
- int ret = 0;
- struct xe_device *xe = pxp->xe;
-
- if (!wait_for_completion_timeout(&pxp->activation,
- msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS)))
- drm_err(&xe->drm, "failed to wait for PXP start before termination\n");
-
- mutex_lock(&pxp->mutex);
+ lockdep_assert_held(&pxp->mutex);
if (pxp->status == XE_PXP_ACTIVE)
pxp->key_instance++;
@@ -192,10 +187,8 @@ static void pxp_terminate(struct xe_pxp *pxp)
* we'll mark the status as needing termination on resume, so no need to
* emit a termination now.
*/
- if (pxp->status == XE_PXP_SUSPENDED) {
- mutex_unlock(&pxp->mutex);
- return;
- }
+ if (pxp->status == XE_PXP_SUSPENDED)
+ return false;
/*
* If we have a termination already in progress, we need to wait for
@@ -205,15 +198,44 @@ static void pxp_terminate(struct xe_pxp *pxp)
*/
if (pxp->status == XE_PXP_TERMINATION_IN_PROGRESS) {
pxp->status = XE_PXP_NEEDS_ADDITIONAL_TERMINATION;
- mutex_unlock(&pxp->mutex);
- return;
+ return false;
}
mark_termination_in_progress(pxp);
- mutex_unlock(&pxp->mutex);
+ return true;
+}
+
+static void pxp_terminate(struct xe_pxp *pxp, bool hw_only)
+{
+ struct xe_device *xe = pxp->xe;
+ int ret = 0;
- pxp_invalidate_queues(pxp);
+ if (!wait_for_completion_timeout(&pxp->activation,
+ msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS)))
+ drm_err(&xe->drm, "failed to wait for PXP start before termination\n");
+
+ if (!hw_only) {
+ bool prep_ok;
+
+ mutex_lock(&pxp->mutex);
+
+ prep_ok = pxp_prep_for_termination(pxp);
+
+ mutex_unlock(&pxp->mutex);
+
+ if (!prep_ok)
+ return;
+
+ pxp_invalidate_queues(pxp);
+ } else {
+ /*
+ * The caller of the HW-only termination should have already
+ * called pxp_prep_for_termination and marked the termination as
+ * in progress.
+ */
+ xe_assert(xe, !completion_done(&pxp->termination));
+ }
ret = pxp_terminate_hw(pxp);
if (ret) {
@@ -259,33 +281,46 @@ static void pxp_terminate_complete(struct xe_pxp *pxp)
mutex_unlock(&pxp->mutex);
}
-static void pxp_irq_work(struct work_struct *work)
+static void pxp_events_work(struct work_struct *work)
{
- struct xe_pxp *pxp = container_of(work, typeof(*pxp), irq.work);
+ struct xe_pxp *pxp = container_of(work, typeof(*pxp), events.work);
struct xe_device *xe = pxp->xe;
+ bool hw_only = false;
u32 events = 0;
- spin_lock_irq(&xe->irq.lock);
- events = pxp->irq.events;
- pxp->irq.events = 0;
- spin_unlock_irq(&xe->irq.lock);
+ events = atomic_xchg(&pxp->events.pending, 0);
if (!events)
return;
/*
- * If we're processing a termination irq while suspending then don't
- * bother, we're going to re-init everything on resume anyway.
+ * If the termination request comes from an irq while we're suspending,
+ * then we can defer it to the resume path instead of waking the device
+ * up.
+ * In the case of the termination on resume the pm reference is taken
+ * in xe_pxp_pm_resume() and released here.
+ * Note that we do not expect both events to be set at the same time,
+ * but if it does happen due to a spurious interrupt we want to behave
+ * as if the only request we got was the one from the resume path; this
+ * is because the termination prep has already been done in
+ * xe_pxp_pm_resume() and it is impossible for any PXP operations to
+ * occur between the prep and the termination completion, so there is no
+ * need for a new SW prep.
*/
- if ((events & PXP_TERMINATION_REQUEST) && !xe_pm_runtime_get_if_active(xe))
+ if (events & PXP_TERMINATION_REQUEST_ON_RESUME) {
+ events &= ~PXP_TERMINATION_REQUEST_IRQ;
+ hw_only = true;
+ }
+
+ if ((events & PXP_TERMINATION_REQUEST_IRQ) && !xe_pm_runtime_get_if_active(xe))
return;
if (events & PXP_TERMINATION_REQUEST) {
- events &= ~PXP_TERMINATION_COMPLETE;
- pxp_terminate(pxp);
+ events &= ~PXP_TERMINATION_COMPLETE_IRQ;
+ pxp_terminate(pxp, hw_only);
}
- if (events & PXP_TERMINATION_COMPLETE)
+ if (events & PXP_TERMINATION_COMPLETE_IRQ)
pxp_terminate_complete(pxp);
if (events & PXP_TERMINATION_REQUEST)
@@ -306,20 +341,18 @@ void xe_pxp_irq_handler(struct xe_device *xe, u16 iir)
return;
}
- lockdep_assert_held(&xe->irq.lock);
-
if (unlikely(!iir))
return;
if (iir & (KCR_PXP_STATE_TERMINATED_INTERRUPT |
KCR_APP_TERMINATED_PER_FW_REQ_INTERRUPT))
- pxp->irq.events |= PXP_TERMINATION_REQUEST;
+ atomic_or(PXP_TERMINATION_REQUEST_IRQ, &pxp->events.pending);
if (iir & KCR_PXP_STATE_RESET_COMPLETE_INTERRUPT)
- pxp->irq.events |= PXP_TERMINATION_COMPLETE;
+ atomic_or(PXP_TERMINATION_COMPLETE_IRQ, &pxp->events.pending);
- if (pxp->irq.events)
- queue_work(pxp->irq.wq, &pxp->irq.work);
+ if (atomic_read(&pxp->events.pending))
+ queue_work(pxp->events.wq, &pxp->events.work);
}
static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable)
@@ -350,7 +383,7 @@ static void pxp_fini(void *arg)
{
struct xe_pxp *pxp = arg;
- destroy_workqueue(pxp->irq.wq);
+ destroy_workqueue(pxp->events.wq);
xe_pxp_destroy_execution_resources(pxp);
/* no need to explicitly disable KCR since we're going to do an FLR */
@@ -417,7 +450,7 @@ int xe_pxp_init(struct xe_device *xe)
INIT_LIST_HEAD(&pxp->queues.list);
spin_lock_init(&pxp->queues.lock);
- INIT_WORK(&pxp->irq.work, pxp_irq_work);
+ INIT_WORK(&pxp->events.work, pxp_events_work);
pxp->xe = xe;
pxp->gt = gt;
@@ -436,8 +469,8 @@ int xe_pxp_init(struct xe_device *xe)
mutex_init(&pxp->mutex);
- pxp->irq.wq = alloc_ordered_workqueue("pxp-wq", 0);
- if (!pxp->irq.wq) {
+ pxp->events.wq = alloc_ordered_workqueue("pxp-wq", 0);
+ if (!pxp->events.wq) {
err = -ENOMEM;
goto out_free;
}
@@ -457,7 +490,7 @@ int xe_pxp_init(struct xe_device *xe)
out_kcr_disable:
kcr_pxp_disable(pxp);
out_wq:
- destroy_workqueue(pxp->irq.wq);
+ destroy_workqueue(pxp->events.wq);
out_free:
drmm_kfree(&xe->drm, pxp);
out:
@@ -904,6 +937,7 @@ wait_for_activation:
fallthrough;
case XE_PXP_ACTIVE:
pxp->key_instance++;
+ pxp->needs_termination_on_resume = true;
needs_queue_inval = true;
break;
}
@@ -939,6 +973,7 @@ wait_for_activation:
*/
void xe_pxp_pm_resume(struct xe_pxp *pxp)
{
+ bool has_pm = false;
int err;
if (!xe_pxp_is_enabled(pxp))
@@ -946,14 +981,57 @@ void xe_pxp_pm_resume(struct xe_pxp *pxp)
err = kcr_pxp_enable(pxp);
+ /*
+ * We want to avoid the device runtime suspending before we're done with
+ * the termination queued below, so we need a runtime PM reference; we
+ * can't call the rpm functions from within the PXP lock, so we take the
+ * ref here. Note that we don't want the rpm resume code to actually run
+ * here as that would call back into this function, but as long as we
+ * don't enable DPM_FLAG_SMART_SUSPEND (which we currently do not) we're
+ * guaranteed to not be runtime suspended at this point, so we can
+ * safely use the get_noresume variant.
+ */
+ if (pxp->needs_termination_on_resume) {
+ has_pm = true;
+
+ xe_assert(pxp->xe, !dev_pm_smart_suspend(pxp->xe->drm.dev));
+ xe_pm_runtime_get_noresume(pxp->xe);
+ }
+
mutex_lock(&pxp->mutex);
xe_assert(pxp->xe, pxp->status == XE_PXP_SUSPENDED);
- if (err)
+ if (err) {
pxp->status = XE_PXP_ERROR;
- else
+ } else {
pxp->status = XE_PXP_NEEDS_TERMINATION;
+ if (pxp->needs_termination_on_resume) {
+ pxp->needs_termination_on_resume = false;
+
+ /*
+ * We can't call pxp_terminate_hw directly from here
+ * because we're not allowed to do allocations within
+ * the rpm resume call, so we defer the termination to
+ * the worker that we use for the termination irqs.
+ * However, we do not want any PXP ops to go through
+ * between the suspend completing and the worker
+ * starting, so we need to do the termination prep
+ * immediately, which will mark the termination as in
+ * progress and stall PXP ops.
+ */
+ if (pxp_prep_for_termination(pxp)) {
+ has_pm = false; /* move PM ref ownership to worker */
+
+ atomic_or(PXP_TERMINATION_REQUEST_ON_RESUME, &pxp->events.pending);
+ queue_work(pxp->events.wq, &pxp->events.work);
+ }
+ }
+ }
+
mutex_unlock(&pxp->mutex);
+
+ if (has_pm)
+ xe_pm_runtime_put(pxp->xe);
}
diff --git a/drivers/gpu/drm/xe/xe_pxp_types.h b/drivers/gpu/drm/xe/xe_pxp_types.h
index ec86306e16f4..8132a9750b6e 100644
--- a/drivers/gpu/drm/xe/xe_pxp_types.h
+++ b/drivers/gpu/drm/xe/xe_pxp_types.h
@@ -85,17 +85,20 @@ struct xe_pxp {
/** @gsc_res: kernel-owned objects for PXP submissions to the GSCCS */
struct xe_pxp_gsc_client_resources gsc_res;
- /** @irq: wrapper for the worker and queue used for PXP irq support */
+ /** @events: wrapper for the worker and queue used for PXP event handling */
struct {
- /** @irq.work: worker that manages irq events. */
+ /** @events.work: worker that manages termination events. */
struct work_struct work;
- /** @irq.wq: workqueue on which to queue the irq work. */
+ /** @events.wq: workqueue on which to queue the work. */
struct workqueue_struct *wq;
- /** @irq.events: pending events, protected with xe->irq.lock. */
- u32 events;
-#define PXP_TERMINATION_REQUEST BIT(0)
-#define PXP_TERMINATION_COMPLETE BIT(1)
- } irq;
+ /** @events.pending: pending events */
+ atomic_t pending;
+#define PXP_TERMINATION_REQUEST_IRQ BIT(0)
+#define PXP_TERMINATION_REQUEST_ON_RESUME BIT(1)
+#define PXP_TERMINATION_REQUEST (PXP_TERMINATION_REQUEST_IRQ | \
+ PXP_TERMINATION_REQUEST_ON_RESUME)
+#define PXP_TERMINATION_COMPLETE_IRQ BIT(2)
+ } events;
/** @mutex: protects the pxp status and the queue list */
struct mutex mutex;
@@ -130,6 +133,14 @@ struct xe_pxp {
* suspend cycles.
*/
u32 last_suspend_key_instance;
+ /**
+ * @needs_termination_on_resume: indicates if PXP termination is needed
+ * on resume. This is set if PXP was active when we suspend and it is
+ * cleared when we queue the termination on resume. Since the suspend
+ * and resume calls cannot execute at the same time, this variable does
+ * not need to be protected by the PXP lock.
+ */
+ bool needs_termination_on_resume;
};
#endif /* _XE_PXP_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c
index 75091bde0d50..65f59f06177f 100644
--- a/drivers/gpu/drm/xe/xe_uc.c
+++ b/drivers/gpu/drm/xe/xe_uc.c
@@ -15,6 +15,7 @@
#include "xe_guc_pc.h"
#include "xe_guc_rc.h"
#include "xe_guc_engine_activity.h"
+#include "xe_guc_submit.h"
#include "xe_huc.h"
#include "xe_sriov.h"
#include "xe_wopcm.h"
@@ -159,12 +160,14 @@ static int vf_uc_load_hw(struct xe_uc *uc)
if (err)
return err;
- uc->guc.submission_state.enabled = true;
-
err = xe_guc_opt_in_features_enable(&uc->guc);
if (err)
return err;
+ err = xe_guc_submit_enable(&uc->guc);
+ if (err)
+ return err;
+
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
if (err)
return err;