diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c | 480 |
1 files changed, 170 insertions, 310 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index b3fc26c59787..3671f9f220bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -47,6 +47,9 @@ * that the the relevant GPU caches have been flushed. */ +static struct kmem_cache *amdgpu_fence_slab; +static atomic_t amdgpu_fence_slab_ref = ATOMIC_INIT(0); + /** * amdgpu_fence_write - write a fence value * @@ -85,24 +88,6 @@ static u32 amdgpu_fence_read(struct amdgpu_ring *ring) } /** - * amdgpu_fence_schedule_check - schedule lockup check - * - * @ring: pointer to struct amdgpu_ring - * - * Queues a delayed work item to check for lockups. - */ -static void amdgpu_fence_schedule_check(struct amdgpu_ring *ring) -{ - /* - * Do not reset the timer here with mod_delayed_work, - * this can livelock in an interaction with TTM delayed destroy. - */ - queue_delayed_work(system_power_efficient_wq, - &ring->fence_drv.lockup_work, - AMDGPU_FENCE_JIFFIES_TIMEOUT); -} - -/** * amdgpu_fence_emit - emit a fence on the requested ring * * @ring: ring the fence is associated with @@ -118,7 +103,7 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner, struct amdgpu_device *adev = ring->adev; /* we are protected by the ring emission mutex */ - *fence = kmalloc(sizeof(struct amdgpu_fence), GFP_KERNEL); + *fence = kmem_cache_alloc(amdgpu_fence_slab, GFP_KERNEL); if ((*fence) == NULL) { return -ENOMEM; } @@ -132,44 +117,20 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner, amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr, (*fence)->seq, AMDGPU_FENCE_FLAG_INT); - trace_amdgpu_fence_emit(ring->adev->ddev, ring->idx, (*fence)->seq); return 0; } /** - * amdgpu_fence_check_signaled - callback from fence_queue + * amdgpu_fence_schedule_fallback - schedule fallback check * - * this function is called with fence_queue lock held, which is also used - * for the fence locking itself, so unlocked variants are used for - * fence_signal, and remove_wait_queue. + * @ring: pointer to struct amdgpu_ring + * + * Start a timer as fallback to our interrupts. */ -static int amdgpu_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key) +static void amdgpu_fence_schedule_fallback(struct amdgpu_ring *ring) { - struct amdgpu_fence *fence; - struct amdgpu_device *adev; - u64 seq; - int ret; - - fence = container_of(wait, struct amdgpu_fence, fence_wake); - adev = fence->ring->adev; - - /* - * We cannot use amdgpu_fence_process here because we're already - * in the waitqueue, in a call from wake_up_all. - */ - seq = atomic64_read(&fence->ring->fence_drv.last_seq); - if (seq >= fence->seq) { - ret = fence_signal_locked(&fence->base); - if (!ret) - FENCE_TRACE(&fence->base, "signaled from irq context\n"); - else - FENCE_TRACE(&fence->base, "was already signaled\n"); - - __remove_wait_queue(&fence->ring->fence_drv.fence_queue, &fence->fence_wake); - fence_put(&fence->base); - } else - FENCE_TRACE(&fence->base, "pending\n"); - return 0; + mod_timer(&ring->fence_drv.fallback_timer, + jiffies + AMDGPU_FENCE_JIFFIES_TIMEOUT); } /** @@ -238,52 +199,12 @@ static bool amdgpu_fence_activity(struct amdgpu_ring *ring) } while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq); if (seq < last_emitted) - amdgpu_fence_schedule_check(ring); + amdgpu_fence_schedule_fallback(ring); return wake; } /** - * amdgpu_fence_check_lockup - check for hardware lockup - * - * @work: delayed work item - * - * Checks for fence activity and if there is none probe - * the hardware if a lockup occured. - */ -static void amdgpu_fence_check_lockup(struct work_struct *work) -{ - struct amdgpu_fence_driver *fence_drv; - struct amdgpu_ring *ring; - - fence_drv = container_of(work, struct amdgpu_fence_driver, - lockup_work.work); - ring = fence_drv->ring; - - if (!down_read_trylock(&ring->adev->exclusive_lock)) { - /* just reschedule the check if a reset is going on */ - amdgpu_fence_schedule_check(ring); - return; - } - - if (amdgpu_fence_activity(ring)) { - wake_up_all(&ring->fence_drv.fence_queue); - } - else if (amdgpu_ring_is_lockup(ring)) { - /* good news we believe it's a lockup */ - dev_warn(ring->adev->dev, "GPU lockup (current fence id " - "0x%016llx last fence id 0x%016llx on ring %d)\n", - (uint64_t)atomic64_read(&fence_drv->last_seq), - fence_drv->sync_seq[ring->idx], ring->idx); - - /* remember that we need an reset */ - ring->adev->needs_reset = true; - wake_up_all(&ring->fence_drv.fence_queue); - } - up_read(&ring->adev->exclusive_lock); -} - -/** * amdgpu_fence_process - process a fence * * @adev: amdgpu_device pointer @@ -299,6 +220,20 @@ void amdgpu_fence_process(struct amdgpu_ring *ring) } /** + * amdgpu_fence_fallback - fallback for hardware interrupts + * + * @work: delayed work item + * + * Checks for fence activity. + */ +static void amdgpu_fence_fallback(unsigned long arg) +{ + struct amdgpu_ring *ring = (void *)arg; + + amdgpu_fence_process(ring); +} + +/** * amdgpu_fence_seq_signaled - check if a fence sequence number has signaled * * @ring: ring the fence is associated with @@ -324,50 +259,6 @@ static bool amdgpu_fence_seq_signaled(struct amdgpu_ring *ring, u64 seq) return false; } -static bool amdgpu_fence_is_signaled(struct fence *f) -{ - struct amdgpu_fence *fence = to_amdgpu_fence(f); - struct amdgpu_ring *ring = fence->ring; - struct amdgpu_device *adev = ring->adev; - - if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) - return true; - - if (down_read_trylock(&adev->exclusive_lock)) { - amdgpu_fence_process(ring); - up_read(&adev->exclusive_lock); - - if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) - return true; - } - return false; -} - -/** - * amdgpu_fence_enable_signaling - enable signalling on fence - * @fence: fence - * - * This function is called with fence_queue lock held, and adds a callback - * to fence_queue that checks if this fence is signaled, and if so it - * signals the fence and removes itself. - */ -static bool amdgpu_fence_enable_signaling(struct fence *f) -{ - struct amdgpu_fence *fence = to_amdgpu_fence(f); - struct amdgpu_ring *ring = fence->ring; - - if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) - return false; - - fence->fence_wake.flags = 0; - fence->fence_wake.private = NULL; - fence->fence_wake.func = amdgpu_fence_check_signaled; - __add_wait_queue(&ring->fence_drv.fence_queue, &fence->fence_wake); - fence_get(f); - FENCE_TRACE(&fence->base, "armed on ring %i!\n", ring->idx); - return true; -} - /* * amdgpu_ring_wait_seq_timeout - wait for seq of the specific ring to signal * @ring: ring to wait on for the seq number @@ -380,7 +271,6 @@ static bool amdgpu_fence_enable_signaling(struct fence *f) */ static int amdgpu_fence_ring_wait_seq(struct amdgpu_ring *ring, uint64_t seq) { - struct amdgpu_device *adev = ring->adev; bool signaled = false; BUG_ON(!ring); @@ -390,9 +280,9 @@ static int amdgpu_fence_ring_wait_seq(struct amdgpu_ring *ring, uint64_t seq) if (atomic64_read(&ring->fence_drv.last_seq) >= seq) return 0; + amdgpu_fence_schedule_fallback(ring); wait_event(ring->fence_drv.fence_queue, ( - (signaled = amdgpu_fence_seq_signaled(ring, seq)) - || adev->needs_reset)); + (signaled = amdgpu_fence_seq_signaled(ring, seq)))); if (signaled) return 0; @@ -441,36 +331,6 @@ int amdgpu_fence_wait_empty(struct amdgpu_ring *ring) } /** - * amdgpu_fence_ref - take a ref on a fence - * - * @fence: amdgpu fence object - * - * Take a reference on a fence (all asics). - * Returns the fence. - */ -struct amdgpu_fence *amdgpu_fence_ref(struct amdgpu_fence *fence) -{ - fence_get(&fence->base); - return fence; -} - -/** - * amdgpu_fence_unref - remove a ref on a fence - * - * @fence: amdgpu fence object - * - * Remove a reference on a fence (all asics). - */ -void amdgpu_fence_unref(struct amdgpu_fence **fence) -{ - struct amdgpu_fence *tmp = *fence; - - *fence = NULL; - if (tmp) - fence_put(&tmp->base); -} - -/** * amdgpu_fence_count_emitted - get the count of emitted fences * * @ring: ring the fence is associated with @@ -621,15 +481,26 @@ int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) atomic64_set(&ring->fence_drv.last_seq, 0); ring->fence_drv.initialized = false; - INIT_DELAYED_WORK(&ring->fence_drv.lockup_work, - amdgpu_fence_check_lockup); - ring->fence_drv.ring = ring; + setup_timer(&ring->fence_drv.fallback_timer, amdgpu_fence_fallback, + (unsigned long)ring); init_waitqueue_head(&ring->fence_drv.fence_queue); if (amdgpu_enable_scheduler) { + long timeout = msecs_to_jiffies(amdgpu_lockup_timeout); + if (timeout == 0) { + /* + * FIXME: + * Delayed workqueue cannot use it directly, + * so the scheduler will not use delayed workqueue if + * MAX_SCHEDULE_TIMEOUT is set. + * Currently keep it simple and silly. + */ + timeout = MAX_SCHEDULE_TIMEOUT; + } r = amd_sched_init(&ring->sched, &amdgpu_sched_ops, - amdgpu_sched_hw_submission, ring->name); + amdgpu_sched_hw_submission, + timeout, ring->name); if (r) { DRM_ERROR("Failed to create scheduler on ring %s.\n", ring->name); @@ -654,6 +525,13 @@ int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) */ int amdgpu_fence_driver_init(struct amdgpu_device *adev) { + if (atomic_inc_return(&amdgpu_fence_slab_ref) == 1) { + amdgpu_fence_slab = kmem_cache_create( + "amdgpu_fence", sizeof(struct amdgpu_fence), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!amdgpu_fence_slab) + return -ENOMEM; + } if (amdgpu_debugfs_fence_init(adev)) dev_err(adev->dev, "fence debugfs file creation failed\n"); @@ -672,9 +550,12 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev) { int i, r; + if (atomic_dec_and_test(&amdgpu_fence_slab_ref)) + kmem_cache_destroy(amdgpu_fence_slab); mutex_lock(&adev->ring_lock); for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; + if (!ring || !ring->fence_drv.initialized) continue; r = amdgpu_fence_wait_empty(ring); @@ -686,6 +567,7 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev) amdgpu_irq_put(adev, ring->fence_drv.irq_src, ring->fence_drv.irq_type); amd_sched_fini(&ring->sched); + del_timer_sync(&ring->fence_drv.fallback_timer); ring->fence_drv.initialized = false; } mutex_unlock(&adev->ring_lock); @@ -773,6 +655,122 @@ void amdgpu_fence_driver_force_completion(struct amdgpu_device *adev) } } +/* + * Common fence implementation + */ + +static const char *amdgpu_fence_get_driver_name(struct fence *fence) +{ + return "amdgpu"; +} + +static const char *amdgpu_fence_get_timeline_name(struct fence *f) +{ + struct amdgpu_fence *fence = to_amdgpu_fence(f); + return (const char *)fence->ring->name; +} + +/** + * amdgpu_fence_is_signaled - test if fence is signaled + * + * @f: fence to test + * + * Test the fence sequence number if it is already signaled. If it isn't + * signaled start fence processing. Returns True if the fence is signaled. + */ +static bool amdgpu_fence_is_signaled(struct fence *f) +{ + struct amdgpu_fence *fence = to_amdgpu_fence(f); + struct amdgpu_ring *ring = fence->ring; + + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) + return true; + + amdgpu_fence_process(ring); + + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) + return true; + + return false; +} + +/** + * amdgpu_fence_check_signaled - callback from fence_queue + * + * this function is called with fence_queue lock held, which is also used + * for the fence locking itself, so unlocked variants are used for + * fence_signal, and remove_wait_queue. + */ +static int amdgpu_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key) +{ + struct amdgpu_fence *fence; + struct amdgpu_device *adev; + u64 seq; + int ret; + + fence = container_of(wait, struct amdgpu_fence, fence_wake); + adev = fence->ring->adev; + + /* + * We cannot use amdgpu_fence_process here because we're already + * in the waitqueue, in a call from wake_up_all. + */ + seq = atomic64_read(&fence->ring->fence_drv.last_seq); + if (seq >= fence->seq) { + ret = fence_signal_locked(&fence->base); + if (!ret) + FENCE_TRACE(&fence->base, "signaled from irq context\n"); + else + FENCE_TRACE(&fence->base, "was already signaled\n"); + + __remove_wait_queue(&fence->ring->fence_drv.fence_queue, &fence->fence_wake); + fence_put(&fence->base); + } else + FENCE_TRACE(&fence->base, "pending\n"); + return 0; +} + +/** + * amdgpu_fence_enable_signaling - enable signalling on fence + * @fence: fence + * + * This function is called with fence_queue lock held, and adds a callback + * to fence_queue that checks if this fence is signaled, and if so it + * signals the fence and removes itself. + */ +static bool amdgpu_fence_enable_signaling(struct fence *f) +{ + struct amdgpu_fence *fence = to_amdgpu_fence(f); + struct amdgpu_ring *ring = fence->ring; + + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) + return false; + + fence->fence_wake.flags = 0; + fence->fence_wake.private = NULL; + fence->fence_wake.func = amdgpu_fence_check_signaled; + __add_wait_queue(&ring->fence_drv.fence_queue, &fence->fence_wake); + fence_get(f); + if (!timer_pending(&ring->fence_drv.fallback_timer)) + amdgpu_fence_schedule_fallback(ring); + FENCE_TRACE(&fence->base, "armed on ring %i!\n", ring->idx); + return true; +} + +static void amdgpu_fence_release(struct fence *f) +{ + struct amdgpu_fence *fence = to_amdgpu_fence(f); + kmem_cache_free(amdgpu_fence_slab, fence); +} + +const struct fence_ops amdgpu_fence_ops = { + .get_driver_name = amdgpu_fence_get_driver_name, + .get_timeline_name = amdgpu_fence_get_timeline_name, + .enable_signaling = amdgpu_fence_enable_signaling, + .signaled = amdgpu_fence_is_signaled, + .wait = fence_default_wait, + .release = amdgpu_fence_release, +}; /* * Fence debugfs @@ -823,141 +821,3 @@ int amdgpu_debugfs_fence_init(struct amdgpu_device *adev) #endif } -static const char *amdgpu_fence_get_driver_name(struct fence *fence) -{ - return "amdgpu"; -} - -static const char *amdgpu_fence_get_timeline_name(struct fence *f) -{ - struct amdgpu_fence *fence = to_amdgpu_fence(f); - return (const char *)fence->ring->name; -} - -static inline bool amdgpu_test_signaled(struct amdgpu_fence *fence) -{ - return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); -} - -static bool amdgpu_test_signaled_any(struct fence **fences, uint32_t count) -{ - int idx; - struct fence *fence; - - for (idx = 0; idx < count; ++idx) { - fence = fences[idx]; - if (fence) { - if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) - return true; - } - } - return false; -} - -struct amdgpu_wait_cb { - struct fence_cb base; - struct task_struct *task; -}; - -static void amdgpu_fence_wait_cb(struct fence *fence, struct fence_cb *cb) -{ - struct amdgpu_wait_cb *wait = - container_of(cb, struct amdgpu_wait_cb, base); - wake_up_process(wait->task); -} - -static signed long amdgpu_fence_default_wait(struct fence *f, bool intr, - signed long t) -{ - struct amdgpu_fence *fence = to_amdgpu_fence(f); - struct amdgpu_device *adev = fence->ring->adev; - - return amdgpu_fence_wait_any(adev, &f, 1, intr, t); -} - -/** - * Wait the fence array with timeout - * - * @adev: amdgpu device - * @array: the fence array with amdgpu fence pointer - * @count: the number of the fence array - * @intr: when sleep, set the current task interruptable or not - * @t: timeout to wait - * - * It will return when any fence is signaled or timeout. - */ -signed long amdgpu_fence_wait_any(struct amdgpu_device *adev, - struct fence **array, uint32_t count, - bool intr, signed long t) -{ - struct amdgpu_wait_cb *cb; - struct fence *fence; - unsigned idx; - - BUG_ON(!array); - - cb = kcalloc(count, sizeof(struct amdgpu_wait_cb), GFP_KERNEL); - if (cb == NULL) { - t = -ENOMEM; - goto err_free_cb; - } - - for (idx = 0; idx < count; ++idx) { - fence = array[idx]; - if (fence) { - cb[idx].task = current; - if (fence_add_callback(fence, - &cb[idx].base, amdgpu_fence_wait_cb)) { - /* The fence is already signaled */ - goto fence_rm_cb; - } - } - } - - while (t > 0) { - if (intr) - set_current_state(TASK_INTERRUPTIBLE); - else - set_current_state(TASK_UNINTERRUPTIBLE); - - /* - * amdgpu_test_signaled_any must be called after - * set_current_state to prevent a race with wake_up_process - */ - if (amdgpu_test_signaled_any(array, count)) - break; - - if (adev->needs_reset) { - t = -EDEADLK; - break; - } - - t = schedule_timeout(t); - - if (t > 0 && intr && signal_pending(current)) - t = -ERESTARTSYS; - } - - __set_current_state(TASK_RUNNING); - -fence_rm_cb: - for (idx = 0; idx < count; ++idx) { - fence = array[idx]; - if (fence && cb[idx].base.func) - fence_remove_callback(fence, &cb[idx].base); - } - -err_free_cb: - kfree(cb); - - return t; -} - -const struct fence_ops amdgpu_fence_ops = { - .get_driver_name = amdgpu_fence_get_driver_name, - .get_timeline_name = amdgpu_fence_get_timeline_name, - .enable_signaling = amdgpu_fence_enable_signaling, - .signaled = amdgpu_fence_is_signaled, - .wait = amdgpu_fence_default_wait, - .release = NULL, -}; |