Skip to content

Commit

Permalink
[PATCH] i386: vMI timer patches
Browse files Browse the repository at this point in the history
VMI timer code.  It works by taking over the local APIC clock when APIC is
configured, which requires a couple hooks into the APIC code.  The backend
timer code could be commonized into the timer infrastructure, but there are
some pieces missing (stolen time, in particular), and the exact semantics of
when to do accounting for NO_IDLE need to be shared between different
hypervisors as well.  So for now, VMI timer is a separate module.

[Adrian Bunk: cleanups]

Subject: VMI timer patches
Signed-off-by: Zachary Amsden <zach@vmware.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Cc: Andi Kleen <ak@suse.de>
Cc: Jeremy Fitzhardinge <jeremy@xensource.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
  • Loading branch information
Zachary Amsden authored and Andi Kleen committed Feb 13, 2007
1 parent 7ce0bcf commit bbab4f3
Show file tree
Hide file tree
Showing 15 changed files with 687 additions and 5 deletions.
9 changes: 9 additions & 0 deletions arch/i386/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1272,3 +1272,12 @@ config X86_TRAMPOLINE
config KTIME_SCALAR
bool
default y

config NO_IDLE_HZ
bool
depends on PARAVIRT
default y
help
Switches the regular HZ timer off when the system is going idle.
This helps a hypervisor detect that the Linux system is idle,
reducing the overhead of idle systems.
2 changes: 1 addition & 1 deletion arch/i386/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_HPET_TIMER) += hpet.o
obj-$(CONFIG_K8_NB) += k8.o

obj-$(CONFIG_VMI) += vmi.o
obj-$(CONFIG_VMI) += vmi.o vmitime.o

# Make sure this is linked after any other paravirt_ops structs: see head.S
obj-$(CONFIG_PARAVIRT) += paravirt.o
Expand Down
2 changes: 1 addition & 1 deletion arch/i386/kernel/apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,7 @@ int __init APIC_init_uniprocessor (void)
if (!skip_ioapic_setup && nr_ioapics)
setup_IO_APIC();
#endif
setup_boot_APIC_clock();
setup_boot_clock();

return 0;
}
Expand Down
5 changes: 5 additions & 0 deletions arch/i386/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,11 @@ ENTRY(name) \
/* The include is where all of the SMP etc. interrupts come from */
#include "entry_arch.h"

/* This alternate entry is needed because we hijack the apic LVTT */
#if defined(CONFIG_VMI) && defined(CONFIG_X86_LOCAL_APIC)
BUILD_INTERRUPT(apic_vmi_timer_interrupt,LOCAL_TIMER_VECTOR)
#endif

KPROBE_ENTRY(page_fault)
RING0_EC_FRAME
pushl $do_page_fault
Expand Down
2 changes: 2 additions & 0 deletions arch/i386/kernel/paravirt.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ struct paravirt_ops paravirt_ops = {
.apic_write = native_apic_write,
.apic_write_atomic = native_apic_write_atomic,
.apic_read = native_apic_read,
.setup_boot_clock = setup_boot_APIC_clock,
.setup_secondary_clock = setup_secondary_APIC_clock,
#endif
.set_lazy_mode = (void *)native_nop,

Expand Down
4 changes: 2 additions & 2 deletions arch/i386/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ static void __cpuinit start_secondary(void *unused)
smp_callin();
while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
rep_nop();
setup_secondary_APIC_clock();
setup_secondary_clock();
if (nmi_watchdog == NMI_IO_APIC) {
disable_8259A_irq(0);
enable_NMI_through_LVT0(NULL);
Expand Down Expand Up @@ -1331,7 +1331,7 @@ static void __init smp_boot_cpus(unsigned int max_cpus)

smpboot_setup_io_apic();

setup_boot_APIC_clock();
setup_boot_clock();

/*
* Synchronize the TSC with the AP
Expand Down
4 changes: 3 additions & 1 deletion arch/i386/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ EXPORT_SYMBOL(get_cmos_time);
static void sync_cmos_clock(unsigned long dummy);

static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
int no_sync_cmos_clock;

static void sync_cmos_clock(unsigned long dummy)
{
Expand Down Expand Up @@ -275,7 +276,8 @@ static void sync_cmos_clock(unsigned long dummy)

void notify_arch_cmos_timer(void)
{
mod_timer(&sync_cmos_timer, jiffies + 1);
if (!no_sync_cmos_clock)
mod_timer(&sync_cmos_timer, jiffies + 1);
}

static long clock_cmos_diff;
Expand Down
4 changes: 4 additions & 0 deletions arch/i386/kernel/tsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* an extra value to store the TSC freq
*/
unsigned int tsc_khz;
unsigned long long (*custom_sched_clock)(void);

int tsc_disable;

Expand Down Expand Up @@ -107,6 +108,9 @@ unsigned long long sched_clock(void)
{
unsigned long long this_offset;

if (unlikely(custom_sched_clock))
return (*custom_sched_clock)();

/*
* in the NUMA case we dont use the TSC as they are not
* synchronized across all CPUs.
Expand Down
45 changes: 45 additions & 0 deletions arch/i386/kernel/vmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <asm/apic.h>
#include <asm/processor.h>
#include <asm/timer.h>
#include <asm/vmi_time.h>

/* Convenient for calling VMI functions indirectly in the ROM */
typedef u32 __attribute__((regparm(1))) (VROMFUNC)(void);
Expand Down Expand Up @@ -67,6 +68,7 @@ struct {
void (*set_linear_mapping)(int, u32, u32, u32);
void (*flush_tlb)(int);
void (*set_initial_ap_state)(int, int);
void (*halt)(void);
} vmi_ops;

/* XXX move this to alternative.h */
Expand Down Expand Up @@ -252,6 +254,19 @@ static void vmi_nop(void)
{
}

/* For NO_IDLE_HZ, we stop the clock when halting the kernel */
#ifdef CONFIG_NO_IDLE_HZ
static fastcall void vmi_safe_halt(void)
{
int idle = vmi_stop_hz_timer();
vmi_ops.halt();
if (idle) {
local_irq_disable();
vmi_account_time_restart_hz_timer();
local_irq_enable();
}
}
#endif

#ifdef CONFIG_DEBUG_PAGE_TYPE

Expand Down Expand Up @@ -727,7 +742,12 @@ static inline int __init activate_vmi(void)
(char *)paravirt_ops.save_fl);
patch_offset(&irq_save_disable_callout[IRQ_PATCH_DISABLE],
(char *)paravirt_ops.irq_disable);
#ifndef CONFIG_NO_IDLE_HZ
para_fill(safe_halt, Halt);
#else
vmi_ops.halt = vmi_get_function(VMI_CALL_Halt);
paravirt_ops.safe_halt = vmi_safe_halt;
#endif
para_fill(wbinvd, WBINVD);
/* paravirt_ops.read_msr = vmi_rdmsr */
/* paravirt_ops.write_msr = vmi_wrmsr */
Expand Down Expand Up @@ -837,6 +857,31 @@ static inline int __init activate_vmi(void)
paravirt_ops.apic_write_atomic = vmi_get_function(VMI_CALL_APICWrite);
#endif

/*
* Check for VMI timer functionality by probing for a cycle frequency method
*/
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_GetCycleFrequency);
if (rel->type != VMI_RELOCATION_NONE) {
vmi_timer_ops.get_cycle_frequency = (void *)rel->eip;
vmi_timer_ops.get_cycle_counter =
vmi_get_function(VMI_CALL_GetCycleCounter);
vmi_timer_ops.get_wallclock =
vmi_get_function(VMI_CALL_GetWallclockTime);
vmi_timer_ops.wallclock_updated =
vmi_get_function(VMI_CALL_WallclockUpdated);
vmi_timer_ops.set_alarm = vmi_get_function(VMI_CALL_SetAlarm);
vmi_timer_ops.cancel_alarm =
vmi_get_function(VMI_CALL_CancelAlarm);
paravirt_ops.time_init = vmi_time_init;
paravirt_ops.get_wallclock = vmi_get_wallclock;
paravirt_ops.set_wallclock = vmi_set_wallclock;
#ifdef CONFIG_X86_LOCAL_APIC
paravirt_ops.setup_boot_clock = vmi_timer_setup_boot_alarm;
paravirt_ops.setup_secondary_clock = vmi_timer_setup_secondary_alarm;
#endif
custom_sched_clock = vmi_sched_clock;
}

/*
* Alternative instruction rewriting doesn't happen soon enough
* to convert VMI_IRET to a call instead of a jump; so we have
Expand Down
Loading

0 comments on commit bbab4f3

Please sign in to comment.