/* * Set up lowcore and control register of the current cpu to * enable TOD clock and clock comparator interrupts.
*/ void init_cpu_timer(void)
{ struct clock_event_device *cd; int cpu;
/* * Initialize the TOD clock and the CPU timer of * the boot cpu.
*/ void __init time_init(void)
{ /* Reset time synchronization interfaces. */
stp_reset();
/* request the clock comparator external interrupt */ if (register_external_irq(EXT_IRQ_CLK_COMP, clock_comparator_interrupt))
panic("Couldn't request external interrupt 0x1004");
/* request the timing alert external interrupt */ if (register_external_irq(EXT_IRQ_TIMING_ALERT, timing_alert_interrupt))
panic("Couldn't request external interrupt 0x1406");
if (__clocksource_register(&clocksource_tod) != 0)
panic("Could not register TOD clock source");
/* Enable TOD clock interrupts on the boot cpu. */
init_cpu_timer();
/* Enable cpu timer interrupts on the boot cpu. */
vtime_init();
}
/* * The get_clock function for the physical clock. It will get the current * TOD clock, subtract the LPAR offset and write the result to *clock. * The function returns 0 if the clock is in sync with the external time * source. If the clock mode is local it will return -EOPNOTSUPP and * -EAGAIN if the clock is not in sync with the external reference.
*/ int get_phys_clock(unsignedlong *clock)
{
atomic_t *sw_ptr; unsignedint sw0, sw1;
sw_ptr = &get_cpu_var(clock_sync_word);
sw0 = atomic_read(sw_ptr);
*clock = get_tod_clock() - lpar_offset;
sw1 = atomic_read(sw_ptr);
put_cpu_var(clock_sync_word); if (sw0 == sw1 && (sw0 & 0x80000000U)) /* Success: time is in sync. */ return 0; if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) return -EOPNOTSUPP; if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) return -EACCES; return -EAGAIN;
}
EXPORT_SYMBOL(get_phys_clock);
/* * Make get_phys_clock() return -EAGAIN.
*/ staticvoid disable_sync_clock(void *dummy)
{
atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word); /* * Clear the in-sync bit 2^31. All get_phys_clock calls will * fail until the sync bit is turned back on. In addition * increase the "sequence" counter to avoid the race of an * stp event and the complete recovery against get_phys_clock.
*/
atomic_andnot(0x80000000, sw_ptr);
atomic_inc(sw_ptr);
}
/* * Make get_phys_clock() return 0 again. * Needs to be called from a context disabled for preemption.
*/ staticvoid enable_sync_clock(void)
{
atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word);
atomic_or(0x80000000, sw_ptr);
}
/* * Function to check if the clock is in sync.
*/ staticinlineint check_sync_clock(void)
{
atomic_t *sw_ptr; int rc;
/* * Apply clock delta to the global data structures. * This is called once on the CPU that performed the clock sync.
*/ staticvoid clock_sync_global(long delta)
{ struct ptff_qto qto;
/* * Apply clock delta to the per-CPU data structures of this CPU. * This is called for each online CPU after the call to clock_sync_global.
*/ staticvoid clock_sync_local(long delta)
{ /* Add the delta to the clock comparator. */ if (get_lowcore()->clock_comparator != clock_comparator_max) {
get_lowcore()->clock_comparator += delta;
set_clock_comparator(get_lowcore()->clock_comparator);
} /* Adjust the last_update_clock time-stamp. */
get_lowcore()->last_update_clock += delta;
}
/* Single threaded workqueue used for stp sync events */ staticstruct workqueue_struct *time_sync_wq;
/* * STP timing alert. There are three causes: * 1) timing status change * 2) link availability change * 3) time control parameter change * In all three cases we are only interested in the clock source state. * If a STP clock source is now available use it.
*/ staticvoid stp_timing_alert(struct stp_irq_parm *intparm)
{ if (intparm->tsc || intparm->lac || intparm->tcpc)
queue_work(time_sync_wq, &stp_work);
}
/* * STP sync check machine check. This is called when the timing state * changes from the synchronized state to the unsynchronized state. * After a STP sync check the clock is not in sync. The machine check * is broadcasted to all cpus at the same time.
*/ int stp_sync_check(void)
{
disable_sync_clock(NULL); return 1;
}
/* * STP island condition machine check. This is called when an attached * server attempts to communicate over an STP link and the servers * have matching CTN ids and have a valid stratum-1 configuration * but the configurations do not match.
*/ int stp_island_check(void)
{
disable_sync_clock(NULL); return 1;
}
staticint stp_sync_clock(void *data)
{ struct clock_sync_data *sync = data; long clock_delta, flags; staticint first; int rc;
enable_sync_clock(); if (xchg(&first, 1) == 0) { /* Wait until all other cpus entered the sync function. */ while (atomic_read(&sync->cpus) != 0)
cpu_relax();
rc = 0; if (stp_info.todoff || stp_info.tmd != 2) {
flags = vdso_update_begin();
rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0,
&clock_delta); if (rc == 0) {
sync->clock_delta = clock_delta;
clock_sync_global(clock_delta);
rc = __store_stpinfo(); if (rc == 0 && stp_info.tmd != 2)
rc = -EAGAIN;
}
vdso_update_end(flags);
}
sync->in_sync = rc ? -EAGAIN : 1;
xchg(&first, 0);
} else { /* Slave */
atomic_dec(&sync->cpus); /* Wait for in_sync to be set. */ while (READ_ONCE(sync->in_sync) == 0)
;
} if (sync->in_sync != 1) /* Didn't work. Clear per-cpu in sync bit again. */
disable_sync_clock(NULL); /* Apply clock delta to per-CPU fields of this CPU. */
clock_sync_local(sync->clock_delta);
return 0;
}
/* * STP work. Check for the STP state and take over the clock * synchronization if the STP clock source is usable.
*/ staticvoid stp_work_fn(struct work_struct *work)
{ struct clock_sync_data stp_sync; int rc;
/* Skip synchronization if the clock is already in sync. */ if (!check_sync_clock()) {
memset(&stp_sync, 0, sizeof(stp_sync));
cpus_read_lock();
atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
stop_machine_cpuslocked(stp_sync_clock, &stp_sync, cpu_online_mask);
cpus_read_unlock();
}
if (!check_sync_clock()) /* * There is a usable clock but the synchronization failed. * Retry after a second.
*/
mod_timer(&stp_timer, jiffies + msecs_to_jiffies(MSEC_PER_SEC));
value = simple_strtoul(buf, NULL, 0); if (value != 0 && value != 1) return -EINVAL; if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) return -EOPNOTSUPP;
mutex_lock(&stp_mutex);
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);
mutex_unlock(&stp_mutex); return count;
}
/* * Can't use DEVICE_ATTR because the attribute should be named * stp/online but dev_attr_online already exists in this file ..
*/ static DEVICE_ATTR_RW(online);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.