i386 No Idle HZ aka dynticks 051219
Subsystems (4)
PATCH 1/1 i386 No Idle HZ aka dynticks 051219
Here is yet another updated version of the dynticks code.
Changes:
Rolled in the deadlock fix for 051218
Rolled in Dominik's latest C states code
Added more helper functions to work with dynticks instead of accessing the dynticks struct directly:
dyn_tick_skipping
Returns if we are currently skipping ticks.
dyn_tick_current_skip
Returns the number of ticks we are currently skipping if we are skipping
and
dyn_tick_next_tick
Returns the next scheduled dyn_tick if we are skipping ticks.
Latest patch, split patches and timer utilties available here:
http://ck.kolivas.org/patches/dyn-ticks/
Rolled up patch in this email.
Cheers,
Con
---
include/linux/dyn-tick.h new file +107 -0
@@ -0,0 +1,107 @@ +/* +* linux/include/linux/dyn-tick.h +* +* Copyright (C) 2004 Nokia Corporation +* Written by Tony Lindgen <[email protected]> and +* Tuukka Tikkanen <[email protected]> +* Rewritten by Con Kolivas <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef _DYN_TICK_TIMER_H +#define _DYN_TICK_TIMER_H + +#include <linux/interrupt.h> + +#define DYN_TICK_SKIPPING (1 << 2) +#define DYN_TICK_ENABLED (1 << 1) +#define DYN_TICK_SUITABLE (1 << 0) + +/* Baseline tick rate of slowest timers in kernel - used for sanity checks */ +#define DYN_TICK_MAX_SKIP (HZ / 5) + +struct dyn_tick_timer { +spinlock_t lock; + +/* dyn_tick init */ +int (*arch_init) (void ); +/* Enables dynamic tick */ +int (*arch_enable)(void ); +/* Disables dynamic tick */ +int (*arch_disable)(void ); +/* Reprograms the timer */ +void (*arch_reprogram)(unsigned long ); +/* Function called when all cpus are idle */ +void (*arch_all_cpus_idle) (int ); + +unsigned short state;/* Current state */ +unsigned int min_skip;/* Min number of ticks to skip */ +unsigned int max_skip;/* Max number of ticks to skip */ +unsigned long next_tick;/* Next tick we're skipping to */ +unsigned long skip;/* Ticks we're currently skipping */ +}; + +extern struct dyn_tick_timer *dyn_tick; +extern spinlock_t *dyn_tick_lock; + +extern void dyn_tick_register (struct dyn_tick_timer *new_timer); + +#ifdef CONFIG_NO_IDLE_HZ +DECLARE_PER_CPU(unsigned int, nohz_cpu); +extern int dyn_tick_enabled (void ); +extern int dyn_tick_skipping (void ); +extern int dyn_tick_current_skip (void ); +extern unsigned long dyn_tick_next_tick (void ); +extern void timer_dyn_reprogram (void ); +extern void dyn_early_reprogram (unsigned int delta); +extern void set_dyn_tick_limits (unsigned int max_skip,unsigned int min_skip); + +#ifdef CONFIG_TIMER_INFO +extern int account_timer (unsigned long function,unsigned long data); +#else +static inline int account_timer (unsigned long function,unsigned long data) +{ +return 0 ; +} +#endif + +#else /* CONFIG_NO_IDLE_HZ */ +static inline int dyn_tick_enabled (void ) +{ +return 0 ; +} + +static inline int dyn_tick_skipping (void ) +{ +return 0 ; +} + +static inline int dyn_tick_current_skip (void ) +{ +return 0 ; +} + +static inline unsigned long dyn_tick_next_tick (void ) +{ +return 0 ; +} + +static inline void set_dyn_tick_limits (unsigned int max_skip, +unsigned int min_skip) +{ +} + +static inline void dyn_early_reprogram (unsigned int delta) +{ +} + +static inline int account_timer (unsigned long function,unsigned long data) +{ +return 0 ; +} +#endif /* CONFIG_NO_IDLE_HZ */ + +#endif /* _DYN_TICK_TIMER_H */
include/linux/timer.h +1 -0
@@ -97,5 +97,6 @@ static inline void add_timer(struct timeextern void init_timers (void );extern void run_local_timers (void );extern void it_real_fn (unsigned long ); +extern void conditional_run_local_timers (void );#endif
kernel/dyn-tick.c new file +305 -0
305 lines not on this page · raw message
kernel/Makefile +2 -0
@@ -32,6 +32,8 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_NO_IDLE_HZ) += dyn-tick.o +obj-$(CONFIG_TIMER_INFO) += timer_top.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/timer.c +10 -0
@@ -33,6 +33,7 @@#include <linux/posix-timers.h> #include <linux/cpu.h> #include <linux/syscalls.h> +#include <linux/dyn-tick.h> #include <asm/uaccess.h> #include <asm/unistd.h>
@@ -540,6 +541,8 @@ found: expires = nte->expires; } } +account_timer ((unsigned long )nte->function, nte->data); +spin_unlock (&base->t_base.lock);return expires; }
@@ -869,6 +872,13 @@ void run_local_timers(void)raise_softirq (TIMER_SOFTIRQ ); } +void conditional_run_local_timers (void ) +{ +tvec_base_t *base = &__get_cpu_var (tvec_bases); + +if (base->timer_jiffies != jiffies) +run_local_timers (); +}/* * Called by the timer interrupt. xtime_lock must already be taken * by the timer IRQ!
drivers/acpi/Kconfig +2 -0
@@ -301,6 +301,8 @@ config X86_PM_TIMER voltage scaling, unlike the commonly used Time Stamp Counter (TSC) timing source. + This timer is selected by dyntick (NO_IDLE_HZ). + So, if you see messages like 'Losing too many ticks!' in the kernel logs, and/or you are using this on a notebook which does not yet have an HPET, you should say "Y" here.
arch/i386/defconfig +1 -0
@@ -91,6 +91,7 @@ CONFIG_X86_INTEL_USERCOPY=y CONFIG_X86_USE_PPRO_CHECKSUM=y # CONFIG_HPET_TIMER is not set # CONFIG_HPET_EMULATE_RTC is not set +# CONFIG_NO_IDLE_HZ is not set CONFIG_SMP=y CONFIG_NR_CPUS=8 CONFIG_SCHED_SMT=y
arch/i386/Kconfig +20 -0
@@ -173,6 +173,26 @@ config HPET_EMULATE_RTC depends on HPET_TIMER && RTC=y default y +config NO_IDLE_HZ + bool "Dynamic Tick Timer - Skip timer ticks during idle" + depends on EXPERIMENTAL + select X86_PM_TIMER + help + This option enables support for skipping timer ticks when the + processor is idle. During system load, timer is continuous. + This option saves power, as it allows the system to stay in + idle mode longer. Currently the only supported timer is ACPI PM + timer. + + Note that you can disable dynamic tick timer either by + passing dyntick=disable command line option, or via sysfs: + + # echo 0 > /sys/devices/system/timer/timer0/dyn_tick + + Most users wishing to lower their power usage while retaining + low latencies will most likely want to say Y here in combination + with a high HZ value (eg 1000). + config SMP bool "Symmetric multi-processing support" ---help---
arch/i386/kernel/apic.c +32 -1
@@ -26,6 +26,7 @@#include <linux/kernel_stat.h> #include <linux/sysdev.h> #include <linux/cpu.h> +#include <linux/dyn-tick.h> #include <asm/atomic.h> #include <asm/smp.h>
@@ -35,6 +36,7 @@#include <asm/arch_hooks.h> #include <asm/hpet.h> #include <asm/i8253.h> +#include <asm/dyn-tick.h> #include <mach_apic.h>
@@ -932,6 +934,9 @@ void (*wait_timer_tick)(void) __devinitd#define APIC_DIVISOR 16 +static u32 apic_timer_val__read_mostly ; +#define APIC_TIMER_VAL ((apic_timer_val) / (HZ)) +static void __setup_APIC_LVTT (unsigned int clocks) {unsigned int lvtt_value, tmp_value, ver;
@@ -950,7 +955,9 @@ static void __setup_APIC_LVTT(unsigned i & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | APIC_TDR_DIV_16); -apic_write_around (APIC_TMICT , clocks/APIC_DIVISOR ); + apic_timer_val = clocks *HZ /APIC_DIVISOR ; + +apic_write_around (APIC_TMICT ,APIC_TIMER_VAL ); }static void __devinit setup_APIC_timer (unsigned int clocks)
@@ -969,6 +976,17 @@ static void __devinit setup_APIC_timer(ulocal_irq_restore (flags); } +/* Used by NO_IDLE_HZ to skip ticks on idle CPUs */ +void reprogram_apic_timer (unsigned long count) +{ +unsigned long flags; + + count = count * apic_timer_val /HZ ; +local_irq_save (flags); +apic_write_around (APIC_TMICT , count); +local_irq_restore (flags); +} +/* * In this function we calibrate APIC bus clocks to the external * timer. Unfortunately we cannot use jiffies and the timer irq
@@ -1064,6 +1082,10 @@ void __init setup_boot_APIC_clock(void)*/ setup_APIC_timer (calibration_result); +if (setup_dyn_tick_use_apic (calibration_result)) +set_dyn_tick_limits ((0xFFFFFFFF / calibration_result) * +APIC_DIVISOR ,0 ); +local_irq_restore (flags); }
@@ -1202,6 +1224,9 @@ fastcall void smp_apic_timer_interrupt(s* interrupt lock, which is the WrongThing (tm) to do. */ irq_enter (); + +dyn_tick_interrupt (regs); +smp_local_timer_interrupt (regs);irq_exit (); }
@@ -1214,6 +1239,9 @@ fastcall void smp_spurious_interrupt(strunsigned long v;irq_enter (); + +dyn_tick_interrupt (regs); +/* * Check if this really is a spurious interrupt and ACK it * if it is a vectored one. Just in case...
@@ -1238,6 +1266,9 @@ fastcall void smp_error_interrupt(structunsigned long v, v1;irq_enter (); + +dyn_tick_interrupt (regs); +/* First tickle the hardware, only then report what went on. -- REW */ v =apic_read (APIC_ESR );apic_write (APIC_ESR ,0 );
arch/i386/kernel/dyn-tick.c new file +178 -0
178 lines not on this page · raw message
arch/i386/kernel/io_apic.c +23 -3
@@ -32,6 +32,7 @@#include <linux/acpi.h> #include <linux/module.h> #include <linux/sysdev.h> +#include <linux/dyn-tick.h> #include <asm/io.h> #include <asm/smp.h>
@@ -1179,6 +1180,7 @@ next:static struct hw_interrupt_type ioapic_level_type;static struct hw_interrupt_type ioapic_edge_type; +static struct hw_interrupt_type ioapic_edge_type_irq0;#define IOAPIC_AUTO -1#define IOAPIC_EDGE 0
@@ -1190,15 +1192,19 @@ static inline void ioapic_register_intr(if ((trigger ==IOAPIC_AUTO &&IO_APIC_irq_trigger (irq)) || trigger ==IOAPIC_LEVEL ) irq_desc[vector].handler = &ioapic_level_type; -else +else if (vector) irq_desc[vector].handler = &ioapic_edge_type; +else + irq_desc[vector].handler = &ioapic_edge_type_irq0;set_intr_gate (vector, interrupt[vector]); }else {if ((trigger ==IOAPIC_AUTO &&IO_APIC_irq_trigger (irq)) || trigger ==IOAPIC_LEVEL ) irq_desc[irq].handler = &ioapic_level_type; -else +else if (irq) irq_desc[irq].handler = &ioapic_edge_type; +else + irq_desc[irq].handler = &ioapic_edge_type_irq0;set_intr_gate (vector, interrupt[irq]); } }
@@ -1311,7 +1317,7 @@ static void __init setup_ExtINT_IRQ0_pin* The timer IRQ doesn't have to know that behind the * scene we have a 8259A-master in AEOI mode ... */ - irq_desc[0 ].handler = &ioapic_edge_type; + irq_desc[0 ].handler = &ioapic_edge_type_irq0;/* * Add it to the IO-APIC irq-routing table:
@@ -2088,6 +2094,20 @@ static struct hw_interrupt_type ioapic_l#endif }; +/* Needed to disable PIT interrupts when all CPUs sleep */ +static struct hw_interrupt_type ioapic_edge_type_irq0__read_mostly = { + .typename ="IO-APIC-edge-irq0" , + .startup = startup_edge_ioapic, + .shutdown = shutdown_edge_ioapic, + .enable = unmask_IO_APIC_irq, + .disable = mask_IO_APIC_irq, + .ack = ack_edge_ioapic, + .end = end_edge_ioapic, +#ifdef CONFIG_SMP + .set_affinity = set_ioapic_affinity, +#endif +}; +static inline void init_IO_APIC_traps (void ) {int irq;
arch/i386/kernel/irq.c +4 -0
@@ -18,6 +18,8 @@#include <linux/notifier.h> #include <linux/cpu.h> #include <linux/delay.h> +#include <linux/dyn-tick.h> +#include <asm/dyn-tick.h> DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_maxaligned_in_smp;EXPORT_PER_CPU_SYMBOL (irq_stat);
@@ -76,6 +78,8 @@ fastcall unsigned int do_IRQ(struct pt_r }#endif +dyn_tick_interrupt (regs); +#ifdef CONFIG_4KSTACKS curctx = (union irq_ctx *)current_thread_info ();
arch/i386/kernel/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o vsyscall.o obj-$(CONFIG_ACPI_SRAT) += srat.o obj-$(CONFIG_HPET_TIMER) += time_hpet.o +obj-$(CONFIG_NO_IDLE_HZ) += dyn-tick.o obj-$(CONFIG_EFI) += efi.o efi_stub.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arch/i386/kernel/process.c +4 -0
@@ -39,6 +39,7 @@#include <linux/ptrace.h> #include <linux/random.h> #include <linux/kprobes.h> +#include <linux/dyn-tick.h> #include <asm/uaccess.h> #include <asm/pgtable.h>
@@ -56,6 +57,7 @@#include <asm/tlbflush.h> #include <asm/cpu.h> +#include <asm/dyn-tick.h> asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
@@ -194,6 +196,8 @@ void cpu_idle(void)play_dead ();__get_cpu_var (irq_stat).idle_timestamp = jiffies; +idle_reprogram_timer (); +idle (); }preempt_enable_no_resched ();
arch/i386/kernel/smp.c +9 -0
@@ -20,9 +20,11 @@#include <linux/interrupt.h> #include <linux/cpu.h> #include <linux/module.h> +#include <linux/dyn-tick.h> #include <asm/mtrr.h> #include <asm/tlbflush.h> +#include <asm/dyn-tick.h> #include <mach_apic.h> /*
@@ -313,6 +315,8 @@ fastcall void smp_invalidate_interrupt(s {unsigned long cpu; +dyn_tick_interrupt (regs); + cpu =get_cpu ();if (!cpu_isset (cpu, flush_cpumask))
@@ -600,6 +604,8 @@ void smp_send_stop(void) fastcall void smp_reschedule_interrupt(struct pt_regs *regs) {ack_APIC_irq (); + +dyn_tick_interrupt (regs); } fastcall void smp_call_function_interrupt(struct pt_regs *regs)
@@ -609,6 +615,9 @@ fastcall void smp_call_function_interrupint wait = call_data->wait;ack_APIC_irq (); + +dyn_tick_interrupt (regs); +/* * Notify initiating CPU that I've grabbed the data and am * about to execute the function
arch/i386/kernel/time.c +13 -6
@@ -46,6 +46,7 @@#include <linux/bcd.h> #include <linux/efi.h> #include <linux/mca.h> +#include <linux/dyn-tick.h> #include <asm/io.h> #include <asm/smp.h>
@@ -56,6 +57,7 @@#include <asm/uaccess.h> #include <asm/processor.h> #include <asm/timer.h> +#include <asm/dyn-tick.h> #include "mach_time.h"
@@ -245,7 +247,7 @@ EXPORT_SYMBOL(profile_pc);* timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ -static inline void do_timer_interrupt (int irq,struct pt_regs *regs) +static inline void do_timer_interrupt (int irq,struct pt_regs *regs,int lost) {#ifdef CONFIG_X86_IO_APIC if (timer_ack) {
@@ -263,7 +265,8 @@ static inline void do_timer_interrupt(in }#endif -do_timer_interrupt_hook (regs); +if (!dyn_tick_enabled () || lost) +do_timer_interrupt_hook (regs);if (MCA_bus) {
@@ -288,6 +291,8 @@ static inline void do_timer_interrupt(in*/ irqreturn_t timer_interrupt (int irq,void *dev_id,struct pt_regs *regs) { +int lost; +/* * Here we are in the timer irq handler. We just have irqs locally * disabled but we don't know if the timer_bh is running on the other
@@ -297,9 +302,9 @@ irqreturn_t timer_interrupt(int irq, voi*/ write_seqlock (&xtime_lock); - cur_timer->mark_offset (); - -do_timer_interrupt (irq, regs); + lost = cur_timer->mark_offset (); + +do_timer_interrupt (irq, regs, lost);write_sequnlock (&xtime_lock);return IRQ_HANDLED ;
@@ -425,7 +430,7 @@ static struct sysdev_class timer_sysclas/* XXX this driverfs stuff should probably go elsewhere later -john */ -static struct sys_device device_timer = { +struct sys_device device_timer = { .id =0 , .cls = &timer_sysclass, };
@@ -481,5 +486,7 @@ void __init time_init(void) cur_timer =select_timer ();printk (KERN_INFO "Using %s for high-res timesource\n" ,cur_timer->name); +dyn_tick_time_init (cur_timer); +time_init_hook (); }
arch/i386/kernel/timers/timer_pit.c +34 -1
@@ -32,9 +32,10 @@ static int __init init_pit(char* overridreturn 0 ; } -static void mark_offset_pit (void ) +static int mark_offset_pit (void ) {/* nothing needed */ +return 1 ; }static unsigned long long monotonic_clock_pit (void )
@@ -148,6 +149,38 @@ static unsigned long get_offset_pit(voidreturn count; } +void disable_pit_timer (void ) +{ + irq_desc[0 ].handler->disable (0 ); +} + +void enable_pit_timer (void ) +{ + irq_desc[0 ].handler->enable (0 ); +} + +/* +* Reprograms the next timer interrupt +* PIT timer reprogramming code taken from APM code. +* Note that PIT timer is a 16-bit timer, which allows max +* skip of only few seconds. +*/ +void reprogram_pit_timer (unsigned long jiffies_to_skip) +{ +int skip; +extern spinlock_t i8253_lock; +unsigned long flags; + + skip = jiffies_to_skip *LATCH ; +if (skip >0xffff ) + skip =0xffff ; + +spin_lock_irqsave (&i8253_lock, flags); +outb_p (0x34 ,PIT_MODE );/* binary, mode 2, LSB/MSB, ch 0 */ +outb_p (skip &0xff ,PIT_CH0 );/* LSB */ +outb (skip >>8 ,PIT_CH0 );/* MSB */ +spin_unlock_irqrestore (&i8253_lock, flags); +}/* tsc timer_opts struct */ struct timer_opts timer_pit = {
arch/i386/kernel/timers/timer_pm.c +15 -17
@@ -15,6 +15,7 @@#include <linux/module.h> #include <linux/device.h> #include <linux/init.h> +#include <linux/dyn-tick.h> #include <asm/types.h> #include <asm/timer.h> #include <asm/smp.h>
@@ -28,7 +29,7 @@#define PMTMR_TICKS_PER_SEC 3579545#define PMTMR_EXPECTED_RATE \ ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10)) - +#define PMTMR_TICKS_PER_JIFFY (PMTMR_EXPECTED_RATE / (CALIBRATE_LATCH/LATCH))/* The I/O port the PMTMR resides at. * The location is detected during setup_arch(),
@@ -127,7 +128,13 @@ pm_good:if (verify_pmtmr_rate () !=0 )return -ENODEV ; +printk ("Using %u PM timer ticks per jiffy \n" ,PMTMR_TICKS_PER_JIFFY ); + + offset_tick =read_pmtmr (); +setup_pit_timer (); +init_cpu_khz (); +set_dyn_tick_limits (((0xFFFFFF /1000000 ) *286 *HZ ) >>10 ,0 );return 0 ; }
@@ -148,10 +155,9 @@ static inline u32 cyc2us(u32 cycles)* this gets called during each timer interrupt * - Called while holding the writer xtime_lock */ -static void mark_offset_pmtmr (void ) +static int mark_offset_pmtmr (void ) {u32 lost, delta, last_offset; -static int first_run =1 ; last_offset = offset_tick;write_seqlock (&monotonic_lock);
@@ -161,29 +167,20 @@ static void mark_offset_pmtmr(void)/* calculate tick interval */ delta = (offset_tick - last_offset) &ACPI_PM_MASK ; -/* convert to usecs */ - delta =cyc2us (delta); -/* update the monotonic base value */ - monotonic_base += delta *NSEC_PER_USEC ; + monotonic_base +=cyc2us (delta) *NSEC_PER_USEC ;write_sequnlock (&monotonic_lock);/* convert to ticks */ delta += offset_delay; - lost = delta / (USEC_PER_SEC /HZ ); - offset_delay = delta % (USEC_PER_SEC /HZ ); - + lost = delta /PMTMR_TICKS_PER_JIFFY ; + offset_delay = delta %PMTMR_TICKS_PER_JIFFY ;/* compensate for lost ticks */ if (lost >=2 ) jiffies_64 += lost -1 ; -/* don't calculate delay for first run, -or if we've got less then a tick */ -if (first_run || (lost <1 )) { - first_run =0 ; - offset_delay =0 ; - } +return lost; }static int pmtmr_resume (void )
@@ -192,6 +189,7 @@ static int pmtmr_resume(void)/* Assume this is the last mark offset time */ offset_tick =read_pmtmr ();write_sequnlock (&monotonic_lock); + offset_delay =0 ;return 0 ; }
@@ -243,7 +241,7 @@ static unsigned long get_offset_pmtmr(vo now =read_pmtmr (); delta = (now - offset)&ACPI_PM_MASK ; -return (unsigned long ) offset_delay +cyc2us (delta); +return (unsigned long )cyc2us (delta + offset_delay); }
arch/i386/kernel/timers/timer_tsc.c +27 -35
142 lines not on this page · raw message
include/asm-i386/apic.h +2 -0
@@ -121,6 +121,7 @@ extern void nmi_watchdog_tick (struct ptextern int APIC_init_uniprocessor (void );extern void disable_APIC_timer (void );extern void enable_APIC_timer (void ); +extern void reprogram_apic_timer (unsigned long count);extern void enable_NMI_through_LVT0 (void * dummy);
@@ -134,6 +135,7 @@ extern int disable_timer_pin_1;#else /* !CONFIG_X86_LOCAL_APIC */ static inline void lapic_shutdown (void ) { } +static inline void reprogram_apic_timer (unsigned long count) { }#endif /* !CONFIG_X86_LOCAL_APIC */
include/asm-i386/dyn-tick.h new file +65 -0
65 lines not on this page · raw message
include/asm-i386/timer.h +38 -1
@@ -1,5 +1,6 @@#ifndef _ASMi386_TIMER_H#define _ASMi386_TIMER_H +#include <linux/jiffies.h> #include <linux/init.h> #include <linux/pm.h>
@@ -19,7 +20,7 @@*/ struct timer_opts {char * name; -void (*mark_offset)(void ); +int (*mark_offset)(void );unsigned long (*get_offset)(void );unsigned long long (*monotonic_clock)(void );void (*delay)(unsigned long );
@@ -38,6 +39,9 @@ struct init_timer_opts {extern struct timer_opts *__init select_timer (void );extern void clock_fallback (void );void setup_pit_timer (void ); +extern void disable_pit_timer (void ); +extern void enable_pit_timer (void ); +extern void reprogram_pit_timer (unsigned long jiffies_to_skip);/* Modifiers for buggy PIT handling */
@@ -67,4 +71,37 @@ extern unsigned long calibrate_tsc_hpet(#ifdef CONFIG_X86_PM_TIMER extern struct init_timer_opts timer_pmtmr_init;#endif +#ifdef CONFIG_NO_IDLE_HZ +static inline void tsc_sanity_check (int lost) +{ +} +#else /* CONFIG_NO_IDLE_HZ */ +extern void cpufreq_delayed_get (void ); + +static inline void tsc_sanity_check (int lost) +{ +static int lost_count =0 ; + +if (lost >=2 ) { + jiffies_64 += lost-1 ; + +/* sanity check to ensure we're not always losing ticks */ +if (lost_count++ >100 ) { +printk (KERN_WARNING "Losing too many ticks!\n" ); +printk (KERN_WARNING "TSC cannot be used as a timesource. \n" ); +printk (KERN_WARNING "Possible reasons for this are:\n" ); +printk (KERN_WARNING " You're running with Speedstep,\n" ); +printk (KERN_WARNING " You don't have DMA enabled for your hard disk (see hdparm),\n" ); +printk (KERN_WARNING " Incorrect TSC synchronization on an SMP system (see dmesg).\n" ); +printk (KERN_WARNING "Falling back to a sane timesource now.\n" ); + +clock_fallback (); + } +/* ... but give the TSC a fair chance */ +if (lost_count >25 ) +cpufreq_delayed_get (); + }else + lost_count =0 ; +} +#endif /* CONFIG_NO_IDLE_HZ */ #endif
arch/i386/kernel/timers/timer_cyclone.c +3 -1
@@ -45,7 +45,7 @@ static seqlock_t monotonic_lock = SEQLOC }while (high != cyclone_timer[1 ]); -static void mark_offset_cyclone (void ) +static int mark_offset_cyclone (void ) {unsigned long lost, delay;unsigned long delta = last_cyclone_low;
@@ -101,6 +101,8 @@ static void mark_offset_cyclone(void)*/ if (lost &&abs (delay - delay_at_last_interrupt) > (900000 /HZ )) jiffies_64++; + +return lost; }static unsigned long get_offset_cyclone (void )
arch/i386/kernel/timers/timer_hpet.c +3 -1
@@ -101,7 +101,7 @@ static unsigned long get_offset_hpet(voireturn edx; } -static void mark_offset_hpet (void ) +static int mark_offset_hpet (void ) {unsigned long long this_offset, last_offset;unsigned long offset;
@@ -124,6 +124,8 @@ static void mark_offset_hpet(void) this_offset = ((unsigned long long )last_tsc_high<<32 )|last_tsc_low; monotonic_base +=cycles_2_ns (this_offset - last_offset);write_sequnlock (&monotonic_lock); + +return 1 ; }static void delay_hpet (unsigned long loops)
arch/i386/kernel/timers/timer_none.c +2 -1
@@ -1,9 +1,10 @@#include <linux/init.h> #include <asm/timer.h> -static void mark_offset_none (void ) +static int mark_offset_none (void ) {/* nothing needed */ +return 1 ; }static unsigned long get_offset_none (void )
kernel/timer_top.c new file +234 -0
234 lines not on this page · raw message
lib/Kconfig.debug +13 -0
@@ -77,6 +77,19 @@ config SCHEDSTATS application, you can say N to avoid the very slight overhead this adds. +config TIMER_INFO + bool "Collect kernel timers statistics" + depends on DEBUG_KERNEL && PROC_FS && NO_IDLE_HZ + help + If you say Y here, additional code will be inserted into the + timer routines to collect statistics about kernel timers being + reprogrammed through dynamic ticks feature. The statistics + will be provided in /proc/timer_info and the behavior of this + feature can be controlled through /proc/timer_input. + The goal is to offer some output to let user applications show + timer pattern usage and allow some tuning in them to + maximise idle time. + config DEBUG_SLAB bool "Debug memory allocations" depends on DEBUG_KERNEL
drivers/acpi/processor_idle.c +69 -6
123 lines not on this page · raw message
include/acpi/processor.h +3 -0
@@ -51,6 +51,7 @@ struct acpi_processor_cx {u32 latency_ticks;u32 power;u32 usage; +u64 time;struct acpi_processor_cx_policy promotion;struct acpi_processor_cx_policy demotion; };
@@ -60,6 +61,8 @@ struct acpi_processor_power {unsigned long bm_check_timestamp;u32 default_state;u32 bm_activity; +u16 last_sleep; +u16 quick_promotion;int count;struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER ];