265 lines
5.3 KiB
C++
265 lines
5.3 KiB
C++
/*
|
|
* \brief Schedules CPU shares for the execution time of a CPU
|
|
* \author Martin Stein
|
|
* \date 2014-10-09
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-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.
|
|
*/
|
|
|
|
#include <base/log.h>
|
|
#include <hw/assert.h>
|
|
#include <kernel/cpu_scheduler.h>
|
|
|
|
using namespace Kernel;
|
|
|
|
|
|
void Cpu_scheduler::_reset(Cpu_share &share)
|
|
{
|
|
share._claim = share._quota;
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_reset_claims(unsigned const p)
|
|
{
|
|
_rcl[p].for_each([&] (Cpu_share &share) { _reset(share); });
|
|
_ucl[p].for_each([&] (Cpu_share &share) { _reset(share); });
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_next_round()
|
|
{
|
|
_residual = _quota;
|
|
_for_each_prio([&] (unsigned const p) { _reset_claims(p); });
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_consumed(unsigned const q)
|
|
{
|
|
if (_residual > q) { _residual -= q; }
|
|
else { _next_round(); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_set_head(Share &s, unsigned const q, bool const c)
|
|
{
|
|
_head_quota = q;
|
|
_head_claims = c;
|
|
_head = &s;
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_next_fill()
|
|
{
|
|
_head->_fill = _fill;
|
|
_fills.head_to_tail();
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_head_claimed(unsigned const r)
|
|
{
|
|
if (!_head->_quota) { return; }
|
|
_head->_claim = r > _head->_quota ? _head->_quota : r;
|
|
if (_head->_claim || !_head->_ready) { return; }
|
|
_rcl[_head->_prio].to_tail(&_head->_claim_item);
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_head_filled(unsigned const r)
|
|
{
|
|
if (_fills.head() != &_head->_fill_item) { return; }
|
|
if (r) { _head->_fill = r; }
|
|
else { _next_fill(); }
|
|
}
|
|
|
|
|
|
bool Cpu_scheduler::_claim_for_head()
|
|
{
|
|
for (signed p = Prio::MAX; p > Prio::MIN - 1; p--) {
|
|
Double_list_item<Cpu_share> *const item { _rcl[p].head() };
|
|
if (!item) { continue; }
|
|
Cpu_share &share { item->payload() };
|
|
if (!share._claim) { continue; }
|
|
_set_head(share, share._claim, 1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Cpu_scheduler::_fill_for_head()
|
|
{
|
|
Double_list_item<Cpu_share> *const item { _fills.head() };
|
|
if (!item) {
|
|
return 0;
|
|
}
|
|
Share &share = item->payload();
|
|
_set_head(share, share._fill, 0);
|
|
return 1;
|
|
}
|
|
|
|
|
|
unsigned Cpu_scheduler::_trim_consumption(unsigned &q)
|
|
{
|
|
q = Genode::min(Genode::min(q, _head_quota), _residual);
|
|
if (!_head_yields) { return _head_quota - q; }
|
|
_head_yields = false;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_quota_introduction(Share &s)
|
|
{
|
|
if (s._ready) { _rcl[s._prio].insert_tail(&s._claim_item); }
|
|
else { _ucl[s._prio].insert_tail(&s._claim_item); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_quota_revokation(Share &s)
|
|
{
|
|
if (s._ready) { _rcl[s._prio].remove(&s._claim_item); }
|
|
else { _ucl[s._prio].remove(&s._claim_item); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::_quota_adaption(Share &s, unsigned const q)
|
|
{
|
|
if (q) { if (s._claim > q) { s._claim = q; } }
|
|
else { _quota_revokation(s); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::update(time_t time)
|
|
{
|
|
unsigned duration = (unsigned) (time - _last_time);
|
|
_last_time = time;
|
|
_need_to_schedule = false;
|
|
|
|
/* do not detract the quota if the head context was removed even now */
|
|
if (_head) {
|
|
unsigned const r = _trim_consumption(duration);
|
|
if (_head_claims) { _head_claimed(r); }
|
|
else { _head_filled(r); }
|
|
_consumed(duration);
|
|
}
|
|
|
|
if (_claim_for_head()) { return; }
|
|
if (_fill_for_head()) { return; }
|
|
_set_head(_idle, _fill, 0);
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::ready_check(Share &s1)
|
|
{
|
|
assert(_head);
|
|
|
|
ready(s1);
|
|
|
|
if (_need_to_schedule) {
|
|
return;
|
|
}
|
|
Share * s2 = _head;
|
|
if (!s1._claim) {
|
|
_need_to_schedule = s2 == &_idle;
|
|
} else if (!_head_claims) {
|
|
_need_to_schedule = true;
|
|
} else if (s1._prio != s2->_prio) {
|
|
_need_to_schedule = s1._prio > s2->_prio;
|
|
} else {
|
|
for (
|
|
; s2 && s2 != &s1;
|
|
s2 =
|
|
Double_list<Cpu_share>::next(&s2->_claim_item) != nullptr ?
|
|
&Double_list<Cpu_share>::next(&s2->_claim_item)->payload() :
|
|
nullptr) ;
|
|
|
|
_need_to_schedule = !s2;
|
|
}
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::ready(Share &s)
|
|
{
|
|
assert(!s._ready && &s != &_idle);
|
|
|
|
_need_to_schedule = true;
|
|
|
|
s._ready = 1;
|
|
s._fill = _fill;
|
|
_fills.insert_tail(&s._fill_item);
|
|
if (!s._quota) { return; }
|
|
_ucl[s._prio].remove(&s._claim_item);
|
|
if (s._claim) { _rcl[s._prio].insert_head(&s._claim_item); }
|
|
else { _rcl[s._prio].insert_tail(&s._claim_item); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::unready(Share &s)
|
|
{
|
|
assert(s._ready && &s != &_idle);
|
|
|
|
_need_to_schedule = true;
|
|
|
|
s._ready = 0;
|
|
_fills.remove(&s._fill_item);
|
|
if (!s._quota) { return; }
|
|
_rcl[s._prio].remove(&s._claim_item);
|
|
_ucl[s._prio].insert_tail(&s._claim_item);
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::yield()
|
|
{
|
|
_head_yields = true;
|
|
_need_to_schedule = true;
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::remove(Share &s)
|
|
{
|
|
assert(&s != &_idle);
|
|
|
|
_need_to_schedule = true;
|
|
if (&s == _head) _head = nullptr;
|
|
if (s._ready) { _fills.remove(&s._fill_item); }
|
|
if (!s._quota) { return; }
|
|
if (s._ready) { _rcl[s._prio].remove(&s._claim_item); }
|
|
else { _ucl[s._prio].remove(&s._claim_item); }
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::insert(Share &s)
|
|
{
|
|
assert(!s._ready);
|
|
_need_to_schedule = true;
|
|
if (!s._quota) { return; }
|
|
s._claim = s._quota;
|
|
_ucl[s._prio].insert_head(&s._claim_item);
|
|
}
|
|
|
|
|
|
void Cpu_scheduler::quota(Share &s, unsigned const q)
|
|
{
|
|
assert(&s != &_idle);
|
|
if (s._quota) { _quota_adaption(s, q); }
|
|
else if (q) { _quota_introduction(s); }
|
|
s._quota = q;
|
|
}
|
|
|
|
|
|
Cpu_share &Cpu_scheduler::head() const
|
|
{
|
|
assert(_head);
|
|
return *_head;
|
|
}
|
|
|
|
|
|
Cpu_scheduler::Cpu_scheduler(Share &i, unsigned const q,
|
|
unsigned const f)
|
|
: _idle(i), _quota(q), _residual(q), _fill(f)
|
|
{ _set_head(i, f, 0); }
|