212 lines
6.2 KiB
C++
212 lines
6.2 KiB
C++
/*
|
|
* \brief Connection to timer service and timeout scheduler
|
|
* \author Martin Stein
|
|
* \date 2016-11-04
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2016-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <timer_session/connection.h>
|
|
#include <base/internal/globals.h>
|
|
|
|
using namespace Genode;
|
|
using namespace Genode::Trace;
|
|
|
|
|
|
void Timer::Connection::_update_real_time()
|
|
{
|
|
Lock_guard<Lock> lock_guard(_real_time_lock);
|
|
|
|
|
|
/*
|
|
* Update timestamp, time, and real-time value
|
|
*/
|
|
|
|
Timestamp ts = 0UL;
|
|
uint64_t us = 0UL;
|
|
uint64_t latency_us = ~0UL;
|
|
|
|
/*
|
|
* We retry reading out timestamp plus remote time until the result
|
|
* fulfills a given latency. If the maximum number of trials is
|
|
* reached, we take the results that has the lowest latency.
|
|
*/
|
|
for (unsigned remote_time_trials = 0;
|
|
remote_time_trials < MAX_REMOTE_TIME_TRIALS; )
|
|
{
|
|
/* read out the two time values close in succession */
|
|
Timestamp volatile new_ts = _timestamp();
|
|
uint64_t volatile new_us = elapsed_us();
|
|
|
|
/* do not proceed until the time difference is at least 1 us */
|
|
if (new_us == _us || new_ts == _ts) { continue; }
|
|
remote_time_trials++;
|
|
|
|
/*
|
|
* If interpolation is not ready, yet we cannot determine a latency
|
|
* and take the values as they are.
|
|
*/
|
|
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
|
|
us = new_us;
|
|
ts = new_ts;
|
|
break;
|
|
}
|
|
/* determine latency between reading out timestamp and time value */
|
|
Timestamp const ts_diff = _timestamp() - new_ts;
|
|
uint64_t const new_latency_us =
|
|
_ts_to_us_ratio(ts_diff, _us_to_ts_factor, _us_to_ts_factor_shift);
|
|
|
|
/* remember results if the latency was better than on the last trial */
|
|
if (new_latency_us < latency_us) {
|
|
us = new_us;
|
|
ts = new_ts;
|
|
|
|
/* take the results if the latency fulfills the given maximum */
|
|
if (latency_us < MAX_REMOTE_TIME_LATENCY_US) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* determine timestamp and time difference */
|
|
uint64_t const us_diff = us - _us;
|
|
Timestamp ts_diff = ts - _ts;
|
|
|
|
/* overwrite timestamp, time, and real time member */
|
|
_us = us;
|
|
_ts = ts;
|
|
_real_time.add(Microseconds(us_diff));
|
|
|
|
|
|
/*
|
|
* Update timestamp-to-time factor and its shift
|
|
*/
|
|
|
|
unsigned factor_shift = _us_to_ts_factor_shift;
|
|
uint64_t old_factor = _us_to_ts_factor;
|
|
Timestamp max_ts_diff = ~(Timestamp)0ULL >> factor_shift;
|
|
Timestamp min_ts_diff_shifted = ~(Timestamp)0ULL >> 1;
|
|
|
|
/*
|
|
* If the calculation type is bigger than the resulting factor type,
|
|
* we have to apply further limitations to avoid a loss at the final cast.
|
|
*/
|
|
if (sizeof(Timestamp) > sizeof(uint64_t)) {
|
|
|
|
Timestamp limit_ts_diff_shifted = (Timestamp)~0UL * us_diff;
|
|
Timestamp const limit_ts_diff = limit_ts_diff_shifted >>
|
|
factor_shift;
|
|
|
|
/*
|
|
* Avoid that we leave the factor shift on such a high level that
|
|
* casting the factor to its final type causes a loss.
|
|
*/
|
|
if (max_ts_diff > limit_ts_diff) {
|
|
max_ts_diff = limit_ts_diff;
|
|
}
|
|
/*
|
|
* Avoid that we raise the factor shift such that casting the factor
|
|
* to its final type causes a loss.
|
|
*/
|
|
limit_ts_diff_shifted >>= 1;
|
|
if (min_ts_diff_shifted > limit_ts_diff_shifted) {
|
|
min_ts_diff_shifted = limit_ts_diff_shifted;
|
|
}
|
|
}
|
|
|
|
struct Factor_update_failed : Genode::Exception { };
|
|
try {
|
|
/* meet the timestamp-difference limit before applying the shift */
|
|
while (ts_diff > max_ts_diff) {
|
|
|
|
/* if possible, lower the shift to meet the limitation */
|
|
if (!factor_shift) {
|
|
error("timestamp difference too big");
|
|
throw Factor_update_failed();
|
|
}
|
|
factor_shift--;
|
|
max_ts_diff = (max_ts_diff << 1) | 1;
|
|
old_factor >>= 1;
|
|
}
|
|
/*
|
|
* Apply current shift to timestamp difference and try to even
|
|
* raise the shift successively to get as much precision as possible.
|
|
*/
|
|
Timestamp ts_diff_shifted = ts_diff << factor_shift;
|
|
while (ts_diff_shifted < us_diff << MIN_FACTOR_LOG2)
|
|
{
|
|
factor_shift++;
|
|
ts_diff_shifted <<= 1;
|
|
old_factor <<= 1;
|
|
}
|
|
/*
|
|
* The cast to uint64_t does not cause a loss because the timestamp
|
|
* type cannot be bigger as the factor type. We also took care that
|
|
* the time difference cannot become null.
|
|
*/
|
|
uint64_t const new_factor =
|
|
(uint64_t)((Timestamp)ts_diff_shifted / us_diff);
|
|
|
|
/* update interpolation-quality value */
|
|
if (old_factor > new_factor) { _update_interpolation_quality(new_factor, old_factor); }
|
|
else { _update_interpolation_quality(old_factor, new_factor); }
|
|
|
|
/* overwrite factor and factor-shift member */
|
|
_us_to_ts_factor_shift = factor_shift;
|
|
_us_to_ts_factor = new_factor;
|
|
|
|
} catch (Factor_update_failed) {
|
|
|
|
/* disable interpolation */
|
|
_interpolation_quality = 0;
|
|
}
|
|
}
|
|
|
|
|
|
Duration Timer::Connection::curr_time()
|
|
{
|
|
_enable_modern_mode();
|
|
|
|
Reconstructible<Lock_guard<Lock> > lock_guard(_real_time_lock);
|
|
Duration interpolated_time(_real_time);
|
|
|
|
/*
|
|
* Interpolate with timestamps only if the factor value
|
|
* remained stable for some time. If we would interpolate with
|
|
* a yet unstable factor, there's an increased risk that the
|
|
* interpolated time falsely reaches an enourmous level. Then
|
|
* the value would stand still for quite some time because we
|
|
* can't let it jump back to a more realistic level.
|
|
*/
|
|
if (_interpolation_quality == MAX_INTERPOLATION_QUALITY)
|
|
{
|
|
/* buffer interpolation related members and free the lock */
|
|
Timestamp const ts = _ts;
|
|
uint64_t const us_to_ts_factor = _us_to_ts_factor;
|
|
unsigned const us_to_ts_factor_shift = _us_to_ts_factor_shift;
|
|
|
|
lock_guard.destruct();
|
|
|
|
/* interpolate time difference since the last real time update */
|
|
Timestamp const ts_diff = _timestamp() - ts;
|
|
uint64_t const us_diff = _ts_to_us_ratio(ts_diff, us_to_ts_factor,
|
|
us_to_ts_factor_shift);
|
|
|
|
interpolated_time.add(Microseconds(us_diff));
|
|
|
|
} else {
|
|
|
|
/* use remote timer instead of timestamps */
|
|
interpolated_time.add(Microseconds(elapsed_us() - _us));
|
|
|
|
lock_guard.destruct();
|
|
}
|
|
return _update_interpolated_time(interpolated_time);
|
|
}
|