aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>2013-09-11 17:24:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-11 18:59:10 -0400
commitbe8a8d069e508d4408125e2b1471f549e7813d25 (patch)
treed69d792fdefbaebc9346f7c3bad36ee4383ef659 /fs
parent80c74f6a40284c5c5d49f3b3289172bbce0b30b8 (diff)
vmcore: introduce ELF header in new memory feature
For s390 we want to use /proc/vmcore for our SCSI stand-alone dump (zfcpdump). We have support where the first HSA_SIZE bytes are saved into a hypervisor owned memory area (HSA) before the kdump kernel is booted. When the kdump kernel starts, it is restricted to use only HSA_SIZE bytes. The advantages of this mechanism are: * No crashkernel memory has to be defined in the old kernel. * Early boot problems (before kexec_load has been done) can be dumped * Non-Linux systems can be dumped. We modify the s390 copy_oldmem_page() function to read from the HSA memory if memory below HSA_SIZE bytes is requested. Since we cannot use the kexec tool to load the kernel in this scenario, we have to build the ELF header in the 2nd (kdump/new) kernel. So with the following patch set we would like to introduce the new function that the ELF header for /proc/vmcore can be created in the 2nd kernel memory. The following steps are done during zfcpdump execution: 1. Production system crashes 2. User boots a SCSI disk that has been prepared with the zfcpdump tool 3. Hypervisor saves CPU state of boot CPU and HSA_SIZE bytes of memory into HSA 4. Boot loader loads kernel into low memory area 5. Kernel boots and uses only HSA_SIZE bytes of memory 6. Kernel saves registers of non-boot CPUs 7. Kernel does memory detection for dump memory map 8. Kernel creates ELF header for /proc/vmcore 9. /proc/vmcore uses this header for initialization 10. The zfcpdump user space reads /proc/vmcore to write dump to SCSI disk - copy_oldmem_page() copies from HSA for memory below HSA_SIZE - copy_oldmem_page() copies from real memory for memory above HSA_SIZE Currently for s390 we create the ELF core header in the 2nd kernel with a small trick. We relocate the addresses in the ELF header in a way that for the /proc/vmcore code it seems to be in the 1st kernel (old) memory and the read_from_oldmem() returns the correct data. This allows the /proc/vmcore code to use the ELF header in the 2nd kernel. This patch: Exchange the old mechanism with the new and much cleaner function call override feature that now offcially allows to create the ELF core header in the 2nd kernel. To use the new feature the following function have to be defined by the architecture backend code to read from new memory: * elfcorehdr_alloc: Allocate ELF header * elfcorehdr_free: Free the memory of the ELF header * elfcorehdr_read: Read from ELF header * elfcorehdr_read_notes: Read from ELF notes Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Acked-by: Vivek Goyal <vgoyal@redhat.com> Cc: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com> Cc: Jan Willeke <willeke@de.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/vmcore.c61
1 files changed, 51 insertions, 10 deletions
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index a1a16eb97c7b..02cb3ff108bc 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -123,6 +123,36 @@ static ssize_t read_from_oldmem(char *buf, size_t count,
123 return read; 123 return read;
124} 124}
125 125
126/*
127 * Architectures may override this function to allocate ELF header in 2nd kernel
128 */
129int __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
130{
131 return 0;
132}
133
134/*
135 * Architectures may override this function to free header
136 */
137void __weak elfcorehdr_free(unsigned long long addr)
138{}
139
140/*
141 * Architectures may override this function to read from ELF header
142 */
143ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos)
144{
145 return read_from_oldmem(buf, count, ppos, 0);
146}
147
148/*
149 * Architectures may override this function to read from notes sections
150 */
151ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)
152{
153 return read_from_oldmem(buf, count, ppos, 0);
154}
155
126/* Read from the ELF header and then the crash dump. On error, negative value is 156/* Read from the ELF header and then the crash dump. On error, negative value is
127 * returned otherwise number of bytes read are returned. 157 * returned otherwise number of bytes read are returned.
128 */ 158 */
@@ -357,7 +387,7 @@ static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr)
357 notes_section = kmalloc(max_sz, GFP_KERNEL); 387 notes_section = kmalloc(max_sz, GFP_KERNEL);
358 if (!notes_section) 388 if (!notes_section)
359 return -ENOMEM; 389 return -ENOMEM;
360 rc = read_from_oldmem(notes_section, max_sz, &offset, 0); 390 rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
361 if (rc < 0) { 391 if (rc < 0) {
362 kfree(notes_section); 392 kfree(notes_section);
363 return rc; 393 return rc;
@@ -444,7 +474,8 @@ static int __init copy_notes_elf64(const Elf64_Ehdr *ehdr_ptr, char *notes_buf)
444 if (phdr_ptr->p_type != PT_NOTE) 474 if (phdr_ptr->p_type != PT_NOTE)
445 continue; 475 continue;
446 offset = phdr_ptr->p_offset; 476 offset = phdr_ptr->p_offset;
447 rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); 477 rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
478 &offset);
448 if (rc < 0) 479 if (rc < 0)
449 return rc; 480 return rc;
450 notes_buf += phdr_ptr->p_memsz; 481 notes_buf += phdr_ptr->p_memsz;
@@ -536,7 +567,7 @@ static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr)
536 notes_section = kmalloc(max_sz, GFP_KERNEL); 567 notes_section = kmalloc(max_sz, GFP_KERNEL);
537 if (!notes_section) 568 if (!notes_section)
538 return -ENOMEM; 569 return -ENOMEM;
539 rc = read_from_oldmem(notes_section, max_sz, &offset, 0); 570 rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
540 if (rc < 0) { 571 if (rc < 0) {
541 kfree(notes_section); 572 kfree(notes_section);
542 return rc; 573 return rc;
@@ -623,7 +654,8 @@ static int __init copy_notes_elf32(const Elf32_Ehdr *ehdr_ptr, char *notes_buf)
623 if (phdr_ptr->p_type != PT_NOTE) 654 if (phdr_ptr->p_type != PT_NOTE)
624 continue; 655 continue;
625 offset = phdr_ptr->p_offset; 656 offset = phdr_ptr->p_offset;
626 rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); 657 rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
658 &offset);
627 if (rc < 0) 659 if (rc < 0)
628 return rc; 660 return rc;
629 notes_buf += phdr_ptr->p_memsz; 661 notes_buf += phdr_ptr->p_memsz;
@@ -810,7 +842,7 @@ static int __init parse_crash_elf64_headers(void)
810 addr = elfcorehdr_addr; 842 addr = elfcorehdr_addr;
811 843
812 /* Read Elf header */ 844 /* Read Elf header */
813 rc = read_from_oldmem((char*)&ehdr, sizeof(Elf64_Ehdr), &addr, 0); 845 rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf64_Ehdr), &addr);
814 if (rc < 0) 846 if (rc < 0)
815 return rc; 847 return rc;
816 848
@@ -837,7 +869,7 @@ static int __init parse_crash_elf64_headers(void)
837 if (!elfcorebuf) 869 if (!elfcorebuf)
838 return -ENOMEM; 870 return -ENOMEM;
839 addr = elfcorehdr_addr; 871 addr = elfcorehdr_addr;
840 rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); 872 rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
841 if (rc < 0) 873 if (rc < 0)
842 goto fail; 874 goto fail;
843 875
@@ -866,7 +898,7 @@ static int __init parse_crash_elf32_headers(void)
866 addr = elfcorehdr_addr; 898 addr = elfcorehdr_addr;
867 899
868 /* Read Elf header */ 900 /* Read Elf header */
869 rc = read_from_oldmem((char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0); 901 rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf32_Ehdr), &addr);
870 if (rc < 0) 902 if (rc < 0)
871 return rc; 903 return rc;
872 904
@@ -892,7 +924,7 @@ static int __init parse_crash_elf32_headers(void)
892 if (!elfcorebuf) 924 if (!elfcorebuf)
893 return -ENOMEM; 925 return -ENOMEM;
894 addr = elfcorehdr_addr; 926 addr = elfcorehdr_addr;
895 rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); 927 rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
896 if (rc < 0) 928 if (rc < 0)
897 goto fail; 929 goto fail;
898 930
@@ -919,7 +951,7 @@ static int __init parse_crash_elf_headers(void)
919 int rc=0; 951 int rc=0;
920 952
921 addr = elfcorehdr_addr; 953 addr = elfcorehdr_addr;
922 rc = read_from_oldmem(e_ident, EI_NIDENT, &addr, 0); 954 rc = elfcorehdr_read(e_ident, EI_NIDENT, &addr);
923 if (rc < 0) 955 if (rc < 0)
924 return rc; 956 return rc;
925 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { 957 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
@@ -952,7 +984,14 @@ static int __init vmcore_init(void)
952{ 984{
953 int rc = 0; 985 int rc = 0;
954 986
955 /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/ 987 /* Allow architectures to allocate ELF header in 2nd kernel */
988 rc = elfcorehdr_alloc(&elfcorehdr_addr, &elfcorehdr_size);
989 if (rc)
990 return rc;
991 /*
992 * If elfcorehdr= has been passed in cmdline or created in 2nd kernel,
993 * then capture the dump.
994 */
956 if (!(is_vmcore_usable())) 995 if (!(is_vmcore_usable()))
957 return rc; 996 return rc;
958 rc = parse_crash_elf_headers(); 997 rc = parse_crash_elf_headers();
@@ -960,6 +999,8 @@ static int __init vmcore_init(void)
960 pr_warn("Kdump: vmcore not initialized\n"); 999 pr_warn("Kdump: vmcore not initialized\n");
961 return rc; 1000 return rc;
962 } 1001 }
1002 elfcorehdr_free(elfcorehdr_addr);
1003 elfcorehdr_addr = ELFCORE_ADDR_ERR;
963 1004
964 proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations); 1005 proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations);
965 if (proc_vmcore) 1006 if (proc_vmcore)