Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b0f8438 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/tmr: handle races with hw when updating the next alarm time



If the time to the next alarm is short enough, we could race with HW and
end up with an ~4 second delay until it triggers.

Fix this by checking again after we update HW.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
Cc: stable@vger.kernel.org
parent 3733bd8b
Loading
Loading
Loading
Loading
+16 −10
Original line number Diff line number Diff line
@@ -36,23 +36,29 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
	unsigned long flags;
	LIST_HEAD(exec);

	/* move any due alarms off the pending list */
	/* Process pending alarms. */
	spin_lock_irqsave(&tmr->lock, flags);
	list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
		if (alarm->timestamp <= nvkm_timer_read(tmr))
		/* Have we hit the earliest alarm that hasn't gone off? */
		if (alarm->timestamp > nvkm_timer_read(tmr)) {
			/* Schedule it.  If we didn't race, we're done. */
			tmr->func->alarm_init(tmr, alarm->timestamp);
			if (alarm->timestamp > nvkm_timer_read(tmr))
				break;
		}

		/* Move to completed list.  We'll drop the lock before
		 * executing the callback so it can reschedule itself.
		 */
		list_move_tail(&alarm->head, &exec);
	}

	/* reschedule interrupt for next alarm time */
	if (!list_empty(&tmr->alarms)) {
		alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
		tmr->func->alarm_init(tmr, alarm->timestamp);
	} else {
	/* Shut down interrupt if no more pending alarms. */
	if (list_empty(&tmr->alarms))
		tmr->func->alarm_fini(tmr);
	}
	spin_unlock_irqrestore(&tmr->lock, flags);

	/* execute any pending alarm handlers */
	/* Execute completed callbacks. */
	list_for_each_entry_safe(alarm, atemp, &exec, head) {
		list_del_init(&alarm->head);
		alarm->func(alarm);