mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-02 18:04:38 -04:00
Merge branch 'etnaviv/fixes' of https://git.pengutronix.de/git/lst/linux into drm-fixes
Fixes a very annoying issue where the driver view of the MMU state gets out of sync with the actual hardware state across a runtime PM cycle, so we end up restarting the GPU with the wrong (potentially already freed) MMU context. Hilarity ensues. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Lucas Stach <l.stach@pengutronix.de> Link: https://patchwork.freedesktop.org/patch/msgid/729a561b6cfed090457bcc856a9e14ed6209fe21.camel@pengutronix.de
This commit is contained in:
@@ -397,8 +397,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
|
||||
if (switch_mmu_context) {
|
||||
struct etnaviv_iommu_context *old_context = gpu->mmu_context;
|
||||
|
||||
etnaviv_iommu_context_get(mmu_context);
|
||||
gpu->mmu_context = mmu_context;
|
||||
gpu->mmu_context = etnaviv_iommu_context_get(mmu_context);
|
||||
etnaviv_iommu_context_put(old_context);
|
||||
}
|
||||
|
||||
|
||||
@@ -294,8 +294,7 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get(
|
||||
list_del(&mapping->obj_node);
|
||||
}
|
||||
|
||||
etnaviv_iommu_context_get(mmu_context);
|
||||
mapping->context = mmu_context;
|
||||
mapping->context = etnaviv_iommu_context_get(mmu_context);
|
||||
mapping->use = 1;
|
||||
|
||||
ret = etnaviv_iommu_map_gem(mmu_context, etnaviv_obj,
|
||||
|
||||
@@ -532,8 +532,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
goto err_submit_objects;
|
||||
|
||||
submit->ctx = file->driver_priv;
|
||||
etnaviv_iommu_context_get(submit->ctx->mmu);
|
||||
submit->mmu_context = submit->ctx->mmu;
|
||||
submit->mmu_context = etnaviv_iommu_context_get(submit->ctx->mmu);
|
||||
submit->exec_state = args->exec_state;
|
||||
submit->flags = args->flags;
|
||||
|
||||
|
||||
@@ -569,6 +569,12 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
|
||||
/* We rely on the GPU running, so program the clock */
|
||||
etnaviv_gpu_update_clock(gpu);
|
||||
|
||||
gpu->fe_running = false;
|
||||
gpu->exec_state = -1;
|
||||
if (gpu->mmu_context)
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
gpu->mmu_context = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -637,19 +643,23 @@ void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch)
|
||||
VIVS_MMUv2_SEC_COMMAND_CONTROL_ENABLE |
|
||||
VIVS_MMUv2_SEC_COMMAND_CONTROL_PREFETCH(prefetch));
|
||||
}
|
||||
|
||||
gpu->fe_running = true;
|
||||
}
|
||||
|
||||
static void etnaviv_gpu_start_fe_idleloop(struct etnaviv_gpu *gpu)
|
||||
static void etnaviv_gpu_start_fe_idleloop(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_iommu_context *context)
|
||||
{
|
||||
u32 address = etnaviv_cmdbuf_get_va(&gpu->buffer,
|
||||
&gpu->mmu_context->cmdbuf_mapping);
|
||||
u16 prefetch;
|
||||
u32 address;
|
||||
|
||||
/* setup the MMU */
|
||||
etnaviv_iommu_restore(gpu, gpu->mmu_context);
|
||||
etnaviv_iommu_restore(gpu, context);
|
||||
|
||||
/* Start command processor */
|
||||
prefetch = etnaviv_buffer_init(gpu);
|
||||
address = etnaviv_cmdbuf_get_va(&gpu->buffer,
|
||||
&gpu->mmu_context->cmdbuf_mapping);
|
||||
|
||||
etnaviv_gpu_start_fe(gpu, address, prefetch);
|
||||
}
|
||||
@@ -832,7 +842,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
||||
/* Now program the hardware */
|
||||
mutex_lock(&gpu->lock);
|
||||
etnaviv_gpu_hw_init(gpu);
|
||||
gpu->exec_state = -1;
|
||||
mutex_unlock(&gpu->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(gpu->dev);
|
||||
@@ -1057,8 +1066,6 @@ void etnaviv_gpu_recover_hang(struct etnaviv_gpu *gpu)
|
||||
spin_unlock(&gpu->event_spinlock);
|
||||
|
||||
etnaviv_gpu_hw_init(gpu);
|
||||
gpu->exec_state = -1;
|
||||
gpu->mmu_context = NULL;
|
||||
|
||||
mutex_unlock(&gpu->lock);
|
||||
pm_runtime_mark_last_busy(gpu->dev);
|
||||
@@ -1370,14 +1377,12 @@ struct dma_fence *etnaviv_gpu_submit(struct etnaviv_gem_submit *submit)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!gpu->mmu_context) {
|
||||
etnaviv_iommu_context_get(submit->mmu_context);
|
||||
gpu->mmu_context = submit->mmu_context;
|
||||
etnaviv_gpu_start_fe_idleloop(gpu);
|
||||
} else {
|
||||
etnaviv_iommu_context_get(gpu->mmu_context);
|
||||
submit->prev_mmu_context = gpu->mmu_context;
|
||||
}
|
||||
if (!gpu->fe_running)
|
||||
etnaviv_gpu_start_fe_idleloop(gpu, submit->mmu_context);
|
||||
|
||||
if (submit->prev_mmu_context)
|
||||
etnaviv_iommu_context_put(submit->prev_mmu_context);
|
||||
submit->prev_mmu_context = etnaviv_iommu_context_get(gpu->mmu_context);
|
||||
|
||||
if (submit->nr_pmrs) {
|
||||
gpu->event[event[1]].sync_point = &sync_point_perfmon_sample_pre;
|
||||
@@ -1579,7 +1584,7 @@ int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms)
|
||||
|
||||
static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
if (gpu->initialized && gpu->mmu_context) {
|
||||
if (gpu->initialized && gpu->fe_running) {
|
||||
/* Replace the last WAIT with END */
|
||||
mutex_lock(&gpu->lock);
|
||||
etnaviv_buffer_end(gpu);
|
||||
@@ -1592,8 +1597,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
|
||||
*/
|
||||
etnaviv_gpu_wait_idle(gpu, 100);
|
||||
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
gpu->mmu_context = NULL;
|
||||
gpu->fe_running = false;
|
||||
}
|
||||
|
||||
gpu->exec_state = -1;
|
||||
@@ -1741,6 +1745,9 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
|
||||
etnaviv_gpu_hw_suspend(gpu);
|
||||
#endif
|
||||
|
||||
if (gpu->mmu_context)
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
|
||||
if (gpu->initialized) {
|
||||
etnaviv_cmdbuf_free(&gpu->buffer);
|
||||
etnaviv_iommu_global_fini(gpu);
|
||||
|
||||
@@ -101,6 +101,7 @@ struct etnaviv_gpu {
|
||||
struct workqueue_struct *wq;
|
||||
struct drm_gpu_scheduler sched;
|
||||
bool initialized;
|
||||
bool fe_running;
|
||||
|
||||
/* 'ring'-buffer: */
|
||||
struct etnaviv_cmdbuf buffer;
|
||||
|
||||
@@ -92,6 +92,10 @@ static void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_iommuv1_context *v1_context = to_v1_context(context);
|
||||
u32 pgtable;
|
||||
|
||||
if (gpu->mmu_context)
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
gpu->mmu_context = etnaviv_iommu_context_get(context);
|
||||
|
||||
/* set base addresses */
|
||||
gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, context->global->memory_base);
|
||||
gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, context->global->memory_base);
|
||||
|
||||
@@ -172,6 +172,10 @@ static void etnaviv_iommuv2_restore_nonsec(struct etnaviv_gpu *gpu,
|
||||
if (gpu_read(gpu, VIVS_MMUv2_CONTROL) & VIVS_MMUv2_CONTROL_ENABLE)
|
||||
return;
|
||||
|
||||
if (gpu->mmu_context)
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
gpu->mmu_context = etnaviv_iommu_context_get(context);
|
||||
|
||||
prefetch = etnaviv_buffer_config_mmuv2(gpu,
|
||||
(u32)v2_context->mtlb_dma,
|
||||
(u32)context->global->bad_page_dma);
|
||||
@@ -192,6 +196,10 @@ static void etnaviv_iommuv2_restore_sec(struct etnaviv_gpu *gpu,
|
||||
if (gpu_read(gpu, VIVS_MMUv2_SEC_CONTROL) & VIVS_MMUv2_SEC_CONTROL_ENABLE)
|
||||
return;
|
||||
|
||||
if (gpu->mmu_context)
|
||||
etnaviv_iommu_context_put(gpu->mmu_context);
|
||||
gpu->mmu_context = etnaviv_iommu_context_get(context);
|
||||
|
||||
gpu_write(gpu, VIVS_MMUv2_PTA_ADDRESS_LOW,
|
||||
lower_32_bits(context->global->v2.pta_dma));
|
||||
gpu_write(gpu, VIVS_MMUv2_PTA_ADDRESS_HIGH,
|
||||
|
||||
@@ -199,6 +199,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu_context *context,
|
||||
*/
|
||||
list_for_each_entry_safe(m, n, &list, scan_node) {
|
||||
etnaviv_iommu_remove_mapping(context, m);
|
||||
etnaviv_iommu_context_put(m->context);
|
||||
m->context = NULL;
|
||||
list_del_init(&m->mmu_node);
|
||||
list_del_init(&m->scan_node);
|
||||
|
||||
@@ -105,9 +105,11 @@ void etnaviv_iommu_dump(struct etnaviv_iommu_context *ctx, void *buf);
|
||||
struct etnaviv_iommu_context *
|
||||
etnaviv_iommu_context_init(struct etnaviv_iommu_global *global,
|
||||
struct etnaviv_cmdbuf_suballoc *suballoc);
|
||||
static inline void etnaviv_iommu_context_get(struct etnaviv_iommu_context *ctx)
|
||||
static inline struct etnaviv_iommu_context *
|
||||
etnaviv_iommu_context_get(struct etnaviv_iommu_context *ctx)
|
||||
{
|
||||
kref_get(&ctx->refcount);
|
||||
return ctx;
|
||||
}
|
||||
void etnaviv_iommu_context_put(struct etnaviv_iommu_context *ctx);
|
||||
void etnaviv_iommu_restore(struct etnaviv_gpu *gpu,
|
||||
|
||||
Reference in New Issue
Block a user