aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2010-07-27 13:29:38 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-07-27 13:29:42 -0400
commit33fea794b9deeb8ffb77e284eb37375b8f45a2c4 (patch)
tree4a9a1cbad368c1f6c2c79e334c68f8ca04721b98
parentf5cdac274c62ab61874374abb60f2310ab979295 (diff)
[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>
-rw-r--r--arch/s390/kernel/time.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index a2163c95eb98..15a7536452d5 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -524,8 +524,11 @@ void etr_switch_to_local(void)
524 if (!etr_eacr.sl) 524 if (!etr_eacr.sl)
525 return; 525 return;
526 disable_sync_clock(NULL); 526 disable_sync_clock(NULL);
527 set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); 527 if (!test_and_set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events)) {
528 queue_work(time_sync_wq, &etr_work); 528 etr_eacr.es = etr_eacr.sl = 0;
529 etr_setr(&etr_eacr);
530 queue_work(time_sync_wq, &etr_work);
531 }
529} 532}
530 533
531/* 534/*
@@ -539,8 +542,11 @@ void etr_sync_check(void)
539 if (!etr_eacr.es) 542 if (!etr_eacr.es)
540 return; 543 return;
541 disable_sync_clock(NULL); 544 disable_sync_clock(NULL);
542 set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); 545 if (!test_and_set_bit(ETR_EVENT_SYNC_CHECK, &etr_events)) {
543 queue_work(time_sync_wq, &etr_work); 546 etr_eacr.es = 0;
547 etr_setr(&etr_eacr);
548 queue_work(time_sync_wq, &etr_work);
549 }
544} 550}
545 551
546/* 552/*
@@ -902,7 +908,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib,
902 * Do not try to get the alternate port aib if the clock 908 * Do not try to get the alternate port aib if the clock
903 * is not in sync yet. 909 * is not in sync yet.
904 */ 910 */
905 if (!check_sync_clock()) 911 if (!eacr.es || !check_sync_clock())
906 return eacr; 912 return eacr;
907 913
908 /* 914 /*
@@ -1064,7 +1070,7 @@ static void etr_work_fn(struct work_struct *work)
1064 * If the clock is in sync just update the eacr and return. 1070 * If the clock is in sync just update the eacr and return.
1065 * If there is no valid sync port wait for a port update. 1071 * If there is no valid sync port wait for a port update.
1066 */ 1072 */
1067 if (check_sync_clock() || sync_port < 0) { 1073 if ((eacr.es && check_sync_clock()) || sync_port < 0) {
1068 etr_update_eacr(eacr); 1074 etr_update_eacr(eacr);
1069 etr_set_tolec_timeout(now); 1075 etr_set_tolec_timeout(now);
1070 goto out_unlock; 1076 goto out_unlock;