git: cc32cc0f8e96 - stable/13 - linuxkpi: Add `io_mapping_map_user()` and `remap_pfn_range()`

From: Jean-Sébastien Pédron <dumbbell_at_FreeBSD.org>
Date: Thu, 16 Feb 2023 11:55:48 UTC
The branch stable/13 has been updated by dumbbell (ports committer):

URL: https://cgit.FreeBSD.org/src/commit/?id=cc32cc0f8e965b5c3d0a7ac6595ae97997a76d1b

commit cc32cc0f8e965b5c3d0a7ac6595ae97997a76d1b
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2023-01-14 12:22:19 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2023-02-16 11:55:13 +0000

    linuxkpi: Add `io_mapping_map_user()` and `remap_pfn_range()`
    
    The code comes from the i915 DRM driver.
    
    In Linux commits b739f125e4ebd73d10ed30a856574e13649119ed and
    b12d691ea5e01db42ccf3b4207e57cb3ce7cfe91 (Linux 5.13), the i915 DRM
    driver dropped specific implementations to use Linux generic functions.
    Therefore I moved the FreeBSD code from that i915 driver to linuxkpi.
    
    However, these commits were later reverted (also in Linux 5.13) so the
    i915 driver doesn't use these functions. But perhaps it will help in the
    future.
    
    To sum up, the code comes from the i915 DRM driver but it doesn't use it
    (i.e. it continues to use its internal implementation).
    
    Reviewed by:    manu
    Approved by:    manu
    Differential Revision:  https://reviews.freebsd.org/D38088
    
    (cherry picked from commit b99bc862324526b3ee6fad335618cbe85ad9e11e)
---
 .../linuxkpi/common/include/linux/io-mapping.h     | 12 +++++
 sys/compat/linuxkpi/common/include/linux/mm.h      |  6 ++-
 sys/compat/linuxkpi/common/src/linux_page.c        | 58 ++++++++++++++++++++++
 3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/io-mapping.h b/sys/compat/linuxkpi/common/include/linux/io-mapping.h
index 5c24f1ff8659..e874c5bcadc7 100644
--- a/sys/compat/linuxkpi/common/include/linux/io-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/io-mapping.h
@@ -37,6 +37,7 @@
 
 #include <linux/types.h>
 #include <linux/io.h>
+#include <linux/mm.h>
 #include <linux/slab.h>
 
 struct io_mapping {
@@ -100,6 +101,17 @@ io_mapping_map_wc(struct io_mapping *mapping, unsigned long offset,
 	return ((char *)mapping->mem + offset);
 }
 
+int lkpi_io_mapping_map_user(struct io_mapping *iomap,
+    struct vm_area_struct *vma, unsigned long addr, unsigned long pfn,
+    unsigned long size);
+
+static inline int
+io_mapping_map_user(struct io_mapping *iomap, struct vm_area_struct *vma,
+    unsigned long addr, unsigned long pfn, unsigned long size)
+{
+	return (lkpi_io_mapping_map_user(iomap, vma, addr, pfn, size));
+}
+
 static inline void
 io_mapping_unmap(void *vaddr)
 {
diff --git a/sys/compat/linuxkpi/common/include/linux/mm.h b/sys/compat/linuxkpi/common/include/linux/mm.h
index 4afc1a953f8e..8bee6188c9cd 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm.h
@@ -218,11 +218,15 @@ apply_to_page_range(struct mm_struct *mm, unsigned long address,
 int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
     unsigned long size);
 
+int lkpi_remap_pfn_range(struct vm_area_struct *vma,
+    unsigned long start_addr, unsigned long start_pfn, unsigned long size,
+    pgprot_t prot);
+
 static inline int
 remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
     unsigned long pfn, unsigned long size, pgprot_t prot)
 {
-	return (-ENOTSUP);
+	return (lkpi_remap_pfn_range(vma, addr, pfn, size, prot));
 }
 
 static inline unsigned long
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 5fa370147045..8b2006668f8f 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
 #include <linux/kernel.h>
 #include <linux/idr.h>
 #include <linux/io.h>
+#include <linux/io-mapping.h>
 
 #ifdef __i386__
 DEFINE_IDR(mtrr_idr);
@@ -334,6 +335,63 @@ retry:
 	return (VM_FAULT_NOPAGE);
 }
 
+int
+lkpi_remap_pfn_range(struct vm_area_struct *vma, unsigned long start_addr,
+    unsigned long start_pfn, unsigned long size, pgprot_t prot)
+{
+	vm_object_t vm_obj;
+	unsigned long addr, pfn;
+	int err = 0;
+
+	vm_obj = vma->vm_obj;
+
+	VM_OBJECT_WLOCK(vm_obj);
+	for (addr = start_addr, pfn = start_pfn;
+	    addr < start_addr + size;
+	    addr += PAGE_SIZE) {
+		vm_fault_t ret;
+retry:
+		ret = lkpi_vmf_insert_pfn_prot_locked(vma, addr, pfn, prot);
+
+		if ((ret & VM_FAULT_OOM) != 0) {
+			VM_OBJECT_WUNLOCK(vm_obj);
+			vm_wait(NULL);
+			VM_OBJECT_WLOCK(vm_obj);
+			goto retry;
+		}
+
+		if ((ret & VM_FAULT_ERROR) != 0) {
+			err = -EFAULT;
+			break;
+		}
+
+		pfn++;
+	}
+	VM_OBJECT_WUNLOCK(vm_obj);
+
+	if (unlikely(err)) {
+		zap_vma_ptes(vma, start_addr,
+		    (pfn - start_pfn) << PAGE_SHIFT);
+		return (err);
+	}
+
+	return (0);
+}
+
+int
+lkpi_io_mapping_map_user(struct io_mapping *iomap,
+    struct vm_area_struct *vma, unsigned long addr,
+    unsigned long pfn, unsigned long size)
+{
+	pgprot_t prot;
+	int ret;
+
+	prot = cachemode2protval(iomap->attr);
+	ret = lkpi_remap_pfn_range(vma, addr, pfn, size, prot);
+
+	return (ret);
+}
+
 /*
  * Although FreeBSD version of unmap_mapping_range has semantics and types of
  * parameters compatible with Linux version, the values passed in are different