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

Commit 33fea794 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Martin Schwidefsky
Browse files

[S390] etr: fix clock synchronization race



The etr events switch-to-local and sync-check disable the synchronous clock
and schedule a work queue that tries to get the clock back into sync.
If another switch-to-local or sync-check event occurs while the work queue
function etr_work_fn still runs the eacr.es bit and the clock_sync_word can
become inconsistent because check_sync_clock only uses the clock_sync_word
to determine if the clock is in sync or not. The second pass of the
etr_work_fn will reset the eacr.es bit but will leave the clock_sync_word
intact. Fix this race by moving the reset of the eacr.es bit into the
switch-to-local and sync-check functions and by checking the eacr.es bit
as well to decide if the clock needs to be synced.

Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent f5cdac27
Loading
Loading
Loading
Loading
+12 −6
Original line number Diff line number Diff line
@@ -524,9 +524,12 @@ void etr_switch_to_local(void)
	if (!etr_eacr.sl)
		return;
	disable_sync_clock(NULL);
	set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
	if (!test_and_set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events)) {
		etr_eacr.es = etr_eacr.sl = 0;
		etr_setr(&etr_eacr);
		queue_work(time_sync_wq, &etr_work);
	}
}

/*
 * ETR sync check machine check. This is called when the ETR OTE and the
@@ -539,9 +542,12 @@ void etr_sync_check(void)
	if (!etr_eacr.es)
		return;
	disable_sync_clock(NULL);
	set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
	if (!test_and_set_bit(ETR_EVENT_SYNC_CHECK, &etr_events)) {
		etr_eacr.es = 0;
		etr_setr(&etr_eacr);
		queue_work(time_sync_wq, &etr_work);
	}
}

/*
 * ETR timing alert. There are two causes:
@@ -902,7 +908,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib,
	 * Do not try to get the alternate port aib if the clock
	 * is not in sync yet.
	 */
	if (!check_sync_clock())
	if (!eacr.es || !check_sync_clock())
		return eacr;

	/*
@@ -1064,7 +1070,7 @@ static void etr_work_fn(struct work_struct *work)
	 * If the clock is in sync just update the eacr and return.
	 * If there is no valid sync port wait for a port update.
	 */
	if (check_sync_clock() || sync_port < 0) {
	if ((eacr.es && check_sync_clock()) || sync_port < 0) {
		etr_update_eacr(eacr);
		etr_set_tolec_timeout(now);
		goto out_unlock;