📄 tick-sched.c
字号:
/* * linux/kernel/time/tick-sched.c * * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de> * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar * Copyright(C) 2006-2007 Timesys Corp., Thomas Gleixner * * No idle tick implementation for low and high resolution timers * * Started by: Thomas Gleixner and Ingo Molnar * * For licencing details see kernel-base/COPYING */#include <linux/cpu.h>#include <linux/err.h>#include <linux/hrtimer.h>#include <linux/interrupt.h>#include <linux/kernel_stat.h>#include <linux/percpu.h>#include <linux/profile.h>#include <linux/sched.h>#include <linux/tick.h>#include <asm/irq_regs.h>#include "tick-internal.h"/* * Per cpu nohz control structure */static DEFINE_PER_CPU(struct tick_sched, tick_cpu_sched);/* * The time, when the last jiffy update happened. Protected by xtime_lock. */static ktime_t last_jiffies_update;struct tick_sched *tick_get_tick_sched(int cpu){ return &per_cpu(tick_cpu_sched, cpu);}/* * Must be called with interrupts disabled ! */static void tick_do_update_jiffies64(ktime_t now){ unsigned long ticks = 0; ktime_t delta; /* Reevalute with xtime_lock held */ write_seqlock(&xtime_lock); delta = ktime_sub(now, last_jiffies_update); if (delta.tv64 >= tick_period.tv64) { delta = ktime_sub(delta, tick_period); last_jiffies_update = ktime_add(last_jiffies_update, tick_period); /* Slow path for long timeouts */ if (unlikely(delta.tv64 >= tick_period.tv64)) { s64 incr = ktime_to_ns(tick_period); ticks = ktime_divns(delta, incr); last_jiffies_update = ktime_add_ns(last_jiffies_update, incr * ticks); } do_timer(++ticks); } write_sequnlock(&xtime_lock);}/* * Initialize and return retrieve the jiffies update. */static ktime_t tick_init_jiffy_update(void){ ktime_t period; write_seqlock(&xtime_lock); /* Did we start the jiffies update yet ? */ if (last_jiffies_update.tv64 == 0) last_jiffies_update = tick_next_period; period = last_jiffies_update; write_sequnlock(&xtime_lock); return period;}/* * NOHZ - aka dynamic tick functionality */#ifdef CONFIG_NO_HZ/* * NO HZ enabled ? */static int tick_nohz_enabled __read_mostly = 1;/* * Enable / Disable tickless mode */static int __init setup_tick_nohz(char *str){ if (!strcmp(str, "off")) tick_nohz_enabled = 0; else if (!strcmp(str, "on")) tick_nohz_enabled = 1; else return 0; return 1;}__setup("nohz=", setup_tick_nohz);/** * tick_nohz_update_jiffies - update jiffies when idle was interrupted * * Called from interrupt entry when the CPU was idle * * In case the sched_tick was stopped on this CPU, we have to check if jiffies * must be updated. Otherwise an interrupt handler could use a stale jiffy * value. We do this unconditionally on any cpu, as we don't know whether the * cpu, which has the update task assigned is in a long sleep. */void tick_nohz_update_jiffies(void){ int cpu = smp_processor_id(); struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); unsigned long flags; ktime_t now; if (!ts->tick_stopped) return; cpu_clear(cpu, nohz_cpu_mask); now = ktime_get(); local_irq_save(flags); tick_do_update_jiffies64(now); local_irq_restore(flags);}/** * tick_nohz_stop_sched_tick - stop the idle tick from the idle task * * When the next event is more than a tick into the future, stop the idle tick * Called either from the idle loop or from irq_exit() when an idle period was * just interrupted by an interrupt which did not cause a reschedule. */void tick_nohz_stop_sched_tick(void){ unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags; struct tick_sched *ts; ktime_t last_update, expires, now, delta; int cpu; local_irq_save(flags); cpu = smp_processor_id(); ts = &per_cpu(tick_cpu_sched, cpu); if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE)) goto end; if (need_resched()) goto end; cpu = smp_processor_id(); if (unlikely(local_softirq_pending())) { static int ratelimit; if (ratelimit < 10) { printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", local_softirq_pending()); ratelimit++; } } now = ktime_get(); /* * When called from irq_exit we need to account the idle sleep time * correctly. */ if (ts->tick_stopped) { delta = ktime_sub(now, ts->idle_entrytime); ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); } ts->idle_entrytime = now; ts->idle_calls++; /* Read jiffies and the time when jiffies were updated last */ do { seq = read_seqbegin(&xtime_lock); last_update = last_jiffies_update; last_jiffies = jiffies; } while (read_seqretry(&xtime_lock, seq)); /* Get the next timer wheel timer */ next_jiffies = get_next_timer_interrupt(last_jiffies); delta_jiffies = next_jiffies - last_jiffies; if (rcu_needs_cpu(cpu)) delta_jiffies = 1; /* * Do not stop the tick, if we are only one off * or if the cpu is required for rcu */ if (!ts->tick_stopped && delta_jiffies == 1) goto out; /* Schedule the tick, if we are at least one jiffie off */ if ((long)delta_jiffies >= 1) { if (delta_jiffies > 1) cpu_set(cpu, nohz_cpu_mask); /* * nohz_stop_sched_tick can be called several times before * the nohz_restart_sched_tick is called. This happens when * interrupts arrive which do not cause a reschedule. In the * first call we save the current tick time, so we can restart * the scheduler tick in nohz_restart_sched_tick. */ if (!ts->tick_stopped) { if (select_nohz_load_balancer(1)) { /* * sched tick not stopped! */ cpu_clear(cpu, nohz_cpu_mask); goto out; } ts->idle_tick = ts->sched_timer.expires; ts->tick_stopped = 1; ts->idle_jiffies = last_jiffies; } /* * If this cpu is the one which updates jiffies, then * give up the assignment and let it be taken by the * cpu which runs the tick timer next, which might be * this cpu as well. If we don't drop this here the * jiffies might be stale and do_timer() never * invoked. */ if (cpu == tick_do_timer_cpu) tick_do_timer_cpu = -1; ts->idle_sleeps++; /* * delta_jiffies >= NEXT_TIMER_MAX_DELTA signals that * there is no timer pending or at least extremly far * into the future (12 days for HZ=1000). In this case * we simply stop the tick timer: */ if (unlikely(delta_jiffies >= NEXT_TIMER_MAX_DELTA)) { ts->idle_expires.tv64 = KTIME_MAX; if (ts->nohz_mode == NOHZ_MODE_HIGHRES) hrtimer_cancel(&ts->sched_timer); goto out; } /* * calculate the expiry time for the next timer wheel * timer */ expires = ktime_add_ns(last_update, tick_period.tv64 * delta_jiffies); ts->idle_expires = expires; if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { hrtimer_start(&ts->sched_timer, expires, HRTIMER_MODE_ABS); /* Check, if the timer was already in the past */ if (hrtimer_active(&ts->sched_timer)) goto out; } else if(!tick_program_event(expires, 0)) goto out; /* * We are past the event already. So we crossed a * jiffie boundary. Update jiffies and raise the * softirq. */ tick_do_update_jiffies64(ktime_get()); cpu_clear(cpu, nohz_cpu_mask); } raise_softirq_irqoff(TIMER_SOFTIRQ);out: ts->next_jiffies = next_jiffies; ts->last_jiffies = last_jiffies;end: local_irq_restore(flags);}/** * nohz_restart_sched_tick - restart the idle tick from the idle task * * Restart the idle tick when the CPU is woken up from idle */void tick_nohz_restart_sched_tick(void){ int cpu = smp_processor_id(); struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); unsigned long ticks; ktime_t now, delta; if (!ts->tick_stopped) return; /* Update jiffies first */ now = ktime_get(); local_irq_disable(); select_nohz_load_balancer(0); tick_do_update_jiffies64(now); cpu_clear(cpu, nohz_cpu_mask);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -