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>
if (!etr_eacr.sl)
return;
disable_sync_clock(NULL);
if (!etr_eacr.sl)
return;
disable_sync_clock(NULL);
- set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
- queue_work(time_sync_wq, &etr_work);
+ 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);
+ }
if (!etr_eacr.es)
return;
disable_sync_clock(NULL);
if (!etr_eacr.es)
return;
disable_sync_clock(NULL);
- set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
- queue_work(time_sync_wq, &etr_work);
+ 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);
+ }
* Do not try to get the alternate port aib if the clock
* is not in sync yet.
*/
* 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())
* 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 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;
etr_update_eacr(eacr);
etr_set_tolec_timeout(now);
goto out_unlock;