mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-22 12:28:02 +00:00
parent
e70c13ce1f
commit
cdb7904daa
@ -1,3 +1,3 @@
|
||||
include $(REP_DIR)/lib/mk/timer.inc
|
||||
|
||||
INC_DIR += $(REP_DIR)/src/drivers/timer/include_pit
|
||||
INC_DIR += $(REP_DIR)/src/drivers/timer/nova
|
||||
|
165
repos/os/src/drivers/timer/nova/platform_timer.h
Normal file
165
repos/os/src/drivers/timer/nova/platform_timer.h
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* \brief Platform timer using Nova timed semaphore down
|
||||
* \author Alexander Boettcher
|
||||
* \date 2014-06-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _PLATFORM_TIMER_H_
|
||||
#define _PLATFORM_TIMER_H_
|
||||
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <trace/timestamp.h>
|
||||
|
||||
class Platform_timer
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::addr_t _sem;
|
||||
unsigned long _timeout;
|
||||
Genode::Trace::Timestamp _tsc_start;
|
||||
unsigned long _tsc_khz;
|
||||
|
||||
/* 1 / ((us / (1000 * 1000)) * (tsc_khz * 1000)) */
|
||||
enum { TSC_FACTOR = 1000ULL };
|
||||
|
||||
/**
|
||||
* Convenience function to calculate time in us value out of tsc
|
||||
*/
|
||||
inline unsigned long _time_in_us(unsigned long long tsc,
|
||||
bool sub_tsc_start = true) const
|
||||
{
|
||||
if (sub_tsc_start)
|
||||
return (tsc - _tsc_start) / (_tsc_khz / TSC_FACTOR);
|
||||
|
||||
return (tsc) / (_tsc_khz / TSC_FACTOR);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Platform_timer()
|
||||
:
|
||||
_sem(~0UL), _timeout(0),
|
||||
_tsc_start(Genode::Trace::timestamp())
|
||||
{
|
||||
/* read out the tsc frequenzy once */
|
||||
Genode::Attached_rom_dataspace _ds("hypervisor_info_page");
|
||||
Nova::Hip * const hip = _ds.local_addr<Nova::Hip>();
|
||||
_tsc_khz = hip->tsc_freq;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return current time-counter value in microseconds
|
||||
*/
|
||||
unsigned long curr_time() const
|
||||
{
|
||||
return _time_in_us(Genode::Trace::timestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return maximum timeout as supported by the platform
|
||||
*/
|
||||
unsigned long max_timeout() { return _time_in_us(~0UL); }
|
||||
|
||||
/**
|
||||
* Schedule next timeout
|
||||
*
|
||||
* \param timeout_usec timeout in microseconds
|
||||
*/
|
||||
void schedule_timeout(unsigned long timeout_usec)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* check whether to cancel last timeout */
|
||||
if (timeout_usec == 0 && _sem != ~0UL) {
|
||||
uint8_t res = Nova::sm_ctrl(_sem, Nova::SEMAPHORE_UP);
|
||||
if (res != Nova::NOVA_OK)
|
||||
nova_die();
|
||||
}
|
||||
|
||||
/* remember timeout to be set during wait_for_timeout call */
|
||||
_timeout = timeout_usec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block for the next scheduled timeout
|
||||
*/
|
||||
void wait_for_timeout(Genode::Thread_base *blocking_thread)
|
||||
{
|
||||
using namespace Genode;
|
||||
using namespace Nova;
|
||||
|
||||
/* XXX quirk start - description below */
|
||||
static unsigned short quirk_count = 0;
|
||||
Trace::Timestamp before = Trace::timestamp();
|
||||
asm volatile ("":::"memory");
|
||||
/* XXX quirk end */
|
||||
|
||||
if (_sem == ~0UL)
|
||||
_sem = blocking_thread->tid().exc_pt_sel + SM_SEL_EC;
|
||||
|
||||
addr_t sem = _sem;
|
||||
|
||||
/* calculate absolute timeout */
|
||||
Trace::Timestamp now = Trace::timestamp();
|
||||
Trace::Timestamp us_64 = _timeout;
|
||||
|
||||
if (_timeout == max_timeout()) {
|
||||
/* tsc_absolute == 0 means blocking without timeout */
|
||||
Genode::uint8_t res = sm_ctrl(sem, SEMAPHORE_DOWN, 0);
|
||||
if (res != Nova::NOVA_OK && res != Nova::NOVA_TIMEOUT)
|
||||
nova_die();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX quirk start
|
||||
*
|
||||
* On some x86 platforms, it happens that the system seems to slow
|
||||
* down dramatically for some unclear reasons so far. When this
|
||||
* happens, the handling of the timeout queue and reprogramming the
|
||||
* next timeout takes so long that the timer IRQ will fire
|
||||
* immediately after acknowledging it. This causes the timer
|
||||
* service to run on a very high rate, which may utilize the CPU
|
||||
* close to the maximum. We try to detect this condition by the
|
||||
* following heuristic and apply this quirk, which programs the
|
||||
* next timeout later in time - so that it will fire not
|
||||
* immediately after acknowledging it.
|
||||
*
|
||||
* This quirk should be removed as soon as it is clear what
|
||||
* triggers the phenomenon.
|
||||
*/
|
||||
unsigned long diff = _time_in_us(now - before, false);
|
||||
|
||||
if (diff)
|
||||
quirk_count++;
|
||||
else
|
||||
quirk_count = 0;
|
||||
|
||||
if (quirk_count > 10) {
|
||||
us_64 += 30000;
|
||||
PWRN("apply timer quirk - diff=%lu, delay timeout %lu->%llu us",
|
||||
diff, _timeout, us_64);
|
||||
quirk_count = 0;
|
||||
}
|
||||
/* XXX quirk end */
|
||||
|
||||
/* block until timeout fires or it gets canceled */
|
||||
unsigned long long tsc_absolute = now + us_64 * (_tsc_khz / TSC_FACTOR);
|
||||
Genode::uint8_t res = sm_ctrl(sem, SEMAPHORE_DOWN, tsc_absolute);
|
||||
if (res != Nova::NOVA_OK && res != Nova::NOVA_TIMEOUT)
|
||||
nova_die();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _PLATFORM_TIMER_H_ */
|
Loading…
Reference in New Issue
Block a user