5 #include "per_cpu_data.h"
7 /** A timeout basic object. It contains the necessary queues and handles
8 enqueuing, dequeuing and handling of timeouts. Real timeout classes
9 should overwrite expired(), which will do the real work, if an
12 class Timeout : public cxx::H_list_item
14 friend class Jdb_timeout_list;
15 friend class Jdb_list_timeouts;
16 friend class Timeout_q;
19 typedef cxx::H_list<Timeout> To_list;
23 * Absolute system time we want to be woken up at.
29 * Default copy constructor (is undefined).
31 Timeout(const Timeout&);
34 * Overwritten timeout handler function.
35 * @return true if a reschedule is necessary, false otherwise.
37 virtual bool expired();
42 unsigned res : 6; // performance optimization
51 * Timeout queue count (2^n) and distance between two queues in 2^n.
55 Wakeup_queue_count = 8,
56 Wakeup_queue_distance = 12 // i.e. (1<<12)us
59 typedef Timeout::To_list To_list;
60 typedef To_list::Iterator Iterator;
61 typedef To_list::Const_iterator Const_iterator;
66 To_list _q[Wakeup_queue_count];
69 * The current programmed timeout.
72 Unsigned64 _old_clock;
75 static Per_cpu<Timeout_q> timeout_queue;
78 //----------------------------------------------------------------------------------
84 #include "lock_guard.h"
86 #include "static_init.h"
92 DEFINE_PER_CPU Per_cpu<Timeout_q> Timeout_q::timeout_queue;
97 Timeout_q::first(int index)
98 { return _q[index & (Wakeup_queue_count-1)]; }
101 Timeout_q::To_list const &
102 Timeout_q::first(int index) const
103 { return _q[index & (Wakeup_queue_count-1)]; }
107 Timeout_q::queues() const { return Wakeup_queue_count; }
111 * Enqueue a new timeout.
113 PUBLIC inline NEEDS[Timeout_q::first, "timer.h", "config.h"]
115 Timeout_q::enqueue(Timeout *to)
117 int queue = (to->_wakeup >> Wakeup_queue_distance) & (Wakeup_queue_count-1);
119 To_list &q = first(queue);
120 Iterator tmp = q.begin();
122 while (tmp != q.end() && tmp->_wakeup < to->_wakeup)
125 q.insert_before(to, tmp);
127 if (Config::Scheduler_one_shot && (to->_wakeup <= _current))
129 _current = to->_wakeup;
130 Timer::update_timer(_current);
136 * Timeout constructor.
147 * Initializes an timeout object.
149 PUBLIC inline NEEDS[<climits>]
153 _wakeup = ULONG_LONG_MAX;
156 /* Yeah, i know, an derived and specialized timeout class for
157 the root node would be nicer. I already had done this, but
158 it was significantly slower than this solution */
159 IMPLEMENT virtual bool
162 kdb_ke("Wakeup List Terminator reached");
167 * Check if timeout is set.
173 return To_list::in_list(this);
177 * Check if timeout has hit.
187 PUBLIC inline NEEDS [<cassert>, "cpu_lock.h", "lock_guard.h",
188 Timeout_q::enqueue, Timeout::is_set]
190 Timeout::set(Unsigned64 clock, unsigned cpu)
192 // XXX uses global kernel lock
193 auto guard = lock_guard(cpu_lock);
198 Timeout_q::timeout_queue.cpu(cpu).enqueue(this);
202 * Return remaining time of timeout.
206 Timeout::get_timeout(Unsigned64 clock)
208 return _wakeup - clock;
211 PUBLIC inline NEEDS [<cassert>, "cpu_lock.h", "lock_guard.h",
212 Timeout::is_set, Timeout_q::enqueue, Timeout::has_hit]
214 Timeout::set_again(unsigned cpu)
216 // XXX uses global kernel lock
217 auto guard = lock_guard(cpu_lock);
223 Timeout_q::timeout_queue.cpu(cpu).enqueue(this);
226 PUBLIC inline NEEDS ["cpu_lock.h", "lock_guard.h", "timer.h",
227 "kdb_ke.h", Timeout::is_set]
231 assert_kdb (cpu_lock.test());
232 To_list::remove(this);
234 // Normaly we should reprogramm the timer in one shot mode
235 // But we let the timer interrupt handler to do this "lazily", to save cycles
239 * Dequeue an expired timeout.
240 * @return true if a reschedule is necessary, false otherwise.
250 * Dequeue an expired timeout.
251 * @return true if a reschedule is necessary, false otherwise.
255 Timeout::dequeue(bool is_expired = true)
257 To_list::remove(this);
266 * Handles the timeouts, i.e. call expired() for the expired timeouts
267 * and programs the "oneshot timer" to the next timeout.
268 * @return true if a reschedule is necessary, false otherwise.
270 PUBLIC inline NEEDS [<cassert>, <climits>, "kip.h", "timer.h", "config.h",
273 Timeout_q::do_timeouts()
275 bool reschedule = false;
277 // We test if the time between 2 activiations of this function is greater
278 // than the distance between two timeout queues.
279 // To avoid to lose timeouts, we calculating the missed queues and
281 // This can only happen, if we dont enter the timer interrupt for a long
282 // time, usual with one shot timer.
283 // Because we initalize old_dequeue_time with zero,
284 // we can get an "miss" on the first timerinterrupt.
285 // But this is booting the system, which is uncritical.
287 // Calculate which timeout queues needs to be checked.
288 int start = (_old_clock >> Wakeup_queue_distance);
289 int diff = (Kip::k()->clock >> Wakeup_queue_distance) - start;
290 int end = (start + diff + 1) & (Wakeup_queue_count - 1);
293 start = start & (Wakeup_queue_count - 1);
295 // test if an complete miss
296 if (diff >= Wakeup_queue_count)
297 start = end = 0; // scan all queues
299 // update old_clock for the next run
300 _old_clock = Kip::k()->clock;
302 // ensure we always terminate
303 assert((end >= 0) && (end < Wakeup_queue_count));
307 To_list &q = first(start);
308 Iterator timeout = q.begin();
310 // now scan this queue for timeouts below current clock
311 while (timeout != q.end() && timeout->_wakeup <= (Kip::k()->clock))
313 reschedule |= timeout->expire();
314 timeout = q.erase(timeout);
318 start = (start + 1) & (Wakeup_queue_count - 1);
324 if (Config::Scheduler_one_shot)
326 // scan all queues for the next minimum
327 //_current = (Unsigned64) ULONG_LONG_MAX;
328 _current = Kip::k()->clock + 10000; //ms
329 bool update_timer = true;
331 for (int i = 0; i < Wakeup_queue_count; i++)
333 // make sure that something enqueued other than the dummy element
334 if (first(i).empty())
339 if (first(i).front()->_wakeup < _current)
340 _current = first(i).front()->_wakeup;
344 Timer::update_timer(_current);
351 Timeout_q::Timeout_q()
352 : _current(ULONG_LONG_MAX), _old_clock(0)
357 Timeout_q::have_timeouts(Timeout const *ignore) const
359 for (unsigned i = 0; i < Wakeup_queue_count; ++i)
361 To_list const &t = first(i);
364 To_list::Const_iterator f = t.begin();
365 if (*f == ignore && (++f) == t.end())