/** * \file timer.c * Licence details are found in the file LICENSE. * * \brief * Simple spinning timer for timeout support in futex. * * \author Balazs Gerofi \par * Copyright (C) 2013 The University of Tokyo * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_PRINT_TIMER #ifdef DEBUG_PRINT_TIMER #define dkprintf kprintf #else #define dkprintf(...) do { if (0) kprintf(__VA_ARGS__); } while (0) #endif #define LOOP_TIMEOUT 500 struct list_head timers; ihk_spinlock_t timers_lock; void init_timers(void) { ihk_mc_spinlock_init(&timers_lock); INIT_LIST_HEAD(&timers); } uint64_t schedule_timeout(uint64_t timeout) { struct thread *thread = cpu_local_var(current); long irqstate; /* Spin sleep.. */ for (;;) { int need_schedule; struct cpu_local_var *v = get_this_cpu_local_var(); uint64_t t_s = rdtsc(); uint64_t t_e; irqstate = ihk_mc_spinlock_lock(&thread->spin_sleep_lock); /* Woken up by someone? */ if (thread->spin_sleep == 0) { t_e = rdtsc(); if ((t_e - t_s) < timeout) { timeout -= (t_e - t_s); } else { timeout = 1; } ihk_mc_spinlock_unlock(&thread->spin_sleep_lock, irqstate); break; } ihk_mc_spinlock_unlock(&thread->spin_sleep_lock, irqstate); /* Give a chance to another thread (if any) in case the core is * oversubscribed, but make sure we will be re-scheduled */ irqstate = ihk_mc_spinlock_lock(&(v->runq_lock)); need_schedule = v->runq_len > 1 ? 1 : 0; if (need_schedule) { xchg4(&(cpu_local_var(current)->status), PS_RUNNING); ihk_mc_spinlock_unlock(&(v->runq_lock), irqstate); schedule(); /* Recheck if woken */ continue; } else { ihk_mc_spinlock_unlock(&(v->runq_lock), irqstate); } /* Spin wait */ while ((rdtsc() - t_s) < LOOP_TIMEOUT) { cpu_pause(); } /* Time out? */ if (timeout < LOOP_TIMEOUT) { timeout = 0; /* We are not sleeping any more */ irqstate = ihk_mc_spinlock_lock(&thread->spin_sleep_lock); thread->spin_sleep = 0; ihk_mc_spinlock_unlock(&thread->spin_sleep_lock, irqstate); break; } else { timeout -= LOOP_TIMEOUT; } } return timeout; } void wake_timers_loop(void) { unsigned long loop_s; struct timer *timer; struct timer *timer_next; dkprintf("timers thread, entering loop\n"); for (;;) { loop_s = rdtsc(); while (rdtsc() < (loop_s + LOOP_TIMEOUT)) { cpu_pause(); } /* Iterate and decrease timeout for all timers, * wake up if timeout reaches zero. */ ihk_mc_spinlock_lock_noirq(&timers_lock); list_for_each_entry_safe(timer, timer_next, &timers, list) { timer->timeout -= LOOP_TIMEOUT; if (timer->timeout < LOOP_TIMEOUT) { timer->timeout = 0; list_del(&timer->list); dkprintf("timers timeout occurred, waking up pid: %d\n", timer->thread->proc->pid); waitq_wakeup(&timer->processes); } } ihk_mc_spinlock_unlock_noirq(&timers_lock); } }