[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: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
committed by
Martin Schwidefsky
parent
f5cdac274c
commit
33fea794b9
@@ -524,8 +524,11 @@ void etr_switch_to_local(void)
|
|||||||
if (!etr_eacr.sl)
|
if (!etr_eacr.sl)
|
||||||
return;
|
return;
|
||||||
disable_sync_clock(NULL);
|
disable_sync_clock(NULL);
|
||||||
set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
|
if (!test_and_set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events)) {
|
||||||
queue_work(time_sync_wq, &etr_work);
|
etr_eacr.es = etr_eacr.sl = 0;
|
||||||
|
etr_setr(&etr_eacr);
|
||||||
|
queue_work(time_sync_wq, &etr_work);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -539,8 +542,11 @@ void etr_sync_check(void)
|
|||||||
if (!etr_eacr.es)
|
if (!etr_eacr.es)
|
||||||
return;
|
return;
|
||||||
disable_sync_clock(NULL);
|
disable_sync_clock(NULL);
|
||||||
set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
|
if (!test_and_set_bit(ETR_EVENT_SYNC_CHECK, &etr_events)) {
|
||||||
queue_work(time_sync_wq, &etr_work);
|
etr_eacr.es = 0;
|
||||||
|
etr_setr(&etr_eacr);
|
||||||
|
queue_work(time_sync_wq, &etr_work);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -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
|
* Do not try to get the alternate port aib if the clock
|
||||||
* is not in sync yet.
|
* is not in sync yet.
|
||||||
*/
|
*/
|
||||||
if (!check_sync_clock())
|
if (!eacr.es || !check_sync_clock())
|
||||||
return eacr;
|
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 the clock is in sync just update the eacr and return.
|
||||||
* If there is no valid sync port wait for a port update.
|
* 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_update_eacr(eacr);
|
||||||
etr_set_tolec_timeout(now);
|
etr_set_tolec_timeout(now);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
Reference in New Issue
Block a user