diff options
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r-- | arch/s390/kernel/time.c | 71 |
1 files changed, 44 insertions, 27 deletions
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index fc468cae4460..f72d41068dc2 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c | |||
@@ -331,6 +331,7 @@ static unsigned long long adjust_time(unsigned long long old, | |||
331 | } | 331 | } |
332 | 332 | ||
333 | static DEFINE_PER_CPU(atomic_t, clock_sync_word); | 333 | static DEFINE_PER_CPU(atomic_t, clock_sync_word); |
334 | static DEFINE_MUTEX(clock_sync_mutex); | ||
334 | static unsigned long clock_sync_flags; | 335 | static unsigned long clock_sync_flags; |
335 | 336 | ||
336 | #define CLOCK_SYNC_HAS_ETR 0 | 337 | #define CLOCK_SYNC_HAS_ETR 0 |
@@ -394,6 +395,20 @@ static void enable_sync_clock(void) | |||
394 | atomic_set_mask(0x80000000, sw_ptr); | 395 | atomic_set_mask(0x80000000, sw_ptr); |
395 | } | 396 | } |
396 | 397 | ||
398 | /* | ||
399 | * Function to check if the clock is in sync. | ||
400 | */ | ||
401 | static inline int check_sync_clock(void) | ||
402 | { | ||
403 | atomic_t *sw_ptr; | ||
404 | int rc; | ||
405 | |||
406 | sw_ptr = &get_cpu_var(clock_sync_word); | ||
407 | rc = (atomic_read(sw_ptr) & 0x80000000U) != 0; | ||
408 | put_cpu_var(clock_sync_sync); | ||
409 | return rc; | ||
410 | } | ||
411 | |||
397 | /* Single threaded workqueue used for etr and stp sync events */ | 412 | /* Single threaded workqueue used for etr and stp sync events */ |
398 | static struct workqueue_struct *time_sync_wq; | 413 | static struct workqueue_struct *time_sync_wq; |
399 | 414 | ||
@@ -485,6 +500,8 @@ static void etr_reset(void) | |||
485 | if (etr_setr(&etr_eacr) == 0) { | 500 | if (etr_setr(&etr_eacr) == 0) { |
486 | etr_tolec = get_clock(); | 501 | etr_tolec = get_clock(); |
487 | set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); | 502 | set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); |
503 | if (etr_port0_online && etr_port1_online) | ||
504 | set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
488 | } else if (etr_port0_online || etr_port1_online) { | 505 | } else if (etr_port0_online || etr_port1_online) { |
489 | pr_warning("The real or virtual hardware system does " | 506 | pr_warning("The real or virtual hardware system does " |
490 | "not provide an ETR interface\n"); | 507 | "not provide an ETR interface\n"); |
@@ -533,8 +550,7 @@ void etr_switch_to_local(void) | |||
533 | { | 550 | { |
534 | if (!etr_eacr.sl) | 551 | if (!etr_eacr.sl) |
535 | return; | 552 | return; |
536 | if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | 553 | disable_sync_clock(NULL); |
537 | disable_sync_clock(NULL); | ||
538 | set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); | 554 | set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); |
539 | queue_work(time_sync_wq, &etr_work); | 555 | queue_work(time_sync_wq, &etr_work); |
540 | } | 556 | } |
@@ -549,8 +565,7 @@ void etr_sync_check(void) | |||
549 | { | 565 | { |
550 | if (!etr_eacr.es) | 566 | if (!etr_eacr.es) |
551 | return; | 567 | return; |
552 | if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | 568 | disable_sync_clock(NULL); |
553 | disable_sync_clock(NULL); | ||
554 | set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); | 569 | set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); |
555 | queue_work(time_sync_wq, &etr_work); | 570 | queue_work(time_sync_wq, &etr_work); |
556 | } | 571 | } |
@@ -914,7 +929,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, | |||
914 | * Do not try to get the alternate port aib if the clock | 929 | * Do not try to get the alternate port aib if the clock |
915 | * is not in sync yet. | 930 | * is not in sync yet. |
916 | */ | 931 | */ |
917 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es) | 932 | if (!check_sync_clock()) |
918 | return eacr; | 933 | return eacr; |
919 | 934 | ||
920 | /* | 935 | /* |
@@ -997,7 +1012,6 @@ static void etr_work_fn(struct work_struct *work) | |||
997 | on_each_cpu(disable_sync_clock, NULL, 1); | 1012 | on_each_cpu(disable_sync_clock, NULL, 1); |
998 | del_timer_sync(&etr_timer); | 1013 | del_timer_sync(&etr_timer); |
999 | etr_update_eacr(eacr); | 1014 | etr_update_eacr(eacr); |
1000 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1001 | goto out_unlock; | 1015 | goto out_unlock; |
1002 | } | 1016 | } |
1003 | 1017 | ||
@@ -1071,18 +1085,13 @@ static void etr_work_fn(struct work_struct *work) | |||
1071 | /* Both ports not usable. */ | 1085 | /* Both ports not usable. */ |
1072 | eacr.es = eacr.sl = 0; | 1086 | eacr.es = eacr.sl = 0; |
1073 | sync_port = -1; | 1087 | sync_port = -1; |
1074 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1075 | } | 1088 | } |
1076 | 1089 | ||
1077 | if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | ||
1078 | eacr.es = 0; | ||
1079 | |||
1080 | /* | 1090 | /* |
1081 | * If the clock is in sync just update the eacr and return. | 1091 | * If the clock is in sync just update the eacr and return. |
1082 | * If there is no valid sync port wait for a port update. | 1092 | * If there is no valid sync port wait for a port update. |
1083 | */ | 1093 | */ |
1084 | if (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) || | 1094 | if (check_sync_clock() || sync_port < 0) { |
1085 | eacr.es || sync_port < 0) { | ||
1086 | etr_update_eacr(eacr); | 1095 | etr_update_eacr(eacr); |
1087 | etr_set_tolec_timeout(now); | 1096 | etr_set_tolec_timeout(now); |
1088 | goto out_unlock; | 1097 | goto out_unlock; |
@@ -1103,13 +1112,11 @@ static void etr_work_fn(struct work_struct *work) | |||
1103 | * and set up a timer to try again after 0.5 seconds | 1112 | * and set up a timer to try again after 0.5 seconds |
1104 | */ | 1113 | */ |
1105 | etr_update_eacr(eacr); | 1114 | etr_update_eacr(eacr); |
1106 | set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1107 | if (now < etr_tolec + (1600000 << 12) || | 1115 | if (now < etr_tolec + (1600000 << 12) || |
1108 | etr_sync_clock_stop(&aib, sync_port) != 0) { | 1116 | etr_sync_clock_stop(&aib, sync_port) != 0) { |
1109 | /* Sync failed. Try again in 1/2 second. */ | 1117 | /* Sync failed. Try again in 1/2 second. */ |
1110 | eacr.es = 0; | 1118 | eacr.es = 0; |
1111 | etr_update_eacr(eacr); | 1119 | etr_update_eacr(eacr); |
1112 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1113 | etr_set_sync_timeout(); | 1120 | etr_set_sync_timeout(); |
1114 | } else | 1121 | } else |
1115 | etr_set_tolec_timeout(now); | 1122 | etr_set_tolec_timeout(now); |
@@ -1191,19 +1198,30 @@ static ssize_t etr_online_store(struct sys_device *dev, | |||
1191 | return -EINVAL; | 1198 | return -EINVAL; |
1192 | if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) | 1199 | if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) |
1193 | return -EOPNOTSUPP; | 1200 | return -EOPNOTSUPP; |
1201 | mutex_lock(&clock_sync_mutex); | ||
1194 | if (dev == &etr_port0_dev) { | 1202 | if (dev == &etr_port0_dev) { |
1195 | if (etr_port0_online == value) | 1203 | if (etr_port0_online == value) |
1196 | return count; /* Nothing to do. */ | 1204 | goto out; /* Nothing to do. */ |
1197 | etr_port0_online = value; | 1205 | etr_port0_online = value; |
1206 | if (etr_port0_online && etr_port1_online) | ||
1207 | set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1208 | else | ||
1209 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1198 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); | 1210 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); |
1199 | queue_work(time_sync_wq, &etr_work); | 1211 | queue_work(time_sync_wq, &etr_work); |
1200 | } else { | 1212 | } else { |
1201 | if (etr_port1_online == value) | 1213 | if (etr_port1_online == value) |
1202 | return count; /* Nothing to do. */ | 1214 | goto out; /* Nothing to do. */ |
1203 | etr_port1_online = value; | 1215 | etr_port1_online = value; |
1216 | if (etr_port0_online && etr_port1_online) | ||
1217 | set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1218 | else | ||
1219 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
1204 | set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); | 1220 | set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); |
1205 | queue_work(time_sync_wq, &etr_work); | 1221 | queue_work(time_sync_wq, &etr_work); |
1206 | } | 1222 | } |
1223 | out: | ||
1224 | mutex_unlock(&clock_sync_mutex); | ||
1207 | return count; | 1225 | return count; |
1208 | } | 1226 | } |
1209 | 1227 | ||
@@ -1471,8 +1489,6 @@ static void stp_timing_alert(struct stp_irq_parm *intparm) | |||
1471 | */ | 1489 | */ |
1472 | void stp_sync_check(void) | 1490 | void stp_sync_check(void) |
1473 | { | 1491 | { |
1474 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) | ||
1475 | return; | ||
1476 | disable_sync_clock(NULL); | 1492 | disable_sync_clock(NULL); |
1477 | queue_work(time_sync_wq, &stp_work); | 1493 | queue_work(time_sync_wq, &stp_work); |
1478 | } | 1494 | } |
@@ -1485,8 +1501,6 @@ void stp_sync_check(void) | |||
1485 | */ | 1501 | */ |
1486 | void stp_island_check(void) | 1502 | void stp_island_check(void) |
1487 | { | 1503 | { |
1488 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) | ||
1489 | return; | ||
1490 | disable_sync_clock(NULL); | 1504 | disable_sync_clock(NULL); |
1491 | queue_work(time_sync_wq, &stp_work); | 1505 | queue_work(time_sync_wq, &stp_work); |
1492 | } | 1506 | } |
@@ -1513,10 +1527,6 @@ static int stp_sync_clock(void *data) | |||
1513 | 1527 | ||
1514 | enable_sync_clock(); | 1528 | enable_sync_clock(); |
1515 | 1529 | ||
1516 | set_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
1517 | if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | ||
1518 | queue_work(time_sync_wq, &etr_work); | ||
1519 | |||
1520 | rc = 0; | 1530 | rc = 0; |
1521 | if (stp_info.todoff[0] || stp_info.todoff[1] || | 1531 | if (stp_info.todoff[0] || stp_info.todoff[1] || |
1522 | stp_info.todoff[2] || stp_info.todoff[3] || | 1532 | stp_info.todoff[2] || stp_info.todoff[3] || |
@@ -1535,9 +1545,6 @@ static int stp_sync_clock(void *data) | |||
1535 | if (rc) { | 1545 | if (rc) { |
1536 | disable_sync_clock(NULL); | 1546 | disable_sync_clock(NULL); |
1537 | stp_sync->in_sync = -EAGAIN; | 1547 | stp_sync->in_sync = -EAGAIN; |
1538 | clear_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
1539 | if (etr_port0_online || etr_port1_online) | ||
1540 | queue_work(time_sync_wq, &etr_work); | ||
1541 | } else | 1548 | } else |
1542 | stp_sync->in_sync = 1; | 1549 | stp_sync->in_sync = 1; |
1543 | xchg(&first, 0); | 1550 | xchg(&first, 0); |
@@ -1569,6 +1576,10 @@ static void stp_work_fn(struct work_struct *work) | |||
1569 | if (rc || stp_info.c == 0) | 1576 | if (rc || stp_info.c == 0) |
1570 | goto out_unlock; | 1577 | goto out_unlock; |
1571 | 1578 | ||
1579 | /* Skip synchronization if the clock is already in sync. */ | ||
1580 | if (check_sync_clock()) | ||
1581 | goto out_unlock; | ||
1582 | |||
1572 | memset(&stp_sync, 0, sizeof(stp_sync)); | 1583 | memset(&stp_sync, 0, sizeof(stp_sync)); |
1573 | get_online_cpus(); | 1584 | get_online_cpus(); |
1574 | atomic_set(&stp_sync.cpus, num_online_cpus() - 1); | 1585 | atomic_set(&stp_sync.cpus, num_online_cpus() - 1); |
@@ -1684,8 +1695,14 @@ static ssize_t stp_online_store(struct sysdev_class *class, | |||
1684 | return -EINVAL; | 1695 | return -EINVAL; |
1685 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) | 1696 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) |
1686 | return -EOPNOTSUPP; | 1697 | return -EOPNOTSUPP; |
1698 | mutex_lock(&clock_sync_mutex); | ||
1687 | stp_online = value; | 1699 | stp_online = value; |
1700 | if (stp_online) | ||
1701 | set_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
1702 | else | ||
1703 | clear_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
1688 | queue_work(time_sync_wq, &stp_work); | 1704 | queue_work(time_sync_wq, &stp_work); |
1705 | mutex_unlock(&clock_sync_mutex); | ||
1689 | return count; | 1706 | return count; |
1690 | } | 1707 | } |
1691 | 1708 | ||