Commit 8283cb43 authored by Martin Schwidefsky's avatar Martin Schwidefsky

[S390] clock sync mode flags

The clock sync mode flag CLOCK_SYNC_STP is not cleared when stp
is set offline. In this case the get_sync_clock() function returns
-EACCESS and the dasd driver will block all i/o until stp is enabled
again. In addition get_sync_clock can return -EACCESS if the clock is
not in sync instead of -EAGAIN.

Rework the stp/etr online handling to fix these problems.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 56e25e97
...@@ -331,6 +331,7 @@ static unsigned long long adjust_time(unsigned long long old, ...@@ -331,6 +331,7 @@ static unsigned long long adjust_time(unsigned long long old,
} }
static DEFINE_PER_CPU(atomic_t, clock_sync_word); static DEFINE_PER_CPU(atomic_t, clock_sync_word);
static DEFINE_MUTEX(clock_sync_mutex);
static unsigned long clock_sync_flags; static unsigned long clock_sync_flags;
#define CLOCK_SYNC_HAS_ETR 0 #define CLOCK_SYNC_HAS_ETR 0
...@@ -394,6 +395,20 @@ static void enable_sync_clock(void) ...@@ -394,6 +395,20 @@ static void enable_sync_clock(void)
atomic_set_mask(0x80000000, sw_ptr); atomic_set_mask(0x80000000, sw_ptr);
} }
/*
* Function to check if the clock is in sync.
*/
static inline int check_sync_clock(void)
{
atomic_t *sw_ptr;
int rc;
sw_ptr = &get_cpu_var(clock_sync_word);
rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
put_cpu_var(clock_sync_sync);
return rc;
}
/* Single threaded workqueue used for etr and stp sync events */ /* Single threaded workqueue used for etr and stp sync events */
static struct workqueue_struct *time_sync_wq; static struct workqueue_struct *time_sync_wq;
...@@ -485,6 +500,8 @@ static void etr_reset(void) ...@@ -485,6 +500,8 @@ static void etr_reset(void)
if (etr_setr(&etr_eacr) == 0) { if (etr_setr(&etr_eacr) == 0) {
etr_tolec = get_clock(); etr_tolec = get_clock();
set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags);
if (etr_port0_online && etr_port1_online)
set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
} else if (etr_port0_online || etr_port1_online) { } else if (etr_port0_online || etr_port1_online) {
pr_warning("The real or virtual hardware system does " pr_warning("The real or virtual hardware system does "
"not provide an ETR interface\n"); "not provide an ETR interface\n");
...@@ -533,8 +550,7 @@ void etr_switch_to_local(void) ...@@ -533,8 +550,7 @@ void etr_switch_to_local(void)
{ {
if (!etr_eacr.sl) if (!etr_eacr.sl)
return; return;
if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) disable_sync_clock(NULL);
disable_sync_clock(NULL);
set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
queue_work(time_sync_wq, &etr_work); queue_work(time_sync_wq, &etr_work);
} }
...@@ -549,8 +565,7 @@ void etr_sync_check(void) ...@@ -549,8 +565,7 @@ void etr_sync_check(void)
{ {
if (!etr_eacr.es) if (!etr_eacr.es)
return; return;
if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) disable_sync_clock(NULL);
disable_sync_clock(NULL);
set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
queue_work(time_sync_wq, &etr_work); queue_work(time_sync_wq, &etr_work);
} }
...@@ -914,7 +929,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, ...@@ -914,7 +929,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 (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es) if (!check_sync_clock())
return eacr; return eacr;
/* /*
...@@ -997,7 +1012,6 @@ static void etr_work_fn(struct work_struct *work) ...@@ -997,7 +1012,6 @@ static void etr_work_fn(struct work_struct *work)
on_each_cpu(disable_sync_clock, NULL, 1); on_each_cpu(disable_sync_clock, NULL, 1);
del_timer_sync(&etr_timer); del_timer_sync(&etr_timer);
etr_update_eacr(eacr); etr_update_eacr(eacr);
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
goto out_unlock; goto out_unlock;
} }
...@@ -1071,18 +1085,13 @@ static void etr_work_fn(struct work_struct *work) ...@@ -1071,18 +1085,13 @@ static void etr_work_fn(struct work_struct *work)
/* Both ports not usable. */ /* Both ports not usable. */
eacr.es = eacr.sl = 0; eacr.es = eacr.sl = 0;
sync_port = -1; sync_port = -1;
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
} }
if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
eacr.es = 0;
/* /*
* 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 (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) || if (check_sync_clock() || sync_port < 0) {
eacr.es || 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;
...@@ -1103,13 +1112,11 @@ static void etr_work_fn(struct work_struct *work) ...@@ -1103,13 +1112,11 @@ static void etr_work_fn(struct work_struct *work)
* and set up a timer to try again after 0.5 seconds * and set up a timer to try again after 0.5 seconds
*/ */
etr_update_eacr(eacr); etr_update_eacr(eacr);
set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
if (now < etr_tolec + (1600000 << 12) || if (now < etr_tolec + (1600000 << 12) ||
etr_sync_clock_stop(&aib, sync_port) != 0) { etr_sync_clock_stop(&aib, sync_port) != 0) {
/* Sync failed. Try again in 1/2 second. */ /* Sync failed. Try again in 1/2 second. */
eacr.es = 0; eacr.es = 0;
etr_update_eacr(eacr); etr_update_eacr(eacr);
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
etr_set_sync_timeout(); etr_set_sync_timeout();
} else } else
etr_set_tolec_timeout(now); etr_set_tolec_timeout(now);
...@@ -1191,19 +1198,30 @@ static ssize_t etr_online_store(struct sys_device *dev, ...@@ -1191,19 +1198,30 @@ static ssize_t etr_online_store(struct sys_device *dev,
return -EINVAL; return -EINVAL;
if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&clock_sync_mutex);
if (dev == &etr_port0_dev) { if (dev == &etr_port0_dev) {
if (etr_port0_online == value) if (etr_port0_online == value)
return count; /* Nothing to do. */ goto out; /* Nothing to do. */
etr_port0_online = value; etr_port0_online = value;
if (etr_port0_online && etr_port1_online)
set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
else
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
queue_work(time_sync_wq, &etr_work); queue_work(time_sync_wq, &etr_work);
} else { } else {
if (etr_port1_online == value) if (etr_port1_online == value)
return count; /* Nothing to do. */ goto out; /* Nothing to do. */
etr_port1_online = value; etr_port1_online = value;
if (etr_port0_online && etr_port1_online)
set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
else
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
queue_work(time_sync_wq, &etr_work); queue_work(time_sync_wq, &etr_work);
} }
out:
mutex_unlock(&clock_sync_mutex);
return count; return count;
} }
...@@ -1471,8 +1489,6 @@ static void stp_timing_alert(struct stp_irq_parm *intparm) ...@@ -1471,8 +1489,6 @@ static void stp_timing_alert(struct stp_irq_parm *intparm)
*/ */
void stp_sync_check(void) void stp_sync_check(void)
{ {
if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
return;
disable_sync_clock(NULL); disable_sync_clock(NULL);
queue_work(time_sync_wq, &stp_work); queue_work(time_sync_wq, &stp_work);
} }
...@@ -1485,8 +1501,6 @@ void stp_sync_check(void) ...@@ -1485,8 +1501,6 @@ void stp_sync_check(void)
*/ */
void stp_island_check(void) void stp_island_check(void)
{ {
if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
return;
disable_sync_clock(NULL); disable_sync_clock(NULL);
queue_work(time_sync_wq, &stp_work); queue_work(time_sync_wq, &stp_work);
} }
...@@ -1513,10 +1527,6 @@ static int stp_sync_clock(void *data) ...@@ -1513,10 +1527,6 @@ static int stp_sync_clock(void *data)
enable_sync_clock(); enable_sync_clock();
set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
queue_work(time_sync_wq, &etr_work);
rc = 0; rc = 0;
if (stp_info.todoff[0] || stp_info.todoff[1] || if (stp_info.todoff[0] || stp_info.todoff[1] ||
stp_info.todoff[2] || stp_info.todoff[3] || stp_info.todoff[2] || stp_info.todoff[3] ||
...@@ -1535,9 +1545,6 @@ static int stp_sync_clock(void *data) ...@@ -1535,9 +1545,6 @@ static int stp_sync_clock(void *data)
if (rc) { if (rc) {
disable_sync_clock(NULL); disable_sync_clock(NULL);
stp_sync->in_sync = -EAGAIN; stp_sync->in_sync = -EAGAIN;
clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
if (etr_port0_online || etr_port1_online)
queue_work(time_sync_wq, &etr_work);
} else } else
stp_sync->in_sync = 1; stp_sync->in_sync = 1;
xchg(&first, 0); xchg(&first, 0);
...@@ -1569,6 +1576,10 @@ static void stp_work_fn(struct work_struct *work) ...@@ -1569,6 +1576,10 @@ static void stp_work_fn(struct work_struct *work)
if (rc || stp_info.c == 0) if (rc || stp_info.c == 0)
goto out_unlock; goto out_unlock;
/* Skip synchronization if the clock is already in sync. */
if (check_sync_clock())
goto out_unlock;
memset(&stp_sync, 0, sizeof(stp_sync)); memset(&stp_sync, 0, sizeof(stp_sync));
get_online_cpus(); get_online_cpus();
atomic_set(&stp_sync.cpus, num_online_cpus() - 1); atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
...@@ -1684,8 +1695,14 @@ static ssize_t stp_online_store(struct sysdev_class *class, ...@@ -1684,8 +1695,14 @@ static ssize_t stp_online_store(struct sysdev_class *class,
return -EINVAL; return -EINVAL;
if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&clock_sync_mutex);
stp_online = value; stp_online = value;
if (stp_online)
set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
else
clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
queue_work(time_sync_wq, &stp_work); queue_work(time_sync_wq, &stp_work);
mutex_unlock(&clock_sync_mutex);
return count; return count;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment