git: 2d90567415d5 - stable/13 - LinuxKPI: Import MTRR support functions from drm-kmod

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Sat, 22 Jan 2022 19:36:38 UTC
The branch stable/13 has been updated by wulf:

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

commit 2d90567415d5b67c86df09eaeda30c40fa3483cc
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2021-12-08 21:12:51 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2022-01-22 19:34:37 +0000

    LinuxKPI: Import MTRR support functions from drm-kmod
    
    They are superseded by PAT and mostly useless nowadays but still can be
    used on Pentium III/IV era processors. Unlike drm-kmod version, this one
    ignores MTRR if PAT is available that fixes confusing "Failed to add WC
    MTRR for [0xXXXX-0xYYYY]: 22; performance may suffer" message often
    appearing during drm-kmod initialization process.
    
    MFC after:      1 week
    Reviewed by:    hselasky, manu
    Differential Revision:  https://reviews.freebsd.org/D33561
    
    (cherry picked from commit 98b129783c478616f59b3b9c9576a303f7ed67de)
---
 sys/compat/linuxkpi/common/include/linux/io.h |  8 +++
 sys/compat/linuxkpi/common/src/linux_page.c   | 72 +++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/sys/compat/linuxkpi/common/include/linux/io.h b/sys/compat/linuxkpi/common/include/linux/io.h
index e402ebed0665..08e1635b70ac 100644
--- a/sys/compat/linuxkpi/common/include/linux/io.h
+++ b/sys/compat/linuxkpi/common/include/linux/io.h
@@ -482,4 +482,12 @@ memunmap(void *addr)
 	iounmap(addr);
 }
 
+#define	__MTRR_ID_BASE	1
+int lkpi_arch_phys_wc_add(unsigned long, unsigned long);
+void lkpi_arch_phys_wc_del(int);
+#define	arch_phys_wc_add(...)	lkpi_arch_phys_wc_add(__VA_ARGS__)
+#define	arch_phys_wc_del(...)	lkpi_arch_phys_wc_del(__VA_ARGS__)
+#define	arch_phys_wc_index(x)	\
+	(((x) < __MTRR_ID_BASE) ? -1 : ((x) - __MTRR_ID_BASE))
+
 #endif	/* _LINUX_IO_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 3c8bc2bd3c5b..df4a124cf3e2 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/rwlock.h>
 #include <sys/proc.h>
 #include <sys/sched.h>
+#include <sys/memrange.h>
 
 #include <machine/bus.h>
 
@@ -63,6 +64,15 @@ __FBSDID("$FreeBSD$");
 #include <linux/preempt.h>
 #include <linux/fs.h>
 #include <linux/shmem_fs.h>
+#include <linux/kernel.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+
+#ifdef __i386__
+DEFINE_IDR(mtrr_idr);
+static MALLOC_DEFINE(M_LKMTRR, "idr", "Linux MTRR compat");
+extern int pat_works;
+#endif
 
 void
 si_meminfo(struct sysinfo *si)
@@ -357,3 +367,65 @@ retry:
 		vm_object_deallocate(devobj);
 	}
 }
+
+int
+lkpi_arch_phys_wc_add(unsigned long base, unsigned long size)
+{
+#ifdef __i386__
+	struct mem_range_desc *mrdesc;
+	int error, id, act;
+
+	/* If PAT is available, do nothing */
+	if (pat_works)
+		return (0);
+
+	mrdesc = malloc(sizeof(*mrdesc), M_LKMTRR, M_WAITOK);
+	mrdesc->mr_base = base;
+	mrdesc->mr_len = size;
+	mrdesc->mr_flags = MDF_WRITECOMBINE;
+	strlcpy(mrdesc->mr_owner, "drm", sizeof(mrdesc->mr_owner));
+	act = MEMRANGE_SET_UPDATE;
+	error = mem_range_attr_set(mrdesc, &act);
+	if (error == 0) {
+		error = idr_get_new(&mtrr_idr, mrdesc, &id);
+		MPASS(idr_find(&mtrr_idr, id) == mrdesc);
+		if (error != 0) {
+			act = MEMRANGE_SET_REMOVE;
+			mem_range_attr_set(mrdesc, &act);
+		}
+	}
+	if (error != 0) {
+		free(mrdesc, M_LKMTRR);
+		pr_warn(
+		    "Failed to add WC MTRR for [%p-%p]: %d; "
+		    "performance may suffer\n",
+		    (void *)base, (void *)(base + size - 1), error);
+	} else
+		pr_warn("Successfully added WC MTRR for [%p-%p]\n",
+		    (void *)base, (void *)(base + size - 1));
+
+	return (error != 0 ? -error : id + __MTRR_ID_BASE);
+#else
+	return (0);
+#endif
+}
+
+void
+lkpi_arch_phys_wc_del(int reg)
+{
+#ifdef __i386__
+	struct mem_range_desc *mrdesc;
+	int act;
+
+	/* Check if arch_phys_wc_add() failed. */
+	if (reg < __MTRR_ID_BASE)
+		return;
+
+	mrdesc = idr_find(&mtrr_idr, reg - __MTRR_ID_BASE);
+	MPASS(mrdesc != NULL);
+	idr_remove(&mtrr_idr, reg - __MTRR_ID_BASE);
+	act = MEMRANGE_SET_REMOVE;
+	mem_range_attr_set(mrdesc, &act);
+	free(mrdesc, M_LKMTRR);
+#endif
+}