[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/12] Add suspend/resume to devices owned by Xen
Add suspend/resume to devices owned by Xen. Signed-off-by Ke Yu <ke.yu@xxxxxxxxx> Signed-off-by Kevin Tian <kevin.tian@xxxxxxxxx> diff -r 3ef0510e44d0 xen/arch/x86/apic.c --- a/xen/arch/x86/apic.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/arch/x86/apic.c Mon May 14 15:05:28 2007 -0400 @@ -579,6 +579,95 @@ void __devinit setup_local_APIC(void) apic_pm_activate(); } +static struct { + int active; + /* r/w apic fields */ + unsigned int apic_id; + unsigned int apic_taskpri; + unsigned int apic_ldr; + unsigned int apic_dfr; + unsigned int apic_spiv; + unsigned int apic_lvtt; + unsigned int apic_lvtpc; + unsigned int apic_lvt0; + unsigned int apic_lvt1; + unsigned int apic_lvterr; + unsigned int apic_tmict; + unsigned int apic_tdcr; + unsigned int apic_thmr; +} apic_pm_state; + +int lapic_suspend(void) +{ + unsigned long flags; + + if (!apic_pm_state.active) + return 0; + + apic_pm_state.apic_id = apic_read(APIC_ID); + apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); + apic_pm_state.apic_ldr = apic_read(APIC_LDR); + apic_pm_state.apic_dfr = apic_read(APIC_DFR); + apic_pm_state.apic_spiv = apic_read(APIC_SPIV); + apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); + apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); + apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); + apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); + apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); + apic_pm_state.apic_tmict = apic_read(APIC_TMICT); + apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); + apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR); + + local_irq_save(flags); + disable_local_APIC(); + local_irq_restore(flags); + return 0; +} + +int lapic_resume(void) +{ + unsigned int l, h; + unsigned long flags; + + if (!apic_pm_state.active) + return 0; + + local_irq_save(flags); + + /* + * Make sure the APICBASE points to the right address + * + * FIXME! This will be wrong if we ever support suspend on + * SMP! We'll need to do this as part of the CPU restore! + */ + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; + wrmsr(MSR_IA32_APICBASE, l, h); + + apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); + apic_write(APIC_ID, apic_pm_state.apic_id); + apic_write(APIC_DFR, apic_pm_state.apic_dfr); + apic_write(APIC_LDR, apic_pm_state.apic_ldr); + apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri); + apic_write(APIC_SPIV, apic_pm_state.apic_spiv); + apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); + apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); + apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr); + apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); + apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); + apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); + apic_write(APIC_TMICT, apic_pm_state.apic_tmict); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + local_irq_restore(flags); + return 0; +} + + /* * If Linux enabled the LAPIC against the BIOS default * disable it down before re-entering the BIOS on shutdown. @@ -602,7 +691,10 @@ void lapic_shutdown(void) local_irq_restore(flags); } -static void apic_pm_activate(void) { } +static void apic_pm_activate(void) +{ + apic_pm_state.active = 1; +} /* * Detect and enable local APICs on non-SMP boards. diff -r 3ef0510e44d0 xen/arch/x86/i8259.c --- a/xen/arch/x86/i8259.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/arch/x86/i8259.c Mon May 14 15:05:28 2007 -0400 @@ -306,6 +306,36 @@ static void mask_and_ack_8259A_vector(un } } +static char irq_trigger[2]; +/** + * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ + */ +static void restore_ELCR(char *trigger) +{ + outb(trigger[0], 0x4d0); + outb(trigger[1], 0x4d1); +} + +static void save_ELCR(char *trigger) +{ + /* IRQ 0,1,2,8,13 are marked as reserved */ + trigger[0] = inb(0x4d0) & 0xF8; + trigger[1] = inb(0x4d1) & 0xDE; +} + +int i8259A_resume(void) +{ + init_8259A(0); + restore_ELCR(irq_trigger); + return 0; +} + +int i8259A_suspend(void) +{ + save_ELCR(irq_trigger); + return 0; +} + void __init init_8259A(int auto_eoi) { unsigned long flags; diff -r 3ef0510e44d0 xen/arch/x86/io_apic.c --- a/xen/arch/x86/io_apic.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/arch/x86/io_apic.c Mon May 14 15:09:20 2007 -0400 @@ -1793,6 +1793,78 @@ void __init setup_IO_APIC(void) register_keyhandler('z', print_IO_APIC_keyhandler, "print ioapic info"); } +struct IO_APIC_route_entry *ioapic_pm_state=NULL; + +void ioapic_pm_state_alloc(void) +{ + int i, nr_entry=0; + + if (ioapic_pm_state!=NULL) + return; + + for (i=0; i<nr_ioapics; i++) + nr_entry += nr_ioapic_registers[i]; + ioapic_pm_state = _xmalloc( sizeof(struct IO_APIC_route_entry)*nr_entry, + sizeof(struct IO_APIC_route_entry)); +} + +int ioapic_suspend(void) +{ + struct IO_APIC_route_entry *entry; + unsigned long flags; + int apic,i; + + ioapic_pm_state_alloc(); + + if (ioapic_pm_state == NULL){ + printk("can not suspend ioapic due to lack of memory\n"); + return 1; + } + + entry = ioapic_pm_state; + spin_lock_irqsave(&ioapic_lock, flags); + for (apic = 0; apic < nr_ioapics; apic++) { + for (i = 0; i < nr_ioapic_registers[apic]; i ++, entry ++ ) { + *(((int *)entry) + 1) = io_apic_read(apic, 0x11 + 2 * i); + *(((int *)entry) + 0) = io_apic_read(apic, 0x10 + 2 * i); + } + } + spin_unlock_irqrestore(&ioapic_lock, flags); + + return 0; +} + +int ioapic_resume(void) +{ + struct IO_APIC_route_entry *entry; + unsigned long flags; + union IO_APIC_reg_00 reg_00; + int i,apic; + + if (ioapic_pm_state == NULL){ + printk("can not resume ioapic due to lack of memory\n"); + return 1; + } + + entry = ioapic_pm_state; + + spin_lock_irqsave(&ioapic_lock, flags); + for (apic = 0; apic < nr_ioapics; apic++){ + reg_00.raw = io_apic_read(apic, 0); + if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid) { + reg_00.bits.ID = mp_ioapics[apic].mpc_apicid; + io_apic_write(apic, 0, reg_00.raw); + } + for (i = 0; i < nr_ioapic_registers[apic]; i ++, entry ++ ) { + io_apic_write(apic, 0x11+2*i, *(((int *)entry)+1)); + io_apic_write(apic, 0x10+2*i, *(((int *)entry)+0)); + } + } + spin_unlock_irqrestore(&ioapic_lock, flags); + + return 0; +} + /* ------------------------------------------------------------------------ -- ACPI-based IOAPIC Configuration ------------------------------------------------------------------------ -- */ diff -r 3ef0510e44d0 xen/arch/x86/time.c --- a/xen/arch/x86/time.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/arch/x86/time.c Mon May 14 15:05:28 2007 -0400 @@ -60,6 +60,16 @@ struct cpu_time { static DEFINE_PER_CPU(struct cpu_time, cpu_time); +struct plt_timer_ops { + char* name; + int (*init)(void); + u64 (*read_count)(void); + void (*suspend)(void); + void (*resume)(void); +}; + +struct plt_timer_ops *cur_timer; + /* * Protected by platform_timer_lock, which must be acquired with interrupts * disabled because pit_overflow() is called from PIT ch0 interrupt context. @@ -68,7 +78,6 @@ static u64 platform_timer_stamp; static u64 platform_timer_stamp; static struct time_scale platform_timer_scale; static DEFINE_SPINLOCK(platform_timer_lock); -static u64 (*read_platform_count)(void); /* * Folding 16-bit PIT into 64-bit software counter is a really critical @@ -299,12 +308,13 @@ static void pit_overflow(void) static void pit_overflow(void) { u16 counter; - - spin_lock_irq(&platform_timer_lock); + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); counter = pit_read_counter(); pit_counter64 += (u16)(pit_stamp - counter); pit_stamp = counter; - spin_unlock_irq(&platform_timer_lock); + spin_unlock_irqrestore(&platform_timer_lock, flags); } static u64 read_pit_count(void) @@ -312,17 +322,33 @@ static u64 read_pit_count(void) return pit_counter64 + (u16)(pit_stamp - pit_read_counter()); } -static void init_pit(void) -{ - read_platform_count = read_pit_count; - +static int init_pit(void) +{ pit_overflow(); platform_timer_stamp = pit_counter64; set_time_scale(&platform_timer_scale, CLOCK_TICK_RATE); printk("Platform timer is %s PIT\n", freq_string(CLOCK_TICK_RATE)); using_pit = 1; -} + return 1; +} + +static void resume_pit(void) +{ + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); + platform_timer_stamp = pit_counter64; + pit_stamp = pit_read_counter(); + spin_unlock_irqrestore(&platform_timer_lock, flags); +} + +static struct plt_timer_ops timer_pit = { + .name = "PIT", + .init = init_pit, + .read_count = read_pit_count, + .resume = resume_pit, +}; /************************************************************ * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET) @@ -336,12 +362,13 @@ static void hpet_overflow(void *unused) static void hpet_overflow(void *unused) { u32 counter; - - spin_lock_irq(&platform_timer_lock); + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); counter = hpet_read32(HPET_COUNTER); hpet_counter64 += (u32)(counter - hpet_stamp); hpet_stamp = counter; - spin_unlock_irq(&platform_timer_lock); + spin_unlock_irqrestore(&platform_timer_lock, flags); set_timer(&hpet_overflow_timer, NOW() + hpet_overflow_period); } @@ -401,8 +428,6 @@ static int init_hpet(void) cfg |= HPET_CFG_ENABLE; hpet_write32(cfg, HPET_CFG); - read_platform_count = read_hpet_count; - hpet_rate = 1000000000000000ULL; /* 10^15 */ (void)do_div(hpet_rate, hpet_period); set_time_scale(&platform_timer_scale, hpet_rate); @@ -427,6 +452,23 @@ static int init_hpet(void) return 1; } + +static void resume_hpet(void) +{ + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); + platform_timer_stamp = hpet_counter64; + hpet_stamp = hpet_read32(HPET_COUNTER); + spin_unlock_irqrestore(&platform_timer_lock, flags); +} + +static struct plt_timer_ops timer_hpet = { + .name = "HPET", + .init = init_hpet, + .read_count = read_hpet_count, + .resume = resume_hpet, +}; /************************************************************ * PLATFORM TIMER 3: IBM 'CYCLONE' TIMER @@ -454,12 +496,13 @@ static void cyclone_overflow(void *unuse static void cyclone_overflow(void *unused) { u32 counter; - - spin_lock_irq(&platform_timer_lock); + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); counter = *cyclone_timer; cyclone_counter64 += (u32)(counter - cyclone_stamp); cyclone_stamp = counter; - spin_unlock_irq(&platform_timer_lock); + spin_unlock_irqrestore(&platform_timer_lock, flags); set_timer(&cyclone_overflow_timer, NOW() + MILLISECS(20000)); } @@ -497,8 +540,6 @@ static int init_cyclone(void) *(map_cyclone_reg(base + CYCLONE_MPCS_OFFSET)) = 1; cyclone_timer = map_cyclone_reg(base + CYCLONE_MPMC_OFFSET); - read_platform_count = read_cyclone_count; - init_timer(&cyclone_overflow_timer, cyclone_overflow, NULL, 0); cyclone_overflow(NULL); platform_timer_stamp = cyclone_counter64; @@ -510,6 +551,23 @@ static int init_cyclone(void) return 1; } +static void resume_cyclone(void) +{ + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); + platform_timer_stamp = cyclone_counter64; + cyclone_stamp = *cyclone_timer; + spin_unlock_irqrestore(&platform_timer_lock, flags); +} + +static struct plt_timer_ops timer_cyclone = { + .name = "CYCLONE", + .init = init_cyclone, + .read_count = read_cyclone_count, + .resume = resume_cyclone, +}; + /************************************************************ * GENERIC PLATFORM TIMER INFRASTRUCTURE */ @@ -525,11 +583,12 @@ static s_time_t read_platform_stime(void { u64 counter; s_time_t stime; - - spin_lock_irq(&platform_timer_lock); - counter = read_platform_count(); + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); + counter = cur_timer->read_count(); stime = __read_platform_stime(counter); - spin_unlock_irq(&platform_timer_lock); + spin_unlock_irqrestore(&platform_timer_lock, flags); return stime; } @@ -538,19 +597,40 @@ static void platform_time_calibration(vo { u64 counter; s_time_t stamp; - - spin_lock_irq(&platform_timer_lock); - counter = read_platform_count(); + unsigned long flags; + + spin_lock_irqsave(&platform_timer_lock, flags); + counter = cur_timer->read_count(); stamp = __read_platform_stime(counter); stime_platform_stamp = stamp; platform_timer_stamp = counter; - spin_unlock_irq(&platform_timer_lock); -} + spin_unlock_irqrestore(&platform_timer_lock, flags); +} + +struct plt_timer_ops *timers[] = { + &timer_cyclone, + &timer_hpet, + &timer_pit, + NULL, +}; static void init_platform_timer(void) { - if ( !init_cyclone() && !init_hpet() ) - init_pit(); + int i = 0; + + while(timers[i]) + { + if (timers[i]->init && timers[i]->init()) + { + cur_timer = timers[i]; + break; + } + + i++; + } + + if (!cur_timer) + panic("Failed to find a usable platform timer source!!!\n"); } @@ -725,6 +805,7 @@ void do_settime(unsigned long secs, unsi rcu_read_unlock(&domlist_read_lock); } +static int on_resume; static void local_time_calibration(void *unused) { struct cpu_time *t = &this_cpu(cpu_time); @@ -872,7 +953,8 @@ void init_percpu_time(void) local_irq_save(flags); rdtscll(t->local_tsc_stamp); - now = (smp_processor_id() == 0) ? 0 : read_platform_stime(); + now = ((smp_processor_id() == 0) && !on_resume) ? + 0 : read_platform_stime(); local_irq_restore(flags); t->stime_master_stamp = now; @@ -881,6 +963,11 @@ void init_percpu_time(void) init_timer(&t->calibration_timer, local_time_calibration, NULL, smp_processor_id()); set_timer(&t->calibration_timer, NOW() + EPOCH); +} + +void destroy_percpu_time(void) +{ + kill_timer(&this_cpu(cpu_time).calibration_timer); } /* Late init function (after all CPUs are booted). */ @@ -926,6 +1013,56 @@ unsigned long get_localtime(struct domai { return wc_sec + (wc_nsec + NOW()) / 1000000000ULL + d->time_offset_seconds; +} + +/* timer suspend/resume */ +static u64 sleep_start; +int time_suspend(void) +{ + sleep_start = get_cmos_time(); + + /* Better to cancel calibration timer for accuracy */ + destroy_percpu_time(); + + if (cur_timer->suspend) + cur_timer->suspend(); + return 0; +} + +int time_resume(void) +{ + u64 sleep_diff; + u64 now; + + on_resume = 1; + + /* Set the clock to HZ Hz */ +#define CLOCK_TICK_RATE 1193180 /* crystal freq (Hz) */ +#define LATCH (((CLOCK_TICK_RATE)+(HZ/2))/HZ) + outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff, PIT_CH0); /* LSB */ + outb(LATCH >> 8, PIT_CH0); /* MSB */ + + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + outb(0xb0, PIT_MODE); + outb(CALIBRATE_LATCH & 0xff, PIT_CH2); + outb(CALIBRATE_LATCH >> 8, PIT_CH2); + + now = get_cmos_time(); + sleep_diff = now - sleep_start; + wc_sec = now; + jiffies += sleep_diff * HZ; + + stime_platform_stamp += SECONDS(sleep_diff); + if (cur_timer->resume) + cur_timer->resume(); + + init_percpu_time(); + /* Resume may happen on an idle vcpu */ + if (!is_idle_vcpu(current)) + update_vcpu_system_time(current); + return 0; } /* diff -r 3ef0510e44d0 xen/drivers/char/console.c --- a/xen/drivers/char/console.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/drivers/char/console.c Mon May 14 15:05:28 2007 -0400 @@ -914,6 +914,26 @@ void __warn(char *file, int line) dump_execution_state(); } +static void dummy_steal_fn(const char *str) +{ + return; +} + +int console_suspend(void) +{ + console_steal(sercon_handle, dummy_steal_fn); + serial_suspend(); + return 0; +} + +int console_resume(void) +{ + serial_init_preirq(); + serial_init_postirq(); + console_giveback(1); + return 0; +} + /* * Local variables: * mode: C diff -r 3ef0510e44d0 xen/drivers/char/serial.c --- a/xen/drivers/char/serial.c Tue May 08 10:21:23 2007 +0100 +++ b/xen/drivers/char/serial.c Mon May 14 15:05:28 2007 -0400 @@ -381,6 +381,17 @@ int serial_irq(int idx) return -1; } +void serial_suspend(void) +{ + int i, irq; + for (i = 0; i < 2; i++) + { + irq = serial_irq(i); + if (irq >= 0) + free_irq(irq); + } +} + void serial_register_uart(int idx, struct uart_driver *driver, void *uart) { /* Store UART-specific info. */ diff -r 3ef0510e44d0 xen/include/asm-x86/apic.h --- a/xen/include/asm-x86/apic.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/asm-x86/apic.h Mon May 14 15:05:28 2007 -0400 @@ -108,6 +108,8 @@ extern int APIC_init_uniprocessor (void) extern int APIC_init_uniprocessor (void); extern void disable_APIC_timer(void); extern void enable_APIC_timer(void); +extern int lapic_suspend(void); +extern int lapic_resume(void); extern int check_nmi_watchdog (void); extern void enable_NMI_through_LVT0 (void * dummy); @@ -123,6 +125,8 @@ extern unsigned int nmi_watchdog; #else /* !CONFIG_X86_LOCAL_APIC */ static inline void lapic_shutdown(void) { } +static inline int lapic_suspend(void) {return 0;} +static inline int lapic_resume(void) {return 0;} #endif /* !CONFIG_X86_LOCAL_APIC */ diff -r 3ef0510e44d0 xen/include/asm-x86/io_apic.h --- a/xen/include/asm-x86/io_apic.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/asm-x86/io_apic.h Mon May 14 15:05:28 2007 -0400 @@ -169,9 +169,13 @@ extern int timer_uses_ioapic_pin_0; #endif /*CONFIG_ACPI_BOOT*/ extern int (*ioapic_renumber_irq)(int ioapic, int irq); +extern int ioapic_suspend(void); +extern int ioapic_resume(void); #else /* !CONFIG_X86_IO_APIC */ #define io_apic_assign_pci_irqs 0 +static inline int ioapic_suspend(void) {return 0}; +static inline int ioapic_resume(void) {return 0}; #endif extern int assign_irq_vector(int irq); diff -r 3ef0510e44d0 xen/include/asm-x86/irq.h --- a/xen/include/asm-x86/irq.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/asm-x86/irq.h Mon May 14 15:05:28 2007 -0400 @@ -35,6 +35,8 @@ void enable_8259A_irq(unsigned int irq); void enable_8259A_irq(unsigned int irq); int i8259A_irq_pending(unsigned int irq); void init_8259A(int aeoi); +int i8259A_suspend(void); +int i8259A_resume(void); void setup_IO_APIC(void); void disable_IO_APIC(void); diff -r 3ef0510e44d0 xen/include/asm-x86/time.h --- a/xen/include/asm-x86/time.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/asm-x86/time.h Mon May 14 15:05:28 2007 -0400 @@ -21,4 +21,6 @@ mktime (unsigned int year, unsigned int unsigned int day, unsigned int hour, unsigned int min, unsigned int sec); +extern int time_suspend(void); +extern int time_resume(void); #endif /* __X86_TIME_H__ */ diff -r 3ef0510e44d0 xen/include/xen/console.h --- a/xen/include/xen/console.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/xen/console.h Mon May 14 15:05:28 2007 -0400 @@ -38,4 +38,7 @@ int console_steal(int handle, void (*fn) /* Give back stolen console. Takes the identifier returned by console_steal. */ void console_giveback(int id); +int console_suspend(void); + +int console_resume(void); #endif /* __CONSOLE_H__ */ diff -r 3ef0510e44d0 xen/include/xen/serial.h --- a/xen/include/xen/serial.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/xen/serial.h Mon May 14 15:05:28 2007 -0400 @@ -104,6 +104,7 @@ int serial_tx_space(int handle); /* Return irq number for specified serial port (identified by index). */ int serial_irq(int idx); +void serial_suspend(void); /* * Initialisation and helper functions for uart drivers. */ diff -r 3ef0510e44d0 xen/include/xen/time.h --- a/xen/include/xen/time.h Tue May 08 10:21:23 2007 +0100 +++ b/xen/include/xen/time.h Mon May 14 15:05:28 2007 -0400 @@ -32,6 +32,7 @@ extern int init_xen_time(void); extern void init_percpu_time(void); +extern void destroy_percpu_time(void); extern unsigned long cpu_khz; Attachment:
xen_add_device_suspend_resume.patch _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |