📄 tick-sched.c
字号:
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); /* Account the idle time */ delta = ktime_sub(now, ts->idle_entrytime); ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); /* * We stopped the tick in idle. Update process times would miss the * time we slept as update_process_times does only a 1 tick * accounting. Enforce that this is accounted to idle ! */ ticks = jiffies - ts->idle_jiffies; /* * We might be one off. Do not randomly account a huge number of ticks! */ if (ticks && ticks < LONG_MAX) { add_preempt_count(HARDIRQ_OFFSET); account_system_time(current, HARDIRQ_OFFSET, jiffies_to_cputime(ticks)); sub_preempt_count(HARDIRQ_OFFSET); } /* * Cancel the scheduled timer and restore the tick */ ts->tick_stopped = 0; hrtimer_cancel(&ts->sched_timer); ts->sched_timer.expires = ts->idle_tick; while (1) { /* Forward the time to expire in the future */ hrtimer_forward(&ts->sched_timer, now, tick_period); if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { hrtimer_start(&ts->sched_timer, ts->sched_timer.expires, HRTIMER_MODE_ABS); /* Check, if the timer was already in the past */ if (hrtimer_active(&ts->sched_timer)) break; } else { if (!tick_program_event(ts->sched_timer.expires, 0)) break; } /* Update jiffies and reread time */ tick_do_update_jiffies64(now); now = ktime_get(); } local_irq_enable();}static int tick_nohz_reprogram(struct tick_sched *ts, ktime_t now){ hrtimer_forward(&ts->sched_timer, now, tick_period); return tick_program_event(ts->sched_timer.expires, 0);}/* * The nohz low res interrupt handler */static void tick_nohz_handler(struct clock_event_device *dev){ struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); struct pt_regs *regs = get_irq_regs(); int cpu = smp_processor_id(); ktime_t now = ktime_get(); dev->next_event.tv64 = KTIME_MAX; /* * Check if the do_timer duty was dropped. We don't care about * concurrency: This happens only when the cpu in charge went * into a long sleep. If two cpus happen to assign themself to * this duty, then the jiffies update is still serialized by * xtime_lock. */ if (unlikely(tick_do_timer_cpu == -1)) tick_do_timer_cpu = cpu; /* Check, if the jiffies need an update */ if (tick_do_timer_cpu == cpu) tick_do_update_jiffies64(now); /* * When we are idle and the tick is stopped, we have to touch * the watchdog as we might not schedule for a really long * time. This happens on complete idle SMP systems while * waiting on the login prompt. We also increment the "start * of idle" jiffy stamp so the idle accounting adjustment we * do when we go busy again does not account too much ticks. */ if (ts->tick_stopped) { touch_softlockup_watchdog(); ts->idle_jiffies++; } update_process_times(user_mode(regs)); profile_tick(CPU_PROFILING); /* Do not restart, when we are in the idle loop */ if (ts->tick_stopped) return; while (tick_nohz_reprogram(ts, now)) { now = ktime_get(); tick_do_update_jiffies64(now); }}/** * tick_nohz_switch_to_nohz - switch to nohz mode */static void tick_nohz_switch_to_nohz(void){ struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); ktime_t next; if (!tick_nohz_enabled) return; local_irq_disable(); if (tick_switch_to_oneshot(tick_nohz_handler)) { local_irq_enable(); return; } ts->nohz_mode = NOHZ_MODE_LOWRES; /* * Recycle the hrtimer in ts, so we can share the * hrtimer_forward with the highres code. */ hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); /* Get the next period */ next = tick_init_jiffy_update(); for (;;) { ts->sched_timer.expires = next; if (!tick_program_event(next, 0)) break; next = ktime_add(next, tick_period); } local_irq_enable(); printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id());}#elsestatic inline void tick_nohz_switch_to_nohz(void) { }#endif /* NO_HZ *//* * High resolution timer specific code */#ifdef CONFIG_HIGH_RES_TIMERS/* * We rearm the timer until we get disabled by the idle code * Called with interrupts disabled and timer->base->cpu_base->lock held. */static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer){ struct tick_sched *ts = container_of(timer, struct tick_sched, sched_timer); struct hrtimer_cpu_base *base = timer->base->cpu_base; struct pt_regs *regs = get_irq_regs(); ktime_t now = ktime_get(); int cpu = smp_processor_id();#ifdef CONFIG_NO_HZ /* * Check if the do_timer duty was dropped. We don't care about * concurrency: This happens only when the cpu in charge went * into a long sleep. If two cpus happen to assign themself to * this duty, then the jiffies update is still serialized by * xtime_lock. */ if (unlikely(tick_do_timer_cpu == -1)) tick_do_timer_cpu = cpu;#endif /* Check, if the jiffies need an update */ if (tick_do_timer_cpu == cpu) tick_do_update_jiffies64(now); /* * Do not call, when we are not in irq context and have * no valid regs pointer */ if (regs) { /* * When we are idle and the tick is stopped, we have to touch * the watchdog as we might not schedule for a really long * time. This happens on complete idle SMP systems while * waiting on the login prompt. We also increment the "start of * idle" jiffy stamp so the idle accounting adjustment we do * when we go busy again does not account too much ticks. */ if (ts->tick_stopped) { touch_softlockup_watchdog(); ts->idle_jiffies++; } /* * update_process_times() might take tasklist_lock, hence * drop the base lock. sched-tick hrtimers are per-CPU and * never accessible by userspace APIs, so this is safe to do. */ spin_unlock(&base->lock); update_process_times(user_mode(regs)); profile_tick(CPU_PROFILING); spin_lock(&base->lock); } /* Do not restart, when we are in the idle loop */ if (ts->tick_stopped) return HRTIMER_NORESTART; hrtimer_forward(timer, now, tick_period); return HRTIMER_RESTART;}/** * tick_setup_sched_timer - setup the tick emulation timer */void tick_setup_sched_timer(void){ struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); ktime_t now = ktime_get(); u64 offset; /* * Emulate tick processing via per-CPU hrtimers: */ hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ts->sched_timer.function = tick_sched_timer; ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ; /* Get the next period (per cpu) */ ts->sched_timer.expires = tick_init_jiffy_update(); offset = ktime_to_ns(tick_period) >> 1; do_div(offset, num_possible_cpus()); offset *= smp_processor_id(); ts->sched_timer.expires = ktime_add_ns(ts->sched_timer.expires, offset); for (;;) { hrtimer_forward(&ts->sched_timer, now, tick_period); hrtimer_start(&ts->sched_timer, ts->sched_timer.expires, HRTIMER_MODE_ABS); /* Check, if the timer was already in the past */ if (hrtimer_active(&ts->sched_timer)) break; now = ktime_get(); }#ifdef CONFIG_NO_HZ if (tick_nohz_enabled) ts->nohz_mode = NOHZ_MODE_HIGHRES;#endif}void tick_cancel_sched_timer(int cpu){ struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); if (ts->sched_timer.base) hrtimer_cancel(&ts->sched_timer); ts->tick_stopped = 0; ts->nohz_mode = NOHZ_MODE_INACTIVE;}#endif /* HIGH_RES_TIMERS *//** * Async notification about clocksource changes */void tick_clock_notify(void){ int cpu; for_each_possible_cpu(cpu) set_bit(0, &per_cpu(tick_cpu_sched, cpu).check_clocks);}/* * Async notification about clock event changes */void tick_oneshot_notify(void){ struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); set_bit(0, &ts->check_clocks);}/** * Check, if a change happened, which makes oneshot possible. * * Called cyclic from the hrtimer softirq (driven by the timer * softirq) allow_nohz signals, that we can switch into low-res nohz * mode, because high resolution timers are disabled (either compile * or runtime). */int tick_check_oneshot_change(int allow_nohz){ struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); if (!test_and_clear_bit(0, &ts->check_clocks)) return 0; if (ts->nohz_mode != NOHZ_MODE_INACTIVE) return 0; if (!timekeeping_is_continuous() || !tick_is_oneshot_available()) return 0; if (!allow_nohz) return 1; tick_nohz_switch_to_nohz(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -