s390/sclp: Determine HSA size dynamically for zfcpdump
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>
Wed, 13 Nov 2013 09:38:27 +0000 (10:38 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 15 Nov 2013 13:08:40 +0000 (14:08 +0100)
Currently we have hardcoded the HSA size to 32 MiB. With this patch the
HSA size is determined dynamically via SCLP in early.c.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/sclp.h
arch/s390/include/asm/setup.h
arch/s390/kernel/crash_dump.c
arch/s390/kernel/early.c
arch/s390/kernel/setup.c
drivers/s390/char/Makefile
drivers/s390/char/sclp.h
drivers/s390/char/sclp_cmd.c
drivers/s390/char/sclp_early.c [new file with mode: 0644]
drivers/s390/char/zcore.c

index 7dc7f9c63b65f35f62de8566de198c97333acef6..593069201bb90099da7efd012763b5723f33f3c9 100644 (file)
@@ -57,5 +57,7 @@ bool sclp_has_vt220(void);
 int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
+void sclp_hsa_size_detect(void);
+unsigned long sclp_get_hsa_size(void);
 
 #endif /* _ASM_S390_SCLP_H */
index df802ee14af6f76591cebdc54030488b42c3d0cc..94cfbe442f124cc720f2436c7e3c34d485cf0139 100644 (file)
@@ -107,9 +107,6 @@ void create_mem_hole(struct mem_chunk mem_chunk[], unsigned long addr,
 #define MACHINE_HAS_RRBM       (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM)
 #endif /* CONFIG_64BIT */
 
-#define ZFCPDUMP_HSA_SIZE      (32UL<<20)
-#define ZFCPDUMP_HSA_SIZE_MAX  (64UL<<20)
-
 /*
  * Console mode. Override with conmode=
  */
index f45b2ab0cb81ae425ce2d72c2de9b6fa4fa241a3..d7658c4b2ed5817c75cd6a90c91b3a1b93738809 100644 (file)
@@ -95,7 +95,7 @@ static void *elfcorehdr_newmem;
 /*
  * Copy one page from zfcpdump "oldmem"
  *
- * For pages below ZFCPDUMP_HSA_SIZE memory from the HSA is copied. Otherwise
+ * For pages below HSA size memory from the HSA is copied. Otherwise
  * real memory copy is used.
  */
 static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
@@ -103,7 +103,7 @@ static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
 {
        int rc;
 
-       if (src < ZFCPDUMP_HSA_SIZE) {
+       if (src < sclp_get_hsa_size()) {
                rc = memcpy_hsa(buf, src, csize, userbuf);
        } else {
                if (userbuf)
@@ -188,18 +188,19 @@ static int remap_oldmem_pfn_range_kdump(struct vm_area_struct *vma,
 /*
  * Remap "oldmem" for zfcpdump
  *
- * We only map available memory above ZFCPDUMP_HSA_SIZE. Memory below
- * ZFCPDUMP_HSA_SIZE is read on demand using the copy_oldmem_page() function.
+ * We only map available memory above HSA size. Memory below HSA size
+ * is read on demand using the copy_oldmem_page() function.
  */
 static int remap_oldmem_pfn_range_zfcpdump(struct vm_area_struct *vma,
                                           unsigned long from,
                                           unsigned long pfn,
                                           unsigned long size, pgprot_t prot)
 {
+       unsigned long hsa_end = sclp_get_hsa_size();
        unsigned long size_hsa;
 
-       if (pfn < ZFCPDUMP_HSA_SIZE >> PAGE_SHIFT) {
-               size_hsa = min(size, ZFCPDUMP_HSA_SIZE - (pfn << PAGE_SHIFT));
+       if (pfn < hsa_end >> PAGE_SHIFT) {
+               size_hsa = min(size, hsa_end - (pfn << PAGE_SHIFT));
                if (size == size_hsa)
                        return 0;
                size -= size_hsa;
@@ -238,9 +239,9 @@ int copy_from_oldmem(void *dest, void *src, size_t count)
                                return rc;
                }
        } else {
-               if ((unsigned long) src < ZFCPDUMP_HSA_SIZE) {
-                       copied = min(count,
-                                    ZFCPDUMP_HSA_SIZE - (unsigned long) src);
+               unsigned long hsa_end = sclp_get_hsa_size();
+               if ((unsigned long) src < hsa_end) {
+                       copied = min(count, hsa_end - (unsigned long) src);
                        rc = memcpy_hsa(dest, (unsigned long) src, copied, 0);
                        if (rc)
                                return rc;
@@ -580,6 +581,9 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
        /* If elfcorehdr= has been passed via cmdline, we use that one */
        if (elfcorehdr_addr != ELFCORE_ADDR_MAX)
                return 0;
+       /* If we cannot get HSA size for zfcpdump return error */
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !sclp_get_hsa_size())
+               return -ENODEV;
        mem_chunk_cnt = get_mem_chunk_cnt();
 
        alloc_size = 0x1000 + get_cpu_cnt() * 0x300 +
index 96543ac400a7820ede40f22bf9636753920456b7..c0462f45cf1b2fd8e9e5a79851e8fe40969cf6d1 100644 (file)
@@ -484,6 +484,7 @@ void __init startup_init(void)
        detect_machine_facilities();
        setup_topology();
        sclp_facilities_detect();
+       sclp_hsa_size_detect();
 #ifdef CONFIG_DYNAMIC_FTRACE
        S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
 #endif
index ffe1c53264a708352622d0159f437f1e176d2a75..4444875266ee028bdcc4d43604a4dea591a1a47c 100644 (file)
@@ -471,8 +471,9 @@ static void __init setup_memory_end(void)
 
 
 #ifdef CONFIG_ZFCPDUMP
-       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !OLDMEM_BASE) {
-               memory_end = ZFCPDUMP_HSA_SIZE;
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
+           !OLDMEM_BASE && sclp_get_hsa_size()) {
+               memory_end = sclp_get_hsa_size();
                memory_end_set = 1;
        }
 #endif
@@ -586,7 +587,7 @@ static unsigned long __init find_crash_base(unsigned long crash_size,
                crash_base = (chunk->addr + chunk->size) - crash_size;
                if (crash_base < crash_size)
                        continue;
-               if (crash_base < ZFCPDUMP_HSA_SIZE_MAX)
+               if (crash_base < sclp_get_hsa_size())
                        continue;
                if (crash_base < (unsigned long) INITRD_START + INITRD_SIZE)
                        continue;
index 17821a026c9ca9b4ae6878bb57c04a56692850e4..b69ab17f13fabef4afc6c44a62534d549505db12 100644 (file)
@@ -3,7 +3,8 @@
 #
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
-        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o
+        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
+        sclp_early.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
index 40d1406289ed2533f6bd4bdffda0b0e19a80a79e..9cb8076f66c41cf35eaeb5932e98f4ca9a76b000 100644 (file)
@@ -172,6 +172,7 @@ int sclp_deactivate(void);
 int sclp_reactivate(void);
 int sclp_service_call(sclp_cmdw_t command, void *sccb);
 int sclp_sync_request(sclp_cmdw_t command, void *sccb);
+int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb);
 
 int sclp_sdias_init(void);
 void sclp_sdias_exit(void);
index 77df9cb00688feeda6cad1fbfd4623fc5a26134e..5bbe6db988b94162bb937cdbb207a56db184f00e 100644 (file)
@@ -59,7 +59,7 @@ static u8 sclp_fac84;
 static unsigned long long rzm;
 static unsigned long long rnmax;
 
-static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
+int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
 {
        int rc;
 
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
new file mode 100644 (file)
index 0000000..7751129
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SCLP early driver
+ *
+ * Copyright IBM Corp. 2013
+ */
+
+#define KMSG_COMPONENT "sclp_early"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <asm/sclp.h>
+#include <asm/ipl.h>
+#include "sclp_sdias.h"
+#include "sclp.h"
+
+static __initdata char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE);
+static unsigned long sclp_hsa_size;
+
+static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb)
+{
+       int rc;
+
+       do {
+               rc = sclp_cmd_sync_early(cmd, sccb);
+       } while (rc == -EBUSY);
+
+       if (rc)
+               return -EIO;
+       if (((struct sccb_header *) sccb)->response_code != 0x0020)
+               return -EIO;
+       return 0;
+}
+
+static void __init sccb_init_eq_size(struct sdias_sccb *sccb)
+{
+       memset(sccb, 0, sizeof(*sccb));
+
+       sccb->hdr.length = sizeof(*sccb);
+       sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb->evbuf.hdr.type = EVTYP_SDIAS;
+       sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
+       sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
+       sccb->evbuf.event_id = 4712;
+       sccb->evbuf.dbs = 1;
+}
+
+static int __init sclp_set_event_mask(unsigned long receive_mask,
+                                     unsigned long send_mask)
+{
+       struct init_sccb *sccb = (void *) &sccb_early;
+
+       memset(sccb, 0, sizeof(*sccb));
+       sccb->header.length = sizeof(*sccb);
+       sccb->mask_length = sizeof(sccb_mask_t);
+       sccb->receive_mask = receive_mask;
+       sccb->send_mask = send_mask;
+       return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
+}
+
+static long __init sclp_hsa_size_init(void)
+{
+       struct sdias_sccb *sccb = (void *) &sccb_early;
+
+       sccb_init_eq_size(sccb);
+       if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
+               return -EIO;
+       if (sccb->evbuf.blk_cnt != 0)
+               return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
+       return 0;
+}
+
+static long __init sclp_hsa_copy_wait(void)
+{
+       struct sccb_header *sccb = (void *) &sccb_early;
+
+       memset(sccb, 0, PAGE_SIZE);
+       sccb->length = PAGE_SIZE;
+       if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
+               return -EIO;
+       return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
+}
+
+unsigned long sclp_get_hsa_size(void)
+{
+       return sclp_hsa_size;
+}
+
+void __init sclp_hsa_size_detect(void)
+{
+       long size;
+
+       /* First try synchronous interface (LPAR) */
+       if (sclp_set_event_mask(0, 0x40000010))
+               return;
+       size = sclp_hsa_size_init();
+       if (size < 0)
+               return;
+       if (size != 0)
+               goto out;
+       /* Then try asynchronous interface (z/VM) */
+       if (sclp_set_event_mask(0x00000010, 0x40000010))
+               return;
+       size = sclp_hsa_size_init();
+       if (size < 0)
+               return;
+       size = sclp_hsa_copy_wait();
+       if (size < 0)
+               return;
+out:
+       sclp_set_event_mask(0, 0);
+       sclp_hsa_size = size;
+}
index ffb1fcf0bf5bed9928f53e1205f8b6b01635f6b6..3d8e4d63f51418c876f9918e0be77be628d2afb3 100644 (file)
@@ -328,9 +328,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
        mem_offs = 0;
 
        /* Copy from HSA data */
-       if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
-               size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
-                          - mem_start));
+       if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) {
+               size = min((count - hdr_count),
+                          (size_t) (sclp_get_hsa_size() - mem_start));
                rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
                if (rc)
                        goto fail;
@@ -490,7 +490,7 @@ static ssize_t zcore_hsa_read(struct file *filp, char __user *buf,
        static char str[18];
 
        if (hsa_available)
-               snprintf(str, sizeof(str), "%lx\n", ZFCPDUMP_HSA_SIZE);
+               snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size());
        else
                snprintf(str, sizeof(str), "0\n");
        return simple_read_from_buffer(buf, count, ppos, str, strlen(str));
@@ -584,17 +584,9 @@ static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)
 
 static int __init check_sdias(void)
 {
-       int rc, act_hsa_size;
-
-       rc = sclp_sdias_blk_count();
-       if (rc < 0) {
+       if (!sclp_get_hsa_size()) {
                TRACE("Could not determine HSA size\n");
-               return rc;
-       }
-       act_hsa_size = (rc - 1) * PAGE_SIZE;
-       if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
-               TRACE("HSA size too small: %i\n", act_hsa_size);
-               return -EINVAL;
+               return -ENODEV;
        }
        return 0;
 }
@@ -662,7 +654,7 @@ static int __init zcore_reipl_init(void)
        ipl_block = (void *) __get_free_page(GFP_KERNEL);
        if (!ipl_block)
                return -ENOMEM;
-       if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE)
+       if (ipib_info.ipib < sclp_get_hsa_size())
                rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
        else
                rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);