#include #include #include #include #include #include struct timer_queue { spinlock_t lock; struct list_head timer_list; }; static DEFINE_PER_CPU(struct timer_queue, timer_queue); int timer_subsys_init(void) { id_t cpu; struct timer_queue *timerq; for_each_cpu_mask(cpu, cpu_present_map) { timerq = &per_cpu(timer_queue, cpu); spin_lock_init(&timerq->lock); list_head_init(&timerq->timer_list); } return 0; } void timer_add(struct timer *timer) { struct timer_queue *timerq; struct list_head *pos; unsigned long irqstate; timerq = &per_cpu(timer_queue, this_cpu); spin_lock_irqsave(&timerq->lock, irqstate); /* Initialize fields we don't expect the caller to set */ list_head_init(&timer->link); timer->cpu = this_cpu; /* Insert the new timer into the CPU's sorted timer_list */ list_for_each(pos, &timerq->timer_list) { struct timer *cur = list_entry(pos, struct timer, link); if (cur->expires > timer->expires) break; } list_add_tail(&timer->link, pos); spin_unlock_irqrestore(&timerq->lock, irqstate); } void timer_del(struct timer *timer) { struct timer_queue *timerq; unsigned long irqstate; timerq = &per_cpu(timer_queue, timer->cpu); spin_lock_irqsave(&timerq->lock, irqstate); /* Remove the timer, if it hasn't already expired */ if (!list_empty(&timer->link)) list_del(&timer->link); spin_unlock_irqrestore(&timerq->lock, irqstate); } static void wakeup_task(uintptr_t task) { sched_wakeup_task((struct task_struct *)task, TASKSTATE_INTERRUPTIBLE); } /* Returns the time remaining */ uint64_t timer_sleep_until(uint64_t when) { struct timer timer; uint64_t now; timer.expires = when; timer.function = &wakeup_task; timer.data = (uintptr_t)current; timer_add(&timer); /* Go to sleep */ set_mb(current->state, TASKSTATE_INTERRUPTIBLE); schedule(); /* Return the time remaining */ now = get_time(); return (when > now) ? (when - now) : 0; } void expire_timers(void) { struct timer_queue *timerq = &per_cpu(timer_queue, this_cpu); struct timer *timer; uint64_t now = get_time(); unsigned long irqstate; do { /* Pop the head entry off of the timer list */ spin_lock_irqsave(&timerq->lock, irqstate); if (!list_empty(&timerq->timer_list)) { timer = list_entry(timerq->timer_list.next, struct timer, link); if (timer->expires <= now) { list_del_init(&timer->link); } else { timer = NULL; } } else { timer = NULL; } spin_unlock_irqrestore(&timerq->lock, irqstate); /* Execute the timer's callback function. * Note that we have released the timerq->lock, so the * callback function is free to call timer_add(). */ if (timer) (*timer->function)(timer->data); } while (timer); }