[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: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Родитель
56e25e9777
Коммит
8283cb43ab
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
* 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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
* 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,
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче