diff options
author | Christian König <deathsimple@vodafone.de> | 2012-07-09 05:52:44 -0400 |
---|---|---|
committer | Christian König <deathsimple@vodafone.de> | 2012-07-17 04:33:15 -0400 |
commit | 55d7c22192becd0ec827a6901899ff56fa985658 (patch) | |
tree | 2876d95b10e9bac71ef168dd998537c731d3222f | |
parent | 45df68035c4964d42ea3850980708ce8674f75b3 (diff) |
drm/radeon: implement ring saving on reset v4
Try to save whatever is on the rings when
we encounter an lockup.
v2: Fix spelling error. Free saved ring data if reset fails.
Add documentation for the new functions.
v3: Some more spelling fixes
v4: It doesn't make sense to save anything if all fences
are signaled
Signed-off-by: Christian König <deathsimple@vodafone.de>
Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 48 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_ring.c | 82 |
3 files changed, 126 insertions, 8 deletions
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 64d39adaad91..6715e4c695fa 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -768,6 +768,10 @@ int radeon_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); | |||
768 | void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring); | 768 | void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring); |
769 | void radeon_ring_lockup_update(struct radeon_ring *ring); | 769 | void radeon_ring_lockup_update(struct radeon_ring *ring); |
770 | bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring); | 770 | bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring); |
771 | unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring, | ||
772 | uint32_t **data); | ||
773 | int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, | ||
774 | unsigned size, uint32_t *data); | ||
771 | int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ring_size, | 775 | int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ring_size, |
772 | unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, | 776 | unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, |
773 | u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop); | 777 | u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop); |
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index bbd09718e956..0302a9f3e674 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c | |||
@@ -996,7 +996,12 @@ int radeon_resume_kms(struct drm_device *dev) | |||
996 | 996 | ||
997 | int radeon_gpu_reset(struct radeon_device *rdev) | 997 | int radeon_gpu_reset(struct radeon_device *rdev) |
998 | { | 998 | { |
999 | int r; | 999 | unsigned ring_sizes[RADEON_NUM_RINGS]; |
1000 | uint32_t *ring_data[RADEON_NUM_RINGS]; | ||
1001 | |||
1002 | bool saved = false; | ||
1003 | |||
1004 | int i, r; | ||
1000 | int resched; | 1005 | int resched; |
1001 | 1006 | ||
1002 | down_write(&rdev->exclusive_lock); | 1007 | down_write(&rdev->exclusive_lock); |
@@ -1005,20 +1010,47 @@ int radeon_gpu_reset(struct radeon_device *rdev) | |||
1005 | resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); | 1010 | resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); |
1006 | radeon_suspend(rdev); | 1011 | radeon_suspend(rdev); |
1007 | 1012 | ||
1013 | for (i = 0; i < RADEON_NUM_RINGS; ++i) { | ||
1014 | ring_sizes[i] = radeon_ring_backup(rdev, &rdev->ring[i], | ||
1015 | &ring_data[i]); | ||
1016 | if (ring_sizes[i]) { | ||
1017 | saved = true; | ||
1018 | dev_info(rdev->dev, "Saved %d dwords of commands " | ||
1019 | "on ring %d.\n", ring_sizes[i], i); | ||
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | retry: | ||
1008 | r = radeon_asic_reset(rdev); | 1024 | r = radeon_asic_reset(rdev); |
1009 | if (!r) { | 1025 | if (!r) { |
1010 | dev_info(rdev->dev, "GPU reset succeed\n"); | 1026 | dev_info(rdev->dev, "GPU reset succeeded, trying to resume\n"); |
1011 | radeon_resume(rdev); | 1027 | radeon_resume(rdev); |
1028 | } | ||
1012 | 1029 | ||
1013 | r = radeon_ib_ring_tests(rdev); | 1030 | radeon_restore_bios_scratch_regs(rdev); |
1014 | if (r) | 1031 | drm_helper_resume_force_mode(rdev->ddev); |
1015 | DRM_ERROR("ib ring test failed (%d).\n", r); | ||
1016 | 1032 | ||
1017 | radeon_restore_bios_scratch_regs(rdev); | 1033 | if (!r) { |
1018 | drm_helper_resume_force_mode(rdev->ddev); | 1034 | for (i = 0; i < RADEON_NUM_RINGS; ++i) { |
1019 | ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); | 1035 | radeon_ring_restore(rdev, &rdev->ring[i], |
1036 | ring_sizes[i], ring_data[i]); | ||
1037 | } | ||
1038 | |||
1039 | r = radeon_ib_ring_tests(rdev); | ||
1040 | if (r) { | ||
1041 | dev_err(rdev->dev, "ib ring test failed (%d).\n", r); | ||
1042 | if (saved) { | ||
1043 | radeon_suspend(rdev); | ||
1044 | goto retry; | ||
1045 | } | ||
1046 | } | ||
1047 | } else { | ||
1048 | for (i = 0; i < RADEON_NUM_RINGS; ++i) { | ||
1049 | kfree(ring_data[i]); | ||
1050 | } | ||
1020 | } | 1051 | } |
1021 | 1052 | ||
1053 | ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); | ||
1022 | if (r) { | 1054 | if (r) { |
1023 | /* bad news, how to tell it to userspace ? */ | 1055 | /* bad news, how to tell it to userspace ? */ |
1024 | dev_info(rdev->dev, "GPU reset failed\n"); | 1056 | dev_info(rdev->dev, "GPU reset failed\n"); |
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index ce8eb9d5af5d..75cbe4641138 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c | |||
@@ -362,6 +362,88 @@ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *rin | |||
362 | return false; | 362 | return false; |
363 | } | 363 | } |
364 | 364 | ||
365 | /** | ||
366 | * radeon_ring_backup - Back up the content of a ring | ||
367 | * | ||
368 | * @rdev: radeon_device pointer | ||
369 | * @ring: the ring we want to back up | ||
370 | * | ||
371 | * Saves all unprocessed commits from a ring, returns the number of dwords saved. | ||
372 | */ | ||
373 | unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring, | ||
374 | uint32_t **data) | ||
375 | { | ||
376 | unsigned size, ptr, i; | ||
377 | int ridx = radeon_ring_index(rdev, ring); | ||
378 | |||
379 | /* just in case lock the ring */ | ||
380 | mutex_lock(&rdev->ring_lock); | ||
381 | *data = NULL; | ||
382 | |||
383 | if (ring->ring_obj == NULL || !ring->rptr_save_reg) { | ||
384 | mutex_unlock(&rdev->ring_lock); | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | /* it doesn't make sense to save anything if all fences are signaled */ | ||
389 | if (!radeon_fence_count_emitted(rdev, ridx)) { | ||
390 | mutex_unlock(&rdev->ring_lock); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | /* calculate the number of dw on the ring */ | ||
395 | ptr = RREG32(ring->rptr_save_reg); | ||
396 | size = ring->wptr + (ring->ring_size / 4); | ||
397 | size -= ptr; | ||
398 | size &= ring->ptr_mask; | ||
399 | if (size == 0) { | ||
400 | mutex_unlock(&rdev->ring_lock); | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | /* and then save the content of the ring */ | ||
405 | *data = kmalloc(size * 4, GFP_KERNEL); | ||
406 | for (i = 0; i < size; ++i) { | ||
407 | (*data)[i] = ring->ring[ptr++]; | ||
408 | ptr &= ring->ptr_mask; | ||
409 | } | ||
410 | |||
411 | mutex_unlock(&rdev->ring_lock); | ||
412 | return size; | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * radeon_ring_restore - append saved commands to the ring again | ||
417 | * | ||
418 | * @rdev: radeon_device pointer | ||
419 | * @ring: ring to append commands to | ||
420 | * @size: number of dwords we want to write | ||
421 | * @data: saved commands | ||
422 | * | ||
423 | * Allocates space on the ring and restore the previously saved commands. | ||
424 | */ | ||
425 | int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, | ||
426 | unsigned size, uint32_t *data) | ||
427 | { | ||
428 | int i, r; | ||
429 | |||
430 | if (!size || !data) | ||
431 | return 0; | ||
432 | |||
433 | /* restore the saved ring content */ | ||
434 | r = radeon_ring_lock(rdev, ring, size); | ||
435 | if (r) | ||
436 | return r; | ||
437 | |||
438 | for (i = 0; i < size; ++i) { | ||
439 | radeon_ring_write(ring, data[i]); | ||
440 | } | ||
441 | |||
442 | radeon_ring_unlock_commit(rdev, ring); | ||
443 | kfree(data); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
365 | int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size, | 447 | int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size, |
366 | unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, | 448 | unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, |
367 | u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop) | 449 | u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop) |