FreeBSD/sys/dev/drm2/radeon/radeon_fence.c
/**
 * radeon_fence_wait_any_seq - wait for a sequence number on any ring
 *
 * @rdev: radeon device pointer
 * @target_seq: sequence number(s) we want to wait for
 * @intr: use interruptable sleep
 *
 * Wait for the requested sequence number(s) to be written by any ring
 * (all asics).  Sequnce number array is indexed by ring id.
 * @intr selects whether to use interruptable (true) or non-interruptable
 * (false) sleep when waiting for the sequence number.  Helper function
 * for radeon_fence_wait_any(), et al.
 * Returns 0 if the sequence number has passed, error for all other cases.
 */
wait for a sequence number on any ring
@rdev: radeon device pointer
@target_seq: sequence number(s) we want to wait for
@intr: use interruptable sleep

Wait for the requested sequence number(s) to be written by any ring
(all asics).  Sequnce number array is indexed by ring id.
@intr selects whether to use interruptable (true) or non-interruptable
(false) sleep when waiting for the sequence number.  Helper function
for radeon_fence_wait_any(), et al.
Returns 0 if the sequence number has passed, error for all other cases.
wait for a sequence number on any ring @rdev : radeon device pointer @target_seq : sequence number ( s ) we want to wait for @intr : use interruptable sleep Wait for the requested sequence number ( s ) to be written by any ring ( all asics ) . Sequnce number array is indexed by ring id . @intr selects whether to use interruptable ( true ) or non - interruptable ( false ) sleep when waiting for the sequence number . Helper function for radeon_fence_wait_any () et al . Returns 0 if the sequence number has passed error for all other cases .
static int radeon_fence_wait_any_seq(struct radeon_device *rdev,
				     u64 *target_seq, bool intr)
{
	unsigned long timeout, last_activity, tmp;
	unsigned i, ring = RADEON_NUM_RINGS;
	bool signaled, fence_queue_locked;
	int r;
	for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) {
		if (!target_seq[i]) {
			continue;
		}
		if (time_after(rdev->fence_drv[i].last_activity, last_activity)) {
			last_activity = rdev->fence_drv[i].last_activity;
		}
		if (i < ring) {
			ring = i;
		}
	}
	if (ring == RADEON_NUM_RINGS) {
		return -ENOENT;
	}
	while (!radeon_fence_any_seq_signaled(rdev, target_seq)) {
		timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT;
		if (time_after(last_activity, timeout)) {
			timeout = last_activity - timeout;
		} else {
			timeout = 1;
		}
		CTR2(KTR_DRM, "radeon fence: wait begin (ring=%d, target_seq=%d)",
		    ring, target_seq[ring]);
		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
			if (target_seq[i]) {
				radeon_irq_kms_sw_irq_get(rdev, i);
			}
		}
		fence_queue_locked = false;
		r = 0;
		while (!(signaled = radeon_fence_any_seq_signaled(rdev,
		    target_seq))) {
			if (!fence_queue_locked) {
				mtx_lock(&rdev->fence_queue_mtx);
				fence_queue_locked = true;
			}
			if (intr) {
				r = cv_timedwait_sig(&rdev->fence_queue,
				    &rdev->fence_queue_mtx,
				    timeout);
			} else {
				r = cv_timedwait(&rdev->fence_queue,
				    &rdev->fence_queue_mtx,
				    timeout);
			}
			if (r == EINTR)
				r = ERESTARTSYS;
			if (r != 0) {
				if (r == EWOULDBLOCK) {
					signaled =
					    radeon_fence_any_seq_signaled(
						rdev, target_seq);
				}
				break;
			}
		}
		if (fence_queue_locked) {
			mtx_unlock(&rdev->fence_queue_mtx);
		}
		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
			if (target_seq[i]) {
				radeon_irq_kms_sw_irq_put(rdev, i);
			}
		}
		if (unlikely(r == ERESTARTSYS)) {
			return -r;
		}
		CTR2(KTR_DRM, "radeon fence: wait end (ring=%d, target_seq=%d)",
		    ring, target_seq[ring]);
		if (unlikely(!signaled)) {
#ifndef __FreeBSD__
			if (r) {
				continue;
			}
#endif
			sx_xlock(&rdev->ring_lock);
			for (i = 0, tmp = 0; i < RADEON_NUM_RINGS; ++i) {
				if (time_after(rdev->fence_drv[i].last_activity, tmp)) {
					tmp = rdev->fence_drv[i].last_activity;
				}
			}
			if (last_activity != tmp) {
				last_activity = tmp;
				sx_xunlock(&rdev->ring_lock);
				continue;
			}
			if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) {
				dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016jx)\n",
					 (uintmax_t)target_seq[ring]);
				for (i = 0; i < RADEON_NUM_RINGS; ++i) {
					rdev->fence_drv[i].last_activity = jiffies;
				}
				rdev->ring[ring].ready = false;
				sx_xunlock(&rdev->ring_lock);
				return -EDEADLK;
			}
			sx_xunlock(&rdev->ring_lock);
		}
	}
	return 0;
}
static int radeon_fence_wait_any_seq ( struct radeon_device * rdev , u64 * target_seq , bool intr ) { unsigned long timeout , last_activity , tmp ; unsigned i , ring = RADEON_NUM_RINGS ; bool signaled , fence_queue_locked ; int r ; for ( i = 0 , last_activity = 0 ; i < RADEON_NUM_RINGS ; ++ i ) { if ( ! target_seq [ i ] ) { continue ; } if ( time_after ( rdev -> fence_drv [ i ] . last_activity , last_activity ) ) { last_activity = rdev -> fence_drv [ i ] . last_activity ; } if ( i < ring ) { ring = i ; } } if ( ring == RADEON_NUM_RINGS ) { return - ENOENT ; } while ( ! radeon_fence_any_seq_signaled ( rdev , target_seq ) ) { timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT ; if ( time_after ( last_activity , timeout ) ) { timeout = last_activity - timeout ; } else { timeout = 1 ; } CTR2 ( KTR_DRM , " " , ring , target_seq [ ring ] ) ; for ( i = 0 ; i < RADEON_NUM_RINGS ; ++ i ) { if ( target_seq [ i ] ) { radeon_irq_kms_sw_irq_get ( rdev , i ) ; } } fence_queue_locked = false ; r = 0 ; while ( ! ( signaled = radeon_fence_any_seq_signaled ( rdev , target_seq ) ) ) { if ( ! fence_queue_locked ) { mtx_lock ( & rdev -> fence_queue_mtx ) ; fence_queue_locked = true ; } if ( intr ) { r = cv_timedwait_sig ( & rdev -> fence_queue , & rdev -> fence_queue_mtx , timeout ) ; } else { r = cv_timedwait ( & rdev -> fence_queue , & rdev -> fence_queue_mtx , timeout ) ; } if ( r == EINTR ) r = ERESTARTSYS ; if ( r != 0 ) { if ( r == EWOULDBLOCK ) { signaled = radeon_fence_any_seq_signaled ( rdev , target_seq ) ; } break ; } } if ( fence_queue_locked ) { mtx_unlock ( & rdev -> fence_queue_mtx ) ; } for ( i = 0 ; i < RADEON_NUM_RINGS ; ++ i ) { if ( target_seq [ i ] ) { radeon_irq_kms_sw_irq_put ( rdev , i ) ; } } if ( unlikely ( r == ERESTARTSYS ) ) { return - r ; } CTR2 ( KTR_DRM , " " , ring , target_seq [ ring ] ) ; if ( unlikely ( ! signaled ) ) { #ifndef __FreeBSD__ if ( r ) { continue ; } #endif sx_xlock ( & rdev -> ring_lock ) ; for ( i = 0 , tmp = 0 ; i < RADEON_NUM_RINGS ; ++ i ) { if ( time_after ( rdev -> fence_drv [ i ] . last_activity , tmp ) ) { tmp = rdev -> fence_drv [ i ] . last_activity ; } } if ( last_activity != tmp ) { last_activity = tmp ; sx_xunlock ( & rdev -> ring_lock ) ; continue ; } if ( radeon_ring_is_lockup ( rdev , ring , & rdev -> ring [ ring ] ) ) { dev_warn ( rdev -> dev , " \n " , ( uintmax_t ) target_seq [ ring ] ) ; for ( i = 0 ; i < RADEON_NUM_RINGS ; ++ i ) { rdev -> fence_drv [ i ] . last_activity = jiffies ; } rdev -> ring [ ring ] . ready = false ; sx_xunlock ( & rdev -> ring_lock ) ; return - EDEADLK ; } sx_xunlock ( & rdev -> ring_lock ) ; } } return 0 ; }
radeon_fence_wait_any_seq - wait for a sequence number on any ring
 @rdev: radeon device pointer
 @target_seq: sequence number(s) we want to wait for
 @intr: use interruptable sleep
radeon_fence_wait_any_seq - wait for a sequence number on any ring @rdev : radeon device pointer @target_seq : sequence number ( s ) we want to wait for @intr : use interruptable sleep
/* use the most recent one as indicator */ /* For lockup detection just pick the lowest ring we are
		 * actively waiting for
		 */ /* nothing to wait for ? */ /* the normal case, timeout is somewhere before last_activity */ /* either jiffies wrapped around, or no fence was signaled in the last 500ms
			 * anyway we will just wait for the minimum amount and then check for a lockup
			 */ /* we were interrupted for some reason and fence
			 * isn't signaled yet, resume waiting */ /* test if somebody else has already decided that this is a lockup */ /* good news we believe it's a lockup */ /* change last activity so nobody else think there is a lockup */ /* mark the ring as not ready any more */