From 4b97cecd54ce12d577107832e0c930bfcce671cd Mon Sep 17 00:00:00 2001 From: Xunlei Pang Date: Mon, 23 May 2016 16:24:10 -0700 Subject: [PATCH 1/6] kexec: introduce a protection mechanism for the crashkernel reserved memory For the cases that some kernel (module) path stamps the crash reserved memory(already mapped by the kernel) where has been loaded the second kernel data, the kdump kernel will probably fail to boot when panic happens (or even not happens) leaving the culprit at large, this is unacceptable. The patch introduces a mechanism for detecting such cases: 1) After each crash kexec loading, it simply marks the reserved memory regions readonly since we no longer access it after that. When someone stamps the region, the first kernel will panic and trigger the kdump. The weak arch_kexec_protect_crashkres() is introduced to do the actual protection. 2) To allow multiple loading, once 1) was done we also need to remark the reserved memory to readwrite each time a system call related to kdump is made. The weak arch_kexec_unprotect_crashkres() is introduced to do the actual protection. The architecture can make its specific implementation by overriding arch_kexec_protect_crashkres() and arch_kexec_unprotect_crashkres(). Signed-off-by: Xunlei Pang Cc: Eric Biederman Cc: Dave Young Cc: Minfei Huang Cc: Vivek Goyal Cc: Baoquan He Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 9b492cf58077a0254eb4b9574029ac6e79add9f9) Signed-off-by: Alex Shi --- include/linux/kexec.h | 2 ++ kernel/kexec.c | 9 ++++++++- kernel/kexec_core.c | 6 ++++++ kernel/kexec_file.c | 8 +++++++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/linux/kexec.h b/include/linux/kexec.h index d140b1e9faa7..638f9cb1f334 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -328,6 +328,8 @@ int __weak arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, unsigned int relsec); int __weak arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, unsigned int relsec); +void arch_kexec_protect_crashkres(void); +void arch_kexec_unprotect_crashkres(void); #else /* !CONFIG_KEXEC_CORE */ struct pt_regs; diff --git a/kernel/kexec.c b/kernel/kexec.c index d873b64fbddc..3680f9ca893f 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -167,8 +167,12 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, return -EBUSY; dest_image = &kexec_image; - if (flags & KEXEC_ON_CRASH) + if (flags & KEXEC_ON_CRASH) { dest_image = &kexec_crash_image; + if (kexec_crash_image) + arch_kexec_unprotect_crashkres(); + } + if (nr_segments > 0) { unsigned long i; @@ -211,6 +215,9 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, image = xchg(dest_image, image); out: + if ((flags & KEXEC_ON_CRASH) && kexec_crash_image) + arch_kexec_protect_crashkres(); + mutex_unlock(&kexec_mutex); kimage_free(image); diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index 11b64a63c0f8..f4d88400639c 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -1532,3 +1532,9 @@ void __weak crash_map_reserved_pages(void) void __weak crash_unmap_reserved_pages(void) {} + +void __weak arch_kexec_protect_crashkres(void) +{} + +void __weak arch_kexec_unprotect_crashkres(void) +{} diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 6030efd4a188..ef2cf637f840 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -327,8 +327,11 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, return -EBUSY; dest_image = &kexec_image; - if (flags & KEXEC_FILE_ON_CRASH) + if (flags & KEXEC_FILE_ON_CRASH) { dest_image = &kexec_crash_image; + if (kexec_crash_image) + arch_kexec_unprotect_crashkres(); + } if (flags & KEXEC_FILE_UNLOAD) goto exchange; @@ -377,6 +380,9 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, exchange: image = xchg(dest_image, image); out: + if ((flags & KEXEC_FILE_ON_CRASH) && kexec_crash_image) + arch_kexec_protect_crashkres(); + mutex_unlock(&kexec_mutex); kimage_free(image); return ret; From cb24e1e5cf7bc66b31fe20be82e5c35ad300fc2e Mon Sep 17 00:00:00 2001 From: Xunlei Pang Date: Mon, 23 May 2016 16:24:13 -0700 Subject: [PATCH 2/6] kexec: provide arch_kexec_protect(unprotect)_crashkres() Implement the protection method for the crash kernel memory reservation for the 64-bit x86 kdump. Signed-off-by: Xunlei Pang Cc: Eric Biederman Cc: Dave Young Cc: Minfei Huang Cc: Vivek Goyal Cc: Baoquan He Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 1e5768ae7500e7ce6eb73e1b263574d5c19606cf) Signed-off-by: Alex Shi --- arch/x86/kernel/machine_kexec_64.c | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index 819ab3f9c9c7..64979821bc2e 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -536,3 +536,48 @@ int arch_kexec_apply_relocations_add(const Elf64_Ehdr *ehdr, return -ENOEXEC; } #endif /* CONFIG_KEXEC_FILE */ + +static int +kexec_mark_range(unsigned long start, unsigned long end, bool protect) +{ + struct page *page; + unsigned int nr_pages; + + /* + * For physical range: [start, end]. We must skip the unassigned + * crashk resource with zero-valued "end" member. + */ + if (!end || start > end) + return 0; + + page = pfn_to_page(start >> PAGE_SHIFT); + nr_pages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; + if (protect) + return set_pages_ro(page, nr_pages); + else + return set_pages_rw(page, nr_pages); +} + +static void kexec_mark_crashkres(bool protect) +{ + unsigned long control; + + kexec_mark_range(crashk_low_res.start, crashk_low_res.end, protect); + + /* Don't touch the control code page used in crash_kexec().*/ + control = PFN_PHYS(page_to_pfn(kexec_crash_image->control_code_page)); + /* Control code page is located in the 2nd page. */ + kexec_mark_range(crashk_res.start, control + PAGE_SIZE - 1, protect); + control += KEXEC_CONTROL_PAGE_SIZE; + kexec_mark_range(control, crashk_res.end, protect); +} + +void arch_kexec_protect_crashkres(void) +{ + kexec_mark_crashkres(true); +} + +void arch_kexec_unprotect_crashkres(void) +{ + kexec_mark_crashkres(false); +} From b67152e919ade82f38a8d7b78861afece5c98caa Mon Sep 17 00:00:00 2001 From: Minfei Huang Date: Mon, 23 May 2016 16:24:16 -0700 Subject: [PATCH 3/6] kexec: make a pair of map/unmap reserved pages in error path For some arch, kexec shall map the reserved pages, then use them, when we try to start the kdump service. kexec may return directly, without unmaping the reserved pages, if it fails during starting service. To fix it, we make a pair of map/unmap reserved pages both in generic path and error path. This patch only affects s390. Other architecturess don't implement the interface of crash_unmap_reserved_pages and crash_map_reserved_pages. It isn't a urgent patch. Kernel can work well without any risk, although the reserved pages are not unmapped before returning in error path. Signed-off-by: Minfei Huang Cc: Vivek Goyal Cc: "Eric W. Biederman" Cc: Xunlei Pang Cc: Baoquan He Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 917a35605f09c0d16aeb2e92c7fbff562e19a116) Signed-off-by: Alex Shi --- kernel/kexec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/kexec.c b/kernel/kexec.c index 3680f9ca893f..b8d875f6c753 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -194,22 +194,25 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, segments, flags); } if (result) - goto out; + goto unmap_page; if (flags & KEXEC_PRESERVE_CONTEXT) image->preserve_context = 1; result = machine_kexec_prepare(image); if (result) - goto out; + goto unmap_page; for (i = 0; i < nr_segments; i++) { result = kimage_load_segment(image, &image->segment[i]); if (result) - goto out; + goto unmap_page; } kimage_terminate(image); +unmap_page: if (flags & KEXEC_ON_CRASH) crash_unmap_reserved_pages(); + if (result) + goto out; } /* Install the new kernel, and Uninstall the old */ image = xchg(dest_image, image); From d54d9726ca36c3ff047af57e6d4ec3fa54d88cad Mon Sep 17 00:00:00 2001 From: Minfei Huang Date: Mon, 23 May 2016 16:24:19 -0700 Subject: [PATCH 4/6] kexec: do a cleanup for function kexec_load There are a lof of work to be done in function kexec_load, not only for allocating structs and loading initram, but also for some misc. To make it more clear, wrap a new function do_kexec_load which is used to allocate structs and load initram. And the pre-work will be done in kexec_load. Signed-off-by: Minfei Huang Cc: Vivek Goyal Cc: "Eric W. Biederman" Cc: Xunlei Pang Cc: Baoquan He Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 0eea08678ebe9f7d8ef98fed974a5bf1a0dd2dd2) Signed-off-by: Alex Shi --- kernel/kexec.c | 125 +++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 56 deletions(-) diff --git a/kernel/kexec.c b/kernel/kexec.c index b8d875f6c753..34b770d7c05a 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -103,6 +103,74 @@ static int kimage_alloc_init(struct kimage **rimage, unsigned long entry, return ret; } +static int do_kexec_load(unsigned long entry, unsigned long nr_segments, + struct kexec_segment __user *segments, unsigned long flags) +{ + struct kimage **dest_image, *image; + unsigned long i; + int ret; + + if (flags & KEXEC_ON_CRASH) { + dest_image = &kexec_crash_image; + if (kexec_crash_image) + arch_kexec_unprotect_crashkres(); + } else { + dest_image = &kexec_image; + } + + if (nr_segments == 0) { + /* Uninstall image */ + kimage_free(xchg(dest_image, NULL)); + return 0; + } + if (flags & KEXEC_ON_CRASH) { + /* + * Loading another kernel to switch to if this one + * crashes. Free any current crash dump kernel before + * we corrupt it. + */ + kimage_free(xchg(&kexec_crash_image, NULL)); + } + + ret = kimage_alloc_init(&image, entry, nr_segments, segments, flags); + if (ret) + return ret; + + if (flags & KEXEC_ON_CRASH) + crash_map_reserved_pages(); + + if (flags & KEXEC_PRESERVE_CONTEXT) + image->preserve_context = 1; + + ret = machine_kexec_prepare(image); + if (ret) + goto out; + + for (i = 0; i < nr_segments; i++) { + ret = kimage_load_segment(image, &image->segment[i]); + if (ret) + goto out; + } + + kimage_terminate(image); + + /* Install the new kernel and uninstall the old */ + image = xchg(dest_image, image); + +out: + if ((flags & KEXEC_ON_CRASH) && kexec_crash_image) + arch_kexec_protect_crashkres(); + + /* + * Once the reserved memory is mapped, we should unmap this memory + * before returning + */ + if (flags & KEXEC_ON_CRASH) + crash_unmap_reserved_pages(); + kimage_free(image); + return ret; +} + /* * Exec Kernel system call: for obvious reasons only root may call it. * @@ -127,7 +195,6 @@ static int kimage_alloc_init(struct kimage **rimage, unsigned long entry, SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, struct kexec_segment __user *, segments, unsigned long, flags) { - struct kimage **dest_image, *image; int result; /* We only trust the superuser with rebooting the system. */ @@ -152,9 +219,6 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, if (nr_segments > KEXEC_SEGMENT_MAX) return -EINVAL; - image = NULL; - result = 0; - /* Because we write directly to the reserved memory * region when loading crash kernels we need a mutex here to * prevent multiple crash kernels from attempting to load @@ -166,63 +230,12 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, if (!mutex_trylock(&kexec_mutex)) return -EBUSY; - dest_image = &kexec_image; - if (flags & KEXEC_ON_CRASH) { - dest_image = &kexec_crash_image; - if (kexec_crash_image) - arch_kexec_unprotect_crashkres(); - } + result = do_kexec_load(entry, nr_segments, segments, flags); - if (nr_segments > 0) { - unsigned long i; - - if (flags & KEXEC_ON_CRASH) { - /* - * Loading another kernel to switch to if this one - * crashes. Free any current crash dump kernel before - * we corrupt it. - */ - - kimage_free(xchg(&kexec_crash_image, NULL)); - result = kimage_alloc_init(&image, entry, nr_segments, - segments, flags); - crash_map_reserved_pages(); - } else { - /* Loading another kernel to reboot into. */ - - result = kimage_alloc_init(&image, entry, nr_segments, - segments, flags); - } - if (result) - goto unmap_page; - - if (flags & KEXEC_PRESERVE_CONTEXT) - image->preserve_context = 1; - result = machine_kexec_prepare(image); - if (result) - goto unmap_page; - - for (i = 0; i < nr_segments; i++) { - result = kimage_load_segment(image, &image->segment[i]); - if (result) - goto unmap_page; - } - kimage_terminate(image); -unmap_page: - if (flags & KEXEC_ON_CRASH) - crash_unmap_reserved_pages(); - if (result) - goto out; - } - /* Install the new kernel, and Uninstall the old */ - image = xchg(dest_image, image); - -out: if ((flags & KEXEC_ON_CRASH) && kexec_crash_image) arch_kexec_protect_crashkres(); mutex_unlock(&kexec_mutex); - kimage_free(image); return result; } From 8702f7853b47db46427cb9f2c17d15739842bcf3 Mon Sep 17 00:00:00 2001 From: Xunlei Pang Date: Mon, 23 May 2016 16:24:22 -0700 Subject: [PATCH 5/6] s390/kexec: consolidate crash_map/unmap_reserved_pages() and arch_kexec_protect(unprotect)_crashkres() Commit 3f625002581b ("kexec: introduce a protection mechanism for the crashkernel reserved memory") is a similar mechanism for protecting the crash kernel reserved memory to previous crash_map/unmap_reserved_pages() implementation, the new one is more generic in name and cleaner in code (besides, some arch may not be allowed to unmap the pgtable). Therefore, this patch consolidates them, and uses the new arch_kexec_protect(unprotect)_crashkres() to replace former crash_map/unmap_reserved_pages() which by now has been only used by S390. The consolidation work needs the crash memory to be mapped initially, this is done in machine_kdump_pm_init() which is after reserve_crashkernel(). Once kdump kernel is loaded, the new arch_kexec_protect_crashkres() implemented for S390 will actually unmap the pgtable like before. Signed-off-by: Xunlei Pang Signed-off-by: Michael Holzheu Acked-by: Michael Holzheu Cc: Heiko Carstens Cc: "Eric W. Biederman" Cc: Minfei Huang Cc: Vivek Goyal Cc: Dave Young Cc: Baoquan He Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 7a0058ec78602da02b34fa2ae3afc523e90d1ab2) Signed-off-by: Alex Shi --- arch/s390/kernel/machine_kexec.c | 28 ++++++++++++++++++---------- include/linux/kexec.h | 2 -- kernel/kexec.c | 12 ------------ kernel/kexec_core.c | 11 ++--------- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index fb0901ec4306..f5bf24df5c6d 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -83,13 +83,13 @@ static int machine_kdump_pm_cb(struct notifier_block *nb, unsigned long action, switch (action) { case PM_SUSPEND_PREPARE: case PM_HIBERNATION_PREPARE: - if (crashk_res.start) - crash_map_reserved_pages(); + if (kexec_crash_image) + arch_kexec_unprotect_crashkres(); break; case PM_POST_SUSPEND: case PM_POST_HIBERNATION: - if (crashk_res.start) - crash_unmap_reserved_pages(); + if (kexec_crash_image) + arch_kexec_protect_crashkres(); break; default: return NOTIFY_DONE; @@ -100,6 +100,8 @@ static int machine_kdump_pm_cb(struct notifier_block *nb, unsigned long action, static int __init machine_kdump_pm_init(void) { pm_notifier(machine_kdump_pm_cb, 0); + /* Create initial mapping for crashkernel memory */ + arch_kexec_unprotect_crashkres(); return 0; } arch_initcall(machine_kdump_pm_init); @@ -134,6 +136,8 @@ static int kdump_csum_valid(struct kimage *image) #endif } +#ifdef CONFIG_CRASH_DUMP + /* * Map or unmap crashkernel memory */ @@ -155,21 +159,25 @@ static void crash_map_pages(int enable) } /* - * Map crashkernel memory + * Unmap crashkernel memory */ -void crash_map_reserved_pages(void) +void arch_kexec_protect_crashkres(void) { - crash_map_pages(1); + if (crashk_res.end) + crash_map_pages(0); } /* - * Unmap crashkernel memory + * Map crashkernel memory */ -void crash_unmap_reserved_pages(void) +void arch_kexec_unprotect_crashkres(void) { - crash_map_pages(0); + if (crashk_res.end) + crash_map_pages(1); } +#endif + /* * Give back memory to hypervisor before new kdump is loaded */ diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 638f9cb1f334..278ee4812300 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -241,8 +241,6 @@ extern void crash_kexec(struct pt_regs *); int kexec_should_crash(struct task_struct *); void crash_save_cpu(struct pt_regs *regs, int cpu); void crash_save_vmcoreinfo(void); -void crash_map_reserved_pages(void); -void crash_unmap_reserved_pages(void); void arch_crash_save_vmcoreinfo(void); __printf(1, 2) void vmcoreinfo_append_str(const char *fmt, ...); diff --git a/kernel/kexec.c b/kernel/kexec.c index 34b770d7c05a..e1acab9c8260 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -136,9 +136,6 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments, if (ret) return ret; - if (flags & KEXEC_ON_CRASH) - crash_map_reserved_pages(); - if (flags & KEXEC_PRESERVE_CONTEXT) image->preserve_context = 1; @@ -161,12 +158,6 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments, if ((flags & KEXEC_ON_CRASH) && kexec_crash_image) arch_kexec_protect_crashkres(); - /* - * Once the reserved memory is mapped, we should unmap this memory - * before returning - */ - if (flags & KEXEC_ON_CRASH) - crash_unmap_reserved_pages(); kimage_free(image); return ret; } @@ -232,9 +223,6 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, result = do_kexec_load(entry, nr_segments, segments, flags); - if ((flags & KEXEC_ON_CRASH) && kexec_crash_image) - arch_kexec_protect_crashkres(); - mutex_unlock(&kexec_mutex); return result; diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index f4d88400639c..aa88c32b54f0 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -926,7 +926,6 @@ int crash_shrink_memory(unsigned long new_size) start = roundup(start, KEXEC_CRASH_MEM_ALIGN); end = roundup(start + new_size, KEXEC_CRASH_MEM_ALIGN); - crash_map_reserved_pages(); crash_free_reserved_phys_range(end, crashk_res.end); if ((start == end) && (crashk_res.parent != NULL)) @@ -940,7 +939,6 @@ int crash_shrink_memory(unsigned long new_size) crashk_res.end = end - 1; insert_resource(&iomem_resource, ram_res); - crash_unmap_reserved_pages(); unlock: mutex_unlock(&kexec_mutex); @@ -1522,17 +1520,12 @@ int kernel_kexec(void) } /* - * Add and remove page tables for crashkernel memory + * Protection mechanism for crashkernel reserved memory after + * the kdump kernel is loaded. * * Provide an empty default implementation here -- architecture * code may override this */ -void __weak crash_map_reserved_pages(void) -{} - -void __weak crash_unmap_reserved_pages(void) -{} - void __weak arch_kexec_protect_crashkres(void) {} From c83ee19a0abdfc2a236dd5e280e9bc18c353abb6 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 23 May 2016 16:24:25 -0700 Subject: [PATCH 6/6] kdump: fix gdb macros work work with newer and 64-bit kernels Lots of little changes needed to be made to clean these up, remove the four byte pointer assumption and traverse the pid queue properly. Also consolidate the traceback code into a single function instead of having three copies of it. Link: http://lkml.kernel.org/r/1462926655-9390-1-git-send-email-minyard@acm.org Signed-off-by: Corey Minyard Acked-by: Baoquan He Cc: Vivek Goyal Cc: Haren Myneni Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit a0c20deae992527ba90df9a6f87d396b7cee3922) Signed-off-by: Alex Shi --- Documentation/kdump/gdbmacros.txt | 96 ++++++++++++++----------------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/Documentation/kdump/gdbmacros.txt b/Documentation/kdump/gdbmacros.txt index 9b9b454b048a..35f6a982a0d5 100644 --- a/Documentation/kdump/gdbmacros.txt +++ b/Documentation/kdump/gdbmacros.txt @@ -15,15 +15,16 @@ define bttnobp set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) - set $pid_off=((size_t)&((struct task_struct *)0)->pids[1].pid_list.next) + set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) + set var $stacksize = sizeof(union thread_union) while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm printf "===================\n" - set var $stackp = $next_t.thread.esp - set var $stack_top = ($stackp & ~4095) + 4096 + set var $stackp = $next_t.thread.sp + set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize while ($stackp < $stack_top) if (*($stackp) > _stext && *($stackp) < _sinittext) @@ -31,13 +32,13 @@ define bttnobp end set $stackp += 4 end - set $next_th=(((char *)$next_t->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm printf "===================\n" - set var $stackp = $next_t.thread.esp - set var $stack_top = ($stackp & ~4095) + 4096 + set var $stackp = $next_t.thread.sp + set var $stack_top = ($stackp & ~($stacksize - 1)) + stacksize while ($stackp < $stack_top) if (*($stackp) > _stext && *($stackp) < _sinittext) @@ -45,7 +46,7 @@ define bttnobp end set $stackp += 4 end - set $next_th=(((char *)$next_th->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end @@ -54,42 +55,44 @@ document bttnobp dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER end +define btthreadstack + set var $pid_task = $arg0 + + printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm + printf "task struct: " + print $pid_task + printf "===================\n" + set var $stackp = $pid_task.thread.sp + set var $stacksize = sizeof(union thread_union) + set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize + set var $stack_bot = ($stackp & ~($stacksize - 1)) + + set $stackp = *((unsigned long *) $stackp) + while (($stackp < $stack_top) && ($stackp > $stack_bot)) + set var $addr = *(((unsigned long *) $stackp) + 1) + info symbol $addr + set $stackp = *((unsigned long *) $stackp) + end +end +document btthreadstack + dump a thread stack using the given task structure pointer +end + + define btt set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) - set $pid_off=((size_t)&((struct task_struct *)0)->pids[1].pid_list.next) + set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t - printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm - printf "===================\n" - set var $stackp = $next_t.thread.esp - set var $stack_top = ($stackp & ~4095) + 4096 - set var $stack_bot = ($stackp & ~4095) + btthreadstack $next_t - set $stackp = *($stackp) - while (($stackp < $stack_top) && ($stackp > $stack_bot)) - set var $addr = *($stackp + 4) - info symbol $addr - set $stackp = *($stackp) - end - - set $next_th=(((char *)$next_t->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th - printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm - printf "===================\n" - set var $stackp = $next_t.thread.esp - set var $stack_top = ($stackp & ~4095) + 4096 - set var $stack_bot = ($stackp & ~4095) - - set $stackp = *($stackp) - while (($stackp < $stack_top) && ($stackp > $stack_bot)) - set var $addr = *($stackp + 4) - info symbol $addr - set $stackp = *($stackp) - end - set $next_th=(((char *)$next_th->pids[1].pid_list.next) - $pid_off) + btthreadstack $next_th + set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end @@ -101,7 +104,7 @@ end define btpid set var $pid = $arg0 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) - set $pid_off=((size_t)&((struct task_struct *)0)->pids[1].pid_list.next) + set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) set var $pid_task = 0 @@ -113,29 +116,18 @@ define btpid set $pid_task = $next_t end - set $next_th=(((char *)$next_t->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th if ($next_th.pid == $pid) set $pid_task = $next_th end - set $next_th=(((char *)$next_th->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end - printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm - printf "===================\n" - set var $stackp = $pid_task.thread.esp - set var $stack_top = ($stackp & ~4095) + 4096 - set var $stack_bot = ($stackp & ~4095) - - set $stackp = *($stackp) - while (($stackp < $stack_top) && ($stackp > $stack_bot)) - set var $addr = *($stackp + 4) - info symbol $addr - set $stackp = *($stackp) - end + btthreadstack $pid_task end document btpid backtrace of pid @@ -145,7 +137,7 @@ end define trapinfo set var $pid = $arg0 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) - set $pid_off=((size_t)&((struct task_struct *)0)->pids[1].pid_list.next) + set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) set var $pid_task = 0 @@ -157,13 +149,13 @@ define trapinfo set $pid_task = $next_t end - set $next_th=(((char *)$next_t->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th if ($next_th.pid == $pid) set $pid_task = $next_th end - set $next_th=(((char *)$next_th->pids[1].pid_list.next) - $pid_off) + set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end