lkml.org 
[lkml]   [2005]   [Apr]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    Subject[PATCH] Dynamic Tick version 050406-1
    From
    Hi all,

    Here's an updated dyn-tick patch. Some minor fixes:

    - Moved updating of time happen before other interrupts are
    called as noted by Russell King.

    - Moved call to next_timer_interrupt to generic code from
    a custom idle module to allow using other idle modules,
    such as amd76x_pm.

    - Added some experimental control options under
    /sys/devices/system/timer/timer0.

    I've also put together a minimal website for dyn-tick:
    http://www.muru.com/linux/dyntick/

    Regards,

    Tony

    The sysfs debug options are following:

    Check the state of dyn tick:
    cat /sys/devices/system/timer/timer0/dyn_tick_state

    Stop dyn-tick, and use normal HZ:
    echo 0 > /sys/devices/system/timer/timer0/dyn_tick_state

    Re-enabled dyn-tick:
    echo 1 > /sys/devices/system/timer/timer0/dyn_tick_state

    Lots of debug output to console, don't use this
    from main console...

    echo 1 > /sys/devices/system/timer/timer0/dyn_tick_dbg

    Kill timer interrupt, and update time only from other
    interrupts. Only use this for PM experiments. Time
    will not stay correct as timers overflow.

    echo 1 > /sys/devices/system/timer/timer0/dyn_tick_int

    diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig
    --- a/arch/i386/Kconfig 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/Kconfig 2005-04-06 01:06:59 -07:00
    @@ -460,6 +460,16 @@
    bool "Provide RTC interrupt"
    depends on HPET_TIMER && RTC=y

    +config NO_IDLE_HZ
    + bool "Dynamic Tick Timer - Skip timer ticks during idle"
    + 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 supported timers are ACPI PM
    + timer, local APIC timer, and TSC timer. HPET timer is currently
    + not supported.
    +
    config SMP
    bool "Symmetric multi-processing support"
    ---help---
    diff -Nru a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c
    --- a/arch/i386/kernel/apic.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/apic.c 2005-04-06 01:06:59 -07:00
    @@ -26,6 +26,7 @@
    #include <linux/mc146818rtc.h>
    #include <linux/kernel_stat.h>
    #include <linux/sysdev.h>
    +#include <linux/dyn-tick-timer.h>

    #include <asm/atomic.h>
    #include <asm/smp.h>
    @@ -909,6 +910,8 @@

    #define APIC_DIVISOR 16

    +static u32 apic_timer_val;
    +
    static void __setup_APIC_LVTT(unsigned int clocks)
    {
    unsigned int lvtt_value, tmp_value, ver;
    @@ -927,7 +930,15 @@
    & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
    | APIC_TDR_DIV_16);

    - apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
    + apic_timer_val = clocks/APIC_DIVISOR;
    +
    +#ifdef CONFIG_NO_IDLE_HZ
    + /* Local APIC timer is 24-bit */
    + if (apic_timer_val)
    + dyn_tick->max_skip = 0xffffff / apic_timer_val;
    +#endif
    +
    + apic_write_around(APIC_TMICT, apic_timer_val);
    }

    static void __init setup_APIC_timer(unsigned int clocks)
    @@ -1040,6 +1051,13 @@
    */
    setup_APIC_timer(calibration_result);

    +#ifdef CONFIG_NO_IDLE_HZ
    + if (calibration_result)
    + dyn_tick->state |= DYN_TICK_USE_APIC;
    + else
    + printk(KERN_INFO "dyn-tick: Cannot use local APIC\n");
    +#endif
    +
    local_irq_enable();
    }

    @@ -1068,6 +1086,18 @@
    }
    }

    +#if defined(CONFIG_NO_IDLE_HZ)
    +void reprogram_apic_timer(unsigned int count)
    +{
    + unsigned long flags;
    +
    + count *= apic_timer_val;
    + local_irq_save(flags);
    + apic_write_around(APIC_TMICT, count);
    + local_irq_restore(flags);
    +}
    +#endif
    +
    /*
    * the frequency of the profiling timer can be changed
    * by writing a multiplier value into /proc/profile.
    @@ -1160,6 +1190,7 @@

    fastcall void smp_apic_timer_interrupt(struct pt_regs *regs)
    {
    + unsigned long seq;
    int cpu = smp_processor_id();

    /*
    @@ -1178,6 +1209,23 @@
    * interrupt lock, which is the WrongThing (tm) to do.
    */
    irq_enter();
    +
    +#ifdef CONFIG_NO_IDLE_HZ
    + /*
    + * Check if we need to wake up PIT interrupt handler.
    + * Otherwise just wake up local APIC timer.
    + */
    + do {
    + seq = read_seqbegin(&xtime_lock);
    + if (dyn_tick->state & (DYN_TICK_ENABLED | DYN_TICK_SKIPPING)) {
    + if (dyn_tick->skip_cpu == cpu && dyn_tick->skip > DYN_TICK_MIN_SKIP)
    + dyn_tick->interrupt(99, NULL, regs);
    + else
    + reprogram_apic_timer(1);
    + }
    + } while (read_seqretry(&xtime_lock, seq));
    +#endif
    +
    smp_local_timer_interrupt(regs);
    irq_exit();
    }
    diff -Nru a/arch/i386/kernel/dyn-tick.c b/arch/i386/kernel/dyn-tick.c
    --- /dev/null Wed Dec 31 16:00:00 196900
    +++ b/arch/i386/kernel/dyn-tick.c 2005-04-06 01:06:59 -07:00
    @@ -0,0 +1,68 @@
    +/*
    + * linux/arch/i386/kernel/dyn-tick.c
    + *
    + * Copyright (C) 2004 Nokia Corporation
    + * Written by Tony Lindgen <tony@atomide.com> and
    + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License as published by the
    + * Free Software Foundation; either version 2 of the License, or (at your
    + * option) any later version.
    + *
    + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
    + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
    + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + *
    + * You should have received a copy of the GNU General Public License along
    + * with this program; if not, write to the Free Software Foundation, Inc.,
    + * 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +#include <linux/version.h>
    +#include <linux/config.h>
    +#include <linux/kernel.h>
    +#include <linux/init.h>
    +//#include <linux/module.h>
    +//#include <linux/interrupt.h>
    +//#include <linux/cpumask.h>
    +//#include <linux/pm.h>
    +#include <linux/dyn-tick-timer.h>
    +//#include <asm/io.h>
    +#include "dyn-tick.h"
    +
    +extern int dyn_tick_late_init(void);
    +
    +void arch_reprogram_timer(void)
    +{
    + if (cpu_has_local_apic()) {
    + disable_pit_tick();
    + if (dyn_tick->state & DYN_TICK_TIMER_INT)
    + reprogram_apic_timer(dyn_tick->skip);
    + } else {
    + if (dyn_tick->state & DYN_TICK_TIMER_INT)
    + reprogram_pit_tick(dyn_tick->skip);
    + else
    + disable_pit_tick();
    + }
    +}
    +
    +static struct dyn_tick_timer arch_dyn_tick_timer = {
    + .arch_reprogram_timer = &arch_reprogram_timer,
    +};
    +
    +int __init dyn_tick_init(void)
    +{
    + arch_dyn_tick_timer.arch_init = dyn_tick_late_init;
    + dyn_tick_register(&arch_dyn_tick_timer);
    +
    + return 0;
    +}
    +arch_initcall(dyn_tick_init);
    diff -Nru a/arch/i386/kernel/dyn-tick.h b/arch/i386/kernel/dyn-tick.h
    --- /dev/null Wed Dec 31 16:00:00 196900
    +++ b/arch/i386/kernel/dyn-tick.h 2005-04-06 01:06:59 -07:00
    @@ -0,0 +1,33 @@
    +/*
    + * linux/arch/i386/kernel/dyn-tick.h
    + *
    + * Copyright (C) 2004 Nokia Corporation
    + * Written by Tony Lindgen <tony@atomide.com> and
    + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License as published by the
    + * Free Software Foundation; either version 2 of the License, or (at your
    + * option) any later version.
    + *
    + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
    + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
    + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + *
    + * You should have received a copy of the GNU General Public License along
    + * with this program; if not, write to the Free Software Foundation, Inc.,
    + * 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +#if defined(CONFIG_SMP) || defined(CONFIG_X86_UP_APIC)
    +#define cpu_has_local_apic() (dyn_tick->state & DYN_TICK_USE_APIC)
    +#else
    +#define cpu_has_local_apic() 0
    +#endif
    diff -Nru a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
    --- a/arch/i386/kernel/irq.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/irq.c 2005-04-06 01:06:59 -07:00
    @@ -15,6 +15,7 @@
    #include <linux/seq_file.h>
    #include <linux/interrupt.h>
    #include <linux/kernel_stat.h>
    +#include <linux/dyn-tick-timer.h>

    DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_maxaligned_in_smp;
    EXPORT_PER_CPU_SYMBOL(irq_stat);
    @@ -102,6 +103,12 @@
    );
    } else
    #endif
    +
    +#ifdef CONFIG_NO_IDLE_HZ
    + if (dyn_tick->state & (DYN_TICK_ENABLED | DYN_TICK_SKIPPING) && irq != 0)
    + dyn_tick->interrupt(irq, NULL, regs);
    +#endif
    +
    __do_IRQ(irq, regs);

    irq_exit();
    diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
    --- a/arch/i386/kernel/process.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/process.c 2005-04-06 01:06:59 -07:00
    @@ -160,6 +160,10 @@
    if (!idle)
    idle = default_idle;

    +#ifdef CONFIG_NO_IDLE_HZ
    + dyn_tick_reprogram_timer();
    +#endif
    +
    __get_cpu_var(irq_stat).idle_timestamp = jiffies;
    idle();
    }
    diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
    --- a/arch/i386/kernel/time.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/time.c 2005-04-06 01:06:59 -07:00
    @@ -46,6 +46,7 @@
    #include <linux/bcd.h>
    #include <linux/efi.h>
    #include <linux/mca.h>
    +#include <linux/dyn-tick-timer.h>

    #include <asm/io.h>
    #include <asm/smp.h>
    @@ -67,6 +68,7 @@
    #include <asm/arch_hooks.h>

    #include "io_ports.h"
    +#include "dyn-tick.h"

    extern spinlock_t i8259A_lock;
    int pit_latch_buggy; /* extern */
    @@ -308,6 +310,66 @@
    return IRQ_HANDLED;
    }

    +#ifdef CONFIG_NO_IDLE_HZ
    +static unsigned long long last_tick;
    +void reprogram_pit_tick(int jiffies_to_skip);
    +extern void replace_timer_interrupt(void * new_handler);
    +
    +#if defined(CONFIG_NO_IDLE_HZ) && defined(CONFIG_X86_LOCAL_APIC)
    +extern void reprogram_apic_timer(unsigned int count);
    +#else
    +void reprogram_apic_timer(unsigned int count) {}
    +#endif
    +
    +#ifdef DEBUG
    +#define dbg_dyn_tick_irq() {if (skipped && skipped < dyn_tick->skip) \
    + printk("%u/%li ", skipped, dyn_tick->skip);}
    +#else
    +#define dbg_dyn_tick_irq() {}
    +#endif
    +
    +/*
    + * This interrupt handler updates the time based on number of jiffies skipped
    + * It would be somewhat more optimized to have a customa handler in each timer
    + * using hardware ticks instead of nanoseconds. Note that CONFIG_NO_IDLE_HZ
    + * currently disables timer fallback on skipped jiffies.
    + */
    +irqreturn_t dyn_tick_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    +{
    + unsigned long flags;
    + volatile unsigned long long now;
    + unsigned int skipped = 0;
    +
    + if (dyn_tick->state & DYN_TICK_DEBUG) {
    + if (irq == 0)
    + printk(".");
    + else
    + printk("%i ", irq);
    + }
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + now = cur_timer->get_hw_time();
    + while (now - last_tick >= NS_TICK_LEN) {
    + last_tick += NS_TICK_LEN;
    + cur_timer->mark_offset();
    + do_timer_interrupt(irq, NULL, regs);
    + skipped++;
    + }
    + if (dyn_tick->state & (DYN_TICK_ENABLED | DYN_TICK_SKIPPING)) {
    + dbg_dyn_tick_irq();
    + dyn_tick->skip = 1;
    + if (cpu_has_local_apic())
    + reprogram_apic_timer(dyn_tick->skip);
    + reprogram_pit_tick(dyn_tick->skip);
    + dyn_tick->state |= DYN_TICK_ENABLED;
    + dyn_tick->state &= ~DYN_TICK_SKIPPING;
    + }
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return IRQ_HANDLED;
    +}
    +#endif /* CONFIG_NO_IDLE_HZ */
    +
    /* not static: needed by APM */
    unsigned long get_cmos_time(void)
    {
    @@ -416,7 +478,7 @@


    /* 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,
    };
    @@ -452,6 +514,69 @@
    }
    #endif

    +#ifdef CONFIG_NO_IDLE_HZ
    +
    +void disable_pit_tick(void)
    +{
    + extern spinlock_t i8253_lock;
    + unsigned long flags;
    + spin_lock_irqsave(&i8253_lock, flags);
    + outb_p(0x31, PIT_MODE); /* binary, mode 1, LSB/MSB, ch 0 */
    + spin_unlock_irqrestore(&i8253_lock, flags);
    +}
    +
    +/*
    + * 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_tick(int 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);
    +}
    +
    +int __init dyn_tick_late_init(void)
    +{
    + unsigned long flags;
    +
    + if (!cur_timer->get_hw_time)
    + return -ENODEV;
    + write_seqlock_irqsave(&xtime_lock, flags);
    + last_tick = cur_timer->get_hw_time();
    + dyn_tick->skip = 1;
    + if (!cpu_has_local_apic())
    + dyn_tick->max_skip = 0xffff/LATCH; /* PIT timer length */
    + printk(KERN_INFO "dyn-tick: Maximum ticks to skip limited to %i\n",
    + dyn_tick->max_skip);
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + if (cur_timer->late_init)
    + cur_timer->late_init();
    + dyn_tick->interrupt = dyn_tick_timer_interrupt;
    + replace_timer_interrupt(dyn_tick->interrupt);
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + dyn_tick->state |= DYN_TICK_ENABLED;
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return 0;
    +}
    +#endif /* CONFIG_NO_IDLE_HZ */
    +
    void __init time_init(void)
    {
    #ifdef CONFIG_HPET_TIMER
    @@ -471,6 +596,15 @@

    cur_timer = select_timer();
    printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
    +
    +#ifdef CONFIG_NO_IDLE_HZ
    + if (strncmp(cur_timer->name, "tsc", 3) == 0 ||
    + strncmp(cur_timer->name, "pmtmr", 3) == 0)
    + dyn_tick->state |= DYN_TICK_SUITABLE;
    + else
    + printk(KERN_INFO "dyn-tick: Cannot use timer %s\n",
    + cur_timer->name);
    +#endif

    time_init_hook();
    }
    diff -Nru a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c
    --- a/arch/i386/kernel/timers/timer_pm.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/timers/timer_pm.c 2005-04-06 01:06:59 -07:00
    @@ -15,6 +15,7 @@
    #include <linux/module.h>
    #include <linux/device.h>
    #include <linux/init.h>
    +#include <linux/dyn-tick-timer.h>
    #include <asm/types.h>
    #include <asm/timer.h>
    #include <asm/smp.h>
    @@ -168,6 +169,7 @@
    monotonic_base += delta * NSEC_PER_USEC;
    write_sequnlock(&monotonic_lock);

    +#ifndef CONFIG_NO_IDLE_HZ
    /* convert to ticks */
    delta += offset_delay;
    lost = delta / (USEC_PER_SEC / HZ);
    @@ -184,6 +186,7 @@
    first_run = 0;
    offset_delay = 0;
    }
    +#endif
    }


    @@ -238,6 +241,25 @@
    return (unsigned long) offset_delay + cyc2us(delta);
    }

    +static unsigned long long ns_time;
    +
    +static unsigned long long get_hw_time_pmtmr(void)
    +{
    + u32 now, delta;
    + static unsigned int last_cycles;
    + now = read_pmtmr();
    + delta = (now - last_cycles) & ACPI_PM_MASK;
    + last_cycles = now;
    + ns_time += cyc2us(delta) * NSEC_PER_USEC;
    + return ns_time;
    +}
    +
    +static void late_init_pmtmr(void)
    +{
    + ns_time = monotonic_clock_pmtmr();
    +}
    +
    +extern irqreturn_t pmtmr_interrupt(int irq, void *dev_id, struct pt_regs *regs);

    /* acpi timer_opts struct */
    static struct timer_opts timer_pmtmr = {
    @@ -245,7 +267,9 @@
    .mark_offset = mark_offset_pmtmr,
    .get_offset = get_offset_pmtmr,
    .monotonic_clock = monotonic_clock_pmtmr,
    + .get_hw_time = get_hw_time_pmtmr,
    .delay = delay_pmtmr,
    + .late_init = late_init_pmtmr,
    };

    struct init_timer_opts __initdata timer_pmtmr_init = {
    diff -Nru a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c
    --- a/arch/i386/kernel/timers/timer_tsc.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/kernel/timers/timer_tsc.c 2005-04-06 01:06:59 -07:00
    @@ -112,6 +112,15 @@
    return delay_at_last_interrupt + edx;
    }

    +static unsigned long get_hw_time_tsc(void)
    +{
    + register unsigned long eax, edx;
    +
    + unsigned long long hw_time;
    + rdtscll(hw_time);
    + return cycles_2_ns(hw_time);
    +}
    +
    static unsigned long long monotonic_clock_tsc(void)
    {
    unsigned long long last_offset, this_offset, base;
    @@ -348,6 +357,7 @@

    rdtsc(last_tsc_low, last_tsc_high);

    +#ifndef CONFIG_NO_IDLE_HZ
    spin_lock(&i8253_lock);
    outb_p(0x00, PIT_MODE); /* latch the count ASAP */

    @@ -415,11 +425,14 @@
    cpufreq_delayed_get();
    } else
    lost_count = 0;
    +#endif
    +
    /* update the monotonic base value */
    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);

    +#ifndef CONFIG_NO_IDLE_HZ
    /* calculate delay_at_last_interrupt */
    count = ((LATCH-1) - count) * TICK_SIZE;
    delay_at_last_interrupt = (count + LATCH/2) / LATCH;
    @@ -430,6 +443,7 @@
    */
    if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
    jiffies_64++;
    +#endif
    }

    static int __init init_tsc(char* override)
    @@ -551,6 +565,7 @@
    .mark_offset = mark_offset_tsc,
    .get_offset = get_offset_tsc,
    .monotonic_clock = monotonic_clock_tsc,
    + .get_hw_time = get_hw_time_tsc,
    .delay = delay_tsc,
    };

    diff -Nru a/arch/i386/mach-default/setup.c b/arch/i386/mach-default/setup.c
    --- a/arch/i386/mach-default/setup.c 2005-04-06 01:06:59 -07:00
    +++ b/arch/i386/mach-default/setup.c 2005-04-06 01:06:59 -07:00
    @@ -85,6 +85,22 @@
    setup_irq(0, &irq0);
    }

    +/**
    + * replace_timer_interrupt - allow replacing timer interrupt handler
    + *
    + * Description:
    + * Can be used to replace timer interrupt handler with a more optimized
    + * handler. Used for enabling and disabling of CONFIG_NO_IDLE_HZ.
    + */
    +void replace_timer_interrupt(void * new_handler)
    +{
    + unsigned long flags;
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + irq0.handler = new_handler;
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +}
    +
    #ifdef CONFIG_MCA
    /**
    * mca_nmi_hook - hook into MCA specific NMI chain
    diff -Nru a/include/asm-i386/timer.h b/include/asm-i386/timer.h
    --- a/include/asm-i386/timer.h 2005-04-06 01:06:59 -07:00
    +++ b/include/asm-i386/timer.h 2005-04-06 01:06:59 -07:00
    @@ -1,6 +1,7 @@
    #ifndef _ASMi386_TIMER_H
    #define _ASMi386_TIMER_H
    #include <linux/init.h>
    +#include <linux/interrupt.h>

    /**
    * struct timer_ops - used to define a timer source
    @@ -21,7 +22,9 @@
    void (*mark_offset)(void);
    unsigned long (*get_offset)(void);
    unsigned long long (*monotonic_clock)(void);
    + unsigned long long (*get_hw_time)(void);
    void (*delay)(unsigned long);
    + void (*late_init)(void);
    };

    struct init_timer_opts {
    diff -Nru a/include/linux/dyn-tick-timer.h b/include/linux/dyn-tick-timer.h
    --- /dev/null Wed Dec 31 16:00:00 196900
    +++ b/include/linux/dyn-tick-timer.h 2005-04-06 01:06:59 -07:00
    @@ -0,0 +1,74 @@
    +/*
    + * linux/include/linux/dyn-tick-timer.h
    + *
    + * Copyright (C) 2004 Nokia Corporation
    + * Written by Tony Lindgen <tony@atomide.com> and
    + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License as published by the
    + * Free Software Foundation; either version 2 of the License, or (at your
    + * option) any later version.
    + *
    + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
    + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
    + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + *
    + * You should have received a copy of the GNU General Public License along
    + * with this program; if not, write to the Free Software Foundation, Inc.,
    + * 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +#ifndef _DYN_TICK_TIMER_H
    +#define _DYN_TICK_TIMER_H
    +
    +#include <linux/interrupt.h>
    +
    +#define DYN_TICK_DEBUG (1 << 31)
    +#define DYN_TICK_TIMER_INT (1 << 4)
    +#define DYN_TICK_USE_APIC (1 << 3)
    +#define DYN_TICK_SKIPPING (1 << 2)
    +#define DYN_TICK_ENABLED (1 << 1)
    +#define DYN_TICK_SUITABLE (1 << 0)
    +
    +struct dyn_tick_state {
    + unsigned int state; /* Current state */
    + int skip_cpu; /* Skip handling processor */
    + unsigned long skip; /* Ticks to skip */
    + unsigned int max_skip; /* Max number of ticks to skip */
    + unsigned long irq_skip_mask; /* Do not update time from these irqs */
    + irqreturn_t (*interrupt)(int, void *, struct pt_regs *);
    +};
    +
    +struct dyn_tick_timer {
    + int (*arch_init) (void);
    + void (*arch_enable) (void);
    + void (*arch_disable) (void);
    + void (*arch_reprogram_timer) (void);
    +};
    +
    +extern struct dyn_tick_state * dyn_tick;
    +extern void dyn_tick_register(struct dyn_tick_timer * new_timer);
    +
    +#define NS_TICK_LEN ((1 * 1000000000)/HZ)
    +#define DYN_TICK_MIN_SKIP 2
    +
    +#ifdef CONFIG_NO_IDLE_HZ
    +
    +extern unsigned long dyn_tick_reprogram_timer(void);
    +
    +#else
    +
    +#define arch_has_safe_halt() 0
    +#define dyn_tick_reprogram_timer() {}
    +
    +
    +#endif /* CONFIG_NO_IDLE_HZ */
    +#endif /* _DYN_TICK_TIMER_H */
    diff -Nru a/kernel/Makefile b/kernel/Makefile
    --- a/kernel/Makefile 2005-04-06 01:06:59 -07:00
    +++ b/kernel/Makefile 2005-04-06 01:06:59 -07:00
    @@ -28,6 +28,7 @@
    obj-$(CONFIG_SYSFS) += ksysfs.o
    obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
    obj-$(CONFIG_SECCOMP) += seccomp.o
    +obj-$(CONFIG_NO_IDLE_HZ) += dyn-tick-timer.o

    ifneq ($(CONFIG_IA64),y)
    # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
    diff -Nru a/kernel/dyn-tick-timer.c b/kernel/dyn-tick-timer.c
    --- /dev/null Wed Dec 31 16:00:00 196900
    +++ b/kernel/dyn-tick-timer.c 2005-04-06 01:06:59 -07:00
    @@ -0,0 +1,254 @@
    +/*
    + * linux/arch/i386/kernel/dyn-tick.c
    + *
    + * Beginnings of generic dynamic tick timer support
    + *
    + * Copyright (C) 2004 Nokia Corporation
    + * Written by Tony Lindgen <tony@atomide.com> and
    + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License as published by the
    + * Free Software Foundation; either version 2 of the License, or (at your
    + * option) any later version.
    + *
    + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
    + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
    + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + *
    + * You should have received a copy of the GNU General Public License along
    + * with this program; if not, write to the Free Software Foundation, Inc.,
    + * 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +#include <linux/version.h>
    +#include <linux/config.h>
    +#include <linux/kernel.h>
    +#include <linux/init.h>
    +#include <linux/module.h>
    +#include <linux/sysdev.h>
    +#include <linux/interrupt.h>
    +#include <linux/cpumask.h>
    +#include <linux/pm.h>
    +#include <linux/dyn-tick-timer.h>
    +#include <asm/io.h>
    +
    +#include "io_ports.h"
    +
    +#define DYN_TICK_VERSION "050301-1"
    +
    +struct dyn_tick_state dyn_tick_state;
    +struct dyn_tick_state * dyn_tick = &dyn_tick_state;
    +struct dyn_tick_timer * dyn_tick_cfg;
    +
    +static void (*orig_idle) (void) = 0;
    +extern void disable_pit_tick(void);
    +extern void reprogram_pit_tick(int jiffies_to_skip);
    +extern void reprogram_apic_timer(unsigned int count);
    +extern void reprogram_pit_tick(int jiffies_to_skip);
    +static cpumask_t dyn_cpu_map;
    +
    +/*
    + * Arch independed code needed to reprogram next timer interrupt.
    + * Gets called from cpu_idle() before entering idle loop. Note that
    + * we want to have all processors idle before reprogramming the
    + * next timer interrupt.
    + */
    +unsigned long dyn_tick_reprogram_timer(void)
    +{
    + int cpu;
    + unsigned long flags;
    + cpumask_t idle_cpus;
    + unsigned long next;
    +
    + if (dyn_tick->state & DYN_TICK_DEBUG)
    + printk("i");
    +
    + if (!(dyn_tick->state & DYN_TICK_ENABLED))
    + return 0;
    +
    + /* Check if we are already skipping ticks and can idle other cpus */
    + if (dyn_tick->state & DYN_TICK_SKIPPING) {
    + reprogram_apic_timer(dyn_tick->skip);
    + return 0;
    + }
    +
    + /* Check if we can start skipping ticks */
    + write_seqlock_irqsave(&xtime_lock, flags);
    + cpu = smp_processor_id();
    + cpu_set(cpu, dyn_cpu_map);
    + cpus_and(idle_cpus, dyn_cpu_map, cpu_online_map);
    + if (cpus_equal(idle_cpus, cpu_online_map)) {
    + next = next_timer_interrupt();
    + if (jiffies > next) {
    + //printk("Too late? next: %lu jiffies: %lu\n",
    + // next, jjiffies);
    + dyn_tick->skip = 1;
    + } else
    + dyn_tick->skip = next_timer_interrupt() - jiffies;
    + if (dyn_tick->skip > DYN_TICK_MIN_SKIP) {
    + if (dyn_tick->skip > dyn_tick->max_skip)
    + dyn_tick->skip = dyn_tick->max_skip;
    +
    + dyn_tick_cfg->arch_reprogram_timer();
    +
    + dyn_tick->skip_cpu = cpu;
    + dyn_tick->state |= DYN_TICK_SKIPPING;
    + }
    + cpus_clear(dyn_cpu_map);
    + }
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return dyn_tick->skip;
    +}
    +
    +void __init dyn_tick_register(struct dyn_tick_timer * arch_timer)
    +{
    + dyn_tick_cfg = arch_timer;
    + printk(KERN_INFO "dyn-tick: Registering dynamic tick timer v%s\n",
    + DYN_TICK_VERSION);
    +}
    +
    +/*
    + * ---------------------------------------------------------------------------
    + * Sysfs interface
    + * ---------------------------------------------------------------------------
    + */
    +
    +extern struct sys_device device_timer;
    +
    +static ssize_t show_dyn_tick_state(struct sys_device *dev, char *buf)
    +{
    + return sprintf(buf, "suitable:\t%i\n"
    + "enabled:\t%i\n"
    + "skipping:\t%i\n"
    + "using APIC:\t%i\n"
    + "int enabled:\t%i\n"
    + "debug:\t\t%i\n",
    + dyn_tick->state & DYN_TICK_SUITABLE,
    + (dyn_tick->state & DYN_TICK_ENABLED) >> 1,
    + (dyn_tick->state & DYN_TICK_SKIPPING) >> 2,
    + (dyn_tick->state & DYN_TICK_USE_APIC) >> 3,
    + (dyn_tick->state & DYN_TICK_TIMER_INT) >> 4,
    + (dyn_tick->state & DYN_TICK_DEBUG) >> 31);
    +}
    +
    +static ssize_t set_dyn_tick_state(struct sys_device *dev, const char * buf,
    + ssize_t count)
    +{
    + unsigned long flags;
    + unsigned int enable = simple_strtoul(buf, NULL, 2);
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + if (enable) {
    + if (dyn_tick_cfg->arch_enable)
    + dyn_tick_cfg->arch_enable();
    + dyn_tick->state |= DYN_TICK_ENABLED;
    + } else {
    + if (dyn_tick_cfg->arch_disable)
    + dyn_tick_cfg->arch_disable();
    + dyn_tick->state &= ~DYN_TICK_ENABLED;
    + }
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return count;
    +}
    +
    +static SYSDEV_ATTR(dyn_tick_state, 0644, show_dyn_tick_state,
    + set_dyn_tick_state);
    +
    +static ssize_t show_dyn_tick_int(struct sys_device *dev, char *buf)
    +{
    + return sprintf(buf, "%i\n",
    + (dyn_tick->state & DYN_TICK_TIMER_INT) >> 4);
    +}
    +
    +static ssize_t set_dyn_tick_int(struct sys_device *dev, const char * buf,
    + ssize_t count)
    +{
    + unsigned long flags;
    + unsigned int enable = simple_strtoul(buf, NULL, 2);
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + if (enable)
    + dyn_tick->state |= DYN_TICK_TIMER_INT;
    + else
    + dyn_tick->state &= ~DYN_TICK_TIMER_INT;
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return count;
    +}
    +
    +static SYSDEV_ATTR(dyn_tick_int, 0644, show_dyn_tick_int, set_dyn_tick_int);
    +
    +static ssize_t show_dyn_tick_dbg(struct sys_device *dev, char *buf)
    +{
    + return sprintf(buf, "%i\n",
    + (dyn_tick->state & DYN_TICK_DEBUG) >> 31);
    +}
    +
    +static ssize_t set_dyn_tick_dbg(struct sys_device *dev, const char * buf,
    + ssize_t count)
    +{
    + unsigned long flags;
    + unsigned int enable = simple_strtoul(buf, NULL, 2);
    +
    + write_seqlock_irqsave(&xtime_lock, flags);
    + if (enable)
    + dyn_tick->state |= DYN_TICK_DEBUG;
    + else
    + dyn_tick->state &= ~DYN_TICK_DEBUG;
    + write_sequnlock_irqrestore(&xtime_lock, flags);
    +
    + return count;
    +}
    +
    +static SYSDEV_ATTR(dyn_tick_dbg, 0644, show_dyn_tick_dbg, set_dyn_tick_dbg);
    +
    +/*
    + * ---------------------------------------------------------------------------
    + * Init functions
    + * ---------------------------------------------------------------------------
    + */
    +
    +static int __init dyn_tick_early_init(void)
    +{
    + dyn_tick->state |= DYN_TICK_TIMER_INT;
    +}
    +
    +subsys_initcall(dyn_tick_early_init);
    +
    +/*
    + * We need to initialize dynamic tick after calibrate delay
    + */
    +static int __init dyn_tick_late_init(void)
    +{
    + int ret = 0;
    +
    + ret = sysdev_create_file(&device_timer, &attr_dyn_tick_state);
    + ret = sysdev_create_file(&device_timer, &attr_dyn_tick_int);
    + ret = sysdev_create_file(&device_timer, &attr_dyn_tick_dbg);
    +
    + if (dyn_tick_cfg->arch_init == NULL ||
    + !(dyn_tick->state & DYN_TICK_SUITABLE))
    + return -ENODEV;
    +
    + ret = dyn_tick_cfg->arch_init();
    + if (ret != 0) {
    + printk(KERN_WARNING "dyn-tick: Init failed\n");
    + return -ENODEV;
    + }
    +
    + printk(KERN_INFO "dyn-tick: Timer using dynamic tick\n");
    +
    + return ret;
    +}
    +
    +late_initcall(dyn_tick_late_init);
    \
     
     \ /
      Last update: 2005-04-06 13:31    [W:0.078 / U:0.036 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site