/** * radeon_ring_test_lockup() - check if ring is lockedup by recording information * @rdev: radeon device structure * @ring: radeon_ring structure holding ring information * * We don't need to initialize the lockup tracking information as we will either * have CP rptr to a different value of jiffies wrap around which will force * initialization of the lockup tracking informations. * * A possible false positivie is if we get call after while and last_cp_rptr == * the current CP rptr, even if it's unlikely it might happen. To avoid this * if the elapsed time since last call is bigger than 2 second than we return * false and update the tracking information. Due to this the caller must call * radeon_ring_test_lockup several time in less than 2sec for lockup to be reported * the fencing code should be cautious about that. * * Caller should write to the ring to force CP to do something so we don't get * false positive when CP is just gived nothing to do. * **/ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { unsigned long cjiffies, elapsed; uint32_t rptr; cjiffies = jiffies; if (!time_after(cjiffies, ring->last_activity)) { /* likely a wrap around */ radeon_ring_lockup_update(ring); return false; } rptr = RREG32(ring->rptr_reg); ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; if (ring->rptr != ring->last_rptr) { /* CP is still working no lockup */ radeon_ring_lockup_update(ring); return false; } elapsed = jiffies_to_msecs(cjiffies - ring->last_activity); if (radeon_lockup_timeout && elapsed >= radeon_lockup_timeout) { dev_err(rdev->dev, "GPU lockup CP stall for more than %lumsec\n", elapsed); return true; } /* give a chance to the GPU ... */ return false; }
/** * radeon_ring_alloc - allocate space on the ring buffer * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * @ndw: number of dwords to allocate in the ring buffer * * Allocate @ndw dwords in the ring buffer (all asics). * Returns 0 on success, error on failure. */ int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw) { int r; /* make sure we aren't trying to allocate more space than there is on the ring */ if (ndw > (ring->ring_size / 4)) return -ENOMEM; /* Align requested size with padding so unlock_commit can * pad safely */ radeon_ring_free_size(rdev, ring); if (ring->ring_free_dw == (ring->ring_size / 4)) { /* This is an empty ring update lockup info to avoid * false positive. */ radeon_ring_lockup_update(ring); } ndw = (ndw + ring->align_mask) & ~ring->align_mask; while (ndw > (ring->ring_free_dw - 1)) { radeon_ring_free_size(rdev, ring); if (ndw < ring->ring_free_dw) { break; } r = radeon_fence_wait_next_locked(rdev, ring->idx); if (r) return r; } ring->count_dw = ndw; ring->wptr_old = ring->wptr; return 0; }
/** * r600_dma_is_lockup - Check if the DMA engine is locked up * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * * Check if the async DMA engine is locked up. * Returns true if the engine appears to be locked up, false if not. */ bool r600_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { u32 reset_mask = r600_gpu_check_soft_reset(rdev); if (!(reset_mask & RADEON_RESET_DMA)) { radeon_ring_lockup_update(rdev, ring); return false; } return radeon_ring_test_lockup(rdev, ring); }
/** * radeon_ring_init - init driver ring struct. * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * @ring_size: size of the ring * @rptr_offs: offset of the rptr writeback location in the WB buffer * @rptr_reg: MMIO offset of the rptr register * @wptr_reg: MMIO offset of the wptr register * @ptr_reg_shift: bit offset of the rptr/wptr values * @ptr_reg_mask: bit mask of the rptr/wptr values * @nop: nop packet for this ring * * Initialize the driver information for the selected ring (all asics). * Returns 0 on success, error on failure. */ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size, unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop) { int r; ring->ring_size = ring_size; ring->rptr_offs = rptr_offs; ring->rptr_reg = rptr_reg; ring->wptr_reg = wptr_reg; ring->ptr_reg_shift = ptr_reg_shift; ring->ptr_reg_mask = ptr_reg_mask; ring->nop = nop; /* Allocate ring buffer */ if (ring->ring_obj == NULL) { r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_GTT, NULL, &ring->ring_obj); if (r) { dev_err(rdev->dev, "(%d) ring create failed\n", r); return r; } r = radeon_bo_reserve(ring->ring_obj, false); if (unlikely(r != 0)) return r; r = radeon_bo_pin(ring->ring_obj, RADEON_GEM_DOMAIN_GTT, &ring->gpu_addr); if (r) { radeon_bo_unreserve(ring->ring_obj); dev_err(rdev->dev, "(%d) ring pin failed\n", r); return r; } r = radeon_bo_kmap(ring->ring_obj, (void **)&ring->ring); radeon_bo_unreserve(ring->ring_obj); if (r) { dev_err(rdev->dev, "(%d) ring map failed\n", r); return r; } } ring->ptr_mask = (ring->ring_size / 4) - 1; ring->ring_free_dw = ring->ring_size / 4; if (rdev->wb.enabled) { u32 index = RADEON_WB_RING0_NEXT_RPTR + (ring->idx * 4); ring->next_rptr_gpu_addr = rdev->wb.gpu_addr + index; ring->next_rptr_cpu_addr = &rdev->wb.wb[index/4]; } if (radeon_debugfs_ring_init(rdev, ring)) { DRM_ERROR("Failed to register debugfs file for rings !\n"); } radeon_ring_lockup_update(ring); return 0; }
/** * radeon_ring_free_size - update the free size * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * * Update the free dw slots in the ring buffer (all asics). */ void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring) { uint32_t rptr = radeon_ring_get_rptr(rdev, ring); /* This works because ring_size is a power of 2 */ ring->ring_free_dw = rptr + (ring->ring_size / 4); ring->ring_free_dw -= ring->wptr; ring->ring_free_dw &= ring->ptr_mask; if (!ring->ring_free_dw) { /* this is an empty ring */ ring->ring_free_dw = ring->ring_size / 4; /* update lockup info to avoid false positive */ radeon_ring_lockup_update(rdev, ring); } }
/** * cik_sdma_is_lockup - Check if the DMA engine is locked up * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * * Check if the async DMA engine is locked up (CIK). * Returns true if the engine appears to be locked up, false if not. */ bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { u32 reset_mask = cik_gpu_check_soft_reset(rdev); u32 mask; if (ring->idx == R600_RING_TYPE_DMA_INDEX) mask = RADEON_RESET_DMA; else mask = RADEON_RESET_DMA1; if (!(reset_mask & mask)) { radeon_ring_lockup_update(rdev, ring); return false; } return radeon_ring_test_lockup(rdev, ring); }
/** * radeon_ring_test_lockup() - check if ring is lockedup by recording information * @rdev: radeon device structure * @ring: radeon_ring structure holding ring information * */ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { uint32_t rptr = radeon_ring_get_rptr(rdev, ring); uint64_t last = atomic64_read(&ring->last_activity); uint64_t elapsed; if (rptr != atomic_read(&ring->last_rptr)) { /* ring is still working, no lockup */ radeon_ring_lockup_update(rdev, ring); return false; } elapsed = jiffies_to_msecs(jiffies_64 - last); if (radeon_lockup_timeout && elapsed >= radeon_lockup_timeout) { dev_err(rdev->dev, "ring %d stalled for more than %llumsec\n", ring->idx, elapsed); return true; } /* give a chance to the GPU ... */ return false; }