genode/repos/base-hw/src/core/include/kernel/cpu.h

326 lines
6.5 KiB
C
Raw Normal View History

/*
* \brief Class for kernel data that is needed to manage a specific CPU
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2014-01-14
*/
/*
* Copyright (C) 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 _KERNEL__CPU_H_
#define _KERNEL__CPU_H_
/* core includes */
#include <timer.h>
2014-07-15 14:51:27 +02:00
#include <cpu.h>
#include <kernel/cpu_scheduler.h>
/* base includes */
#include <unmanaged_singleton.h>
namespace Kernel
{
/**
* Context of a job (thread, VM, idle) that shall be executed by a CPU
*/
class Cpu_job;
/**
* Ability to do a domain update on all CPUs
*/
class Cpu_domain_update;
/**
* Execution context that is scheduled on CPU idle
*/
class Cpu_idle;
/**
* Class for kernel data that is needed to manage a specific CPU
*/
class Cpu;
/**
* Provides a CPU object for every available CPU
*/
class Cpu_pool;
/**
* Return singleton of CPU pool
*/
Cpu_pool * cpu_pool();
}
class Kernel::Cpu_domain_update : public Double_list_item
{
friend class Cpu_domain_update_list;
private:
bool _pending[NR_OF_CPUS];
unsigned _domain_id;
/**
* Domain-update back-end
*/
void _domain_update() { Genode::Cpu::flush_tlb_by_pid(_domain_id); }
/**
* Perform the domain update on the executing CPU
*/
void _do();
protected:
/**
* Constructor
*/
Cpu_domain_update()
{
for (unsigned i = 0; i < NR_OF_CPUS; i++) { _pending[i] = false; }
}
/**
* Do an update of domain 'id' on all CPUs and return if this blocks
*/
bool _do_global(unsigned const id);
/**
* Notice that the update isn't pending on any CPU anymore
*/
virtual void _cpu_domain_update_unblocks() = 0;
};
class Kernel::Cpu_job : public Cpu_share
{
protected:
Cpu * _cpu;
2014-07-15 14:51:27 +02:00
Cpu_lazy_state _lazy_state;
/**
* Handle interrupt exception that occured during execution on CPU 'id'
*/
void _interrupt(unsigned const id);
/**
* Insert context into the scheduling of this CPU
*/
void _schedule();
/**
* Remove context from the scheduling of this CPU
*/
void _unschedule();
/**
* Yield the currently scheduled CPU share of this context
*/
void _yield();
public:
/**
* Handle exception that occured during execution on CPU 'id'
*/
virtual void exception(unsigned const id) = 0;
/**
* Continue execution on CPU 'id'
*/
virtual void proceed(unsigned const id) = 0;
/**
thread API & CPU session: accounting of CPU quota In the init configuration one can configure the donation of CPU time via 'resource' tags that have the attribute 'name' set to "CPU" and the attribute 'quantum' set to the percentage of CPU quota that init shall donate. The pattern is the same as when donating RAM quota. ! <start name="test"> ! <resource name="CPU" quantum="75"/> ! </start> This would cause init to try donating 75% of its CPU quota to the child "test". Init and core do not preserve CPU quota for their own requirements by default as it is done with RAM quota. The CPU quota that a process owns can be applied through the thread constructor. The constructor has been enhanced by an argument that indicates the percentage of the programs CPU quota that shall be granted to the new thread. So 'Thread(33, "test")' would cause the backing CPU session to try to grant 33% of the programs CPU quota to the thread "test". By now, the CPU quota of a thread can't be altered after construction. Constructing a thread with CPU quota 0 doesn't mean the thread gets never scheduled but that the thread has no guaranty to receive CPU time. Such threads have to live with excess CPU time. Threads that already existed in the official repositories of Genode were adapted in the way that they receive a quota of 0. This commit also provides a run test 'cpu_quota' in base-hw (the only kernel that applies the CPU-quota scheme currently). The test basically runs three threads with different physical CPU quota. The threads simply count for 30 seconds each and the test then checks wether the counter values relate to the CPU-quota distribution. fix #1275
2014-10-16 11:15:46 +02:00
* Construct a job with scheduling priority 'p' and time quota 'q'
*/
thread API & CPU session: accounting of CPU quota In the init configuration one can configure the donation of CPU time via 'resource' tags that have the attribute 'name' set to "CPU" and the attribute 'quantum' set to the percentage of CPU quota that init shall donate. The pattern is the same as when donating RAM quota. ! <start name="test"> ! <resource name="CPU" quantum="75"/> ! </start> This would cause init to try donating 75% of its CPU quota to the child "test". Init and core do not preserve CPU quota for their own requirements by default as it is done with RAM quota. The CPU quota that a process owns can be applied through the thread constructor. The constructor has been enhanced by an argument that indicates the percentage of the programs CPU quota that shall be granted to the new thread. So 'Thread(33, "test")' would cause the backing CPU session to try to grant 33% of the programs CPU quota to the thread "test". By now, the CPU quota of a thread can't be altered after construction. Constructing a thread with CPU quota 0 doesn't mean the thread gets never scheduled but that the thread has no guaranty to receive CPU time. Such threads have to live with excess CPU time. Threads that already existed in the official repositories of Genode were adapted in the way that they receive a quota of 0. This commit also provides a run test 'cpu_quota' in base-hw (the only kernel that applies the CPU-quota scheme currently). The test basically runs three threads with different physical CPU quota. The threads simply count for 30 seconds each and the test then checks wether the counter values relate to the CPU-quota distribution. fix #1275
2014-10-16 11:15:46 +02:00
Cpu_job(Cpu_priority const p, unsigned const q)
: Cpu_share(p, q), _cpu(0) { }
/**
* Destructor
*/
~Cpu_job();
/**
* Link job to CPU 'cpu'
*/
void affinity(Cpu * const cpu);
/***************
** Accessors **
***************/
void cpu(Cpu * const cpu) { _cpu = cpu; }
2014-07-15 14:51:27 +02:00
Cpu_lazy_state * lazy_state() { return &_lazy_state; }
};
class Kernel::Cpu_idle : public Genode::Cpu::User_context, public Cpu_job
{
private:
static constexpr size_t stack_size = sizeof(addr_t) * 32;
char _stack[stack_size] __attribute__((aligned(16)));
/**
* Main function of all idle threads
*/
static void _main() { while (1) { Genode::Cpu::wait_for_interrupt(); } }
public:
/**
* Construct idle context for CPU 'cpu'
*/
Cpu_idle(Cpu * const cpu);
/**
* Handle exception that occured during execution on CPU 'cpu'
*/
void exception(unsigned const cpu)
{
switch (cpu_exception) {
case INTERRUPT_REQUEST: _interrupt(cpu); return;
case FAST_INTERRUPT_REQUEST: _interrupt(cpu); return;
case RESET: return;
default: assert(0); }
}
/**
* Continue execution on CPU 'cpu_id'
*/
void proceed(unsigned const cpu_id);
};
class Kernel::Cpu : public Genode::Cpu
{
private:
typedef Cpu_job Job;
unsigned const _id;
Cpu_idle _idle;
Timer * const _timer;
Cpu_scheduler _scheduler;
bool _ip_interrupt_pending;
unsigned _quota() const { return _timer->ms_to_tics(cpu_quota_ms); }
unsigned _fill() const { return _timer->ms_to_tics(cpu_fill_ms); }
Job * _head() const { return static_cast<Job *>(_scheduler.head()); }
public:
/**
* Construct object for CPU 'id' with scheduling timer 'timer'
*/
Cpu(unsigned const id, Timer * const timer)
:
_id(id), _idle(this), _timer(timer),
_scheduler(&_idle, _quota(), _fill()),
_ip_interrupt_pending(false) { }
/**
* Check if IRQ 'i' was due to a scheduling timeout
*/
bool timer_irq(unsigned const i) { return _timer->interrupt_id(_id) == i; }
/**
* Notice that the IPI of the CPU isn't pending anymore
*/
void ip_interrupt_handled() { _ip_interrupt_pending = false; }
/**
* Raise the IPI of the CPU
*/
void trigger_ip_interrupt();
/**
* Schedule 'job' at this CPU
*/
void schedule(Job * const job);
/**
* Handle recent exception of the CPU and proceed its user execution
*/
void exception()
{
/* update old job */
Job * const old_job = _head();
old_job->exception(_id);
/* update scheduler */
unsigned const old_time = _scheduler.head_quota();
unsigned const new_time = _timer->value(_id);
unsigned quota = old_time > new_time ? old_time - new_time : 1;
_scheduler.update(quota);
/* get new job */
Job * const new_job = _head();
quota = _scheduler.head_quota();
assert(quota);
_timer->start_one_shot(quota, _id);
/* switch between lazy state of old and new job */
Cpu_lazy_state * const old_state = old_job->lazy_state();
Cpu_lazy_state * const new_state = new_job->lazy_state();
prepare_proceeding(old_state, new_state);
/* resume new job */
new_job->proceed(_id);
}
/***************
** Accessors **
***************/
unsigned id() const { return _id; }
Cpu_scheduler * scheduler() { return &_scheduler; }
};
class Kernel::Cpu_pool
{
private:
Timer _timer;
char _cpus[NR_OF_CPUS][sizeof(Cpu)];
public:
/**
* Construct pool and thereby objects for all available CPUs
*/
Cpu_pool()
{
for (unsigned id = 0; id < NR_OF_CPUS; id++) {
new (_cpus[id]) Cpu(id, &_timer); }
}
/**
* Return object of CPU 'id'
*/
Cpu * cpu(unsigned const id) const
{
assert(id < NR_OF_CPUS);
char * const p = const_cast<char *>(_cpus[id]);
return reinterpret_cast<Cpu *>(p);
}
/**
* Return object of primary CPU
*/
Cpu * primary_cpu() const { return cpu(Cpu::primary_id()); }
thread API & CPU session: accounting of CPU quota In the init configuration one can configure the donation of CPU time via 'resource' tags that have the attribute 'name' set to "CPU" and the attribute 'quantum' set to the percentage of CPU quota that init shall donate. The pattern is the same as when donating RAM quota. ! <start name="test"> ! <resource name="CPU" quantum="75"/> ! </start> This would cause init to try donating 75% of its CPU quota to the child "test". Init and core do not preserve CPU quota for their own requirements by default as it is done with RAM quota. The CPU quota that a process owns can be applied through the thread constructor. The constructor has been enhanced by an argument that indicates the percentage of the programs CPU quota that shall be granted to the new thread. So 'Thread(33, "test")' would cause the backing CPU session to try to grant 33% of the programs CPU quota to the thread "test". By now, the CPU quota of a thread can't be altered after construction. Constructing a thread with CPU quota 0 doesn't mean the thread gets never scheduled but that the thread has no guaranty to receive CPU time. Such threads have to live with excess CPU time. Threads that already existed in the official repositories of Genode were adapted in the way that they receive a quota of 0. This commit also provides a run test 'cpu_quota' in base-hw (the only kernel that applies the CPU-quota scheme currently). The test basically runs three threads with different physical CPU quota. The threads simply count for 30 seconds each and the test then checks wether the counter values relate to the CPU-quota distribution. fix #1275
2014-10-16 11:15:46 +02:00
/*
* Accessors
*/
Timer * timer() { return &_timer; }
};
#endif /* _KERNEL__CPU_H_ */