mirror of
https://github.com/torvalds/linux.git
synced 2024-12-17 16:43:08 +00:00
drm/radeon: fix a bug with the ring syncing code
Rings need to lock in order, otherwise the ring subsystem can deadlock. v2: fix error handling and number of locked doublewords. v3: stop creating unneeded semaphores. Signed-off-by: Christian König <deathsimple@vodafone.de> Reviewed-by: Jerome Glisse <jglisse@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
parent
bfb9a07785
commit
8f676c4c6f
@ -460,6 +460,10 @@ void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring,
|
|||||||
struct radeon_semaphore *semaphore);
|
struct radeon_semaphore *semaphore);
|
||||||
void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
|
void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
|
||||||
struct radeon_semaphore *semaphore);
|
struct radeon_semaphore *semaphore);
|
||||||
|
int radeon_semaphore_sync_rings(struct radeon_device *rdev,
|
||||||
|
struct radeon_semaphore *semaphore,
|
||||||
|
bool sync_to[RADEON_NUM_RINGS],
|
||||||
|
int dst_ring);
|
||||||
void radeon_semaphore_free(struct radeon_device *rdev,
|
void radeon_semaphore_free(struct radeon_device *rdev,
|
||||||
struct radeon_semaphore *semaphore);
|
struct radeon_semaphore *semaphore);
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
|
|||||||
static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
|
static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
|
||||||
{
|
{
|
||||||
bool sync_to_ring[RADEON_NUM_RINGS] = { };
|
bool sync_to_ring[RADEON_NUM_RINGS] = { };
|
||||||
|
bool need_sync = false;
|
||||||
int i, r;
|
int i, r;
|
||||||
|
|
||||||
for (i = 0; i < p->nrelocs; i++) {
|
for (i = 0; i < p->nrelocs; i++) {
|
||||||
@ -126,36 +127,24 @@ static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
|
|||||||
|
|
||||||
if (!(p->relocs[i].flags & RADEON_RELOC_DONT_SYNC)) {
|
if (!(p->relocs[i].flags & RADEON_RELOC_DONT_SYNC)) {
|
||||||
struct radeon_fence *fence = p->relocs[i].robj->tbo.sync_obj;
|
struct radeon_fence *fence = p->relocs[i].robj->tbo.sync_obj;
|
||||||
if (!radeon_fence_signaled(fence)) {
|
if (fence->ring != p->ring && !radeon_fence_signaled(fence)) {
|
||||||
sync_to_ring[fence->ring] = true;
|
sync_to_ring[fence->ring] = true;
|
||||||
|
need_sync = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
|
if (!need_sync) {
|
||||||
/* no need to sync to our own or unused rings */
|
return 0;
|
||||||
if (i == p->ring || !sync_to_ring[i] || !p->rdev->ring[i].ready)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!p->ib->fence->semaphore) {
|
|
||||||
r = radeon_semaphore_create(p->rdev, &p->ib->fence->semaphore);
|
|
||||||
if (r)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = radeon_ring_lock(p->rdev, &p->rdev->ring[i], 3);
|
|
||||||
if (r)
|
|
||||||
return r;
|
|
||||||
radeon_semaphore_emit_signal(p->rdev, i, p->ib->fence->semaphore);
|
|
||||||
radeon_ring_unlock_commit(p->rdev, &p->rdev->ring[i]);
|
|
||||||
|
|
||||||
r = radeon_ring_lock(p->rdev, &p->rdev->ring[p->ring], 3);
|
|
||||||
if (r)
|
|
||||||
return r;
|
|
||||||
radeon_semaphore_emit_wait(p->rdev, p->ring, p->ib->fence->semaphore);
|
|
||||||
radeon_ring_unlock_commit(p->rdev, &p->rdev->ring[p->ring]);
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
r = radeon_semaphore_create(p->rdev, &p->ib->fence->semaphore);
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return radeon_semaphore_sync_rings(p->rdev, p->ib->fence->semaphore,
|
||||||
|
sync_to_ring, p->ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
|
int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
|
||||||
|
@ -149,6 +149,62 @@ void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
|
|||||||
radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, true);
|
radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int radeon_semaphore_sync_rings(struct radeon_device *rdev,
|
||||||
|
struct radeon_semaphore *semaphore,
|
||||||
|
bool sync_to[RADEON_NUM_RINGS],
|
||||||
|
int dst_ring)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
|
||||||
|
unsigned num_ops = i == dst_ring ? RADEON_NUM_RINGS : 1;
|
||||||
|
|
||||||
|
/* don't lock unused rings */
|
||||||
|
if (!sync_to[i] && i != dst_ring)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* prevent GPU deadlocks */
|
||||||
|
if (!rdev->ring[i].ready) {
|
||||||
|
dev_err(rdev->dev, "Trying to sync to a disabled ring!");
|
||||||
|
r = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = radeon_ring_lock(rdev, &rdev->ring[i], num_ops * 8);
|
||||||
|
if (r)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
|
||||||
|
/* no need to sync to our own or unused rings */
|
||||||
|
if (!sync_to[i] || i == dst_ring)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
radeon_semaphore_emit_signal(rdev, i, semaphore);
|
||||||
|
radeon_semaphore_emit_wait(rdev, dst_ring, semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
|
||||||
|
|
||||||
|
/* don't unlock unused rings */
|
||||||
|
if (!sync_to[i] && i != dst_ring)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
radeon_ring_unlock_commit(rdev, &rdev->ring[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* unlock all locks taken so far */
|
||||||
|
for (--i; i >= 0; --i) {
|
||||||
|
if (sync_to[i] || i == dst_ring) {
|
||||||
|
radeon_ring_unlock_undo(rdev, &rdev->ring[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
void radeon_semaphore_free(struct radeon_device *rdev,
|
void radeon_semaphore_free(struct radeon_device *rdev,
|
||||||
struct radeon_semaphore *semaphore)
|
struct radeon_semaphore *semaphore)
|
||||||
{
|
{
|
||||||
|
@ -222,8 +222,8 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
|
|||||||
{
|
{
|
||||||
struct radeon_device *rdev;
|
struct radeon_device *rdev;
|
||||||
uint64_t old_start, new_start;
|
uint64_t old_start, new_start;
|
||||||
struct radeon_fence *fence;
|
struct radeon_fence *fence, *old_fence;
|
||||||
int r, i;
|
int r;
|
||||||
|
|
||||||
rdev = radeon_get_rdev(bo->bdev);
|
rdev = radeon_get_rdev(bo->bdev);
|
||||||
r = radeon_fence_create(rdev, &fence, radeon_copy_ring_index(rdev));
|
r = radeon_fence_create(rdev, &fence, radeon_copy_ring_index(rdev));
|
||||||
@ -242,6 +242,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
|
DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
|
||||||
|
radeon_fence_unref(&fence);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
switch (new_mem->mem_type) {
|
switch (new_mem->mem_type) {
|
||||||
@ -253,42 +254,35 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
|
DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
|
||||||
|
radeon_fence_unref(&fence);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (!rdev->ring[radeon_copy_ring_index(rdev)].ready) {
|
if (!rdev->ring[radeon_copy_ring_index(rdev)].ready) {
|
||||||
DRM_ERROR("Trying to move memory with ring turned off.\n");
|
DRM_ERROR("Trying to move memory with ring turned off.\n");
|
||||||
|
radeon_fence_unref(&fence);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0);
|
BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0);
|
||||||
|
|
||||||
/* sync other rings */
|
/* sync other rings */
|
||||||
if (rdev->family >= CHIP_R600) {
|
old_fence = bo->sync_obj;
|
||||||
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
|
if (old_fence && old_fence->ring != fence->ring
|
||||||
/* no need to sync to our own or unused rings */
|
&& !radeon_fence_signaled(old_fence)) {
|
||||||
if (i == radeon_copy_ring_index(rdev) || !rdev->ring[i].ready)
|
bool sync_to_ring[RADEON_NUM_RINGS] = { };
|
||||||
continue;
|
sync_to_ring[old_fence->ring] = true;
|
||||||
|
|
||||||
if (!fence->semaphore) {
|
r = radeon_semaphore_create(rdev, &fence->semaphore);
|
||||||
r = radeon_semaphore_create(rdev, &fence->semaphore);
|
if (r) {
|
||||||
/* FIXME: handle semaphore error */
|
radeon_fence_unref(&fence);
|
||||||
if (r)
|
return r;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
r = radeon_ring_lock(rdev, &rdev->ring[i], 3);
|
r = radeon_semaphore_sync_rings(rdev, fence->semaphore,
|
||||||
/* FIXME: handle ring lock error */
|
sync_to_ring, fence->ring);
|
||||||
if (r)
|
if (r) {
|
||||||
continue;
|
radeon_fence_unref(&fence);
|
||||||
radeon_semaphore_emit_signal(rdev, i, fence->semaphore);
|
return r;
|
||||||
radeon_ring_unlock_commit(rdev, &rdev->ring[i]);
|
|
||||||
|
|
||||||
r = radeon_ring_lock(rdev, &rdev->ring[radeon_copy_ring_index(rdev)], 3);
|
|
||||||
/* FIXME: handle ring lock error */
|
|
||||||
if (r)
|
|
||||||
continue;
|
|
||||||
radeon_semaphore_emit_wait(rdev, radeon_copy_ring_index(rdev), fence->semaphore);
|
|
||||||
radeon_ring_unlock_commit(rdev, &rdev->ring[radeon_copy_ring_index(rdev)]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user