]> git.baikalelectronics.ru Git - kernel.git/commitdiff
s390/crash: support multi-segment iterators
authorAlexander Gordeev <agordeev@linux.ibm.com>
Tue, 19 Jul 2022 05:16:36 +0000 (07:16 +0200)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Wed, 20 Jul 2022 15:21:41 +0000 (17:21 +0200)
Make it possible to handle not only single-, but also multi-
segment iterators in copy_oldmem_iter() callback. Change the
semantics of called functions to match the iterator model -
instead of an error code the exact number of bytes copied is
returned.

The swap page used to copy data to user space is adopted for
kernel space too. That does not bring any performance impact.

Suggested-by: Matthew Wilcox <willy@infradead.org>
Fixes: cc02e6e21aa5 ("s390/crash: add missing iterator advance in copy_oldmem_page()")
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Tested-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Link: https://lore.kernel.org/r/5af6da3a0bffe48a90b0b7139ecf6a818b2d18e8.1658206891.git.agordeev@linux.ibm.com
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
arch/s390/include/asm/os_info.h
arch/s390/include/asm/sclp.h
arch/s390/kernel/crash_dump.c
drivers/s390/char/zcore.c

index 147a8d547ef9ef495e4e8d79a8641da0af26ec75..85248d8fee0cd11c3ea2ba2f99c75757a602c196 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef _ASM_S390_OS_INFO_H
 #define _ASM_S390_OS_INFO_H
 
+#include <linux/uio.h>
+
 #define OS_INFO_VERSION_MAJOR  1
 #define OS_INFO_VERSION_MINOR  1
 #define OS_INFO_MAGIC          0x4f53494e464f535aULL /* OSINFOSZ */
@@ -39,7 +41,20 @@ u32 os_info_csum(struct os_info *os_info);
 
 #ifdef CONFIG_CRASH_DUMP
 void *os_info_old_entry(int nr, unsigned long *size);
-int copy_oldmem_kernel(void *dst, unsigned long src, size_t count);
+size_t copy_oldmem_iter(struct iov_iter *iter, unsigned long src, size_t count);
+
+static inline int copy_oldmem_kernel(void *dst, unsigned long src, size_t count)
+{
+       struct iov_iter iter;
+       struct kvec kvec;
+
+       kvec.iov_base = dst;
+       kvec.iov_len = count;
+       iov_iter_kvec(&iter, WRITE, &kvec, 1, count);
+       if (copy_oldmem_iter(&iter, src, count) < count)
+               return -EFAULT;
+       return 0;
+}
 #else
 static inline void *os_info_old_entry(int nr, unsigned long *size)
 {
index 236b34b75ddb8daca11f013dfc2881ebc8b5c21b..24ee532fab844188801fc14ff3f2eaeaf78885ad 100644 (file)
@@ -17,6 +17,7 @@
 #define EXT_SCCB_READ_CPU      (3 * PAGE_SIZE)
 
 #ifndef __ASSEMBLY__
+#include <linux/uio.h>
 #include <asm/chpid.h>
 #include <asm/cpu.h>
 
@@ -142,8 +143,7 @@ int sclp_pci_deconfigure(u32 fid);
 int sclp_ap_configure(u32 apid);
 int sclp_ap_deconfigure(u32 apid);
 int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid);
-int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
-int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
+size_t memcpy_hsa_iter(struct iov_iter *iter, unsigned long src, size_t count);
 void sclp_ocf_cpc_name_copy(char *dst);
 
 static inline int sclp_get_core_info(struct sclp_core_info *info, int early)
index 1662f1d81abe27f7870de75ceb15d228c9b9b0bd..bad8f47fc5d696a7cba7de8651bfa8d2f3290136 100644 (file)
@@ -116,102 +116,35 @@ void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs)
        memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128));
 }
 
-/*
- * 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 memory of the old, dumped system to a kernel space virtual address
- */
-int copy_oldmem_kernel(void *dst, unsigned long src, size_t count)
-{
-       unsigned long len;
-       void *ra;
-       int rc;
-
-       while (count) {
-               if (!oldmem_data.start && src < sclp.hsa_size) {
-                       /* Copy from zfcp/nvme dump HSA area */
-                       len = min(count, sclp.hsa_size - src);
-                       rc = memcpy_hsa_kernel(dst, src, len);
-                       if (rc)
-                               return rc;
-               } else {
-                       /* Check for swapped kdump oldmem areas */
-                       if (oldmem_data.start && src - oldmem_data.start < oldmem_data.size) {
-                               src -= oldmem_data.start;
-                               len = min(count, oldmem_data.size - src);
-                       } else if (oldmem_data.start && src < oldmem_data.size) {
-                               len = min(count, oldmem_data.size - src);
-                               src += oldmem_data.start;
-                       } else {
-                               len = count;
-                       }
-                       if (is_vmalloc_or_module_addr(dst)) {
-                               ra = load_real_addr(dst);
-                               len = min(PAGE_SIZE - offset_in_page(ra), len);
-                       } else {
-                               ra = dst;
-                       }
-                       if (memcpy_real(ra, src, len))
-                               return -EFAULT;
-               }
-               dst += len;
-               src += len;
-               count -= len;
-       }
-       return 0;
-}
-
-/*
- * Copy memory from kernel (real) to user (virtual)
- */
-static int copy_to_user_real(void __user *dest, unsigned long src, unsigned long count)
+static size_t copy_to_iter_real(struct iov_iter *iter, unsigned long src, size_t count)
 {
-       unsigned long offs = 0, size;
+       size_t len, copied, res = 0;
 
        mutex_lock(&memcpy_real_mutex);
-       while (offs < count) {
-               size = min(PAGE_SIZE, count - offs);
-               if (memcpy_real(memcpy_real_buf, src + offs, size))
+       while (count) {
+               len = min(PAGE_SIZE, count);
+               if (memcpy_real(memcpy_real_buf, src, len))
                        break;
-               if (copy_to_user(dest + offs, memcpy_real_buf, size))
+               copied = copy_to_iter(memcpy_real_buf, len, iter);
+               count -= copied;
+               src += copied;
+               res += copied;
+               if (copied < len)
                        break;
-               offs += size;
        }
        mutex_unlock(&memcpy_real_mutex);
-       if (offs < count)
-               return -EFAULT;
-       return 0;
+       return res;
 }
 
-/*
- * Copy memory of the old, dumped system to a user space virtual address
- */
-static int copy_oldmem_user(void __user *dst, unsigned long src, size_t count)
+size_t copy_oldmem_iter(struct iov_iter *iter, unsigned long src, size_t count)
 {
-       unsigned long len;
-       int rc;
+       size_t len, copied, res = 0;
 
        while (count) {
                if (!oldmem_data.start && src < sclp.hsa_size) {
                        /* Copy from zfcp/nvme dump HSA area */
                        len = min(count, sclp.hsa_size - src);
-                       rc = memcpy_hsa_user(dst, src, len);
-                       if (rc)
-                               return rc;
+                       copied = memcpy_hsa_iter(iter, src, len);
                } else {
                        /* Check for swapped kdump oldmem areas */
                        if (oldmem_data.start && src - oldmem_data.start < oldmem_data.size) {
@@ -223,15 +156,15 @@ static int copy_oldmem_user(void __user *dst, unsigned long src, size_t count)
                        } else {
                                len = count;
                        }
-                       rc = copy_to_user_real(dst, src, len);
-                       if (rc)
-                               return rc;
+                       copied = copy_to_iter_real(iter, src, len);
                }
-               dst += len;
-               src += len;
-               count -= len;
+               count -= copied;
+               src += copied;
+               res += copied;
+               if (copied < len)
+                       break;
        }
-       return 0;
+       return res;
 }
 
 /*
@@ -241,26 +174,9 @@ ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn, size_t csize,
                         unsigned long offset)
 {
        unsigned long src;
-       int rc;
 
-       if (!(iter_is_iovec(iter) || iov_iter_is_kvec(iter)))
-               return -EINVAL;
-       /* Multi-segment iterators are not supported */
-       if (iter->nr_segs > 1)
-               return -EINVAL;
-       if (!csize)
-               return 0;
        src = pfn_to_phys(pfn) + offset;
-
-       /* XXX: pass the iov_iter down to a common function */
-       if (iter_is_iovec(iter))
-               rc = copy_oldmem_user(iter->iov->iov_base, src, csize);
-       else
-               rc = copy_oldmem_kernel(iter->kvec->iov_base, src, csize);
-       if (rc < 0)
-               return rc;
-       iov_iter_advance(iter, csize);
-       return csize;
+       return copy_oldmem_iter(iter, src, csize);
 }
 
 /*
index 92b32ce645b95483dc32a3aab56db8c62a4ff4a0..f6da215ccf9f6c2a158173395fdf441423b4096a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/debugfs.h>
 #include <linux/panic_notifier.h>
 #include <linux/reboot.h>
+#include <linux/uio.h>
 
 #include <asm/asm-offsets.h>
 #include <asm/ipl.h>
@@ -54,38 +55,37 @@ static DEFINE_MUTEX(hsa_buf_mutex);
 static char hsa_buf[PAGE_SIZE] __aligned(PAGE_SIZE);
 
 /*
- * Copy memory from HSA to user memory (not reentrant):
+ * Copy memory from HSA to iterator (not reentrant):
  *
- * @dest:  User buffer where memory should be copied to
+ * @iter:  Iterator where memory should be copied to
  * @src:   Start address within HSA where data should be copied
  * @count: Size of buffer, which should be copied
  */
-int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
+size_t memcpy_hsa_iter(struct iov_iter *iter, unsigned long src, size_t count)
 {
-       unsigned long offset, bytes;
+       size_t bytes, copied, res = 0;
+       unsigned long offset;
 
        if (!hsa_available)
-               return -ENODATA;
+               return 0;
 
        mutex_lock(&hsa_buf_mutex);
        while (count) {
                if (sclp_sdias_copy(hsa_buf, src / PAGE_SIZE + 2, 1)) {
                        TRACE("sclp_sdias_copy() failed\n");
-                       mutex_unlock(&hsa_buf_mutex);
-                       return -EIO;
+                       break;
                }
                offset = src % PAGE_SIZE;
                bytes = min(PAGE_SIZE - offset, count);
-               if (copy_to_user(dest, hsa_buf + offset, bytes)) {
-                       mutex_unlock(&hsa_buf_mutex);
-                       return -EFAULT;
-               }
-               src += bytes;
-               dest += bytes;
-               count -= bytes;
+               copied = copy_to_iter(hsa_buf + offset, bytes, iter);
+               count -= copied;
+               src += copied;
+               res += copied;
+               if (copied < bytes)
+                       break;
        }
        mutex_unlock(&hsa_buf_mutex);
-       return 0;
+       return res;
 }
 
 /*
@@ -95,28 +95,16 @@ int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
  * @src:   Start address within HSA where data should be copied
  * @count: Size of buffer, which should be copied
  */
-int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
+static inline int memcpy_hsa_kernel(void *dst, unsigned long src, size_t count)
 {
-       unsigned long offset, bytes;
+       struct iov_iter iter;
+       struct kvec kvec;
 
-       if (!hsa_available)
-               return -ENODATA;
-
-       mutex_lock(&hsa_buf_mutex);
-       while (count) {
-               if (sclp_sdias_copy(hsa_buf, src / PAGE_SIZE + 2, 1)) {
-                       TRACE("sclp_sdias_copy() failed\n");
-                       mutex_unlock(&hsa_buf_mutex);
-                       return -EIO;
-               }
-               offset = src % PAGE_SIZE;
-               bytes = min(PAGE_SIZE - offset, count);
-               memcpy(dest, hsa_buf + offset, bytes);
-               src += bytes;
-               dest += bytes;
-               count -= bytes;
-       }
-       mutex_unlock(&hsa_buf_mutex);
+       kvec.iov_base = dst;
+       kvec.iov_len = count;
+       iov_iter_kvec(&iter, WRITE, &kvec, 1, count);
+       if (memcpy_hsa_iter(&iter, src, count) < count)
+               return -EIO;
        return 0;
 }