s390/dump: cleanup CPU save area handling
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 29 Oct 2015 09:59:15 +0000 (10:59 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 27 Nov 2015 08:24:14 +0000 (09:24 +0100)
Introduce save_area_alloc(), save_area_boot_cpu(), save_area_add_regs()
and save_area_add_vxrs to deal with storing the CPU state in case of a
system dump. Remove struct save_area and save_area_ext, and create a new
struct save_area as a local definition to arch/s390/kernel/crash_dump.c.
Copy each individual field from the hardware status area to the save area,
storing the minimum of required data.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/ipl.h
arch/s390/include/asm/lowcore.h
arch/s390/kernel/crash_dump.c
arch/s390/kernel/smp.c
drivers/s390/char/zcore.c

index 1dc55db8cd819edba850ca1f820fd1e34a72f900..6fc44dca193ef8d90646fb0f67e1b128a3dc8a2a 100644 (file)
@@ -88,12 +88,11 @@ struct ipl_parameter_block {
  */
 extern u32 ipl_flags;
 
-struct dump_save_areas {
-       struct save_area_ext **areas;
-       int count;
-};
-
-extern struct dump_save_areas dump_save_areas;
+struct save_area;
+struct save_area * __init save_area_alloc(bool is_boot_cpu);
+struct save_area * __init save_area_boot_cpu(void);
+void __init save_area_add_regs(struct save_area *, void *regs);
+void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs);
 
 extern void do_reipl(void);
 extern void do_halt(void);
index afe1cfebf1a4a2f736220c85fe4f07c5467c3fb5..5dbbf199ba2ed736a98154503f28de1fd12dc581 100644 (file)
 #define LC_ORDER 1
 #define LC_PAGES 2
 
-struct save_area {
-       u64     fp_regs[16];
-       u64     gp_regs[16];
-       u8      psw[16];
-       u8      pad1[8];
-       u32     pref_reg;
-       u32     fp_ctrl_reg;
-       u8      pad2[4];
-       u32     tod_reg;
-       u64     timer;
-       u64     clk_cmp;
-       u8      pad3[8];
-       u32     acc_regs[16];
-       u64     ctrl_regs[16];
-} __packed;
-
-struct save_area_ext {
-       struct save_area        sa;
-       __vector128             vx_regs[32];
-};
-
 struct _lowcore {
        __u8    pad_0x0000[0x0014-0x0000];      /* 0x0000 */
        __u32   ipl_parmblock_ptr;              /* 0x0014 */
index 0d59c0705c4fbb3e71142c89fb8e30232154cecf..823ed6ab53c84de6e37cb61834dc9cc9fc0fc326 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/bootmem.h>
 #include <linux/elf.h>
+#include <asm/asm-offsets.h>
 #include <linux/memblock.h>
 #include <asm/os_info.h>
 #include <asm/elf.h>
@@ -32,7 +33,84 @@ static struct memblock_type oldmem_type = {
        .regions = &oldmem_region,
 };
 
-struct dump_save_areas dump_save_areas;
+struct save_area {
+       struct list_head list;
+       u64 psw[2];
+       u64 ctrs[16];
+       u64 gprs[16];
+       u32 acrs[16];
+       u64 fprs[16];
+       u32 fpc;
+       u32 prefix;
+       u64 todpreg;
+       u64 timer;
+       u64 todcmp;
+       u64 vxrs_low[16];
+       __vector128 vxrs_high[16];
+};
+
+static LIST_HEAD(dump_save_areas);
+
+/*
+ * Allocate a save area
+ */
+struct save_area * __init save_area_alloc(bool is_boot_cpu)
+{
+       struct save_area *sa;
+
+       sa = (void *) memblock_alloc(sizeof(*sa), 8);
+       if (!sa)
+               return NULL;
+       if (is_boot_cpu)
+               list_add(&sa->list, &dump_save_areas);
+       else
+               list_add_tail(&sa->list, &dump_save_areas);
+       return sa;
+}
+
+/*
+ * Return the address of the save area for the boot CPU
+ */
+struct save_area * __init save_area_boot_cpu(void)
+{
+       if (list_empty(&dump_save_areas))
+               return NULL;
+       return list_first_entry(&dump_save_areas, struct save_area, list);
+}
+
+/*
+ * Copy CPU registers into the save area
+ */
+void __init save_area_add_regs(struct save_area *sa, void *regs)
+{
+       struct _lowcore *lc;
+
+       lc = (struct _lowcore *)(regs - __LC_FPREGS_SAVE_AREA);
+       memcpy(&sa->psw, &lc->psw_save_area, sizeof(sa->psw));
+       memcpy(&sa->ctrs, &lc->cregs_save_area, sizeof(sa->ctrs));
+       memcpy(&sa->gprs, &lc->gpregs_save_area, sizeof(sa->gprs));
+       memcpy(&sa->acrs, &lc->access_regs_save_area, sizeof(sa->acrs));
+       memcpy(&sa->fprs, &lc->floating_pt_save_area, sizeof(sa->fprs));
+       memcpy(&sa->fpc, &lc->fpt_creg_save_area, sizeof(sa->fpc));
+       memcpy(&sa->prefix, &lc->prefixreg_save_area, sizeof(sa->prefix));
+       memcpy(&sa->todpreg, &lc->tod_progreg_save_area, sizeof(sa->todpreg));
+       memcpy(&sa->timer, &lc->cpu_timer_save_area, sizeof(sa->timer));
+       memcpy(&sa->todcmp, &lc->clock_comp_save_area, sizeof(sa->todcmp));
+}
+
+/*
+ * Copy vector registers into the save area
+ */
+void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs)
+{
+       int i;
+
+       /* Copy lower halves of vector registers 0-15 */
+       for (i = 0; i < 16; i++)
+               memcpy(&sa->vxrs_low[i], &vxrs[i].u[2], 8);
+       /* Copy vector registers 16-31 */
+       memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128));
+}
 
 /*
  * Return physical address for virtual address
@@ -232,8 +310,8 @@ static void *kzalloc_panic(int len)
 /*
  * Initialize ELF note
  */
-static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
-                    const char *name)
+static void *nt_init_name(void *buf, Elf64_Word type, void *desc, int d_len,
+                         const char *name)
 {
        Elf64_Nhdr *note;
        u64 len;
@@ -253,137 +331,42 @@ static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
        return PTR_ADD(buf, len);
 }
 
-/*
- * Initialize prstatus note
- */
-static void *nt_prstatus(void *ptr, struct save_area *sa)
+static inline void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len)
 {
-       struct elf_prstatus nt_prstatus;
-       static int cpu_nr = 1;
-
-       memset(&nt_prstatus, 0, sizeof(nt_prstatus));
-       memcpy(&nt_prstatus.pr_reg.gprs, sa->gp_regs, sizeof(sa->gp_regs));
-       memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
-       memcpy(&nt_prstatus.pr_reg.acrs, sa->acc_regs, sizeof(sa->acc_regs));
-       nt_prstatus.pr_pid = cpu_nr;
-       cpu_nr++;
-
-       return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
-                        "CORE");
+       return nt_init_name(buf, type, desc, d_len, KEXEC_CORE_NOTE_NAME);
 }
 
 /*
- * Initialize fpregset (floating point) note
+ * Fill ELF notes for one CPU with save area registers
  */
-static void *nt_fpregset(void *ptr, struct save_area *sa)
+static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
 {
+       struct elf_prstatus nt_prstatus;
        elf_fpregset_t nt_fpregset;
 
+       /* Prepare prstatus note */
+       memset(&nt_prstatus, 0, sizeof(nt_prstatus));
+       memcpy(&nt_prstatus.pr_reg.gprs, sa->gprs, sizeof(sa->gprs));
+       memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
+       memcpy(&nt_prstatus.pr_reg.acrs, sa->acrs, sizeof(sa->acrs));
+       nt_prstatus.pr_pid = cpu;
+       /* Prepare fpregset (floating point) note */
        memset(&nt_fpregset, 0, sizeof(nt_fpregset));
-       memcpy(&nt_fpregset.fpc, &sa->fp_ctrl_reg, sizeof(sa->fp_ctrl_reg));
-       memcpy(&nt_fpregset.fprs, &sa->fp_regs, sizeof(sa->fp_regs));
-
-       return nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset),
-                      "CORE");
-}
-
-/*
- * Initialize timer note
- */
-static void *nt_s390_timer(void *ptr, struct save_area *sa)
-{
-       return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
-                        KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize TOD clock comparator note
- */
-static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
-{
-       return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
-                      sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize TOD programmable register note
- */
-static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
-{
-       return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
-                      sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize control register note
- */
-static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
-{
-       return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
-                      sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize prefix register note
- */
-static void *nt_s390_prefix(void *ptr, struct save_area *sa)
-{
-       return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
-                        sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize vxrs high note (full 128 bit VX registers 16-31)
- */
-static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs)
-{
-       return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16],
-                      16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize vxrs low note (lower halves of VX registers 0-15)
- */
-static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs)
-{
-       Elf64_Nhdr *note;
-       u64 len;
-       int i;
-
-       note = (Elf64_Nhdr *)ptr;
-       note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1;
-       note->n_descsz = 16 * 8;
-       note->n_type = NT_S390_VXRS_LOW;
-       len = sizeof(Elf64_Nhdr);
-
-       memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz);
-       len = roundup(len + note->n_namesz, 4);
-
-       ptr += len;
-       /* Copy lower halves of SIMD registers 0-15 */
-       for (i = 0; i < 16; i++) {
-               memcpy(ptr, &vx_regs[i].u[2], 8);
-               ptr += 8;
-       }
-       return ptr;
-}
-
-/*
- * Fill ELF notes for one CPU with save area registers
- */
-static void *fill_cpu_elf_notes(void *ptr, struct save_area *sa,
-                               __vector128 *vx_regs)
-{
-       ptr = nt_prstatus(ptr, sa);
-       ptr = nt_fpregset(ptr, sa);
-       ptr = nt_s390_timer(ptr, sa);
-       ptr = nt_s390_tod_cmp(ptr, sa);
-       ptr = nt_s390_tod_preg(ptr, sa);
-       ptr = nt_s390_ctrs(ptr, sa);
-       ptr = nt_s390_prefix(ptr, sa);
-       if (MACHINE_HAS_VX && vx_regs) {
-               ptr = nt_s390_vx_low(ptr, vx_regs);
-               ptr = nt_s390_vx_high(ptr, vx_regs);
+       memcpy(&nt_fpregset.fpc, &sa->fpc, sizeof(sa->fpc));
+       memcpy(&nt_fpregset.fprs, &sa->fprs, sizeof(sa->fprs));
+       /* Create ELF notes for the CPU */
+       ptr = nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus));
+       ptr = nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset));
+       ptr = nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer));
+       ptr = nt_init(ptr, NT_S390_TODCMP, &sa->todcmp, sizeof(sa->todcmp));
+       ptr = nt_init(ptr, NT_S390_TODPREG, &sa->todpreg, sizeof(sa->todpreg));
+       ptr = nt_init(ptr, NT_S390_CTRS, &sa->ctrs, sizeof(sa->ctrs));
+       ptr = nt_init(ptr, NT_S390_PREFIX, &sa->prefix, sizeof(sa->prefix));
+       if (MACHINE_HAS_VX) {
+               ptr = nt_init(ptr, NT_S390_VXRS_HIGH,
+                             &sa->vxrs_high, sizeof(sa->vxrs_high));
+               ptr = nt_init(ptr, NT_S390_VXRS_LOW,
+                             &sa->vxrs_low, sizeof(sa->vxrs_low));
        }
        return ptr;
 }
@@ -398,8 +381,7 @@ static void *nt_prpsinfo(void *ptr)
        memset(&prpsinfo, 0, sizeof(prpsinfo));
        prpsinfo.pr_sname = 'R';
        strcpy(prpsinfo.pr_fname, "vmlinux");
-       return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
-                      KEXEC_CORE_NOTE_NAME);
+       return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo));
 }
 
 /*
@@ -441,7 +423,7 @@ static void *nt_vmcoreinfo(void *ptr)
                vmcoreinfo = get_vmcoreinfo_old(&size);
        if (!vmcoreinfo)
                return ptr;
-       return nt_init(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
+       return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
 }
 
 /*
@@ -470,13 +452,12 @@ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
  */
 static int get_cpu_cnt(void)
 {
-       int i, cpus = 0;
+       struct save_area *sa;
+       int cpus = 0;
 
-       for (i = 0; i < dump_save_areas.count; i++) {
-               if (dump_save_areas.areas[i]->sa.pref_reg == 0)
-                       continue;
-               cpus++;
-       }
+       list_for_each_entry(sa, &dump_save_areas, list)
+               if (sa->prefix != 0)
+                       cpus++;
        return cpus;
 }
 
@@ -521,18 +502,16 @@ static void loads_init(Elf64_Phdr *phdr, u64 loads_offset)
  */
 static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
 {
-       struct save_area_ext *sa_ext;
+       struct save_area *sa;
        void *ptr_start = ptr;
-       int i;
+       int cpu;
 
        ptr = nt_prpsinfo(ptr);
 
-       for (i = 0; i < dump_save_areas.count; i++) {
-               sa_ext = dump_save_areas.areas[i];
-               if (sa_ext->sa.pref_reg == 0)
-                       continue;
-               ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs);
-       }
+       cpu = 1;
+       list_for_each_entry(sa, &dump_save_areas, list)
+               if (sa->prefix != 0)
+                       ptr = fill_cpu_elf_notes(ptr, cpu++, sa);
        ptr = nt_vmcoreinfo(ptr);
        memset(phdr, 0, sizeof(*phdr));
        phdr->p_type = PT_NOTE;
index 2a69077d482cb0bed7a49e8b3c78ee7673878692..9da95d8dfc62cbb013fc00aca4bcc4ea4b336c60 100644 (file)
@@ -585,7 +585,7 @@ int smp_store_status(int cpu)
  *    This case does not exist for s390 anymore, setup_arch explicitly
  *    deactivates the elfcorehdr= kernel parameter
  */
-static __init void smp_save_cpu_vxrs(struct save_area_ext *sa_ext, u16 addr,
+static __init void smp_save_cpu_vxrs(struct save_area *sa, u16 addr,
                                     bool is_boot_cpu, unsigned long page)
 {
        __vector128 *vxrs = (__vector128 *) page;
@@ -594,10 +594,10 @@ static __init void smp_save_cpu_vxrs(struct save_area_ext *sa_ext, u16 addr,
                vxrs = boot_cpu_vector_save_area;
        else
                __pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, page);
-       memcpy(&sa_ext->vx_regs, vxrs, sizeof(sa_ext->vx_regs));
+       save_area_add_vxrs(sa, vxrs);
 }
 
-static __init void smp_save_cpu_regs(struct save_area_ext *sa_ext, u16 addr,
+static __init void smp_save_cpu_regs(struct save_area *sa, u16 addr,
                                     bool is_boot_cpu, unsigned long page)
 {
        void *regs = (void *) page;
@@ -606,13 +606,13 @@ static __init void smp_save_cpu_regs(struct save_area_ext *sa_ext, u16 addr,
                copy_oldmem_kernel(regs, (void *) __LC_FPREGS_SAVE_AREA, 512);
        else
                __pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, page);
-       memcpy(&sa_ext->sa, regs, sizeof(sa_ext->sa));
+       save_area_add_regs(sa, regs);
 }
 
 void __init smp_save_dump_cpus(void)
 {
-       int addr, cpu, boot_cpu_addr, max_cpu_addr;
-       struct save_area_ext *sa_ext;
+       int addr, boot_cpu_addr, max_cpu_addr;
+       struct save_area *sa;
        unsigned long page;
        bool is_boot_cpu;
 
@@ -625,29 +625,20 @@ void __init smp_save_dump_cpus(void)
                panic("could not allocate memory for save area\n");
        /* Set multi-threading state to the previous system. */
        pcpu_set_smt(sclp.mtid_prev);
-       max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
-       for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
-               if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) ==
-                   SIGP_CC_NOT_OPERATIONAL)
-                       continue;
-               cpu += 1;
-       }
-       dump_save_areas.areas = (void *)memblock_alloc(sizeof(void *) * cpu, 8);
-       dump_save_areas.count = cpu;
        boot_cpu_addr = stap();
-       for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
+       max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
+       for (addr = 0; addr <= max_cpu_addr; addr++) {
                if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) ==
                    SIGP_CC_NOT_OPERATIONAL)
                        continue;
-               sa_ext = (void *) memblock_alloc(sizeof(*sa_ext), 8);
-               dump_save_areas.areas[cpu] = sa_ext;
-               if (!sa_ext)
-                       panic("could not allocate memory for save area\n");
                is_boot_cpu = (addr == boot_cpu_addr);
-               cpu += 1;
+               /* Allocate save area */
+               sa = save_area_alloc(is_boot_cpu);
+               if (!sa)
+                       panic("could not allocate memory for save area\n");
                if (MACHINE_HAS_VX)
                        /* Get the vector registers */
-                       smp_save_cpu_vxrs(sa_ext, addr, is_boot_cpu, page);
+                       smp_save_cpu_vxrs(sa, addr, is_boot_cpu, page);
                /*
                 * For a zfcp dump OLDMEM_BASE == NULL and the registers
                 * of the boot CPU are stored in the HSA. To retrieve
@@ -656,7 +647,7 @@ void __init smp_save_dump_cpus(void)
                 */
                if (!is_boot_cpu || OLDMEM_BASE)
                        /* Get the CPU registers */
-                       smp_save_cpu_regs(sa_ext, addr, is_boot_cpu, page);
+                       smp_save_cpu_regs(sa, addr, is_boot_cpu, page);
        }
        memblock_free(page, PAGE_SIZE);
        diag308_reset();
index bed191a39c5bae0a0d93be7a961b53cbeaf6bfbe..5043ecfa1fbcfecf10cc8c2ae34dd1b0e591d6c1 100644 (file)
@@ -117,18 +117,17 @@ int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
 
 static int __init init_cpu_info(void)
 {
-       struct save_area_ext *sa_ext;
+       struct save_area *sa;
 
        /* get info for boot cpu from lowcore, stored in the HSA */
-
-       sa_ext = dump_save_areas.areas[0];
-       if (!sa_ext)
+       sa = save_area_boot_cpu();
+       if (!sa)
                return -ENOMEM;
-       if (memcpy_hsa_kernel(&sa_ext->sa, __LC_FPREGS_SAVE_AREA,
-                             sizeof(struct save_area)) < 0) {
+       if (memcpy_hsa_kernel(hsa_buf, __LC_FPREGS_SAVE_AREA, 512) < 0) {
                TRACE("could not copy from HSA\n");
                return -EIO;
        }
+       save_area_add_regs(sa, hsa_buf); /* vx registers are saved in smp.c */
        return 0;
 }