> Archived-At: <http://permalink.gmane.org/gmane.linux.ports.ia64/14677> > > This patch is the kexec-kdump patch re-based to 2.6.18-rc4 kernel. > > Changes since last patch include: > > 1. re-base the patch to 2.6.18-rc4 > 2. per-cpu register dumping, so that user space tool can unwind kernel stack. > This should really done by INIT event. But unfortunately it seems that the > firmware on the machine I am using does not handle INIT event correctly... > So current I dump per-cpu registers by simple IPI. > > 3. fix on_line_cpu issue, > previous patch stop APs by smp_send_stop. AP will clear his bit in > cpu_online_map, then crash tools will see a cpu_online_map which only > contains 1 cpu in vmcore file. > > 4. reserve efi memmap area and ia64 boot param area for the first kernel. > > To test crash dump. > > Compile a kernel with CONFIG_KEXEC CONFIG_CRASH_DUMP > CONFIG_PROC_VMCORE enabled. > > using this kernel for both host kernel and crash dumping kernel. > > Boot this kernel with kernel parameter "crashkernel=XXX@YYY". > XXX should be a size big enough for crash kernel to run, YYY should > be a physical address which contains more than XXX memory and aligned to 64M. > you can check it in /proc/iomem. > Be careful of choosing the size and address to reserve. > The second kernel may hang silently if there is too few memory to use. > > After the first kernel boots, the "Crash kernel" region will show in /proc/iomem. > then load the same kernel with command. > > kexec -p vmlinux.gz --initrd=initrd --append="root=... maxcpus=1" > > trigger a crash with echo c > /proc/sysrq-trigger > > after the crash kernel boots, > copy /proc/vmcore to disk > > gdb vmlinux-of-first-kernel vmcore > then you can check the stack and variables of first kernel. > > kexec -l + kexec -e may not work. > That is because of a recent change in mpt-fusion driver > shutdown code. > There was some delay in mptscsih_remove path. > the code was removed in 2.6.17, that cause MPT fusion driver unable to reinitialize. > add a delay back to mptscsih_remove can solve this problem. > > > Signed-off-by: > Zou Nan hai <nanhai.zou@xxxxxxxxx> Incremental version of the patch posted by Zou Nan hai Cc: Zou Nan hai <nanhai.zou@xxxxxxxxx> Signed-off-by: Simon Horman <horms@xxxxxxxxxxxx> arch/ia64/kernel/crash.c | 77 ++++++------------ arch/ia64/kernel/efi.c | 2 arch/ia64/kernel/machine_kexec.c | 66 ++++++++++------ arch/ia64/kernel/relocate_kernel.S | 147 ++++++++++++++++++++++++++++++++++-- arch/ia64/kernel/setup.c | 9 ++ arch/ia64/kernel/smp.c | 18 ++++ include/asm-ia64/kexec.h | 6 + include/linux/irq.h | 1 kernel/irq/manage.c | 13 --- 9 files changed, 243 insertions(+), 96 deletions(-) diff --git a/arch/ia64/kernel/crash.c b/arch/ia64/kernel/crash.c index a1a192c..5b7514c 100644 --- a/arch/ia64/kernel/crash.c +++ b/arch/ia64/kernel/crash.c @@ -43,25 +43,23 @@ size_t copy_oldmem_page(unsigned long pf static void device_shootdown(void) { - struct pci_dev *dev; - irq_desc_t *desc; - u16 pci_command; + irq_desc_t *idesc; + int irq; + for (irq = 0; irq < NR_IRQS; irq++) { + idesc = irq_desc + irq; + if (!idesc || !idesc->action) + continue; + disable_irq_nosync(irq); + idesc->chip->end(irq); + idesc->chip->shutdown(irq); + } - list_for_each_entry(dev, &pci_devices, global_list) { - desc = irq_desc + dev->irq; - if (!desc->action) - continue; - pci_read_config_word(dev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_MASTER) { - pci_command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, pci_command); - } - disable_irq_nosync(dev->irq); - desc->chip->end(dev->irq); - } +#ifdef CONFIG_IA64_HP_ZX1 + ioc_iova_disable(); +#endif } -static Elf64_Word +static inline Elf64_Word *append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data, size_t data_len) { @@ -83,41 +81,25 @@ final_note(void *buf) memset(buf, 0, sizeof(struct elf_note)); } -static void -crash_save_this_cpu(void) +extern void ia64_dump_cpu_regs(void *); + +void +crash_save_this_cpu() { void *buf; struct elf_prstatus prstatus; int cpu = smp_processor_id(); + unsigned long cfm, sof, sol; elf_greg_t *dst = (elf_greg_t *)&prstatus.pr_reg; - memset(&prstatus, 0, sizeof(prstatus)); prstatus.pr_pid = current->pid; - dst[1] = ia64_getreg(_IA64_REG_GP); - dst[12] = ia64_getreg(_IA64_REG_SP); - dst[13] = ia64_getreg(_IA64_REG_TP); - - dst[42] = ia64_getreg(_IA64_REG_IP); - dst[45] = ia64_getreg(_IA64_REG_AR_RSC); - - ia64_setreg(_IA64_REG_AR_RSC, 0); - ia64_srlz_i(); - - dst[46] = ia64_getreg(_IA64_REG_AR_BSP); - dst[47] = ia64_getreg(_IA64_REG_AR_BSPSTORE); - - dst[48] = ia64_getreg(_IA64_REG_AR_RNAT); - dst[49] = ia64_getreg(_IA64_REG_AR_CCV); - dst[50] = ia64_getreg(_IA64_REG_AR_UNAT); - - dst[51] = ia64_getreg(_IA64_REG_AR_FPSR); - dst[52] = ia64_getreg(_IA64_REG_AR_PFS); - dst[53] = ia64_getreg(_IA64_REG_AR_LC); - - dst[54] = ia64_getreg(_IA64_REG_AR_LC); - dst[55] = ia64_getreg(_IA64_REG_AR_CSD); - dst[56] = ia64_getreg(_IA64_REG_AR_SSD); + ia64_dump_cpu_regs(dst); + cfm = dst[43]; + sol = (cfm >> 7) & 0x7f; + sof = cfm & 0x7f; + dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46], + sof - sol); buf = (u64 *) per_cpu_ptr(crash_notes, cpu); if (!buf) @@ -138,15 +120,10 @@ machine_crash_shutdown(struct pt_regs *p * In practice this means shooting down the other cpus in * an SMP system. */ - if (in_interrupt()) { + if (in_interrupt()) ia64_eoi(); - } - crash_save_this_cpu(); device_shootdown(); #ifdef CONFIG_SMP - smp_send_stop(); -#endif -#ifdef CONFIG_IA64_HP_ZX1 - ioc_iova_disable(); + kdump_smp_send_stop(); #endif } diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 52729a9..6935452 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -1124,6 +1124,8 @@ efi_initialize_iomem_resources(struct re insert_resource(res, code_resource); insert_resource(res, data_resource); #ifdef CONFIG_KEXEC + insert_resource(res, &efi_memmap_res); + insert_resource(res, &boot_param_res); if (crashk_res.end > crashk_res.start) insert_resource(res, &crashk_res); #endif diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S diff --git a/arch/ia64/kernel/machine_kexec.c b/arch/ia64/kernel/machine_kexec.c index 573a6b8..2073a3d 100644 --- a/arch/ia64/kernel/machine_kexec.c +++ b/arch/ia64/kernel/machine_kexec.c @@ -1,5 +1,5 @@ /* - * arch/ia64/kernel/machine_kexec.c + * arch/ia64/kernel/machine_kexec.c * * Handle transition of Linux booting another kernel * Copyright (C) 2005 Hewlett-Packard Development Comapny, L.P. @@ -27,6 +27,21 @@ #include <asm/meminit.h> typedef void (*relocate_new_kernel_t)(unsigned long, unsigned long, struct ia64_boot_param *, unsigned long); +static struct kimage *ia64_kimage; +struct resource efi_memmap_res = { + .name = "EFI Memory Map", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +struct resource boot_param_res = { + .name = "Boot parameter", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + /* * Do what every setup is needed on image and the @@ -41,10 +56,11 @@ int machine_kexec_prepare(struct kimage func = (unsigned long *)&relocate_new_kernel; /* Pre-load control code buffer to minimize work in kexec path */ control_code_buffer = page_address(image->control_code_page); - memcpy((void *)control_code_buffer, (const void *)func[0], + memcpy((void *)control_code_buffer, (const void *)func[0], relocate_new_kernel_size); - flush_icache_range((unsigned long)control_code_buffer, + flush_icache_range((unsigned long)control_code_buffer, (unsigned long)control_code_buffer + relocate_new_kernel_size); + ia64_kimage = image; return 0; } @@ -55,26 +71,6 @@ void machine_kexec_cleanup(struct kimage void machine_shutdown(void) { -#ifdef CONFIG_PCI - struct pci_dev *dev = NULL; - irq_desc_t *idesc; - cpumask_t mask = CPU_MASK_NONE; - /* Disable all PCI devices */ - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (!(dev->is_enabled)) - continue; - idesc = irq_desc + dev->irq; - if (!idesc) - continue; - cpu_set(0, mask); - disable_irq_nosync(dev->irq); - idesc->chip->end(dev->irq); - idesc->chip->set_affinity(dev->irq, mask); - idesc->action = NULL; - pci_disable_device(dev); - } -#endif - #ifdef CONFIG_HOTPLUG_CPU { int cpu; @@ -85,7 +81,25 @@ #ifdef CONFIG_HOTPLUG_CPU } } #elif defined(CONFIG_SMP) - smp_call_function(kexec_stop_this_cpu, (void *)image->start, 0, 0); + smp_call_function(kexec_stop_this_cpu, (void *)ia64_kimage->start, 0, 0); +#endif +#ifdef CONFIG_PCI + { + struct pci_dev *dev = NULL; + irq_desc_t *idesc; + /* Disable all PCI devices */ + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + if (!(dev->is_enabled)) + continue; + idesc = irq_desc + dev->irq; + if (!idesc||!idesc->chip) + continue; + disable_irq_nosync(dev->irq); + idesc->chip->end(dev->irq); + idesc->action = NULL; + pci_disable_device(dev); + } + } #endif @@ -96,7 +110,7 @@ #endif /* * Do not allocate memory (or fail in any way) in machine_kexec(). - * We are past the point of no return, committed to rebooting now. + * We are past the point of no return, committed to rebooting now. */ extern void *efi_get_pal_addr(void); void machine_kexec(struct kimage *image) @@ -104,6 +118,8 @@ void machine_kexec(struct kimage *image) relocate_new_kernel_t rnk; void *pal_addr = efi_get_pal_addr(); unsigned long code_addr = (unsigned long)page_address(image->control_code_page); + if (image->type == KEXEC_TYPE_CRASH) + crash_save_this_cpu(); /* Interrupts aren't acceptable while we reboot */ ia64_set_itv(1<<16); local_irq_disable(); diff --git a/arch/ia64/kernel/relocate_kernel.S b/arch/ia64/kernel/relocate_kernel.S index 09bd041..ffc1bde 100644 --- a/arch/ia64/kernel/relocate_kernel.S +++ b/arch/ia64/kernel/relocate_kernel.S @@ -1,5 +1,5 @@ /* - * arch/ia64/kernel/relocate_kernel.S + * arch/ia64/kernel/relocate_kernel.S * * Relocate kexec'able kernel and start it * @@ -304,7 +304,7 @@ check_irr0: cmp.eq p6,p0=0,r8 (p6) br.cond.sptk.few check_irr0 br.few call_start - + check_irr1: mov r8=cr.irr1 ;; @@ -313,7 +313,7 @@ check_irr1: cmp.eq p6,p0=0,r8 (p6) br.cond.sptk.few check_irr1 br.few call_start - + check_irr2: mov r8=cr.irr2 ;; @@ -322,7 +322,7 @@ check_irr2: cmp.eq p6,p0=0,r8 (p6) br.cond.sptk.few check_irr2 br.few call_start - + check_irr3: mov r8=cr.irr3 ;; @@ -331,7 +331,7 @@ check_irr3: cmp.eq p6,p0=0,r8 (p6) br.cond.sptk.few check_irr3 br.few call_start - + call_start: mov cr.eoi=r0 ;; @@ -351,3 +351,140 @@ END(kexec_fake_sal_rendez) relocate_new_kernel_size: data8 kexec_fake_sal_rendez_end - relocate_new_kernel +GLOBAL_ENTRY(ia64_dump_cpu_regs) + .prologue + alloc loc0=ar.pfs,1,2,0,0 + .body + mov ar.rsc=0 // put RSE in enforced lazy mode + add loc1=4*8, in0 // save r4 and r5 first + ;; +{ + flushrs // flush dirty regs to backing store + srlz.i +} + st8 [loc1]=r4, 8 + ;; + st8 [loc1]=r5, 8 + ;; + add loc1=32*8, in0 + mov r4=ar.rnat + ;; + st8 [in0]=r0, 8 // r0 + st8 [loc1]=r4, 8 // rnat + mov r5=pr + ;; + st8 [in0]=r1, 8 // r1 + st8 [loc1]=r5, 8 // pr + mov r4=b0 + ;; + st8 [in0]=r2, 8 // r2 + st8 [loc1]=r4, 8 // b0 + mov r5=b1; + ;; + st8 [in0]=r3, 24 // r3 + st8 [loc1]=r5, 8 // b1 + mov r4=b2 + ;; + st8 [in0]=r6, 8 // r6 + st8 [loc1]=r4, 8 // b2 + mov r5=b3 + ;; + st8 [in0]=r7, 8 // r7 + st8 [loc1]=r5, 8 // b3 + mov r4=b4 + ;; + st8 [in0]=r8, 8 // r8 + st8 [loc1]=r4, 8 // b4 + mov r5=b5 + ;; + st8 [in0]=r9, 8 // r9 + st8 [loc1]=r5, 8 // b5 + mov r4=b6 + ;; + st8 [in0]=r10, 8 // r10 + st8 [loc1]=r5, 8 // b6 + mov r5=b7 + ;; + st8 [in0]=r11, 8 // r11 + st8 [loc1]=r5, 8 // b7 + mov r4=b0 + ;; + st8 [in0]=r12, 8 // r12 + st8 [loc1]=r4, 8 // ip + mov r5=loc0 + ;; + st8 [in0]=r13, 8 // r13 + extr.u r5=r5, 0, 38 // ar.pfs.pfm + mov r4=r0 // user mask + ;; + st8 [in0]=r14, 8 // r14 + st8 [loc1]=r5, 8 // cfm + ;; + st8 [in0]=r15, 8 // r15 + st8 [loc1]=r4, 8 // user mask + mov r5=ar.rsc + ;; + st8 [in0]=r16, 8 // r16 + st8 [loc1]=r5, 8 // ar.rsc + mov r4=ar.bsp + ;; + st8 [in0]=r17, 8 // r17 + st8 [loc1]=r4, 8 // ar.bsp + mov r5=ar.bspstore + ;; + st8 [in0]=r18, 8 // r18 + st8 [loc1]=r5, 8 // ar.bspstore + mov r4=ar.rnat + ;; + st8 [in0]=r19, 8 // r19 + st8 [loc1]=r4, 8 // ar.rnat + mov r5=ar.ccv + ;; + st8 [in0]=r20, 8 // r20 + st8 [loc1]=r5, 8 // ar.ccv + mov r4=ar.unat + ;; + st8 [in0]=r21, 8 // r21 + st8 [loc1]=r4, 8 // ar.unat + mov r5 = ar.fpsr + ;; + st8 [in0]=r22, 8 // r22 + st8 [loc1]=r5, 8 // ar.fpsr + mov r4 = ar.unat + ;; + st8 [in0]=r23, 8 // r23 + st8 [loc1]=r4, 8 // unat + mov r5 = ar.fpsr + ;; + st8 [in0]=r24, 8 // r24 + st8 [loc1]=r5, 8 // fpsr + mov r4 = ar.pfs + ;; + st8 [in0]=r25, 8 // r25 + st8 [loc1]=r4, 8 // ar.pfs + mov r5 = ar.lc + ;; + st8 [in0]=r26, 8 // r26 + st8 [loc1]=r5, 8 // ar.lc + mov r4 = ar.ec + ;; + st8 [in0]=r27, 8 // r27 + st8 [loc1]=r4, 8 // ar.ec + mov r5 = ar.csd + ;; + st8 [in0]=r28, 8 // r28 + st8 [loc1]=r5, 8 // ar.csd + mov r4 = ar.ssd + ;; + st8 [in0]=r29, 8 // r29 + st8 [loc1]=r4, 8 // ar.ssd + ;; + st8 [in0]=r30, 8 // r30 + ;; + st8 [in0]=r31, 8 // r31 + mov ar.pfs=loc0 + ;; + br.ret.sptk.many rp +END(ia64_dump_cpu_regs) + + diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 74821be..14ee171 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -275,6 +275,15 @@ #ifdef CONFIG_KEXEC n++; } } + efi_memmap_res.start = ia64_boot_param->efi_memmap; + efi_memmap_res.end = efi_memmap_res.start + + ia64_boot_param->efi_memmap_size; + printk("efi_memmap start %lx %lx\n", + efi_memmap_res.start, + efi_memmap_res.end); + boot_param_res.start = __pa(ia64_boot_param); + boot_param_res.end = boot_param_res.start + + sizeof(*ia64_boot_param); } #endif diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index 6337278..dee9fd7 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -67,6 +67,7 @@ static volatile struct call_data_struct #define IPI_CALL_FUNC 0 #define IPI_CPU_STOP 1 +#define IPI_KDUMP_CPU_STOP 3 /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned; @@ -184,7 +185,15 @@ handle_IPI (int irq, void *dev_id, struc case IPI_CPU_STOP: stop_this_cpu(); break; - +#ifdef CONFIG_CRASH_DUMP + case IPI_KDUMP_CPU_STOP: + { + local_irq_disable(); + crash_save_this_cpu(); + cpu_halt(); + } + break; +#endif default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); break; @@ -400,6 +409,13 @@ smp_send_stop (void) { send_IPI_allbutself(IPI_CPU_STOP); } +#ifdef CONFIG_CRASH_DUMP +void +kdump_smp_send_stop() +{ + send_IPI_allbutself(IPI_KDUMP_CPU_STOP); +} +#endif int __init setup_profiling_timer (unsigned int multiplier) diff --git a/include/asm-ia64/kexec.h b/include/asm-ia64/kexec.h index d45c03f..a035136 100644 --- a/include/asm-ia64/kexec.h +++ b/include/asm-ia64/kexec.h @@ -23,7 +23,7 @@ #define POW2(n) (1ULL << (n)) DECLARE_PER_CPU(u64, ia64_mca_pal_base); const extern unsigned int relocate_new_kernel_size; volatile extern long kexec_rendez; -extern void relocate_new_kernel(unsigned long, unsigned long, +extern void relocate_new_kernel(unsigned long, unsigned long, struct ia64_boot_param *, unsigned long); extern void kexec_fake_sal_rendez(void *start, unsigned long wake_up, unsigned long pal_base); @@ -31,4 +31,8 @@ static inline void crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs) { } +extern struct resource efi_memmap_res; +extern struct resource boot_param_res; +extern void kdump_smp_send_stop(void); +extern void crash_save_this_cpu(void); #endif /* _ASM_IA64_KEXEC_H */ diff --git a/include/asm-ia64/machvec_hpzx1.h b/include/asm-ia64/machvec_hpzx1.h diff --git a/include/asm-ia64/meminit.h b/include/asm-ia64/meminit.h diff --git a/include/asm-ia64/smp.h b/include/asm-ia64/smp.h diff --git a/include/linux/irq.h b/include/linux/irq.h index 9b5349f..fbf6d90 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -182,7 +182,6 @@ typedef struct irq_desc irq_desc_t; #include <asm/hw_irq.h> extern int setup_irq(unsigned int irq, struct irqaction *new); -extern void terminate_irqs(void); #ifdef CONFIG_GENERIC_HARDIRQS diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index e058bad..a795d6f 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -475,16 +475,3 @@ #endif return retval; } EXPORT_SYMBOL(request_irq); - -/* - * Terminate any outstanding interrupts - */ -void terminate_irqs(void) -{ - irq_desc_t *desc = irq_desc; - int i; - - for (i = 0; i < NR_IRQS; i++, desc++) - if (desc->action && desc->chip->end) - desc->chip->end(i); -} -- -- Horms H: http://www.vergenet.net/~horms/ W: http://www.valinux.co.jp/en/ - To unsubscribe from this list: send the line "unsubscribe linux-ia64" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html
![]() |