From 3b4a8a957c7b82009b245317e1834c7ed2766f13 Mon Sep 17 00:00:00 2001 From: "vh249@kneesaa.uk.xensource.com" Date: Sun, 2 Oct 2005 17:45:32 -0400 Subject: [PATCH 01/18] remove useless ptep_establish_new function (called set_pte_at now) Signed-off-by: Vincent Hanquez --- .../include/asm-generic/pgtable.h | 221 ------------------ .../include/asm-xen/asm-i386/pgtable.h | 11 - 2 files changed, 232 deletions(-) delete mode 100644 linux-2.6-xen-sparse/include/asm-generic/pgtable.h diff --git a/linux-2.6-xen-sparse/include/asm-generic/pgtable.h b/linux-2.6-xen-sparse/include/asm-generic/pgtable.h deleted file mode 100644 index 08ae3e8e9f..0000000000 --- a/linux-2.6-xen-sparse/include/asm-generic/pgtable.h +++ /dev/null @@ -1,221 +0,0 @@ -#ifndef _ASM_GENERIC_PGTABLE_H -#define _ASM_GENERIC_PGTABLE_H - -#ifndef __HAVE_ARCH_PTEP_ESTABLISH -/* - * Establish a new mapping: - * - flush the old one - * - update the page tables - * - inform the TLB about the new one - * - * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock. - * - * Note: the old pte is known to not be writable, so we don't need to - * worry about dirty bits etc getting lost. - */ -#ifndef __HAVE_ARCH_SET_PTE_ATOMIC -#define ptep_establish(__vma, __address, __ptep, __entry) \ -do { \ - set_pte_at((__vma)->vm_mm, (__address), __ptep, __entry); \ - flush_tlb_page(__vma, __address); \ -} while (0) -#else /* __HAVE_ARCH_SET_PTE_ATOMIC */ -#define ptep_establish(__vma, __address, __ptep, __entry) \ -do { \ - set_pte_atomic(__ptep, __entry); \ - flush_tlb_page(__vma, __address); \ -} while (0) -#endif /* __HAVE_ARCH_SET_PTE_ATOMIC */ -#endif - -#ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS -/* - * Largely same as above, but only sets the access flags (dirty, - * accessed, and writable). Furthermore, we know it always gets set - * to a "more permissive" setting, which allows most architectures - * to optimize this. - */ -#define ptep_set_access_flags(__vma, __address, __ptep, __entry, __dirty) \ -do { \ - set_pte_at((__vma)->vm_mm, (__address), __ptep, __entry); \ - flush_tlb_page(__vma, __address); \ -} while (0) -#endif - -#ifndef __HAVE_ARCH_PTEP_ESTABLISH_NEW -/* - * Establish a mapping where none previously existed - */ -#define ptep_establish_new(__vma, __address, __ptep, __entry) \ -do { \ - set_pte(__ptep, __entry); \ -} while (0) -#endif - -#ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG -#define ptep_test_and_clear_young(__vma, __address, __ptep) \ -({ \ - pte_t __pte = *(__ptep); \ - int r = 1; \ - if (!pte_young(__pte)) \ - r = 0; \ - else \ - set_pte_at((__vma)->vm_mm, (__address), \ - (__ptep), pte_mkold(__pte)); \ - r; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH -#define ptep_clear_flush_young(__vma, __address, __ptep) \ -({ \ - int __young; \ - __young = ptep_test_and_clear_young(__vma, __address, __ptep); \ - if (__young) \ - flush_tlb_page(__vma, __address); \ - __young; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY -#define ptep_test_and_clear_dirty(__vma, __address, __ptep) \ -({ \ - pte_t __pte = *__ptep; \ - int r = 1; \ - if (!pte_dirty(__pte)) \ - r = 0; \ - else \ - set_pte_at((__vma)->vm_mm, (__address), (__ptep), \ - pte_mkclean(__pte)); \ - r; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH -#define ptep_clear_flush_dirty(__vma, __address, __ptep) \ -({ \ - int __dirty; \ - __dirty = ptep_test_and_clear_dirty(__vma, __address, __ptep); \ - if (__dirty) \ - flush_tlb_page(__vma, __address); \ - __dirty; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR -#define ptep_get_and_clear(__mm, __address, __ptep) \ -({ \ - pte_t __pte = *(__ptep); \ - pte_clear((__mm), (__address), (__ptep)); \ - __pte; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_CLEAR_FLUSH -#define ptep_clear_flush(__vma, __address, __ptep) \ -({ \ - pte_t __pte; \ - __pte = ptep_get_and_clear((__vma)->vm_mm, __address, __ptep); \ - flush_tlb_page(__vma, __address); \ - __pte; \ -}) -#endif - -#ifndef __HAVE_ARCH_PTEP_SET_WRPROTECT -static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep) -{ - pte_t old_pte = *ptep; - set_pte_at(mm, address, ptep, pte_wrprotect(old_pte)); -} -#endif - -#ifndef __HAVE_ARCH_PTE_SAME -#define pte_same(A,B) (pte_val(A) == pte_val(B)) -#endif - -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define page_test_and_clear_dirty(page) (0) -#endif - -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG -#define page_test_and_clear_young(page) (0) -#endif - -#ifndef __HAVE_ARCH_PGD_OFFSET_GATE -#define pgd_offset_gate(mm, addr) pgd_offset(mm, addr) -#endif - -#ifndef __HAVE_ARCH_LAZY_MMU_PROT_UPDATE -#define lazy_mmu_prot_update(pte) do { } while (0) -#endif - -/* - * When walking page tables, get the address of the next boundary, - * or the end address of the range if that comes earlier. Although no - * vma end wraps to 0, rounded up __boundary may wrap to 0 throughout. - */ - -#define pgd_addr_end(addr, end) \ -({ unsigned long __boundary = ((addr) + PGDIR_SIZE) & PGDIR_MASK; \ - (__boundary - 1 < (end) - 1)? __boundary: (end); \ -}) - -#ifndef pud_addr_end -#define pud_addr_end(addr, end) \ -({ unsigned long __boundary = ((addr) + PUD_SIZE) & PUD_MASK; \ - (__boundary - 1 < (end) - 1)? __boundary: (end); \ -}) -#endif - -#ifndef pmd_addr_end -#define pmd_addr_end(addr, end) \ -({ unsigned long __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \ - (__boundary - 1 < (end) - 1)? __boundary: (end); \ -}) -#endif - -#ifndef __ASSEMBLY__ -/* - * When walking page tables, we usually want to skip any p?d_none entries; - * and any p?d_bad entries - reporting the error before resetting to none. - * Do the tests inline, but report and clear the bad entry in mm/memory.c. - */ -void pgd_clear_bad(pgd_t *); -void pud_clear_bad(pud_t *); -void pmd_clear_bad(pmd_t *); - -static inline int pgd_none_or_clear_bad(pgd_t *pgd) -{ - if (pgd_none(*pgd)) - return 1; - if (unlikely(pgd_bad(*pgd))) { - pgd_clear_bad(pgd); - return 1; - } - return 0; -} - -static inline int pud_none_or_clear_bad(pud_t *pud) -{ - if (pud_none(*pud)) - return 1; - if (unlikely(pud_bad(*pud))) { - pud_clear_bad(pud); - return 1; - } - return 0; -} - -static inline int pmd_none_or_clear_bad(pmd_t *pmd) -{ - if (pmd_none(*pmd)) - return 1; - if (unlikely(pmd_bad(*pmd))) { - pmd_clear_bad(pmd); - return 1; - } - return 0; -} -#endif /* !__ASSEMBLY__ */ - -#endif /* _ASM_GENERIC_PGTABLE_H */ diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pgtable.h b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pgtable.h index 6deb1d8eb8..59cc6f60e0 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pgtable.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pgtable.h @@ -412,17 +412,6 @@ do { \ ptep_set_access_flags(__vma, __address, __ptep, __entry, 1); \ } while (0) -#define __HAVE_ARCH_PTEP_ESTABLISH_NEW -#define ptep_establish_new(__vma, __address, __ptep, __entry) \ -do { \ - if (likely((__vma)->vm_mm == current->mm)) { \ - BUG_ON(HYPERVISOR_update_va_mapping((__address), \ - __entry, 0)); \ - } else { \ - xen_l1_entry_update((__ptep), (__entry)); \ - } \ -} while (0) - #ifndef CONFIG_XEN_SHADOW_MODE void make_lowmem_page_readonly(void *va); void make_lowmem_page_writable(void *va); From 5cd7b9b4377b53f955040ffa051d1c26e308bec9 Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Sun, 2 Oct 2005 23:56:13 +0100 Subject: [PATCH 02/18] The stopgap in changeset 7152:a73ab0ddf990 was broken -- revert the change to domain_lookup_by_name, as it causes an infinite loop on startup. Leave the domain_lookup change. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendDomain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 1d5ebce231..f21d08edbb 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -275,7 +275,6 @@ class XendDomain: return self.domains.get(id) def domain_lookup_by_name(self, name): - self.refresh() dominfo = self.domains.get_by_name(name) if not dominfo: try: From 38f9dc4f90105eefda8156efb86bb7f566275ad8 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 10:26:29 +0100 Subject: [PATCH 03/18] Dynamically allocate mmu_update array in direct_remap_pfn_range(). This allows larger batches and avoids stack overflow. Signed-off-by: Keir Fraser --- .../arch/xen/i386/mm/ioremap.c | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/xen/i386/mm/ioremap.c b/linux-2.6-xen-sparse/arch/xen/i386/mm/ioremap.c index dea309b13c..7d989863df 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/mm/ioremap.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/mm/ioremap.c @@ -52,24 +52,30 @@ static int __direct_remap_pfn_range(struct mm_struct *mm, pgprot_t prot, domid_t domid) { - int i; + int i, rc; unsigned long start_address; -#define MAX_DIRECTMAP_MMU_QUEUE 130 - mmu_update_t u[MAX_DIRECTMAP_MMU_QUEUE], *v = u, *w = u; + mmu_update_t *u, *v, *w; + + u = v = w = (mmu_update_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); + if (u == NULL) + return -ENOMEM; start_address = address; flush_cache_all(); for (i = 0; i < size; i += PAGE_SIZE) { - if ((v - u) == MAX_DIRECTMAP_MMU_QUEUE) { + if ((v - u) == (PAGE_SIZE / sizeof(mmu_update_t))) { /* Fill in the PTE pointers. */ - generic_page_range(mm, start_address, - address - start_address, - direct_remap_area_pte_fn, &w); + rc = generic_page_range(mm, start_address, + address - start_address, + direct_remap_area_pte_fn, &w); + if (rc) + goto out; w = u; + rc = -EFAULT; if (HYPERVISOR_mmu_update(u, v - u, NULL, domid) < 0) - return -EFAULT; + goto out; v = u; start_address = address; } @@ -89,13 +95,19 @@ static int __direct_remap_pfn_range(struct mm_struct *mm, /* get the ptep's filled in */ generic_page_range(mm, start_address, address - start_address, direct_remap_area_pte_fn, &w); + rc = -EFAULT; if (unlikely(HYPERVISOR_mmu_update(u, v - u, NULL, domid) < 0)) - return -EFAULT; + goto out; } + rc = 0; + + out: flush_tlb_all(); - return 0; + free_page((unsigned long)u); + + return rc; } int direct_remap_pfn_range(struct vm_area_struct *vma, From 0603254247671c416109940b56e6a5766fd403f5 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 15:02:23 +0100 Subject: [PATCH 04/18] Do not pickle vcpu state on save/restore. We can construct suitable initial state for each vcpu during restore, without need for pickle/unpickle. Signed-off-by: Keir Fraser --- .../arch/xen/i386/kernel/process.c | 7 ++ .../arch/xen/i386/kernel/smpboot.c | 104 +++++++----------- .../arch/xen/i386/kernel/traps.c | 6 + linux-2.6-xen-sparse/arch/xen/kernel/reboot.c | 25 +---- .../arch/xen/x86_64/kernel/smpboot.c | 13 +-- .../arch/xen/x86_64/kernel/traps.c | 6 + 6 files changed, 67 insertions(+), 94 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c index d75dcd67a9..38940bbea6 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c @@ -141,6 +141,13 @@ static inline void play_dead(void) } #endif /* CONFIG_HOTPLUG_CPU */ +void cpu_restore(void) +{ + play_dead(); + local_irq_enable(); + cpu_idle(); +} + /* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c index dba898d0c3..7fe4d8da07 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c @@ -802,7 +802,6 @@ static int __init do_boot_cpu(int apicid) extern void hypervisor_callback(void); extern void failsafe_callback(void); extern void smp_trap_init(trap_info_t *); - int i; cpu = ++cpucount; /* @@ -853,12 +852,6 @@ static int __init do_boot_cpu(int apicid) /* FPU is set up to default initial state. */ memset(&ctxt.fpu_ctxt, 0, sizeof(ctxt.fpu_ctxt)); - /* Virtual IDT is empty at start-of-day. */ - for ( i = 0; i < 256; i++ ) - { - ctxt.trap_ctxt[i].vector = i; - ctxt.trap_ctxt[i].cs = FLAT_KERNEL_CS; - } smp_trap_init(ctxt.trap_ctxt); /* No LDT. */ @@ -1585,61 +1578,48 @@ void smp_resume(void) local_setup_timer_irq(); } -static atomic_t vcpus_rebooting; - -static void restore_vcpu_ready(void) +void vcpu_prepare(int vcpu) { + extern void hypervisor_callback(void); + extern void failsafe_callback(void); + extern void smp_trap_init(trap_info_t *); + extern void cpu_restore(void); + vcpu_guest_context_t ctxt; + struct task_struct *idle = idle_task(vcpu); - atomic_dec(&vcpus_rebooting); -} - -void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt) -{ - int r; - int gdt_pages; - r = HYPERVISOR_vcpu_pickle(vcpu, ctxt); - if (r != 0) - panic("pickling vcpu %d -> %d!\n", vcpu, r); - - /* Translate from machine to physical addresses where necessary, - so that they can be translated to our new machine address space - after resume. libxc is responsible for doing this to vcpu0, - but we do it to the others. */ - gdt_pages = (ctxt->gdt_ents + 511) / 512; - ctxt->ctrlreg[3] = machine_to_phys(ctxt->ctrlreg[3]); - for (r = 0; r < gdt_pages; r++) - ctxt->gdt_frames[r] = mfn_to_pfn(ctxt->gdt_frames[r]); -} - -int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt) -{ - int r; - int gdt_pages = (ctxt->gdt_ents + 511) / 512; - - /* This is kind of a hack, and implicitly relies on the fact that - the vcpu stops in a place where all of the call clobbered - registers are already dead. */ - ctxt->user_regs.esp -= 4; - ((unsigned long *)ctxt->user_regs.esp)[0] = ctxt->user_regs.eip; - ctxt->user_regs.eip = (unsigned long)restore_vcpu_ready; - - /* De-canonicalise. libxc handles this for vcpu 0, but we need - to do it for the other vcpus. */ - ctxt->ctrlreg[3] = phys_to_machine(ctxt->ctrlreg[3]); - for (r = 0; r < gdt_pages; r++) - ctxt->gdt_frames[r] = pfn_to_mfn(ctxt->gdt_frames[r]); - - atomic_set(&vcpus_rebooting, 1); - r = HYPERVISOR_boot_vcpu(vcpu, ctxt); - if (r != 0) { - printk(KERN_EMERG "Failed to reboot vcpu %d (%d)\n", vcpu, r); - return -1; - } - - /* Make sure we wait for the new vcpu to come up before trying to do - anything with it or starting the next one. */ - while (atomic_read(&vcpus_rebooting)) - barrier(); - - return 0; + if (vcpu == 0) + return; + + memset(&ctxt, 0, sizeof(ctxt)); + + ctxt.user_regs.ds = __USER_DS; + ctxt.user_regs.es = __USER_DS; + ctxt.user_regs.fs = 0; + ctxt.user_regs.gs = 0; + ctxt.user_regs.ss = __KERNEL_DS; + ctxt.user_regs.cs = __KERNEL_CS; + ctxt.user_regs.eip = (unsigned long)cpu_restore; + ctxt.user_regs.esp = idle->thread.esp; + ctxt.user_regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_IOPL_RING1; + + memset(&ctxt.fpu_ctxt, 0, sizeof(ctxt.fpu_ctxt)); + + smp_trap_init(ctxt.trap_ctxt); + + ctxt.ldt_ents = 0; + + ctxt.gdt_frames[0] = virt_to_mfn(cpu_gdt_descr[vcpu].address); + ctxt.gdt_ents = cpu_gdt_descr[vcpu].size / 8; + + ctxt.kernel_ss = __KERNEL_DS; + ctxt.kernel_sp = idle->thread.esp0; + + ctxt.event_callback_cs = __KERNEL_CS; + ctxt.event_callback_eip = (unsigned long)hypervisor_callback; + ctxt.failsafe_callback_cs = __KERNEL_CS; + ctxt.failsafe_callback_eip = (unsigned long)failsafe_callback; + + ctxt.ctrlreg[3] = virt_to_mfn(swapper_pg_dir) << PAGE_SHIFT; + + (void)HYPERVISOR_boot_vcpu(vcpu, &ctxt); } diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/traps.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/traps.c index 323e7409c9..81f701b8a4 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/traps.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/traps.c @@ -1012,6 +1012,12 @@ void __init trap_init(void) void smp_trap_init(trap_info_t *trap_ctxt) { trap_info_t *t = trap_table; + int i; + + for (i = 0; i < 256; i++) { + trap_ctxt[i].vector = i; + trap_ctxt[i].cs = FLAT_KERNEL_CS; + } for (t = trap_table; t->address; t++) { trap_ctxt[t->vector].flags = t->flags; diff --git a/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c b/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c index 5a093f6795..59fc5f0b78 100644 --- a/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c +++ b/linux-2.6-xen-sparse/arch/xen/kernel/reboot.c @@ -74,11 +74,8 @@ static int __do_suspend(void *ignore) extern unsigned long *pfn_to_mfn_frame_list[]; #ifdef CONFIG_SMP - static vcpu_guest_context_t suspended_cpu_records[NR_CPUS]; - cpumask_t prev_online_cpus, prev_present_cpus; - - void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt); - int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt); + cpumask_t prev_online_cpus; + int vcpu_prepare(int vcpu); #endif extern void xencons_resume(void); @@ -132,16 +129,6 @@ static int __do_suspend(void *ignore) preempt_enable(); -#ifdef CONFIG_SMP - cpus_clear(prev_present_cpus); - for_each_present_cpu(i) { - if (i == 0) - continue; - save_vcpu_context(i, &suspended_cpu_records[i]); - cpu_set(i, prev_present_cpus); - } -#endif - gnttab_suspend(); #ifdef __i386__ @@ -189,11 +176,6 @@ static int __do_suspend(void *ignore) time_resume(); -#ifdef CONFIG_SMP - for_each_cpu_mask(i, prev_present_cpus) - restore_vcpu_context(i, &suspended_cpu_records[i]); -#endif - __sti(); xencons_resume(); @@ -201,6 +183,9 @@ static int __do_suspend(void *ignore) xenbus_resume(); #ifdef CONFIG_SMP + for_each_present_cpu(i) + vcpu_prepare(i); + out_reenable_cpus: for_each_cpu_mask(i, prev_online_cpus) { j = cpu_up(i); diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c index ee0ebd51c8..a48b7aa469 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c @@ -742,12 +742,6 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid) /* FPU is set up to default initial state. */ memset(&ctxt.fpu_ctxt, 0, sizeof(ctxt.fpu_ctxt)); - /* Virtual IDT is empty at start-of-day. */ - for ( i = 0; i < 256; i++ ) - { - ctxt.trap_ctxt[i].vector = i; - ctxt.trap_ctxt[i].cs = FLAT_KERNEL_CS; - } smp_trap_init(ctxt.trap_ctxt); /* No LDT. */ @@ -1267,13 +1261,8 @@ void smp_resume(void) local_setup_timer_irq(); } -void save_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt) +void vcpu_prepare(int vcpu) { } -int restore_vcpu_context(int vcpu, vcpu_guest_context_t *ctxt) -{ - return 0; -} - #endif diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c index 9008537635..fc4defe342 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c @@ -956,6 +956,12 @@ void __init trap_init(void) void smp_trap_init(trap_info_t *trap_ctxt) { trap_info_t *t = trap_table; + int i; + + for (i = 0; i < 256; i++) { + trap_ctxt[i].vector = i; + trap_ctxt[i].cs = FLAT_KERNEL_CS; + } for (t = trap_table; t->address; t++) { trap_ctxt[t->vector].flags = t->flags; From 7978a107d1ce693834615f94e320cbb399b8314b Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 15:04:27 +0100 Subject: [PATCH 05/18] x86/64 was using the plain i386 version of the microcode driver instead of the Xen version. Signed-Off-By: Nitin A Kamble --- linux-2.6-xen-sparse/arch/xen/x86_64/kernel/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/Makefile b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/Makefile index d08ec82b83..973508d7a4 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/Makefile +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/Makefile @@ -51,7 +51,7 @@ bootflag-y += ../../../i386/kernel/bootflag.o cpuid-$(subst m,y,$(CONFIG_X86_CPUID)) += ../../../i386/kernel/cpuid.o topology-y += ../../../i386/mach-default/topology.o #swiotlb-$(CONFIG_SWIOTLB) += ../../../ia64/lib/swiotlb.o -microcode-$(subst m,y,$(CONFIG_MICROCODE)) += ../../../i386/kernel/microcode.o +microcode-$(subst m,y,$(CONFIG_MICROCODE)) += ../../i386/kernel/microcode.o intel_cacheinfo-y += ../../../i386/kernel/cpu/intel_cacheinfo.o quirks-y += ../../i386/kernel/quirks.o From 11dfa156fe68f14b59641f251e68f96210ff21f6 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 15:05:37 +0100 Subject: [PATCH 06/18] Allow adjustment of the size of TPM transfer buffers to the size that a lower-layer driver supports. Signed-off-by: Stefan Berger --- .../drivers/char/tpm/tpm_nopci.c | 22 ++++++++++++++----- .../drivers/char/tpm/tpm_nopci.h | 1 + .../drivers/char/tpm/tpm_xen.c | 3 +++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.c b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.c index eca3aa7804..872cf33e43 100644 --- a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.c +++ b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.c @@ -30,7 +30,8 @@ enum { TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 2048, + TPM_MIN_BUFSIZE = 2048, + TPM_MAX_BUFSIZE = 65536, TPM_NUM_DEVICES = 256, TPM_NUM_MASK_ENTRIES = TPM_NUM_DEVICES / (8 * sizeof(int)) }; @@ -63,7 +64,7 @@ static void user_reader_timeout(unsigned long ptr) down(&chip->buffer_mutex); atomic_set(&chip->data_pending, 0); - memset(chip->data_buffer, 0, TPM_BUFSIZE); + memset(chip->data_buffer, 0, chip->vendor->buffersize); up(&chip->buffer_mutex); } @@ -458,7 +459,8 @@ int tpm_open(struct inode *inode, struct file *file) spin_unlock(&driver_lock); - chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + chip->data_buffer = kmalloc(chip->vendor->buffersize * sizeof(u8), + GFP_KERNEL); if (chip->data_buffer == NULL) { chip->num_opens--; put_device(chip->dev); @@ -507,8 +509,8 @@ ssize_t tpm_write(struct file * file, const char __user * buf, down(&chip->buffer_mutex); - if (in_size > TPM_BUFSIZE) - in_size = TPM_BUFSIZE; + if (in_size > chip->vendor->buffersize) + in_size = chip->vendor->buffersize; if (copy_from_user (chip->data_buffer, (void __user *) buf, in_size)) { @@ -517,7 +519,9 @@ ssize_t tpm_write(struct file * file, const char __user * buf, } /* atomic tpm command send and result receive */ - out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); + out_size = tpm_transmit(chip, + chip->data_buffer, + chip->vendor->buffersize); atomic_set(&chip->data_pending, out_size); up(&chip->buffer_mutex); @@ -667,6 +671,12 @@ int tpm_register_hardware_nopci(struct device *dev, chip->vendor = entry; + if (entry->buffersize < TPM_MIN_BUFSIZE) { + entry->buffersize = TPM_MIN_BUFSIZE; + } else if (entry->buffersize > TPM_MAX_BUFSIZE) { + entry->buffersize = TPM_MAX_BUFSIZE; + } + chip->dev_num = -1; for (i = 0; i < TPM_NUM_MASK_ENTRIES; i++) diff --git a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.h b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.h index a4d1cc32cb..e1988549c6 100644 --- a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.h +++ b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_nopci.h @@ -62,6 +62,7 @@ struct tpm_vendor_specific { u8 req_complete_val; u8 req_canceled; u16 base; /* TPM base address */ + u32 buffersize; /* The device's requested buffersize */ int (*recv) (struct tpm_chip *, u8 *, size_t); int (*send) (struct tpm_chip *, u8 *, size_t); diff --git a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c index fc6d1807b6..2dcb31b857 100644 --- a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c +++ b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c @@ -445,6 +445,7 @@ static struct tpm_vendor_specific tpm_xen = { .base = 0, .attr = TPM_DEVICE_ATTRS, .miscdev.fops = &tpm_xen_ops, + .buffersize = 64 * 1024, }; static struct device tpm_device = { @@ -477,6 +478,8 @@ static int __init init_xen(void) return rc; } + tpm_xen.buffersize = tpmfe.max_tx_size; + if ((rc = tpm_register_hardware_nopci(&tpm_device, &tpm_xen)) < 0) { device_unregister(&tpm_device); tpm_fe_unregister_receiver(); From 836ead33c4861277544c98f2f359160b72462b39 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 19:14:02 +0100 Subject: [PATCH 07/18] Create new vcpu_op() hypercall. Replaces old boot_vcpu() hypercall and vcpu-related schedop commands. Signed-off-by: Keir Fraser --- .../arch/xen/i386/kernel/process.c | 3 +- .../arch/xen/i386/kernel/smpboot.c | 10 +- .../arch/xen/x86_64/kernel/smpboot.c | 6 +- .../include/asm-xen/asm-i386/hypercall.h | 40 +------- .../include/asm-xen/asm-ia64/hypercall.h | 18 ---- .../include/asm-xen/asm-x86_64/hypercall.h | 22 +---- xen/arch/x86/x86_32/entry.S | 4 +- xen/arch/x86/x86_64/entry.S | 4 +- xen/common/domain.c | 98 ++++++++++++------- xen/common/schedule.c | 82 +--------------- xen/include/public/vcpu.h | 55 +++++++++++ xen/include/public/xen.h | 6 +- 12 files changed, 146 insertions(+), 202 deletions(-) create mode 100644 xen/include/public/vcpu.h diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c index 38940bbea6..ee5d05ab7b 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/process.c @@ -49,6 +49,7 @@ #include #include #include +#include #ifdef CONFIG_MATH_EMULATION #include #endif @@ -178,7 +179,7 @@ void cpu_idle (void) don't printk. */ __get_cpu_var(cpu_state) = CPU_DEAD; /* Tell hypervisor to take vcpu down. */ - HYPERVISOR_vcpu_down(cpu); + HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL); #endif play_dead(); local_irq_enable(); diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c index 7fe4d8da07..c3e11cc0c2 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/smpboot.c @@ -63,6 +63,7 @@ #include #include +#include /* Set if we find a B stepping CPU */ static int __initdata smp_b_stepping; @@ -882,11 +883,13 @@ static int __init do_boot_cpu(int apicid) ctxt.ctrlreg[3] = virt_to_mfn(swapper_pg_dir) << PAGE_SHIFT; - boot_error = HYPERVISOR_boot_vcpu(cpu, &ctxt); + boot_error = HYPERVISOR_vcpu_op(VCPUOP_create, cpu, &ctxt); if (boot_error) printk("boot error: %ld\n", boot_error); if (!boot_error) { + HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); + /* * allow APs to start initializing. */ @@ -1499,7 +1502,7 @@ int __devinit __cpu_up(unsigned int cpu) #ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_XEN /* Tell hypervisor to bring vcpu up. */ - HYPERVISOR_vcpu_up(cpu); + HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); #endif /* Already up, and in cpu_quiescent now? */ if (cpu_isset(cpu, smp_commenced_mask)) { @@ -1621,5 +1624,6 @@ void vcpu_prepare(int vcpu) ctxt.ctrlreg[3] = virt_to_mfn(swapper_pg_dir) << PAGE_SHIFT; - (void)HYPERVISOR_boot_vcpu(vcpu, &ctxt); + (void)HYPERVISOR_vcpu_op(VCPUOP_create, vcpu, &ctxt); + (void)HYPERVISOR_vcpu_op(VCPUOP_up, vcpu, NULL); } diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c index a48b7aa469..86a050bba1 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/smpboot.c @@ -62,8 +62,8 @@ #include #ifdef CONFIG_XEN #include - #include +#include #endif /* Change for real CPU hotplug. Note other files need to be fixed @@ -771,11 +771,13 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid) ctxt.ctrlreg[3] = virt_to_mfn(init_level4_pgt) << PAGE_SHIFT; - boot_error = HYPERVISOR_boot_vcpu(cpu, &ctxt); + boot_error = HYPERVISOR_vcpu_op(VCPUOP_create, cpu, &ctxt); if (boot_error) printk("boot error: %ld\n", boot_error); if (!boot_error) { + HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); + /* * allow APs to start initializing. */ diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/hypercall.h b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/hypercall.h index 6bdcb310ff..4509c065b3 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/hypercall.h @@ -316,26 +316,10 @@ HYPERVISOR_vm_assist( } static inline int -HYPERVISOR_boot_vcpu( - unsigned long vcpu, vcpu_guest_context_t *ctxt) +HYPERVISOR_vcpu_op( + int cmd, int vcpuid, void *extra_args) { - return _hypercall2(int, boot_vcpu, vcpu, ctxt); -} - -static inline int -HYPERVISOR_vcpu_up( - int vcpu) -{ - return _hypercall2(int, sched_op, SCHEDOP_vcpu_up | - (vcpu << SCHEDOP_vcpushift), 0); -} - -static inline int -HYPERVISOR_vcpu_pickle( - int vcpu, vcpu_guest_context_t *ctxt) -{ - return _hypercall2(int, sched_op, SCHEDOP_vcpu_pickle | - (vcpu << SCHEDOP_vcpushift), ctxt); + return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); } static inline int @@ -357,24 +341,6 @@ HYPERVISOR_suspend( return ret; } -static inline int -HYPERVISOR_vcpu_down( - int vcpu) -{ - int ret; - unsigned long ign1; - /* Yes, I really do want to clobber edx here: when we resume a - vcpu after unpickling a multi-processor domain, it returns - here, but clobbers all of the call clobbered registers. */ - __asm__ __volatile__ ( - TRAP_INSTR - : "=a" (ret), "=b" (ign1) - : "0" (__HYPERVISOR_sched_op), - "1" (SCHEDOP_vcpu_down | (vcpu << SCHEDOP_vcpushift)) - : "memory", "ecx", "edx" ); - return ret; -} - #endif /* __HYPERCALL_H__ */ /* diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-ia64/hypercall.h b/linux-2.6-xen-sparse/include/asm-xen/asm-ia64/hypercall.h index b3c1079277..f2b785143b 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-ia64/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-ia64/hypercall.h @@ -601,24 +601,6 @@ HYPERVISOR_vm_assist( return 1; } -static inline int -HYPERVISOR_boot_vcpu( - unsigned long vcpu, vcpu_guest_context_t *ctxt) -{ -#if 0 - int ret; - unsigned long ign1, ign2; - - __asm__ __volatile__ ( - TRAP_INSTR - : "=a" (ret), "=b" (ign1), "=c" (ign2) - : "0" (__HYPERVISOR_boot_vcpu), "1" (vcpu), "2" (ctxt) - : "memory"); - - return ret; -#endif - return 1; -} #endif #endif /* __HYPERCALL_H__ */ diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h index 11e869b730..3421a300e5 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h @@ -302,26 +302,10 @@ HYPERVISOR_vm_assist( } static inline int -HYPERVISOR_boot_vcpu( - unsigned long vcpu, vcpu_guest_context_t *ctxt) +HYPERVISOR_vcpu_op( + int cmd, int vcpuid, void *extra_args) { - return _hypercall2(int, boot_vcpu, vcpu, ctxt); -} - -static inline int -HYPERVISOR_vcpu_up( - int vcpu) -{ - return _hypercall2(int, sched_op, SCHEDOP_vcpu_up | - (vcpu << SCHEDOP_vcpushift), 0); -} - -static inline int -HYPERVISOR_vcpu_pickle( - int vcpu, vcpu_guest_context_t *ctxt) -{ - return _hypercall2(int, sched_op, SCHEDOP_vcpu_pickle | - (vcpu << SCHEDOP_vcpushift), ctxt); + return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); } static inline int diff --git a/xen/arch/x86/x86_32/entry.S b/xen/arch/x86/x86_32/entry.S index 1fee8f010b..31bd7b372f 100644 --- a/xen/arch/x86/x86_32/entry.S +++ b/xen/arch/x86/x86_32/entry.S @@ -808,7 +808,7 @@ ENTRY(hypercall_table) .long do_vm_assist .long do_update_va_mapping_otherdomain .long do_switch_vm86 - .long do_boot_vcpu + .long do_vcpu_op .long do_ni_hypercall /* 25 */ .long do_mmuext_op .long do_acm_op /* 27 */ @@ -841,7 +841,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_vm_assist */ .byte 5 /* do_update_va_mapping_otherdomain */ .byte 0 /* do_switch_vm86 */ - .byte 2 /* do_boot_vcpu */ + .byte 3 /* do_vcpu_op */ .byte 0 /* do_ni_hypercall */ /* 25 */ .byte 4 /* do_mmuext_op */ .byte 1 /* do_acm_op */ diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S index a696b65dfe..10be586c78 100644 --- a/xen/arch/x86/x86_64/entry.S +++ b/xen/arch/x86/x86_64/entry.S @@ -629,7 +629,7 @@ ENTRY(hypercall_table) .quad do_vm_assist .quad do_update_va_mapping_otherdomain .quad do_switch_to_user - .quad do_boot_vcpu + .quad do_vcpu_op .quad do_set_segment_base /* 25 */ .quad do_mmuext_op .quad do_acm_op @@ -662,7 +662,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_vm_assist */ .byte 4 /* do_update_va_mapping_otherdomain */ .byte 0 /* do_switch_to_user */ - .byte 2 /* do_boot_vcpu */ + .byte 3 /* do_vcpu_op */ .byte 2 /* do_set_segment_base */ /* 25 */ .byte 4 /* do_mmuext_op */ .byte 1 /* do_acm_op */ diff --git a/xen/common/domain.c b/xen/common/domain.c index eaea4bcc9d..a4969811ee 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Both these structures are protected by the domlist_lock. */ rwlock_t domlist_lock = RW_LOCK_UNLOCKED; @@ -366,37 +367,17 @@ int set_info_guest(struct domain *d, dom0_setdomaininfo_t *setdomaininfo) return rc; } -/* - * final_setup_guest is used for final setup and launching of domains other - * than domain 0. ie. the domains that are being built by the userspace dom0 - * domain builder. - */ -long do_boot_vcpu(unsigned long vcpu, struct vcpu_guest_context *ctxt) +int boot_vcpu(struct domain *d, int vcpuid, struct vcpu_guest_context *ctxt) { - struct domain *d = current->domain; struct vcpu *v; - int rc = 0; - struct vcpu_guest_context *c; + int rc; - if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] != NULL) ) - return -EINVAL; + ASSERT(d->vcpu[vcpuid] == NULL); - if ( alloc_vcpu_struct(d, vcpu) == NULL ) + if ( alloc_vcpu_struct(d, vcpuid) == NULL ) return -ENOMEM; - if ( (c = xmalloc(struct vcpu_guest_context)) == NULL ) - { - rc = -ENOMEM; - goto out; - } - - if ( copy_from_user(c, ctxt, sizeof(*c)) ) - { - rc = -EFAULT; - goto out; - } - - v = d->vcpu[vcpu]; + v = d->vcpu[vcpuid]; atomic_set(&v->pausecnt, 0); v->cpumap = CPUMAP_RUNANYWHERE; @@ -405,22 +386,73 @@ long do_boot_vcpu(unsigned long vcpu, struct vcpu_guest_context *ctxt) arch_do_boot_vcpu(v); - if ( (rc = arch_set_info_guest(v, c)) != 0 ) + if ( (rc = arch_set_info_guest(v, ctxt)) != 0 ) goto out; sched_add_domain(v); - /* domain_unpause_by_systemcontroller */ - if ( test_and_clear_bit(_VCPUF_ctrl_pause, &v->vcpu_flags) ) - vcpu_wake(v); + set_bit(_VCPUF_down, &v->vcpu_flags); + clear_bit(_VCPUF_ctrl_pause, &v->vcpu_flags); - xfree(c); return 0; out: - xfree(c); - arch_free_vcpu_struct(d->vcpu[vcpu]); - d->vcpu[vcpu] = NULL; + arch_free_vcpu_struct(d->vcpu[vcpuid]); + d->vcpu[vcpuid] = NULL; + return rc; +} + +long do_vcpu_op(int cmd, int vcpuid, void *arg) +{ + struct domain *d = current->domain; + struct vcpu *v; + struct vcpu_guest_context *ctxt; + long rc = 0; + + if ( (vcpuid < 0) || (vcpuid >= MAX_VIRT_CPUS) ) + return -EINVAL; + + if ( ((v = d->vcpu[vcpuid]) == NULL) && (cmd != VCPUOP_create) ) + return -ENOENT; + + switch ( cmd ) + { + case VCPUOP_create: + if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL ) + { + rc = -ENOMEM; + break; + } + + if ( copy_from_user(ctxt, arg, sizeof(*ctxt)) ) + { + xfree(ctxt); + rc = -EFAULT; + break; + } + + LOCK_BIGLOCK(d); + rc = (d->vcpu[vcpuid] == NULL) ? boot_vcpu(d, vcpuid, ctxt) : -EEXIST; + UNLOCK_BIGLOCK(d); + + xfree(ctxt); + break; + + case VCPUOP_up: + if ( test_and_clear_bit(_VCPUF_down, &v->vcpu_flags) ) + vcpu_wake(v); + break; + + case VCPUOP_down: + if ( !test_and_set_bit(_VCPUF_down, &v->vcpu_flags) ) + vcpu_sleep_nosync(v); + break; + + case VCPUOP_is_up: + rc = !test_bit(_VCPUF_down, &v->vcpu_flags); + break; + } + return rc; } diff --git a/xen/common/schedule.c b/xen/common/schedule.c index 77e0055e8c..fe6de36988 100644 --- a/xen/common/schedule.c +++ b/xen/common/schedule.c @@ -270,69 +270,6 @@ static long do_yield(void) return 0; } -/* Mark target vcpu as non-runnable so it is not scheduled */ -static long do_vcpu_down(int vcpu) -{ - struct vcpu *target; - - if ( vcpu > MAX_VIRT_CPUS ) - return -EINVAL; - - target = current->domain->vcpu[vcpu]; - if ( target == NULL ) - return -ESRCH; - set_bit(_VCPUF_down, &target->vcpu_flags); - - return 0; -} - -/* Mark target vcpu as runnable and wake it */ -static long do_vcpu_up(int vcpu) -{ - struct vcpu *target; - - if (vcpu > MAX_VIRT_CPUS) - return -EINVAL; - - target = current->domain->vcpu[vcpu]; - if ( target == NULL ) - return -ESRCH; - clear_bit(_VCPUF_down, &target->vcpu_flags); - /* wake vcpu */ - vcpu_wake(target); - - return 0; -} - -static long do_vcpu_pickle(int vcpu, unsigned long arg) -{ - struct vcpu *v; - vcpu_guest_context_t *c; - int ret = 0; - - if (vcpu >= MAX_VIRT_CPUS) - return -EINVAL; - v = current->domain->vcpu[vcpu]; - if (!v) - return -ESRCH; - /* Don't pickle vcpus which are currently running */ - if (!test_bit(_VCPUF_down, &v->vcpu_flags)) { - return -EBUSY; - } - c = xmalloc(vcpu_guest_context_t); - if (!c) - return -ENOMEM; - arch_getdomaininfo_ctxt(v, c); - if (copy_to_user((vcpu_guest_context_t *)arg, - (const vcpu_guest_context_t *)c, sizeof(*c))) - ret = -EFAULT; - xfree(c); - return ret; -} - -/* - * Demultiplex scheduler-related hypercalls. - */ long do_sched_op(unsigned long op, unsigned long arg) { long ret = 0; @@ -359,21 +296,6 @@ long do_sched_op(unsigned long op, unsigned long arg) domain_shutdown((u8)(op >> SCHEDOP_reasonshift)); break; } - case SCHEDOP_vcpu_down: - { - ret = do_vcpu_down((int)(op >> SCHEDOP_vcpushift)); - break; - } - case SCHEDOP_vcpu_up: - { - ret = do_vcpu_up((int)(op >> SCHEDOP_vcpushift)); - break; - } - case SCHEDOP_vcpu_pickle: - { - ret = do_vcpu_pickle((int)(op >> SCHEDOP_vcpushift), arg); - break; - } default: ret = -ENOSYS; @@ -395,8 +317,8 @@ long do_set_timer_op(s_time_t timeout) return 0; } -/** sched_id - fetch ID of current scheduler */ -int sched_id() +/* sched_id - fetch ID of current scheduler */ +int sched_id(void) { return ops.sched_id; } diff --git a/xen/include/public/vcpu.h b/xen/include/public/vcpu.h new file mode 100644 index 0000000000..c034f3f59e --- /dev/null +++ b/xen/include/public/vcpu.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * vcpu.h + * + * VCPU creation and hotplug. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VCPU_H__ +#define __XEN_PUBLIC_VCPU_H__ + +/* + * Prototype for this hypercall is: + * int vcpu_op(int cmd, int vcpuid, void *extra_args) + * @cmd == VCPUOP_??? (VCPU operation). + * @vcpuid == VCPU to operate on. + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Create a new VCPU. This must be called before a VCPU can be referred to + * in any other hypercall (e.g., to bind event channels). The new VCPU + * will not run until it is brought up by VCPUOP_up. + * + * @extra_arg == pointer to vcpu_guest_context structure containing initial + * state for the new VCPU. + */ +#define VCPUOP_create 0 + +/* + * Bring up a newly-created or previously brought-down VCPU. This makes the + * VCPU runnable. + */ +#define VCPUOP_up 1 + +/* + * Bring down a VCPU (i.e., make it non-runnable). + * There are a few caveats that callers should observe: + * 1. This operation may return, and VCPU_is_up may return false, before the + * VCPU stops running (i.e., the command is asynchronous). It is a good + * idea to ensure that the VCPU has entered a non-critical loop before + * bringing it down. Alternatively, this operation is guaranteed + * synchronous if invoked by the VCPU itself. + * 2. After a VCPU is created, there is currently no way to drop all its + * references to domain memory. Even a VCPU that is down still holds + * memory references via its pagetable base pointer and GDT. It is good + * practise to move a VCPU onto an 'idle' or default page table, LDT and + * GDT before bringing it down. + */ +#define VCPUOP_down 2 + +/* Returns 1 if the given VCPU is up. */ +#define VCPUOP_is_up 3 + +#endif /* __XEN_PUBLIC_VCPU_H__ */ diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h index d891566a4e..4775299773 100644 --- a/xen/include/public/xen.h +++ b/xen/include/public/xen.h @@ -55,7 +55,7 @@ #define __HYPERVISOR_update_va_mapping_otherdomain 22 #define __HYPERVISOR_switch_vm86 23 /* x86/32 only */ #define __HYPERVISOR_switch_to_user 23 /* x86/64 only */ -#define __HYPERVISOR_boot_vcpu 24 +#define __HYPERVISOR_vcpu_op 24 #define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ #define __HYPERVISOR_mmuext_op 26 #define __HYPERVISOR_acm_op 27 @@ -201,12 +201,8 @@ struct mmuext_op { #define SCHEDOP_yield 0 /* Give up the CPU voluntarily. */ #define SCHEDOP_block 1 /* Block until an event is received. */ #define SCHEDOP_shutdown 2 /* Stop executing this domain. */ -#define SCHEDOP_vcpu_down 3 /* make target VCPU not-runnable. */ -#define SCHEDOP_vcpu_up 4 /* make target VCPU runnable. */ -#define SCHEDOP_vcpu_pickle 5 /* save a vcpu's context to memory. */ #define SCHEDOP_cmdmask 255 /* 8-bit command. */ #define SCHEDOP_reasonshift 8 /* 8-bit reason code. (SCHEDOP_shutdown) */ -#define SCHEDOP_vcpushift 8 /* 8-bit VCPU target. (SCHEDOP_up|down) */ /* * Reason codes for SCHEDOP_shutdown. These may be interpreted by control From 72835ed4aa93536d30ffc1ab54f9c9d10fc92a28 Mon Sep 17 00:00:00 2001 From: "jrb44@plym.cl.cam.ac.uk" Date: Mon, 3 Oct 2005 23:28:24 +0100 Subject: [PATCH 08/18] Possible fix for the SEDF scheduler assertion problem. Signed-off-by: Stephan Diestelhorst --- xen/common/sched_sedf.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index 1a498dcd30..b6b2f83c71 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -500,9 +500,15 @@ static inline void update_queues(s_time_t now, struct list_head* runq, curinf->vcpu->domain->domain_id, curinf->vcpu->vcpu_id); __del_from_queue(curinf->vcpu); - + /*move them to their next period*/ curinf->deadl_abs += curinf->period; + /*ensure that the start of the next period is in the future*/ + if (unlikely(PERIOD_BEGIN(curinf) < now)) { + curinf->deadl_abs += + (DIV_UP(now - PERIOD_BEGIN(curinf), + curinf->period)) * curinf->period; + } /*and put them back into the queue*/ __add_to_waitqueue_sort(curinf->vcpu); continue; @@ -645,7 +651,7 @@ static inline struct task_slice sedf_do_extra_schedule (s_time_t now, s_time_t end_xt, struct list_head *extraq[], int cpu) { struct task_slice ret; struct sedf_vcpu_info *runinf; - + ASSERT(end_xt > now); /* Enough time left to use for extratime? */ if (end_xt - now < EXTRA_QUANTUM) goto return_idle; From c9577eb490d71da71eaabdadb621ca33d1941f4b Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 00:21:49 +0100 Subject: [PATCH 09/18] Added method XendDomain.privilegedDomain and use that inside DevController to avoid hard-coding the domain ID 0. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendDomain.py | 5 +++++ tools/python/xen/xend/server/DevController.py | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index f21d08edbb..81302ad031 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -284,6 +284,11 @@ class XendDomain: pass return dominfo + + def privilegedDomain(self): + return self.domains[PRIV_DOMAIN] + + def domain_unpause(self, id): """Unpause domain execution. diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py index 0ede664ba0..c9fc72ebaa 100644 --- a/tools/python/xen/xend/server/DevController.py +++ b/tools/python/xen/xend/server/DevController.py @@ -189,8 +189,17 @@ class DevController: """ import xen.xend.XendDomain - backdom = xen.xend.XendDomain.instance().domain_lookup_by_name( - sxp.child_value(config, 'backend', '0')) + xd = xen.xend.XendDomain.instance() + + backdom_name = sxp.child_value(config, 'backend') + if backdom_name: + backdom = xd.domain_lookup_by_name(backdom_name) + else: + backdom = xd.privilegedDomain() + + if not backdom: + raise VmError("Cannot configure device for unknown backend %s" % + backdom_name) frontpath = self.frontendPath(devid) backpath = self.backendPath(backdom, devid) From 0b630dbfcac8efea89f2fd7a9a53bfdbf8d6f4ba Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 00:30:49 +0100 Subject: [PATCH 10/18] Remove the reason parameter from XendDomain.destroy -- it is useless. Accept the halt shutdown reason inside XendDomainInfo. This indicates that the domain should be shutdown just like a poweroff, except that the domain is not restarted, regardless of the on_poweroff configuration setting. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendDomain.py | 19 ++++--------------- tools/python/xen/xend/XendDomainInfo.py | 4 ++++ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 81302ad031..9e13e8735e 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -316,16 +316,10 @@ class XendDomain: raise XendError(str(ex)) - def domain_shutdown(self, domid, reason='poweroff'): + def domain_shutdown(self, domid, reason = 'poweroff'): """Shutdown domain (nicely). - - poweroff: restart according to exit code and restart mode - - reboot: restart on exit - - halt: do not restart - Returns immediately. - - @param id: domain id - @param reason: shutdown type: poweroff, reboot, suspend, halt + @param reason: shutdown reason: poweroff, reboot, suspend, halt """ self.callInfo(domid, XendDomainInfo.shutdown, reason) @@ -335,13 +329,8 @@ class XendDomain: return self.callInfo(domid, XendDomainInfo.send_sysrq, key) - def domain_destroy(self, domid, reason='halt'): - """Terminate domain immediately. - - halt: cancel any restart for the domain - - reboot schedule a restart for the domain - - @param domid: domain id - """ + def domain_destroy(self, domid): + """Terminate domain immediately.""" if domid == PRIV_DOMAIN: raise XendError("Cannot destroy privileged domain %i" % domid) diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 5a4d145a6f..7a479aa698 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -59,12 +59,16 @@ DOMAIN_SUSPEND = 2 """Shutdown code for crash.""" DOMAIN_CRASH = 3 +"""Shutdown code for halt.""" +DOMAIN_HALT = 4 + """Map shutdown codes to strings.""" shutdown_reasons = { DOMAIN_POWEROFF: "poweroff", DOMAIN_REBOOT : "reboot", DOMAIN_SUSPEND : "suspend", DOMAIN_CRASH : "crash", + DOMAIN_HALT : "halt" } restart_modes = [ From f020dc6069b1a6f78cab64d2ab70c474969adc2f Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 00:56:24 +0100 Subject: [PATCH 11/18] Remove the reason code from the destroy action -- the reason is only of value on a graceful shutdown. Clarify the semantic difference between poweroff and halt (we use halt to mean 'shutdown and stop' and poweroff to mean 'shutdown and do whatever the configured behaviour is'). Signed-off-by: Ewan Mellor --- tools/python/xen/sv/DomInfo.py | 4 ++-- tools/python/xen/xend/XendClient.py | 5 ++--- tools/python/xen/xend/server/SrvDomain.py | 5 +---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/tools/python/xen/sv/DomInfo.py b/tools/python/xen/sv/DomInfo.py index 12135cd4fb..89feca01a5 100755 --- a/tools/python/xen/sv/DomInfo.py +++ b/tools/python/xen/sv/DomInfo.py @@ -139,7 +139,7 @@ class DomActionTab( ActionTab ): if not dom is None and dom != '0': if DEBUG: print ">DomShutDown %s" % dom try: - server.xend_domain_shutdown( int( dom ), "halt" ) + server.xend_domain_shutdown( int( dom ), "poweroff" ) except: pass @@ -175,7 +175,7 @@ class DomActionTab( ActionTab ): if not dom is None and dom != '0': if DEBUG: print ">DomDestroy %s" % dom try: - server.xend_domain_destroy( int( dom ), "halt" ) + server.xend_domain_destroy(int( dom )) except: pass diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index e0b4babc49..0919ae6da6 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -232,10 +232,9 @@ class Xend: {'op' : 'sysrq', 'key' : key}) - def xend_domain_destroy(self, id, reason): + def xend_domain_destroy(self, id): return self.xendPost(self.domainurl(id), - {'op' : 'destroy', - 'reason' : reason }) + {'op' : 'destroy' }) def xend_domain_save(self, id, filename): return self.xendPost(self.domainurl(id), diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index 896f5b4003..6b9cb7b202 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -71,8 +71,7 @@ class SrvDomain(SrvDir): def op_destroy(self, op, req): fn = FormFn(self.xd.domain_destroy, - [['dom', 'int'], - ['reason', 'str']]) + [['dom', 'int']]) val = fn(req.args, {'dom': self.dom.domid}) req.setHeader("Location", "%s/.." % req.prePathURL()) return val @@ -232,8 +231,6 @@ class SrvDomain(SrvDir): req.write('
' % url) req.write('') - req.write('Halt') - req.write('Reboot') req.write('
') req.write('
' % url) From b379670db6c40fd450dd31761b86f462c1ee9028 Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 00:57:34 +0100 Subject: [PATCH 12/18] Remove the reason code from the destroy action -- the reason is only of value on a graceful shutdown. Clarify the semantic difference between poweroff and halt (we use halt to mean 'shutdown and stop' and poweroff to mean 'shutdown and do whatever the configured behaviour is'). Signed-off-by: Ewan Mellor --- tools/python/xen/xm/destroy.py | 16 +++------------- tools/python/xen/xm/shutdown.py | 20 ++++++++------------ 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/tools/python/xen/xm/destroy.py b/tools/python/xen/xm/destroy.py index 66977be871..345b04a1d7 100644 --- a/tools/python/xen/xm/destroy.py +++ b/tools/python/xen/xm/destroy.py @@ -21,19 +21,15 @@ from xen.xend.XendClient import server from xen.xm.opts import * -gopts = Opts(use="""[options] [DOM] +gopts = Opts(use="""[DOM] -Destroy a domain, optionally restarting it. +Destroy a domain. """) gopts.opt('help', short='h', fn=set_true, default=0, use="Print this help.") -gopts.opt('reboot', short='R', - fn=set_true, default=0, - use='Destroy and restart.') - def main(argv): opts = gopts args = opts.parse(argv) @@ -42,10 +38,4 @@ def main(argv): return if len(args) < 1: opts.err('Missing domain') dom = args[0] - if opts.vals.reboot: - mode = 'reboot' - else: - mode = 'halt' - server.xend_domain_destroy(dom, mode) - - + server.xend_domain_destroy(dom) diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py index e814f03f56..7f626838bc 100644 --- a/tools/python/xen/xm/shutdown.py +++ b/tools/python/xen/xm/shutdown.py @@ -73,19 +73,15 @@ def shutdown(opts, doms, mode, wait): opts.info("All domains terminated") def shutdown_mode(opts): - mode = 'poweroff' - if opts.vals.wait: - mode = 'halt' - if opts.vals.reboot: - opts.err("Can't specify wait and reboot") + if opts.vals.halt and opts.vals.reboot: + opts.err("Can't specify halt and reboot") + + if opts.vals.halt: + return 'halt' + elif opts.vals.reboot: + return 'reboot' else: - if opts.vals.halt and opts.vals.reboot: - opts.err("Can't specify halt and reboot") - if opts.vals.halt: - mode = 'halt' - elif opts.vals.reboot: - mode = 'reboot' - return mode + return 'poweroff' def main_all(opts, args): mode = shutdown_mode(opts) From 3c28a0f427f681d86445a001306546e831e839e4 Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 02:21:28 +0100 Subject: [PATCH 13/18] Move XendDomainInfo.{create,recreate,parseConfig} to the top level of the domain. This allows us to refer to them using an import statement, rather than a from .. import. This is a step towards getting rid of the xroot hack. All other references to XendDomainInfo methods need to be doubly qualified (once for the module, once for the class). Remove XendDomainDict, replacing it with a simple dictionary, folding the get_by_name method into XendDomain. Replace XendDomain.refresh_lock with a domains_lock which goes around any reference to XendDomain.domains or anything that will create or destroy a domain. This serialises most accesses through XendDomain, ensuring that we will not return stale state when racing against the watches fired in separate threads. This should have fixed bugs #270 and #234. Added a number of domain_get_xyz methods. Those named xyz_nr are to allow components internal to XendDomain (XendDomainInfo, XendCheckpoint) to call back into XendDomain without triggering further calls to XendDomain.refresh. The other methods simply make it clear which fallback behaviour is expected. Replace XendDomainInfo.domain_exists with XendDomainInfo.domain_by_name; the internals of this method needed to change to match those changes above, and it has been a misnomer for some time. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendDomain.py | 252 ++++++++------- tools/python/xen/xend/XendDomainInfo.py | 297 +++++++++--------- tools/python/xen/xend/server/DevController.py | 2 +- tools/python/xen/xend/server/SrvDomainDir.py | 2 +- 4 files changed, 290 insertions(+), 263 deletions(-) diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 9e13e8735e..f7da646e40 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -22,14 +22,16 @@ Needs to be persistent for one uptime. """ import os +import string import threading import xen.lowlevel.xc +import XendDomainInfo + from xen.xend import sxp from xen.xend import XendRoot from xen.xend import XendCheckpoint -from xen.xend.XendDomainInfo import XendDomainInfo from xen.xend import EventServer from xen.xend.XendError import XendError from xen.xend.XendLogging import log @@ -45,21 +47,10 @@ __all__ = [ "XendDomain" ] PRIV_DOMAIN = 0 -class XendDomainDict(dict): - def get_by_name(self, name): - try: - return filter(lambda d: d.getName() == name, self.values())[0] - except IndexError, err: - return None - class XendDomain: """Index of all domains. Singleton. """ - """Dict of domain info indexed by domain id.""" - domains = None - - ## public: def __init__(self): @@ -68,19 +59,30 @@ class XendDomain: # to import XendDomain from XendDomainInfo causes unbounded recursion. # So we stuff the XendDomain instance (self) into xroot's components. xroot.add_component("xen.xend.XendDomain", self) - self.domains = XendDomainDict() - self.refresh_lock = threading.Condition() + self.domains = {} + self.domains_lock = threading.Condition() self.watchReleaseDomain() - self.refresh() - self.dom0_setup() + + self.domains_lock.acquire() + try: + self.refresh() + self.dom0_setup() + finally: + self.domains_lock.release() + def list(self): """Get list of domain objects. @return: domain objects """ - self.refresh() - return self.domains.values() + self.domains_lock.acquire() + try: + self.refresh() + return self.domains.values() + finally: + self.domains_lock.release() + def list_sorted(self): """Get list of domain objects, sorted by name. @@ -103,7 +105,12 @@ class XendDomain: ## private: def onReleaseDomain(self): - self.refresh() + self.domains_lock.acquire() + try: + self.refresh() + finally: + self.domains_lock.release() + def watchReleaseDomain(self): from xen.xend.xenstore.xswatch import xswatch @@ -133,16 +140,8 @@ class XendDomain: return dominfo - def recreate_domain(self, xeninfo): - """Refresh initial domain info from db.""" - - dominfo = XendDomainInfo.recreate(xeninfo) - self._add_domain(dominfo) - return dominfo - - def dom0_setup(self): - dom0 = self.domain_lookup(PRIV_DOMAIN) + dom0 = self.domains[PRIV_DOMAIN] dom0.dom0_enforce_vcpus() @@ -179,50 +178,33 @@ class XendDomain: def refresh(self): """Refresh domain list from Xen. """ - self.refresh_lock.acquire() - try: - doms = self.xen_domains() - for d in self.domains.values(): - info = doms.get(d.getDomid()) - if info: - d.update(info) - else: - self._delete_domain(d.getDomid()) - for d in doms: - if d not in self.domains and not doms[d]['dying']: - try: - self.recreate_domain(doms[d]) - except: - if d == PRIV_DOMAIN: - log.exception( - "Failed to recreate information for domain " - "%d. Doing nothing except crossing my " - "fingers.", d) - else: - log.exception( - "Failed to recreate information for domain " - "%d. Destroying it in the hope of " - "recovery.", d) - try: - xc.domain_destroy(dom = d) - except: - log.exception('Destruction of %d failed.', d) - finally: - self.refresh_lock.release() - - - def update_domain(self, id): - """Update information for a single domain. - - @param id: domain id - """ - dominfo = self.xen_domain(id) - if dominfo: - d = self.domains.get(id) - if d: - d.update(dominfo) - else: - self._delete_domain(id) + doms = self.xen_domains() + for d in self.domains.values(): + info = doms.get(d.getDomid()) + if info: + d.update(info) + else: + self._delete_domain(d.getDomid()) + for d in doms: + if d not in self.domains and not doms[d]['dying']: + try: + dominfo = XendDomainInfo.recreate(doms[d]) + self._add_domain(dominfo) + except: + if d == PRIV_DOMAIN: + log.exception( + "Failed to recreate information for domain " + "%d. Doing nothing except crossing my " + "fingers.", d) + else: + log.exception( + "Failed to recreate information for domain " + "%d. Destroying it in the hope of " + "recovery.", d) + try: + xc.domain_destroy(dom = d) + except: + log.exception('Destruction of %d failed.', d) ## public: @@ -233,9 +215,14 @@ class XendDomain: @param config: configuration @return: domain """ - dominfo = XendDomainInfo.create(config) - self._add_domain(dominfo) - return dominfo + self.domains_lock.acquire() + try: + dominfo = XendDomainInfo.create(config) + self._add_domain(dominfo) + return dominfo + finally: + self.domains_lock.release() + def domain_configure(self, config): """Configure an existing domain. @@ -260,33 +247,72 @@ class XendDomain: raise XendError("can't read guest state file %s: %s" % (src, ex[1])) - def domain_get(self, id): - """Get up-to-date info about a domain. - - @param id: domain id - @return: domain object (or None) - """ - self.update_domain(id) - return self.domains.get(id) - def domain_lookup(self, id): - self.refresh() - return self.domains.get(id) + self.domains_lock.acquire() + try: + self.refresh() + return self.domains.get(id) + finally: + self.domains_lock.release() - def domain_lookup_by_name(self, name): - dominfo = self.domains.get_by_name(name) - if not dominfo: - try: - id = int(name) - dominfo = self.domain_lookup(id) - except ValueError: - pass - return dominfo + + def domain_lookup_nr(self, id): + self.domains_lock.acquire() + try: + return self.domains.get(id) + finally: + self.domains_lock.release() + + + def domain_lookup_by_name_or_id(self, name): + self.domains_lock.acquire() + try: + self.refresh() + return self.domain_lookup_by_name_or_id_nr(name) + finally: + self.domains_lock.release() + + + def domain_lookup_by_name_or_id_nr(self, name): + self.domains_lock.acquire() + try: + dominfo = self.domain_lookup_by_name_nr(name) + + if dominfo: + return dominfo + else: + try: + return self.domains.get(int(name)) + except ValueError: + return None + finally: + self.domains_lock.release() + + + def domain_lookup_by_name_nr(self, name): + self.domains_lock.acquire() + try: + matching = filter(lambda d: d.getName() == name, + self.domains.values()) + n = len(matching) + if n == 1: + return matching[0] + elif n > 1: + raise XendError( + 'Name uniqueness has been violated for name %s' % name) + else: + return None + finally: + self.domains_lock.release() def privilegedDomain(self): - return self.domains[PRIV_DOMAIN] + self.domains_lock.acquire() + try: + return self.domains[PRIV_DOMAIN] + finally: + self.domains_lock.release() def domain_unpause(self, id): @@ -321,12 +347,13 @@ class XendDomain: @param reason: shutdown reason: poweroff, reboot, suspend, halt """ - self.callInfo(domid, XendDomainInfo.shutdown, reason) + self.callInfo(domid, XendDomainInfo.XendDomainInfo.shutdown, reason) def domain_sysrq(self, domid, key): """Send a SysRq to the specified domain.""" - return self.callInfo(domid, XendDomainInfo.send_sysrq, key) + return self.callInfo(domid, XendDomainInfo.XendDomainInfo.send_sysrq, + key) def domain_destroy(self, domid): @@ -449,32 +476,39 @@ class XendDomain: def domain_device_create(self, domid, devconfig): """Create a new device for the specified domain. """ - return self.callInfo(domid, XendDomainInfo.device_create, devconfig) + return self.callInfo(domid, + XendDomainInfo.XendDomainInfo.device_create, + devconfig) def domain_device_configure(self, domid, devconfig, devid): """Configure an existing device in the specified domain. @return: updated device configuration """ - return self.callInfo(domid, XendDomainInfo.device_configure, + return self.callInfo(domid, + XendDomainInfo.XendDomainInfo.device_configure, devconfig, devid) def domain_device_refresh(self, domid, devtype, devid): """Refresh a device.""" - return self.callInfo(domid, XendDomainInfo.device_refresh, devtype, - devid) + return self.callInfo(domid, + XendDomainInfo.XendDomainInfo.device_refresh, + devtype, devid) def domain_device_destroy(self, domid, devtype, devid): """Destroy a device.""" - return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype, - devid) + return self.callInfo(domid, + XendDomainInfo.XendDomainInfo.destroyDevice, + devtype, devid) def domain_devtype_ls(self, domid, devtype): """Get list of device sxprs for the specified domain.""" - return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype) + return self.callInfo(domid, + XendDomainInfo.XendDomainInfo.getDeviceSxprs, + devtype) def domain_vif_limit_set(self, id, vif, credit, period): @@ -518,7 +552,8 @@ class XendDomain: @param mem: memory target (in MiB) """ - self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10) + self.callInfo(domid, XendDomainInfo.XendDomainInfo.setMemoryTarget, + mem << 10) def domain_vcpu_hotplug(self, domid, vcpu, state): @@ -527,12 +562,13 @@ class XendDomain: @param vcpu: target VCPU in domain @param state: which state VCPU will become """ - self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state) + self.callInfo(domid, XendDomainInfo.XendDomainInfo.vcpu_hotplug, vcpu, + state) def domain_dumpcore(self, domid): """Save a core dump for a crashed domain.""" - self.callInfo(domid, XendDomainInfo.dumpCore) + self.callInfo(domid, XendDomainInfo.XendDomainInfo.dumpCore) ## private: diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 7a479aa698..4547088856 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -120,6 +120,68 @@ ROUNDTRIPPING_CONFIG_ENTRIES = [ ] +def create(config): + """Create a VM from a configuration. + + @param config configuration + @raise: VmError for invalid configuration + """ + + log.debug("XendDomainInfo.create(%s)", config) + + vm = XendDomainInfo(getUuid(), parseConfig(config)) + vm.construct() + vm.refreshShutdown() + return vm + + +def recreate(xeninfo): + """Create the VM object for an existing domain. The domain must not + be dying, as the paths in the store should already have been removed, + and asking us to recreate them causes problems.""" + + log.debug("XendDomainInfo.recreate(%s)", xeninfo) + + assert not xeninfo['dying'] + + domid = xeninfo['dom'] + try: + dompath = GetDomainPath(domid) + if not dompath: + raise XendError( + 'No domain path in store for existing domain %d' % domid) + vmpath = xstransact.Read(dompath, "vm") + if not vmpath: + raise XendError( + 'No vm path in store for existing domain %d' % domid) + uuid = xstransact.Read(vmpath, "uuid") + if not uuid: + raise XendError( + 'No vm/uuid path in store for existing domain %d' % domid) + + log.info("Recreating domain %d, UUID %s.", domid, uuid) + + vm = XendDomainInfo(uuid, xeninfo, domid, True) + + except Exception, exn: + log.warn(str(exn)) + + uuid = getUuid() + + log.info("Recreating domain %d with new UUID %s.", domid, uuid) + + vm = XendDomainInfo(uuid, xeninfo, domid, True) + vm.storeVmDetails() + vm.storeDomDetails() + + vm.create_channel() + if domid == 0: + vm.initStoreConnection() + + vm.refreshShutdown(xeninfo) + return vm + + def restore(config): """Create a domain and a VM object to do a restore. @@ -134,7 +196,7 @@ def restore(config): except TypeError, exn: raise VmError('Invalid ssidref in config: %s' % exn) - vm = XendDomainInfo(uuid, XendDomainInfo.parseConfig(config), + vm = XendDomainInfo(uuid, parseConfig(config), xc.domain_create(ssidref = ssidref)) vm.storeVmDetails() vm.configure() @@ -143,10 +205,87 @@ def restore(config): return vm -def domain_exists(name): +def parseConfig(config): + def get_cfg(name, conv = None): + val = sxp.child_value(config, name) + + if conv and not val is None: + try: + return conv(val) + except TypeError, exn: + raise VmError( + 'Invalid setting %s = %s in configuration: %s' % + (name, val, str(exn))) + else: + return val + + + log.debug("parseConfig: config is %s" % str(config)) + + result = {} + + for e in ROUNDTRIPPING_CONFIG_ENTRIES: + result[e[0]] = get_cfg(e[0], e[1]) + + result['memory'] = get_cfg('memory', int) + result['mem_kb'] = get_cfg('mem_kb', int) + result['maxmem'] = get_cfg('maxmem', int) + result['maxmem_kb'] = get_cfg('maxmem_kb', int) + result['cpu'] = get_cfg('cpu', int) + result['image'] = get_cfg('image') + + try: + if result['image']: + result['vcpus'] = int(sxp.child_value(result['image'], + 'vcpus', 1)) + else: + result['vcpus'] = 1 + except TypeError, exn: + raise VmError( + 'Invalid configuration setting: vcpus = %s: %s' % + (sxp.child_value(result['image'], 'vcpus', 1), str(exn))) + + result['backend'] = [] + for c in sxp.children(config, 'backend'): + result['backend'].append(sxp.name(sxp.child0(c))) + + result['device'] = [] + for d in sxp.children(config, 'device'): + c = sxp.child0(d) + result['device'].append((sxp.name(c), c)) + + # Configuration option "restart" is deprecated. Parse it, but + # let on_xyz override it if they are present. + restart = get_cfg('restart') + if restart: + def handle_restart(event, val): + if not event in result: + result[event] = val + + if restart == "onreboot": + handle_restart('on_poweroff', 'destroy') + handle_restart('on_reboot', 'restart') + handle_restart('on_crash', 'destroy') + elif restart == "always": + handle_restart('on_poweroff', 'restart') + handle_restart('on_reboot', 'restart') + handle_restart('on_crash', 'restart') + elif restart == "never": + handle_restart('on_poweroff', 'destroy') + handle_restart('on_reboot', 'destroy') + handle_restart('on_crash', 'destroy') + else: + log.warn("Ignoring malformed and deprecated config option " + "restart = %s", restart) + + log.debug("parseConfig: result is %s" % str(result)) + return result + + +def domain_by_name(name): # See comment in XendDomain constructor. xd = get_component('xen.xend.XendDomain') - return xd.domain_lookup_by_name(name) + return xd.domain_lookup_by_name_nr(name) def shutdown_reason(code): """Get a shutdown reason from a code. @@ -181,152 +320,6 @@ class XendDomainInfo: MINIMUM_RESTART_TIME = 20 - def create(cls, config): - """Create a VM from a configuration. - - @param config configuration - @raise: VmError for invalid configuration - """ - - log.debug("XendDomainInfo.create(%s)", config) - - vm = cls(getUuid(), cls.parseConfig(config)) - vm.construct() - vm.refreshShutdown() - return vm - - create = classmethod(create) - - - def recreate(cls, xeninfo): - """Create the VM object for an existing domain. The domain must not - be dying, as the paths in the store should already have been removed, - and asking us to recreate them causes problems.""" - - log.debug("XendDomainInfo.recreate(%s)", xeninfo) - - assert not xeninfo['dying'] - - domid = xeninfo['dom'] - try: - dompath = GetDomainPath(domid) - if not dompath: - raise XendError( - 'No domain path in store for existing domain %d' % domid) - vmpath = xstransact.Read(dompath, "vm") - if not vmpath: - raise XendError( - 'No vm path in store for existing domain %d' % domid) - uuid = xstransact.Read(vmpath, "uuid") - if not uuid: - raise XendError( - 'No vm/uuid path in store for existing domain %d' % domid) - - log.info("Recreating domain %d, UUID %s.", domid, uuid) - - vm = cls(uuid, xeninfo, domid, True) - - except Exception, exn: - log.warn(str(exn)) - - uuid = getUuid() - - log.info("Recreating domain %d with new UUID %s.", domid, uuid) - - vm = cls(uuid, xeninfo, domid, True) - vm.storeVmDetails() - vm.storeDomDetails() - - vm.create_channel() - if domid == 0: - vm.initStoreConnection() - - vm.refreshShutdown(xeninfo) - return vm - - recreate = classmethod(recreate) - - - def parseConfig(cls, config): - def get_cfg(name, conv = None): - val = sxp.child_value(config, name) - - if conv and not val is None: - try: - return conv(val) - except TypeError, exn: - raise VmError( - 'Invalid setting %s = %s in configuration: %s' % - (name, val, str(exn))) - else: - return val - - - log.debug("parseConfig: config is %s" % str(config)) - - result = {} - - for e in ROUNDTRIPPING_CONFIG_ENTRIES: - result[e[0]] = get_cfg(e[0], e[1]) - - result['memory'] = get_cfg('memory', int) - result['mem_kb'] = get_cfg('mem_kb', int) - result['maxmem'] = get_cfg('maxmem', int) - result['maxmem_kb'] = get_cfg('maxmem_kb', int) - result['cpu'] = get_cfg('cpu', int) - result['image'] = get_cfg('image') - - try: - if result['image']: - result['vcpus'] = int(sxp.child_value(result['image'], - 'vcpus', 1)) - else: - result['vcpus'] = 1 - except TypeError, exn: - raise VmError( - 'Invalid configuration setting: vcpus = %s: %s' % - (sxp.child_value(result['image'], 'vcpus', 1), str(exn))) - - result['backend'] = [] - for c in sxp.children(config, 'backend'): - result['backend'].append(sxp.name(sxp.child0(c))) - - result['device'] = [] - for d in sxp.children(config, 'device'): - c = sxp.child0(d) - result['device'].append((sxp.name(c), c)) - - # Configuration option "restart" is deprecated. Parse it, but - # let on_xyz override it if they are present. - restart = get_cfg('restart') - if restart: - def handle_restart(event, val): - if not event in result: - result[event] = val - - if restart == "onreboot": - handle_restart('on_poweroff', 'destroy') - handle_restart('on_reboot', 'restart') - handle_restart('on_crash', 'destroy') - elif restart == "always": - handle_restart('on_poweroff', 'restart') - handle_restart('on_reboot', 'restart') - handle_restart('on_crash', 'restart') - elif restart == "never": - handle_restart('on_poweroff', 'destroy') - handle_restart('on_reboot', 'destroy') - handle_restart('on_crash', 'destroy') - else: - log.warn("Ignoring malformed and deprecated config option " - "restart = %s", restart) - - log.debug("parseConfig: result is %s" % str(result)) - return result - - - parseConfig = classmethod(parseConfig) - - def __init__(self, uuid, info, domid = None, augment = False): self.uuid = uuid @@ -957,10 +950,8 @@ class XendDomainInfo: if c in '_-.:/+': continue if c in string.ascii_letters: continue raise VmError('invalid vm name') - dominfo = domain_exists(name) - # When creating or rebooting, a domain with my name should not exist. - # When restoring, a domain with my name will exist, but it should have - # my domain id. + + dominfo = domain_by_name(name) if not dominfo: return if dominfo.is_terminated(): diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py index c9fc72ebaa..e93d4051cd 100644 --- a/tools/python/xen/xend/server/DevController.py +++ b/tools/python/xen/xend/server/DevController.py @@ -193,7 +193,7 @@ class DevController: backdom_name = sxp.child_value(config, 'backend') if backdom_name: - backdom = xd.domain_lookup_by_name(backdom_name) + backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name) else: backdom = xd.privilegedDomain() diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index 430eb592ea..0bbcd8e607 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -38,7 +38,7 @@ class SrvDomainDir(SrvDir): self.xd = XendDomain.instance() def domain(self, x): - dom = self.xd.domain_lookup_by_name(x) + dom = self.xd.domain_lookup_by_name_or_id(x) if not dom: raise XendError('No such domain ' + str(x)) return SrvDomain(dom) From 5f189b105084773c6754df712fcfcb9315f7d7aa Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 02:25:04 +0100 Subject: [PATCH 14/18] Move the import for XendError, and use a bare import, not a from...import. This should silence the error message when the program is terminated during startup. Signed-off-by: Ewan Mellor --- tools/python/xen/xm/main.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 648c5968a7..10b70f1470 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -30,9 +30,14 @@ from getopt import getopt import socket import warnings warnings.filterwarnings('ignore', category=FutureWarning) + +import xen.xend.XendError + from xen.xend import PrettyPrint from xen.xend import sxp from xen.xm.opts import * + + shorthelp = """Usage: xm [args] Control, list, and manipulate Xen guest instances @@ -687,7 +692,6 @@ def main(argv=sys.argv): args = argv[2:] if cmd: try: - from xen.xend.XendClient import XendError rc = cmd(args) if rc: usage() @@ -698,7 +702,7 @@ def main(argv=sys.argv): except IOError: err("Most commands need root access. Please try again as root") sys.exit(1) - except XendError, ex: + except xen.xend.XendError.XendError, ex: if len(args) > 0: handle_xend_error(argv[1], args[0], ex) else: @@ -719,6 +723,3 @@ def main(argv=sys.argv): if __name__ == "__main__": main() - - - From 3a62270915694e279bd3909e177c757836e9a5ce Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 02:28:41 +0100 Subject: [PATCH 15/18] Cope with missing configuration information inside the configuration method. This allows us to better recover if the store has been deleted. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/server/blkif.py | 6 ++++-- tools/python/xen/xend/server/netif.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index bfc559f74f..34bc4278c6 100755 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -69,8 +69,10 @@ class BlkifController(DevController): 'dev', 'type', 'params', 'read-only') - result.append(['dev', dev]) - result.append(['uname', typ + ":" + params]) + if dev: + result.append(['dev', dev]) + if typ and params: + result.append(['uname', typ + ":" + params]) if ro: result.append(['mode', 'r']) else: diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index d195363ba5..6041e5ed46 100755 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -80,11 +80,15 @@ class NetifController(DevController): 'script', 'ip', 'bridge', 'mac') - result.append(['script', - script.replace(xroot.network_script_dir + os.sep, "")]) + if script: + result.append(['script', + script.replace(xroot.network_script_dir + os.sep, + "")]) if ip: result.append(['ip', ip.split(" ")]) - result.append(['bridge', bridge]) - result.append(['mac', mac]) + if bridge: + result.append(['bridge', bridge]) + if mac: + result.append(['mac', mac]) return result From f8b1e5d5199b0fdcaee626aa47130f04b3bce28a Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Tue, 4 Oct 2005 09:20:48 +0100 Subject: [PATCH 16/18] Export some more symbols. --- linux-2.6-xen-sparse/arch/xen/i386/kernel/setup.c | 1 + linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c | 1 + linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c | 1 + 3 files changed, 3 insertions(+) diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/setup.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/setup.c index 7915b4f394..84b769a14b 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/setup.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/setup.c @@ -365,6 +365,7 @@ EXPORT_SYMBOL(phys_to_machine_mapping); /* Raw start-of-day parameters from the hypervisor. */ start_info_t *xen_start_info; +EXPORT_SYMBOL(xen_start_info); static void __init limit_regions(unsigned long long size) { diff --git a/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c b/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c index cef53c81cc..99f928e1a3 100644 --- a/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c +++ b/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c @@ -629,6 +629,7 @@ void notify_remote_via_irq(int irq) if (VALID_EVTCHN(evtchn)) notify_remote_via_evtchn(evtchn); } +EXPORT_SYMBOL(notify_remote_via_irq); void irq_resume(void) { diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c index 3a090e24c1..2df3585c08 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c @@ -86,6 +86,7 @@ DEFINE_PER_CPU(int, nr_multicall_ents); /* Raw start-of-day parameters from the hypervisor. */ start_info_t *xen_start_info; +EXPORT_SYMBOL(xen_start_info); #endif /* From 6b6edfe1d5609abbba73a53cb26909518f2a1580 Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 11:01:38 +0100 Subject: [PATCH 17/18] Use the domain ID to refer to backends, not the UUID. This means that a new backend is used on a localhost live migrate; migration was failing otherwise. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/server/DevController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py index e93d4051cd..7992adc143 100644 --- a/tools/python/xen/xend/server/DevController.py +++ b/tools/python/xen/xend/server/DevController.py @@ -230,7 +230,7 @@ class DevController: return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(), self.deviceClass, - self.vm.getUuid(), devid) + self.vm.getDomid(), devid) def frontendPath(self, devid): From 81c4fa512f7d16b65d40357a515ff2e20312a82c Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Tue, 4 Oct 2005 11:14:50 +0100 Subject: [PATCH 18/18] Fix localhost live migration. We were overvigorously wiping out the store entries when a domain closed and on save, which meant that the /vm entries disappeared when a localhost migration occurred. XendCheckpoint has had extra exception handling and logging added. It also now calls back through XendDomain.restore_,which has the correct locking semantics to prevent race conditions during migration. Added assertions to XendCheckpoint to ensure that the channels are set after XendDomainInfo.restore. I don't see why they would not be, and the old code meant that in the case that they were not, IntroduceDomain would not be called on the new domain, breaking Xend restart. relocate calls through XendDomain.domain_restore_fd rather than directly to XendCheckpoint to isolate XendCheckpoint from the rest of the world, and to allow XendDomain to pass itself into XendCheckpoint for a callback. Simplify the XendCheckpoint / XendDomainInfo interlock, giving only two states, OK and TERMINATED. If XendCheckpoint asks for a suspend, but sees a shutdown, it is valid for it to proceed -- either way the domain has stopped. Higher level tools may wish to disallow this, but at the very least, there is no sense in waiting for a suspend that will never come. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendCheckpoint.py | 216 ++++++++++++----------- tools/python/xen/xend/XendDomain.py | 51 ++++-- tools/python/xen/xend/XendDomainInfo.py | 71 +++++--- tools/python/xen/xend/server/relocate.py | 4 +- 4 files changed, 195 insertions(+), 147 deletions(-) diff --git a/tools/python/xen/xend/XendCheckpoint.py b/tools/python/xen/xend/XendCheckpoint.py index 9c38fab5b1..38e5370725 100644 --- a/tools/python/xen/xend/XendCheckpoint.py +++ b/tools/python/xen/xend/XendCheckpoint.py @@ -1,4 +1,5 @@ # Copyright (C) 2005 Christian Limpach +# Copyright (C) 2005 XenSource Ltd # This file is subject to the terms and conditions of the GNU General # Public License. See the file "COPYING" in the main directory of @@ -15,7 +16,6 @@ from xen.util.xpopen import xPopen3 import xen.lowlevel.xc -import XendDomainInfo from xen.xend.xenstore.xsutil import IntroduceDomain from XendError import XendError @@ -42,58 +42,75 @@ def read_exact(fd, size, errmsg): raise XendError(errmsg) return buf -def save(xd, fd, dominfo, live): +def save(fd, dominfo, live): write_exact(fd, SIGNATURE, "could not write guest state file: signature") config = sxp.to_string(dominfo.sxpr()) - write_exact(fd, pack("!i", len(config)), - "could not write guest state file: config len") - write_exact(fd, config, "could not write guest state file: config") - # xc_save takes three customization parameters: maxit, max_f, and flags - # the last controls whether or not save is 'live', while the first two - # further customize behaviour when 'live' save is enabled. Passing "0" - # simply uses the defaults compiled into libxenguest; see the comments - # and/or code in xc_linux_save() for more information. - cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd), - str(dominfo.getDomid()), "0", "0", str(int(live)) ] - log.info("[xc_save] " + join(cmd)) - child = xPopen3(cmd, True, -1, [fd, xc.handle()]) + domain_name = dominfo.getName() + + if live: + dominfo.setName('migrating-' + domain_name) + + try: + write_exact(fd, pack("!i", len(config)), + "could not write guest state file: config len") + write_exact(fd, config, "could not write guest state file: config") + + # xc_save takes three customization parameters: maxit, max_f, and + # flags the last controls whether or not save is 'live', while the + # first two further customize behaviour when 'live' save is + # enabled. Passing "0" simply uses the defaults compiled into + # libxenguest; see the comments and/or code in xc_linux_save() for + # more information. + cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd), + str(dominfo.getDomid()), "0", "0", str(int(live)) ] + log.info("[xc_save] " + join(cmd)) + child = xPopen3(cmd, True, -1, [fd, xc.handle()]) - lasterr = "" - p = select.poll() - p.register(child.fromchild.fileno()) - p.register(child.childerr.fileno()) - while True: - r = p.poll() - for (fd, event) in r: - if not event & select.POLLIN: - continue - if fd == child.childerr.fileno(): - l = child.childerr.readline() - log.error(l.rstrip()) - lasterr = l.rstrip() - if fd == child.fromchild.fileno(): - l = child.fromchild.readline() - if l.rstrip() == "suspend": - log.info("suspending %d" % dominfo.getDomid()) - xd.domain_shutdown(dominfo.getDomid(), reason='suspend') - dominfo.state_wait(XendDomainInfo.STATE_VM_SUSPENDED) - log.info("suspend %d done" % dominfo.getDomid()) - child.tochild.write("done\n") - child.tochild.flush() - if filter(lambda (fd, event): event & select.POLLHUP, r): - break + lasterr = "" + p = select.poll() + p.register(child.fromchild.fileno()) + p.register(child.childerr.fileno()) + while True: + r = p.poll() + for (fd, event) in r: + if not event & select.POLLIN: + continue + if fd == child.childerr.fileno(): + l = child.childerr.readline() + log.error(l.rstrip()) + lasterr = l.rstrip() + if fd == child.fromchild.fileno(): + l = child.fromchild.readline() + if l.rstrip() == "suspend": + log.info("suspending %d", dominfo.getDomid()) + dominfo.shutdown('suspend') + dominfo.waitForShutdown() + log.info("suspend %d done", dominfo.getDomid()) + child.tochild.write("done\n") + child.tochild.flush() + if filter(lambda (fd, event): event & select.POLLHUP, r): + break - if child.wait() >> 8 == 127: - lasterr = "popen %s failed" % PATH_XC_SAVE - if child.wait() != 0: - raise XendError("xc_save failed: %s" % lasterr) + if child.wait() >> 8 == 127: + lasterr = "popen %s failed" % PATH_XC_SAVE + if child.wait() != 0: + raise XendError("xc_save failed: %s" % lasterr) - dominfo.destroy() - return None + dominfo.destroyDomain() + except Exception, exn: + log.exception("Save failed on domain %s (%d).", domain_name, + dominfo.getDomid()) + try: + if live: + dominfo.setName(domain_name) + except: + log.exception("Failed to reset the migrating domain's name") + raise Exception, exn -def restore(fd): + +def restore(xd, fd): signature = read_exact(fd, len(SIGNATURE), "not a valid guest state file: signature read") if signature != SIGNATURE: @@ -112,71 +129,72 @@ def restore(fd): raise XendError("not a valid guest state file: config parse") vmconfig = p.get_val() - dominfo = XendDomainInfo.restore(vmconfig) - l = read_exact(fd, sizeof_unsigned_long, - "not a valid guest state file: pfn count read") - nr_pfns = unpack("=L", l)[0] # XXX endianess - if nr_pfns > 1024*1024: # XXX - raise XendError( - "not a valid guest state file: pfn count out of range") + dominfo = xd.restore_(vmconfig) + + assert dominfo.store_channel + assert dominfo.console_channel + + try: + l = read_exact(fd, sizeof_unsigned_long, + "not a valid guest state file: pfn count read") + nr_pfns = unpack("=L", l)[0] # XXX endianess + if nr_pfns > 1024*1024: # XXX + raise XendError( + "not a valid guest state file: pfn count out of range") - if dominfo.store_channel: store_evtchn = dominfo.store_channel.port2 - else: - store_evtchn = 0 - - if dominfo.console_channel: console_evtchn = dominfo.console_channel.port2 - else: - console_evtchn = 0 - cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd), - str(dominfo.getDomid()), str(nr_pfns), - str(store_evtchn), str(console_evtchn)] - log.info("[xc_restore] " + join(cmd)) - child = xPopen3(cmd, True, -1, [fd, xc.handle()]) - child.tochild.close() + cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd), + str(dominfo.getDomid()), str(nr_pfns), + str(store_evtchn), str(console_evtchn)] + log.info("[xc_restore] " + join(cmd)) + child = xPopen3(cmd, True, -1, [fd, xc.handle()]) + child.tochild.close() - lasterr = "" - p = select.poll() - p.register(child.fromchild.fileno()) - p.register(child.childerr.fileno()) - while True: - r = p.poll() - for (fd, event) in r: - if not event & select.POLLIN: - continue - if fd == child.childerr.fileno(): - l = child.childerr.readline() - log.error(l.rstrip()) - lasterr = l.rstrip() - if fd == child.fromchild.fileno(): - l = child.fromchild.readline() - while l: - log.info(l.rstrip()) - m = re.match(r"^(store-mfn) (\d+)\n$", l) - if m: - if dominfo.store_channel: + lasterr = "" + p = select.poll() + p.register(child.fromchild.fileno()) + p.register(child.childerr.fileno()) + while True: + r = p.poll() + for (fd, event) in r: + if not event & select.POLLIN: + continue + if fd == child.childerr.fileno(): + l = child.childerr.readline() + log.error(l.rstrip()) + lasterr = l.rstrip() + if fd == child.fromchild.fileno(): + l = child.fromchild.readline() + while l: + log.info(l.rstrip()) + m = re.match(r"^(store-mfn) (\d+)\n$", l) + if m: store_mfn = int(m.group(2)) dominfo.setStoreRef(store_mfn) IntroduceDomain(dominfo.getDomid(), store_mfn, dominfo.store_channel.port1, dominfo.getDomainPath()) - m = re.match(r"^(console-mfn) (\d+)\n$", l) - if m: - dominfo.setConsoleRef(int(m.group(2))) - try: - l = child.fromchild.readline() - except: - l = None - if filter(lambda (fd, event): event & select.POLLHUP, r): - break + m = re.match(r"^(console-mfn) (\d+)\n$", l) + if m: + dominfo.setConsoleRef(int(m.group(2))) + try: + l = child.fromchild.readline() + except: + l = None + if filter(lambda (fd, event): event & select.POLLHUP, r): + break - if child.wait() >> 8 == 127: - lasterr = "popen %s failed" % PATH_XC_RESTORE - if child.wait() != 0: - raise XendError("xc_restore failed: %s" % lasterr) + if child.wait() >> 8 == 127: + lasterr = "popen %s failed" % PATH_XC_RESTORE + if child.wait() != 0: + raise XendError("xc_restore failed: %s" % lasterr) - return dominfo + return dominfo + except: + log.exception("Restore failed") + dominfo.destroy() + raise diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index f7da646e40..69a9917b1f 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -239,14 +239,42 @@ class XendDomain: """ try: - fd = os.open(src, os.O_RDONLY) - dominfo = XendCheckpoint.restore(fd) - self._add_domain(dominfo) - return dominfo + return self.domain_restore_fd(os.open(src, os.O_RDONLY)) except OSError, ex: raise XendError("can't read guest state file %s: %s" % (src, ex[1])) + def domain_restore_fd(self, fd): + """Restore a domain from the given file descriptor.""" + + try: + XendCheckpoint.restore(self, fd) + except Exception, ex: + log.exception("Restore failed") + raise + + + def restore_(self, config): + """Create a domain as part of the restore process. This is called + only from {@link XendCheckpoint}. + + A restore request comes into XendDomain through {@link + #domain_restore} or {@link #domain_restore_fd}. That request is + forwarded immediately to XendCheckpoint which, when it is ready, will + call this method. It is necessary to come through here rather than go + directly to {@link XendDomainInfo.restore} because we need to + serialise the domain creation process, but cannot lock + domain_restore_fd as a whole, otherwise we will deadlock waiting for + the old domain to die. + """ + self.domains_lock.acquire() + try: + dominfo = XendDomainInfo.restore(config) + self._add_domain(dominfo) + return dominfo + finally: + self.domains_lock.release() + def domain_lookup(self, id): self.domains_lock.acquire() @@ -384,19 +412,8 @@ class XendDomain: port = xroot.get_xend_relocation_port() sock = relocate.setupRelocation(dst, port) - # temporarily rename domain for localhost migration - if dst == "localhost": - dominfo.setName("tmp-" + dominfo.getName()) - - try: - XendCheckpoint.save(self, sock.fileno(), dominfo, live) - except: - if dst == "localhost": - dominfo.setName( - string.replace(dominfo.getName(), "tmp-", "", 1)) - raise + XendCheckpoint.save(sock.fileno(), dominfo, live) - return None def domain_save(self, id, dst): """Start saving a domain to file. @@ -411,7 +428,7 @@ class XendDomain: fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) # For now we don't support 'live checkpoint' - return XendCheckpoint.save(self, fd, dominfo, False) + return XendCheckpoint.save(fd, dominfo, False) except OSError, ex: raise XendError("can't write guest state file %s: %s" % diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 4547088856..5eaa0b71fa 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -80,7 +80,6 @@ restart_modes = [ STATE_VM_OK = "ok" STATE_VM_TERMINATED = "terminated" -STATE_VM_SUSPENDED = "suspended" """Flag for a block device backend domain.""" SIF_BLK_BE_DOMAIN = (1<<4) @@ -624,21 +623,22 @@ class XendDomainInfo: # The domain no longer exists. This will occur if we have # scheduled a timer to check for shutdown timeouts and the # shutdown succeeded. It will also occur if someone - # destroys a domain beneath us. We clean up, just in - # case. + # destroys a domain beneath us. We clean up the domain, + # just in case, but we can't clean up the VM, because that + # VM may have migrated to a different domain on this + # machine. self.cleanupDomain() - self.cleanupVm() return if xeninfo['dying']: # Dying means that a domain has been destroyed, but has not - # yet been cleaned up by Xen. This could persist indefinitely - # if, for example, another domain has some of its pages - # mapped. We might like to diagnose this problem in the - # future, but for now all we do is make sure that it's not - # us holding the pages, by calling the cleanup methods. + # yet been cleaned up by Xen. This state could persist + # indefinitely if, for example, another domain has some of its + # pages mapped. We might like to diagnose this problem in the + # future, but for now all we do is make sure that it's not us + # holding the pages, by calling cleanupDomain. We can't + # clean up the VM, as above. self.cleanupDomain() - self.cleanupVm() return elif xeninfo['crashed']: @@ -651,10 +651,11 @@ class XendDomainInfo: restart_reason = 'crash' elif xeninfo['shutdown']: - if self.readDom('xend/shutdown'): + if self.readDom('xend/shutdown_completed'): # We've seen this shutdown already, but we are preserving # the domain for debugging. Leave it alone. - pass + return + else: reason = shutdown_reason(xeninfo['shutdown_reason']) @@ -664,7 +665,7 @@ class XendDomainInfo: self.clearRestart() if reason == 'suspend': - self.state_set(STATE_VM_SUSPENDED) + self.state_set(STATE_VM_TERMINATED) # Don't destroy the domain. XendCheckpoint will do # this once it has finished. elif reason in ['poweroff', 'reboot']: @@ -701,7 +702,7 @@ class XendDomainInfo: if not reason in shutdown_reasons.values(): raise XendError('invalid reason:' + reason) self.storeDom("control/shutdown", reason) - if not reason == 'suspend': + if reason != 'suspend': self.storeDom('xend/shutdown_start_time', time.time()) @@ -720,11 +721,6 @@ class XendDomainInfo: "rename-restart" : self.renameRestart}[self.info['on_' + reason]]() - def preserve(self): - log.info("Preserving dead domain %s (%d).", self.info['name'], - self.domid) - - def renameRestart(self): self.restart(True) @@ -814,9 +810,9 @@ class XendDomainInfo: ## public: - def state_wait(self, state): + def waitForShutdown(self): self.state_updated.acquire() - while self.state != state: + while self.state == STATE_VM_OK: self.state_updated.wait() self.state_updated.release() @@ -1054,7 +1050,6 @@ class XendDomainInfo: """Cleanup domain resources; release devices. Idempotent. Nothrow guarantee.""" - self.state_set(STATE_VM_TERMINATED) self.release_devices() self.closeStoreChannel() self.closeConsoleChannel() @@ -1087,8 +1082,14 @@ class XendDomainInfo: log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid)) - self.cleanupDomain() self.cleanupVm() + self.destroyDomain() + + + def destroyDomain(self): + log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid)) + + self.cleanupDomain() try: if self.domid is not None: @@ -1096,6 +1097,8 @@ class XendDomainInfo: except Exception: log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.") + self.state_set(STATE_VM_TERMINATED) + ## private: @@ -1243,14 +1246,18 @@ class XendDomainInfo: try: if rename: - self.preserveShutdownDomain() + self.preserveForRestart() else: - self.cleanupDomain() self.destroy() try: xd = get_component('xen.xend.XendDomain') - xd.domain_unpause(xd.domain_create(config).getDomid()) + new_dom = xd.domain_create(config) + try: + xc.domain_unpause(new_dom.getDomid()) + except: + new_dom.destroy() + raise except Exception, exn: log.exception('Failed to restart domain %d.', self.domid) finally: @@ -1260,7 +1267,7 @@ class XendDomainInfo: # self.exportToDB() - def preserveShutdownDomain(self): + def preserveForRestart(self): """Preserve a domain that has been shut down, by giving it a new UUID, cloning the VM details, and giving it a new name. This allows us to keep this domain for debugging, but restart a new one in its place @@ -1276,8 +1283,14 @@ class XendDomainInfo: self.uuid = new_uuid self.vmpath = VMROOT + new_uuid self.storeVmDetails() - self.storeDom('vm', self.vmpath) - self.storeDom('xend/shutdown', 'True') + self.preserve() + + + def preserve(self): + log.info("Preserving dead domain %s (%d).", self.info['name'], + self.domid) + self.storeDom('xend/shutdown_completed', 'True') + self.set_state(STATE_VM_TERMINATED) def generateShutdownName(self): diff --git a/tools/python/xen/xend/server/relocate.py b/tools/python/xen/xend/server/relocate.py index 610c459af2..42d3720409 100644 --- a/tools/python/xen/xend/server/relocate.py +++ b/tools/python/xen/xend/server/relocate.py @@ -28,7 +28,6 @@ from xen.xend import EventServer from xen.xend.XendError import XendError from xen.xend import XendRoot from xen.xend.XendLogging import log -from xen.xend import XendCheckpoint eserver = EventServer.instance() @@ -120,7 +119,8 @@ class RelocationProtocol(protocol.Protocol): if self.transport: self.send_reply(["ready", name]) self.transport.sock.setblocking(1) - XendCheckpoint.restore(self.transport.sock.fileno()) + xd = xroot.get_component("xen.xend.XendDomain") + xd.domain_restore_fd(self.transport.sock.fileno()) self.transport.sock.setblocking(0) else: log.error(name + ": no transport")