diff options
| author | Kyle Evans <kevans@FreeBSD.org> | 2026-05-19 03:22:21 +0000 |
|---|---|---|
| committer | Kyle Evans <kevans@FreeBSD.org> | 2026-05-19 03:22:21 +0000 |
| commit | 67f7f2781daa9bd398b424ffe2bd0be67f37f03d (patch) | |
| tree | 6e7edcffce0ad0bcb56b5c2672de40436399fbac | |
| parent | 762e451b318c8cbfddbcaaa92a218e8b45306eb9 (diff) | |
linuxkpi: work with numpages > 1 in the set_pages_*() KPIs
These calls are used for buddy pages at least in drm's ttm_pool, which
leads to a panic when we invoke lowmem handlers and drm tries to shrink
the pool.
Cope with numpages > 1 by traversing the contiguous pages and executing
the adjustment there, as well, as suggested by markj@. Previous
versions have tried to use the corresponding `set_memory_*()` functions,
but it is believed that not updating `md.pat_mode` breaks subsequent
userspace mappings in ways that may result in things like screen tearing
or other artifacts when running i915kms.
This stabilized my amdgpu laptop running two VMs, chromium and a
concurrent buildworld.
Reviewed by: bz, markj
Differential Revision: https://reviews.freebsd.org/D57004
| -rw-r--r-- | sys/compat/linuxkpi/common/include/asm/set_memory.h | 17 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/src/linux_page.c | 21 |
2 files changed, 26 insertions, 12 deletions
diff --git a/sys/compat/linuxkpi/common/include/asm/set_memory.h b/sys/compat/linuxkpi/common/include/asm/set_memory.h index f328fcabd243..f45a51a9710b 100644 --- a/sys/compat/linuxkpi/common/include/asm/set_memory.h +++ b/sys/compat/linuxkpi/common/include/asm/set_memory.h @@ -62,35 +62,28 @@ set_memory_wb(unsigned long addr, int numpages) return (-pmap_change_attr((void *)addr, len, VM_MEMATTR_WRITE_BACK)); } +int lkpi_set_pages_attr(struct page *page, int numpages, vm_memattr_t ma); + static inline int set_pages_uc(struct page *page, int numpages) { - KASSERT(numpages == 1, ("%s: numpages %d", __func__, numpages)); - - pmap_page_set_memattr(page, VM_MEMATTR_UNCACHEABLE); - return (0); + return (lkpi_set_pages_attr(page, numpages, VM_MEMATTR_UNCACHEABLE)); } static inline int set_pages_wc(struct page *page, int numpages) { - KASSERT(numpages == 1, ("%s: numpages %d", __func__, numpages)); - #ifdef VM_MEMATTR_WRITE_COMBINING - pmap_page_set_memattr(page, VM_MEMATTR_WRITE_COMBINING); + return (lkpi_set_pages_attr(page, numpages, VM_MEMATTR_WRITE_COMBINING)); #else return (set_pages_uc(page, numpages)); #endif - return (0); } static inline int set_pages_wb(struct page *page, int numpages) { - KASSERT(numpages == 1, ("%s: numpages %d", __func__, numpages)); - - pmap_page_set_memattr(page, VM_MEMATTR_WRITE_BACK); - return (0); + return (lkpi_set_pages_attr(page, numpages, VM_MEMATTR_WRITE_BACK)); } static inline int diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c index b91115a5ff16..62bfa07dd297 100644 --- a/sys/compat/linuxkpi/common/src/linux_page.c +++ b/sys/compat/linuxkpi/common/src/linux_page.c @@ -710,6 +710,27 @@ lkpi_arch_phys_wc_del(int reg) #endif } +int +lkpi_set_pages_attr(struct page *page, int numpages, vm_memattr_t ma) +{ + while (numpages-- > 0) { + /* + * pmap_page_set_memattr() would only update the DMAP mapping + * if it's a normal page, leaving the kernel map untouched. + */ + MPASS(page->object != kernel_object); + + /* + * pmap_page_set_memattr() sets page->md.pat_mode, which is + * crucial for future userspace mappings. + */ + pmap_page_set_memattr(page, ma); + page++; + } + + return (0); +} + /* * This is a highly simplified version of the Linux page_frag_cache. * We only support up-to 1 single page as fragment size and we will |
