Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What context does a Linux kernel timer function runs in?

When a timer created with the add_timer API expires and the function assigned at the timer structure runs, in what context does it run? Is it interrupt context or some kernel process context?

like image 825
user1977760 Avatar asked Jan 14 '13 15:01

user1977760


People also ask

What is context in Linux kernel?

● Context: Entity whom the kernel is running code on behalf of. ● Process context and Kernel Context are preemptible.

How timer is implemented in Linux?

Actually, the Linux kernel provides two types of timers called dynamic timers and interval timers. First type of timers is used by the kernel, and the second can be used by user mode. The timer_list structure contains actual dynamic timers.

Which hardware is responsible for generating timer interrupts in Linux kernel?

Timer interrupts are generated by the system's timing hardware at regular intervals; this interval is set by the kernel according to the value of HZ, which is an architecture-dependent value defined in <linux/param.

How is time managed in kernel space?

The kernel keeps track of the flow of time by means of timer interrupts.


1 Answers

It is of course in interrupt context, more precisely, in softirq context, see below (kernel/timer.c):

static inline void __run_timers(struct tvec_base *base)
{
        struct timer_list *timer;

        spin_lock_irq(&base->lock);
        while (time_after_eq(jiffies, base->timer_jiffies)) {
                struct list_head work_list;
                struct list_head *head = &work_list;
                int index = base->timer_jiffies & TVR_MASK;

                /*
                 * Cascade timers:
                 */
                if (!index &&
                        (!cascade(base, &base->tv2, INDEX(0))) &&
                                (!cascade(base, &base->tv3, INDEX(1))) &&
                                        !cascade(base, &base->tv4, INDEX(2)))
                        cascade(base, &base->tv5, INDEX(3));
                ++base->timer_jiffies;
                list_replace_init(base->tv1.vec + index, &work_list);
                while (!list_empty(head)) {
                        void (*fn)(unsigned long);
                        unsigned long data;
                        bool irqsafe;

                        timer = list_first_entry(head, struct timer_list,entry);
                        fn = timer->function;
                        data = timer->data;
                        irqsafe = tbase_get_irqsafe(timer->base);

                        timer_stats_account_timer(timer);

                        base->running_timer = timer;
                        detach_expired_timer(timer, base);

                        if (irqsafe) {
                                spin_unlock(&base->lock);
                                call_timer_fn(timer, fn, data); // <=========== HERE
                                spin_lock(&base->lock);
                        } else {
                                spin_unlock_irq(&base->lock);
                                call_timer_fn(timer, fn, data); // <============ HERE
                                spin_lock_irq(&base->lock);
                        }
                }
        }
        base->running_timer = NULL;
        spin_unlock_irq(&base->lock);
}


/*
 * This function runs timers and the timer-tq in bottom half context.
 */
static void run_timer_softirq(struct softirq_action *h)
{
        struct tvec_base *base = __this_cpu_read(tvec_bases);

        hrtimer_run_pending();

        if (time_after_eq(jiffies, base->timer_jiffies))
                __run_timers(base);
}

void __init init_timers(void)
{
        int err;

        /* ensure there are enough low bits for flags in timer->base pointer */
        BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);

        err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
                               (void *)(long)smp_processor_id());
        init_timer_stats();

        BUG_ON(err != NOTIFY_OK);
        register_cpu_notifier(&timers_nb);
        open_softirq(TIMER_SOFTIRQ, run_timer_softirq); // <============= HERE
}
like image 98
Cong Wang Avatar answered Sep 21 '22 03:09

Cong Wang