Resolves: bz1097904
https://bugzilla.redhat.com/show_bug.cgi?id=1097904

This is back ported from upstream. It need be edited manually since
several irrelevant commits are not back ported.

commit 191a2fa0a8d2bbb64c98f9b1976fcb37ee5eae6b
Author: Michael Holzheu <holz...@linux.vnet.ibm.com>
Date:   Thu Jul 18 12:18:27 2013 +0200

    s390/kdump: Allow copy_oldmem_page() copy to virtual memory

    The kdump mmap patch series (git commit 83086978c63afd7c73e1c) changed the
    requirements for copy_oldmem_page(). Now this function is used for copying
    to virtual memory.

    So implement vmalloc support for the s390 version of copy_oldmem_page().

    Signed-off-by: Michael Holzheu <holz...@linux.vnet.ibm.com>
    Signed-off-by: Martin Schwidefsky <schwidef...@de.ibm.com>

Signed-off-by: Baoquan He <b...@redhat.com>
---
 arch/s390/kernel/crash_dump.c | 49 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 46 insertions(+), 3 deletions(-)

diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index b66edb9..0f70217 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -56,6 +56,47 @@ static int __init setup_elfcorehdr(char *arg)
 early_param("elfcorehdr", setup_elfcorehdr);
 
 /*
+ * Return physical address for virtual address
+ */
+static inline void *load_real_addr(void *addr)
+{
+       unsigned long real_addr;
+
+       asm volatile(
+                  "    lra     %0,0(%1)\n"
+                  "    jz      0f\n"
+                  "    la      %0,0\n"
+                  "0:"
+                  : "=a" (real_addr) : "a" (addr) : "cc");
+       return (void *)real_addr;
+}
+
+/*
+ * Copy up to one page to vmalloc or real memory
+ */
+static ssize_t copy_page_real(void *buf, void *src, size_t csize)
+{
+       size_t size;
+
+       if (is_vmalloc_addr(buf)) {
+               BUG_ON(csize >= PAGE_SIZE);
+               /* If buf is not page aligned, copy first part */
+               size = min(roundup(__pa(buf), PAGE_SIZE) - __pa(buf), csize);
+               if (size) {
+                       if (memcpy_real(load_real_addr(buf), src, size))
+                               return -EFAULT;
+                       buf += size;
+                       src += size;
+               }
+               /* Copy second part */
+               size = csize - size;
+               return (size) ? memcpy_real(load_real_addr(buf), src, size) : 0;
+       } else {
+               return memcpy_real(buf, src, csize);
+       }
+}
+
+/*
  * Copy one page from "oldmem"
  *
  * For the kdump reserved memory this functions performs a swap operation:
@@ -66,6 +107,7 @@ ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
                         size_t csize, unsigned long offset, int userbuf)
 {
        unsigned long src;
+       int rc;
 
        if (!csize)
                return 0;
@@ -77,10 +119,11 @@ ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
                 src < OLDMEM_BASE + OLDMEM_SIZE)
                src -= OLDMEM_BASE;
        if (userbuf)
-               copy_to_user_real((void __user *) buf, (void *) src, csize);
+               rc = copy_to_user_real((void __force __user *) buf,
+                                       (void *) src, csize);
        else
-               memcpy_real(buf, (void *) src, csize);
-       return csize;
+               rc = copy_page_real(buf, (void *) src, csize);
+       return (rc == 0) ? csize : rc;
 }
 
 /*
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to