/* * \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 #include using namespace Genode; using namespace Genode::Trace; void Timer::Connection::_update_real_time() { Lock_guard 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(_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); }