i386 no idle HZ aka Dynticks 051203
Subsystems (4)
PATCH 1/1 i386 no idle HZ aka Dynticks 051203
Here is an updated complete patch implementing no idle hz ticks aka dynticks for i386.
Changes in this version:
-Significant rework separating out architecture dependent portions -Bugfixes with respect to timers firing off at load -Added dyn_early_reprogram API to allow other code to interact with dynticks for benefit (eg ACPI) and to perform normal timers -Make bus mastering detection code dynticks smart -Numerous cleanups
-Latest version of timertop
-Sanity limits on max and min ticks to skip
Split out patches, timertop and pmstats utilities and latest patch available here:
http://ck.kolivas.org/patches/dyn-ticks/
Cheers,
Con
---
Implement i386 no-idle-hz aka dynticks rolled up patch.
Original code by Tony Lindgen <[email protected]>,
Tuukka Tikkanen <[email protected]>,
Srivatsa Vaddagiri <[email protected]> and
Daniel Petrini <[email protected]>.
Rewritten and updated by Con Kolivas <[email protected]>
arch/i386/Kconfig | 16 ++ arch/i386/defconfig | 1 arch/i386/kernel/Makefile | 1 arch/i386/kernel/apic.c | 33 ++++ arch/i386/kernel/dyn-tick.c | 143 ++++++++++++++++++ arch/i386/kernel/io_apic.c | 26 ++- arch/i386/kernel/irq.c | 4 arch/i386/kernel/process.c | 4 arch/i386/kernel/smp.c | 9 + arch/i386/kernel/time.c | 19 +- arch/i386/kernel/timers/timer_cyclone.c | 4 arch/i386/kernel/timers/timer_hpet.c | 4 arch/i386/kernel/timers/timer_none.c | 3 arch/i386/kernel/timers/timer_pit.c | 35 ++++ arch/i386/kernel/timers/timer_pm.c | 32 +--- arch/i386/kernel/timers/timer_tsc.c | 62 +++----- drivers/acpi/Kconfig | 2 drivers/acpi/processor_idle.c | 28 +++ include/asm-i386/apic.h | 2 include/asm-i386/dyn-tick.h | 65 ++++++++ include/asm-i386/timer.h | 43 +++++ include/linux/dyn-tick.h | 87 +++++++++++ include/linux/timer.h | 1 kernel/Makefile | 2 kernel/dyn-tick.c | 246 ++++++++++++++++++++++++++++++++ kernel/timer.c | 10 + kernel/timer_top.c | 237 ++++++++++++++++++++++++++++++ lib/Kconfig.debug | 13 + 28 files changed, 1064 insertions(+), 68 deletions(-)
include/linux/dyn-tick.h new file +87 -0
@@ -0,0 +1,87 @@ +/* +* 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) + +/* Ok to be 0 but there is no point reprogramming for <2ms intervals */ +#define DYN_TICK_MIN_SKIP (HZ / 500) +/* Baseline tick rate of slowest timers in kernel */ +#define DYN_TICK_MAX_SKIP (HZ / 5) + +struct dyn_tick_timer { +spinlock_t lock; + +int (*arch_init) (void );/* dyn_tick init */ +int (*arch_enable)(void );/* Enables dynamic tick */ +int (*arch_disable)(void );/* Disables dynamic tick */ +void (*arch_reprogram)(unsigned long );/* Reprograms the timer */ +int (*arch_handler)(int ,void *,struct pt_regs *); +void (*arch_all_cpus_idle) (int ); + +unsigned short state;/* Current state */ +unsigned int min_skip;/* Max number of ticks to skip */ +unsigned int max_skip;/* Max number of ticks to skip */ +unsigned long skip_tick;/* Current tick we started skipping */ +unsigned long last_skip;/* Last tick we started skipping */ +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 +extern int dyn_tick_enabled (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 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 +246 -0
246 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 +16 -0
@@ -173,6 +173,22 @@ 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 + 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 +143 -0
143 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");
@@ -193,6 +195,8 @@ void cpu_idle(void)if (cpu_is_offline (cpu))play_dead (); +idle_reprogram_timer (); +__get_cpu_var (irq_stat).idle_timestamp = jiffies;idle (); }
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 +42 -1
64 lines not on this page · raw message
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 )
drivers/acpi/processor_idle.c +27 -1
@@ -38,6 +38,7 @@#include <linux/dmi.h> #include <linux/moduleparam.h> #include <linux/sched.h> /* need_resched() */ +#include <linux/dyn-tick.h> #include <asm/io.h> #include <asm/uaccess.h>
@@ -60,6 +61,7 @@ module_param(max_cstate, uint, 0644);static unsigned int nocst =0 ;module_param (nocst, uint,0000 ); +#define BM_RELAX (msecs_to_jiffies(32))/* Time to calm down */ /* * bm_history -- bit-mask with a bit per jiffy of bus-master activity * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms
@@ -178,6 +180,17 @@ static void acpi_safe_halt(void)static atomic_t c3_cpu_count; +static inline int skipped_since_bm_check (void ) +{ +/* +* If we've been skipping ticks in the last few ticks there wouldn't be +* any busmaster activity. +*/ +if (dyn_tick->last_skip + dyn_tick->skip >= jiffies) +return dyn_tick->skip; +return 0 ; +} +static void acpi_processor_idle (void ) {struct acpi_processor *pr =NULL ;
@@ -223,6 +236,18 @@ static void acpi_processor_idle(void)if (pr->flags.bm_check) {u32 bm_status =0 ;unsigned long diff = jiffies - pr->power.bm_check_timestamp; +int skipped =0 ; + +if (dyn_tick_enabled ()) { + skipped =skipped_since_bm_check (); +if (skipped) { + diff =0 ; +do { +if (!(pr->power.bm_activity >>=1 )) +break ; + }while (skipped--); + } + }if (diff >32 ) diff =32 ;
@@ -238,7 +263,8 @@ static void acpi_processor_idle(void)acpi_get_register (ACPI_BITREG_BUS_MASTER_STATUS , &bm_status,ACPI_MTX_DO_NOT_LOCK );if (bm_status) { - pr->power.bm_activity++; +if (skipped <BM_RELAX ) + pr->power.bm_activity++;acpi_set_register (ACPI_BITREG_BUS_MASTER_STATUS ,1 ,ACPI_MTX_DO_NOT_LOCK ); }
kernel/timer_top.c new file +237 -0
237 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. This 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 + timers pattern usage and allow some tuning in them to + maximize idle time. + config DEBUG_SLAB bool "Debug memory allocations" depends on DEBUG_KERNEL
fix cpufreq-ondemand by accounting skipped ticks as idle ticks [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
Account ticks skipped dynamically as idle ticks.
This allows the ondemand cpufreq governor to work correctly with dyntick.
arch/i386/kernel/dyn-tick.c +3 -0
@@ -19,6 +19,7 @@#include <linux/dyn-tick.h> #include <linux/timer.h> #include <linux/irq.h> +#include <linux/kernel_stat.h> #include <asm/apic.h> #include <asm/dyn-tick.h>
@@ -109,6 +110,8 @@ void dyn_tick_interrupt(struct pt_regs *do_timer (regs);if (cpu_has_local_apic ())enable_pit_timer (); +if (lost) +kstat_cpu (cpu).cpustat.idle += (lost -1 ); }cpu_clear (cpu, nohz_cpu_mask);spin_unlock (&dyn_tick->lock);
Account time spent in C-States [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Con KolivasKeep track of the time actually spent sleeping in C2, C3-type sleep; not only of the number of invocations of these sleep types. This is especially useful when using "dynamic ticks" when the number of invocations do not correlate to length of sleep.
drivers/acpi/processor_idle.c +12 -4
@@ -300,8 +300,6 @@ static void acpi_processor_idle(void) } } - cx->usage++; -#ifdef CONFIG_HOTPLUG_CPU /* * Check for P_LVL2_UP flag before entering C2 and above on
@@ -409,6 +407,15 @@ static void acpi_processor_idle(void)local_irq_enable ();return ; } + cx->usage++; +if (cx->type !=ACPI_STATE_C1 ) { +if (sleep_ticks >0 ) + cx->time += sleep_ticks; + }else { +/* for C1, where we don't know the exact value, assume 0.5 of +* a jiffy */ + cx->time += (PM_TIMER_FREQUENCY / (2 *HZ )); + } next_state = pr->power.state;
@@ -1014,9 +1021,10 @@ static int acpi_processor_power_seq_showelse seq_puts (seq,"demotion[--] " ); -seq_printf (seq,"latency[%03d] usage[%08d]\n" , +seq_printf (seq,"latency[%03d] usage[%08d] time[%020llu]\n" , pr->power.states[i].latency, - pr->power.states[i].usage); + pr->power.states[i].usage, + pr->power.states[i].time); } end:
include/acpi/processor.h +1 -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; };
Re: fix cpufreq-ondemand by accounting skipped ticks as idle ticks [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Dominik BrodowskiQuoted 3 lines · Dominik Brodowski
Account ticks skipped dynamically as idle ticks.
This allows the ondemand cpufreq governor to work correctly with dyntick.
I never got around to studying it well enough to figure out how to fix it. Thanks very much!
Cheers,
Con
busmaster and C-States [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Con KolivasInstead of Con Koliva's approach to fix dyntick and the busmaster logic in the ACPI idle handler (see
http://ck.kolivas.org/patches/dyn-ticks/split-out/dyntick-busmaster_support.patch
) I propose this different patch as a replacement. Its skipped_since_bm_check() is broken, as it returns the number of ticks we hope to go to sleep for _now_, not how long we slept last time.
Some test result:
ck:
*C2: type[C2] promotion[C3] demotion[C1] latency[001]
usage[00066141] time[00000000001817309417]
C3: type[C3] promotion[--] demotion[C2] latency[085]
usage[00005760] time[00000000000138436523]At average, sleeping for 7 ticks when in C2
At average, sleeping for 6 ticks when in C3
me:
C2: type[C2] promotion[C3] demotion[C1] latency[001]
usage[00143551] time[00000000002930357624]
*C3: type[C3] promotion[--] demotion[C2] latency[085]
usage[00079181] time[00000000002733459218]
At average, sleeping for 5 ticks when in C2
At average, sleeping for 9 ticks when in C3
Not perfect yet (but see the next patch), but better.
Only avoid entering C3-type sleep if there is bus master activity at the moment.
Also, bus mastering activity going on while we slept can be ignored. Therefore, adjust the bm_check_timestamp accordingly.
drivers/acpi/processor_idle.c +6 -2
@@ -256,7 +256,7 @@ static void acpi_processor_idle(void) pr->power.bm_check_timestamp = jiffies;/* -* Apply bus mastering demotion policy. Automatically demote +* If bus mastering is active, automatically demote * to avoid a faulty transition. Note that the processor * won't enter a low-power state during this call (to this * funciton) but should upon the next.
@@ -267,7 +267,7 @@ static void acpi_processor_idle(void)* qualification. This may, however, introduce DMA * issues (e.g. floppy DMA transfer overrun/underrun). */ -if (pr->power.bm_activity & cx->demotion.threshold.bm) { +if (bm_status && cx->demotion.threshold.bm) {local_irq_enable (); next_state = cx->demotion.state; goto end;
@@ -391,6 +391,10 @@ static void acpi_processor_idle(void) cx->time += (PM_TIMER_FREQUENCY / (2 *HZ )); } +if (pr->flags.bm_check) + pr->power.bm_check_timestamp += sleep_ticks / + (PM_TIMER_FREQUENCY /HZ ); + next_state = pr->power.state;/*
C-State policy and dynticks [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Con KolivasMake the ACPI C-States policy smarter when dynticks are enabled:
- reprogram the timer if bus mastering activity is present, so that we don't sleep too long in C2, but hopefully can switch to C3 then.
- promote to a higher C-State more easily: if we slept for more than 2 jiffies the last time, and we're intended to sleep for more than 2 jiffies now, promote. This is justified as the current policy looks at whether we slept longer than a certain threshold for the last 4 or 10 times; but we usually sleep much longer when dynticks are enabled.
drivers/acpi/processor_idle.c +29 -3
@@ -38,6 +38,7 @@#include <linux/dmi.h> #include <linux/moduleparam.h> #include <linux/sched.h> /* need_resched() */ +#include <linux/dyn-tick.h> #include <asm/io.h> #include <asm/uaccess.h>
@@ -60,6 +61,8 @@ module_param(max_cstate, uint, 0644);static unsigned int nocst =0 ;module_param (nocst, uint,0000 ); +#define BM_JIFFIES (HZ >= 800 ? 4 : 2) +/* * bm_history -- bit-mask with a bit per jiffy of bus-master activity * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms
@@ -177,6 +180,7 @@ static void acpi_safe_halt(void) }static atomic_t c3_cpu_count; +static int last_sleep =0 ;static void acpi_processor_idle (void ) {
@@ -259,7 +263,7 @@ static void acpi_processor_idle(void)* If bus mastering is active, automatically demote * to avoid a faulty transition. Note that the processor * won't enter a low-power state during this call (to this -* funciton) but should upon the next. +* function) but should upon the next. * * TBD: A better policy might be to fallback to the demotion * state (use it for this quantum only) istead of
@@ -270,10 +274,31 @@ static void acpi_processor_idle(void)if (bm_status && cx->demotion.threshold.bm) {local_irq_enable (); next_state = cx->demotion.state; +if (dyn_tick_enabled ()) +dyn_early_reprogram (BM_JIFFIES ); + last_sleep =0 ;/* do not promote in fast-path */ + goto end; + } + } + +/* +* Fast-path promotion: if we slept for more than 2 jiffies the last +* time, and we're intended to sleep for more than 2 jiffies now, +* promote. Note that the processor won't enter a low-power state +* during this call (to this funciton) but should upon the next. +*/ +if (dyn_tick_enabled ()) { +if (cx->promotion.state && cx->promotion.count && + (last_sleep >BM_JIFFIES ) && + (dyn_tick->skip >BM_JIFFIES )) { +local_irq_enable (); + next_state = cx->promotion.state; goto end; } } + last_sleep =0 ; +#ifdef CONFIG_HOTPLUG_CPU /* * Check for P_LVL2_UP flag before entering C2 and above on
@@ -391,9 +416,10 @@ static void acpi_processor_idle(void) cx->time += (PM_TIMER_FREQUENCY / (2 *HZ )); } + last_sleep = sleep_ticks / (PM_TIMER_FREQUENCY /HZ ); +if (pr->flags.bm_check) - pr->power.bm_check_timestamp += sleep_ticks / - (PM_TIMER_FREQUENCY /HZ ); + pr->power.bm_check_timestamp += last_sleep; next_state = pr->power.state;
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files for problems? Stop! Download the new AJAX search engine that makes searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
Dyntick effectiveness [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Con KolivasQuoted 2 lines · Con Kolivas
Here is an updated complete patch implementing no idle hz ticks aka dynticks for i386.
A few stats about running dynticks on my notebook (1.4G Pentium M)
CPU frequency C4 dyntick avg. battery rate[*]
------------------------------------------------------------
0.6 GHz on off 990 mA
0.6 GHz on on 973 mA (-2%)
1.4 GHz on off 1400 mA fan starts very soon (~15sec)
1.4 GHz on on 1100 mA (-21%) fan starts after ~5min
0.6 GHz off off 1130 mA
0.6 GHz off on 1050 mA (-7%)
1.4 GHz off off 1480 mA
1.4 GHz off on 1180 mA (-20%)
[*] as reported by cat /proc/acpi/battery/BAT1/state .
Power consumption during normal operation (C0) and in processor idle states, as per datasheet:
speed C0 C1 C2 C3 C4
-----------------------------------------------------
1.4GHz 22W 7.3W 7.3W 5.1W 0.55W
0.6GHz 6W 1.8W 1.8W 1.1W 0.55W
=> Most useful when CPU frequency scaling isn't available (Celerons? Do they
have "Deeper Sleep State" -- no, they don't. That's why I did the test run with C4 disabled. The values for 1.4G without C4 should be similar to what can be seen on Celerons.)
=> Only minimal additional power savings when CPU frequency scaling works
well
=> Possible reason: entering power states takes more time when at higher
speed
Dominik
Re: fix cpufreq-ondemand by accounting skipped ticks as idle ticks [Was: [PATCH] i386 no idle HZ aka Dynticks 051203]
In reply to Dominik BrodowskiQuoted 3 lines · Dominik Brodowski
Account ticks skipped dynamically as idle ticks.
This allows the ondemand cpufreq governor to work correctly with dyntick.
Dominik one thing I noticed a while back was that ondemand also polls at a frequency that creates a timer at around 140 HZ. Tweaking the ondemand/ tunables and making it poll ten times less frequently made a big difference to this (obviously) but did obviously slow down the scaling speed - this was the frequency required to bring it to that of the background timers (<=20HZ). I see scope for this polling to be dynamic too :D
Cheers,
Con