From: Jerome Glisse <jgli...@redhat.com> This add a command buffer dumping facilities, that will dump command buffer and all associated bo that most likely triggered a lockup.
Idea is that we go through unsignaled fence and we dump the ib of the oldest unsignaled fence. Dumping is a 2 step process on lockup detection we try to allocate a big object that will old all the the current state (ib pm4 packet, bo content, relocation table). Upon reading radeon_lockup_blob debugfs file user will get this big blob and kernel will free memory. Kernel side try to handle as gracefully as possible failure such as mapping bo by not dumping such bo. Userspace tools those need to have enough logic to handle such cases. Signed-off-by: Jerome Glisse <jgli...@redhat.com> --- drivers/gpu/drm/radeon/radeon.h | 14 ++++- drivers/gpu/drm/radeon/radeon_cs.c | 20 ++++-- drivers/gpu/drm/radeon/radeon_device.c | 3 + drivers/gpu/drm/radeon/radeon_fence.c | 19 +++++ drivers/gpu/drm/radeon/radeon_gart.c | 10 ++- drivers/gpu/drm/radeon/radeon_ring.c | 118 ++++++++++++++++++++++++++++++++ 6 files changed, 173 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 7b2125b..c9f51be 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -241,6 +241,7 @@ struct radeon_fence { int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring); int radeon_fence_driver_init(struct radeon_device *rdev); void radeon_fence_driver_fini(struct radeon_device *rdev); +void radeon_fence_blob_faulty_ib(struct radeon_device *rdev, int ring); int radeon_fence_create(struct radeon_device *rdev, struct radeon_fence **fence, int ring); int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence *fence); void radeon_fence_process(struct radeon_device *rdev, int ring); @@ -569,6 +570,10 @@ void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc); */ struct radeon_cs_reloc; +#define RADEON_IB_TYPE_NONE 0 +#define RADEON_IB_TYPE_CS 1 +#define RADEON_IB_TYPE_CS_VM 2 + struct radeon_ib { struct radeon_sa_bo sa_bo; uint32_t length_dw; @@ -579,6 +584,7 @@ struct radeon_ib { bool is_const_ib; unsigned nrelocs; struct radeon_cs_reloc *relocs; + unsigned type; }; struct radeon_ring { @@ -745,6 +751,7 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *cp, unsigne unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop); void radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *cp); +void radeon_lockup_build_blob(struct radeon_device *rdev, struct radeon_ib *ib); /* @@ -756,6 +763,7 @@ struct radeon_cs_reloc { struct radeon_bo_list lobj; uint32_t handle; uint32_t flags; + uint64_t gpu_addr; }; struct radeon_cs_chunk { @@ -1496,6 +1504,9 @@ struct radeon_device { unsigned debugfs_count; /* virtual memory */ struct radeon_vm_manager vm_manager; + uint32_t *blob; + unsigned blob_size_dw; + struct mutex blob_mutex; }; int radeon_device_init(struct radeon_device *rdev, @@ -1742,7 +1753,8 @@ void radeon_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm); int radeon_vm_bo_update_pte(struct radeon_device *rdev, struct radeon_vm *vm, struct radeon_bo *bo, - struct ttm_mem_reg *mem); + struct ttm_mem_reg *mem, + uint64_t *gpu_addr); void radeon_vm_bo_invalidate(struct radeon_device *rdev, struct radeon_bo *bo); int radeon_vm_bo_add(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index ecef708..0c0bcaa 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -334,6 +334,7 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, struct radeon_cs_parser *parser) { struct radeon_cs_chunk *ib_chunk; + unsigned i; int r; if (parser->chunk_ib_idx == -1) @@ -369,6 +370,10 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, DRM_ERROR("Failed to synchronize rings !\n"); } parser->ib->vm_id = 0; + parser->ib->type = RADEON_IB_TYPE_CS; + for (i = 0; i < parser->nrelocs; ++i) { + parser->relocs[i].gpu_addr = parser->relocs[i].lobj.gpu_offset; + } r = radeon_ib_schedule(rdev, parser->ib); if (r) { DRM_ERROR("Failed to schedule IB !\n"); @@ -379,13 +384,13 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser, struct radeon_vm *vm) { - struct radeon_bo_list *lobj; - struct radeon_bo *bo; - int r; + unsigned i; + + for (i = 0; i < parser->nrelocs; ++i) { + struct radeon_bo *bo = parser->relocs[i].robj; + int r; - list_for_each_entry(lobj, &parser->validated, tv.head) { - bo = lobj->bo; - r = radeon_vm_bo_update_pte(parser->rdev, vm, bo, &bo->tbo.mem); + r = radeon_vm_bo_update_pte(parser->rdev, vm, bo, &bo->tbo.mem, &parser->relocs[i].gpu_addr); if (r) { return r; } @@ -476,17 +481,18 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, * offset inside the pool bo */ parser->const_ib->gpu_addr = parser->const_ib->sa_bo.offset; + parser->ib->type = RADEON_IB_TYPE_NONE; r = radeon_ib_schedule(rdev, parser->const_ib); if (r) goto out; } - parser->ib->vm_id = vm->id; /* ib pool is bind at 0 in virtual address space to gpu_addr is the * offset inside the pool bo */ parser->ib->gpu_addr = parser->ib->sa_bo.offset; parser->ib->is_const_ib = false; + parser->ib->type = RADEON_IB_TYPE_CS_VM; r = radeon_ib_schedule(rdev, parser->ib); out: if (!r) { diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 5df53dd..bf327fa 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -723,6 +723,7 @@ int radeon_device_init(struct radeon_device *rdev, /* mutex initialization are all done here so we * can recall function without having locking issues */ mutex_init(&rdev->cs_mutex); + mutex_init(&rdev->blob_mutex); for (i = 0; i < RADEON_NUM_RINGS; ++i) mutex_init(&rdev->ring[i].mutex); mutex_init(&rdev->dc_hw_i2c_mutex); @@ -984,6 +985,8 @@ int radeon_gpu_reset(struct radeon_device *rdev) int r; int resched; + radeon_fence_blob_faulty_ib(rdev, RADEON_RING_TYPE_GFX_INDEX); + radeon_save_bios_scratch_regs(rdev); /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 2e56101..16cbc65 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -405,6 +405,25 @@ bool radeon_fence_set_associated_ib(struct radeon_fence *fence, struct radeon_ib return isset; } +void radeon_fence_blob_faulty_ib(struct radeon_device *rdev, int ring) +{ + struct radeon_fence *fence; + struct list_head *i; + unsigned long irq_flags; + uint32_t seq; + + write_lock_irqsave(&rdev->fence_lock, irq_flags); + seq = radeon_fence_read(rdev, ring); + list_for_each(i, &rdev->fence_drv[ring].emitted) { + fence = list_entry(i, struct radeon_fence, list); + if (fence->seq != seq && fence->ib) { + radeon_lockup_build_blob(rdev, fence->ib); + break; + } + } + write_unlock_irqrestore(&rdev->fence_lock, irq_flags); +} + int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) { unsigned long irq_flags; diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index b4a4982..a491edf 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -435,7 +435,7 @@ retry_id: vm->id = id; list_add_tail(&vm->list, &rdev->vm_manager.lru_vm); return radeon_vm_bo_update_pte(rdev, vm, rdev->sa_manager.bo, - &rdev->sa_manager.bo->tbo.mem); + &rdev->sa_manager.bo->tbo.mem, NULL); } /* object have to be reserved */ @@ -542,7 +542,8 @@ static u64 radeon_vm_get_addr(struct radeon_device *rdev, int radeon_vm_bo_update_pte(struct radeon_device *rdev, struct radeon_vm *vm, struct radeon_bo *bo, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *mem, + uint64_t *gpu_addr) { struct radeon_bo_va *bo_va; unsigned ngpu_pages, i; @@ -562,6 +563,9 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev, if (bo_va->valid) return 0; + if (gpu_addr) { + *gpu_addr = bo_va->soffset; + } ngpu_pages = radeon_bo_ngpu_pages(bo); bo_va->flags &= ~RADEON_VM_PAGE_VALID; bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM; @@ -599,7 +603,7 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev, mutex_lock(&vm->mutex); mutex_lock(&rdev->cs_mutex); - radeon_vm_bo_update_pte(rdev, vm, bo, NULL); + radeon_vm_bo_update_pte(rdev, vm, bo, NULL, NULL); mutex_unlock(&rdev->cs_mutex); list_del(&bo_va->vm_list); mutex_unlock(&vm->mutex); diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index c635aad..de93ba3 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -34,6 +34,8 @@ #include "radeon.h" #include "atom.h" +static int radeon_debugfs_lockup_init(struct radeon_device *rdev); + /* * IB. */ @@ -116,6 +118,7 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib) } if (destroy) { + tmp->type = RADEON_IB_TYPE_NONE; radeon_sa_bo_free(rdev, &tmp->sa_bo); radeon_fence_unref(&tmp->fence); kfree(tmp->relocs); @@ -163,6 +166,9 @@ int radeon_ib_pool_init(struct radeon_device *rdev) DRM_INFO("radeon: ib pool ready.\n"); rdev->ib_pool_ready = true; + if (radeon_debugfs_lockup_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for lockup blob!\n"); + } return 0; } @@ -465,3 +471,115 @@ void radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *ring) radeon_bo_unref(&ring_obj); } } + +#define BLOB_TYPE_GLOBAL 0 +#define BLOB_TYPE_CS 1 +#define BLOB_TYPE_RELOC 2 +#define BLOB_TYPE_BO 3 +#define BLOB_TYPE_CS_VM 4 + +static unsigned radeon_blob_out(u32 *blob, unsigned offset, + unsigned type, unsigned id, + unsigned size, void *ptr) +{ + blob[offset++] = type; + blob[offset++] = id; + blob[offset++] = size; + if (ptr) { + memcpy(&blob[offset], ptr, size * 4); + return offset + size; + } + return offset; +} + +void radeon_lockup_build_blob(struct radeon_device *rdev, struct radeon_ib *ib) +{ + unsigned i, size_dw, offset; + + /* 3 dwords per object: cs, relocs, and one for each bo */ + size_dw = 6 + ib->nrelocs * 4; + size_dw += ib->length_dw + ib->nrelocs * 3; + for (i = 0; i < ib->nrelocs; ++i) { + struct radeon_bo *bo = ib->relocs[i].robj; + size_dw += (radeon_bo_size(bo) >> 2); + } + mutex_lock(&rdev->blob_mutex); + vfree(rdev->blob); + rdev->blob = vmalloc(size_dw * 4); + if (rdev->blob == NULL) { + mutex_unlock(&rdev->blob_mutex); + return; + } + + rdev->blob_size_dw = size_dw; + offset = 0; + offset = radeon_blob_out(rdev->blob, offset, BLOB_TYPE_GLOBAL, + 0, size_dw, NULL); + if (RADEON_IB_TYPE_CS_VM) { + offset = radeon_blob_out(rdev->blob, offset, BLOB_TYPE_CS_VM, + 0, ib->length_dw, ib->ptr); + } else { + offset = radeon_blob_out(rdev->blob, offset, BLOB_TYPE_CS, + 0, ib->length_dw, ib->ptr); + } + /* dump relocation */ + rdev->blob[offset++] = BLOB_TYPE_RELOC; + rdev->blob[offset++] = 0; + rdev->blob[offset++] = ib->nrelocs * 4; + for (i = 0; i < ib->nrelocs; ++i) { + rdev->blob[offset++] = ib->relocs[i].handle; + rdev->blob[offset++] = lower_32_bits(ib->relocs[i].gpu_addr); + rdev->blob[offset++] = upper_32_bits(ib->relocs[i].gpu_addr); + rdev->blob[offset++] = ib->relocs[i].flags; + } + /* dump bo */ + for (i = 0; i < ib->nrelocs; ++i) { + struct radeon_bo *bo = ib->relocs[i].robj; + void *ptr; + + if (!radeon_bo_kmap(bo, &ptr)) { + offset = radeon_blob_out(rdev->blob, offset, BLOB_TYPE_BO, + ib->relocs[i].handle, + (radeon_bo_size(bo) >> 2), + ptr); + radeon_bo_kunmap(bo); + } + } + mutex_unlock(&rdev->blob_mutex); +} + + +#if defined(CONFIG_DEBUG_FS) +static int radeon_debugfs_blob(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + int r = 0; + + mutex_lock(&rdev->blob_mutex); + if (rdev->blob) { + r = seq_write(m, rdev->blob, rdev->blob_size_dw * 4); + vfree(rdev->blob); + rdev->blob = 0; + rdev->blob_size_dw = 0; + } + mutex_unlock(&rdev->blob_mutex); + return r; +} + +static struct drm_info_list radeon_debugfs_ring_info_list[] = { + {"radeon_lockup_blob", radeon_debugfs_blob, 0, NULL}, +}; +#endif + + +static int radeon_debugfs_lockup_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list, + ARRAY_SIZE(radeon_debugfs_ring_info_list)); +#else + return 0; +#endif +} -- 1.7.7.6 _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel