Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: userfaultfd: add pgtable_supports_uffd_wp()

Some platforms can customize the PTE/PMD entry uffd-wp bit making it
unavailable even if the architecture provides the resource. This patch
adds a macro API pgtable_supports_uffd_wp() that allows architectures to
define their specific implementations to check if the uffd-wp bit is
available on which device the kernel is running.

Also this patch is removing "ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP" and
"ifdef CONFIG_PTE_MARKER_UFFD_WP" in favor of pgtable_supports_uffd_wp()
and uffd_supports_wp_marker() checks respectively that default to
IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP) and
"IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP) &&
IS_ENABLED(CONFIG_PTE_MARKER_UFFD_WP)" if not overridden by the
architecture, no change in behavior is expected.

Link: https://lkml.kernel.org/r/20251113072806.795029-3-zhangchunyan@iscas.ac.cn
Signed-off-by: Chunyan Zhang <zhangchunyan@iscas.ac.cn>
Acked-by: David Hildenbrand <david@redhat.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Alexandre Ghiti <alexghiti@rivosinc.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andrew Jones <ajones@ventanamicro.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Conor Dooley <conor.dooley@microchip.com>
Cc: Conor Dooley <conor@kernel.org>
Cc: Deepak Gupta <debug@rivosinc.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Yuanchu Xie <yuanchu@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Chunyan Zhang and committed by
Andrew Morton
f59c0924 277a1ae3

+78 -44
+11 -11
fs/userfaultfd.c
··· 1289 1289 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING) 1290 1290 vm_flags |= VM_UFFD_MISSING; 1291 1291 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP) { 1292 - #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP 1293 - goto out; 1294 - #endif 1292 + if (!pgtable_supports_uffd_wp()) 1293 + goto out; 1294 + 1295 1295 vm_flags |= VM_UFFD_WP; 1296 1296 } 1297 1297 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) { ··· 1999 1999 uffdio_api.features &= 2000 2000 ~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM); 2001 2001 #endif 2002 - #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP 2003 - uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP; 2004 - #endif 2005 - #ifndef CONFIG_PTE_MARKER_UFFD_WP 2006 - uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM; 2007 - uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED; 2008 - uffdio_api.features &= ~UFFD_FEATURE_WP_ASYNC; 2009 - #endif 2002 + if (!pgtable_supports_uffd_wp()) 2003 + uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP; 2004 + 2005 + if (!uffd_supports_wp_marker()) { 2006 + uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM; 2007 + uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED; 2008 + uffdio_api.features &= ~UFFD_FEATURE_WP_ASYNC; 2009 + } 2010 2010 2011 2011 ret = -EINVAL; 2012 2012 if (features & ~uffdio_api.features)
+17
include/asm-generic/pgtable_uffd.h
··· 1 1 #ifndef _ASM_GENERIC_PGTABLE_UFFD_H 2 2 #define _ASM_GENERIC_PGTABLE_UFFD_H 3 3 4 + /* 5 + * Some platforms can customize the uffd-wp bit, making it unavailable 6 + * even if the architecture provides the resource. 7 + * Adding this API allows architectures to add their own checks for the 8 + * devices on which the kernel is running. 9 + * Note: When overriding it, please make sure the 10 + * CONFIG_HAVE_ARCH_USERFAULTFD_WP is part of this macro. 11 + */ 12 + #ifndef pgtable_supports_uffd_wp 13 + #define pgtable_supports_uffd_wp() IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP) 14 + #endif 15 + 16 + static inline bool uffd_supports_wp_marker(void) 17 + { 18 + return pgtable_supports_uffd_wp() && IS_ENABLED(CONFIG_PTE_MARKER_UFFD_WP); 19 + } 20 + 4 21 #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP 5 22 static __always_inline int pte_uffd_wp(pte_t pte) 6 23 {
+5 -3
include/linux/mm_inline.h
··· 553 553 554 554 return dstm; 555 555 } 556 - #endif 557 556 558 557 /* 559 558 * If this pte is wr-protected by uffd-wp in any form, arm the special pte to ··· 570 571 pte_install_uffd_wp_if_needed(struct vm_area_struct *vma, unsigned long addr, 571 572 pte_t *pte, pte_t pteval) 572 573 { 573 - #ifdef CONFIG_PTE_MARKER_UFFD_WP 574 574 bool arm_uffd_pte = false; 575 + 576 + if (!uffd_supports_wp_marker()) 577 + return false; 575 578 576 579 /* The current status of the pte should be "cleared" before calling */ 577 580 WARN_ON_ONCE(!pte_none(ptep_get(pte))); ··· 603 602 make_pte_marker(PTE_MARKER_UFFD_WP)); 604 603 return true; 605 604 } 606 - #endif 605 + 607 606 return false; 608 607 } 609 608 ··· 617 616 618 617 return true; 619 618 } 619 + #endif 620 620 621 621 /** 622 622 * num_pages_contiguous() - determine the number of contiguous pages
+41 -28
include/linux/userfaultfd_k.h
··· 228 228 if (wp_async && (vm_flags == VM_UFFD_WP)) 229 229 return true; 230 230 231 - #ifndef CONFIG_PTE_MARKER_UFFD_WP 232 231 /* 233 232 * If user requested uffd-wp but not enabled pte markers for 234 233 * uffd-wp, then shmem & hugetlbfs are not supported but only 235 234 * anonymous. 236 235 */ 237 - if ((vm_flags & VM_UFFD_WP) && !vma_is_anonymous(vma)) 236 + if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && 237 + !vma_is_anonymous(vma)) 238 238 return false; 239 - #endif 240 239 241 240 /* By default, allow any of anon|shmem|hugetlb */ 242 241 return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) || ··· 290 291 void userfaultfd_release_all(struct mm_struct *mm, 291 292 struct userfaultfd_ctx *ctx); 292 293 294 + static inline bool userfaultfd_wp_use_markers(struct vm_area_struct *vma) 295 + { 296 + /* Only wr-protect mode uses pte markers */ 297 + if (!userfaultfd_wp(vma)) 298 + return false; 299 + 300 + /* File-based uffd-wp always need markers */ 301 + if (!vma_is_anonymous(vma)) 302 + return true; 303 + 304 + /* 305 + * Anonymous uffd-wp only needs the markers if WP_UNPOPULATED 306 + * enabled (to apply markers on zero pages). 307 + */ 308 + return userfaultfd_wp_unpopulated(vma); 309 + } 310 + 311 + /* 312 + * Returns true if this is a swap pte and was uffd-wp wr-protected in either 313 + * forms (pte marker or a normal swap pte), false otherwise. 314 + */ 315 + static inline bool pte_swp_uffd_wp_any(pte_t pte) 316 + { 317 + if (!uffd_supports_wp_marker()) 318 + return false; 319 + 320 + if (pte_present(pte)) 321 + return false; 322 + 323 + if (pte_swp_uffd_wp(pte)) 324 + return true; 325 + 326 + if (pte_is_uffd_wp_marker(pte)) 327 + return true; 328 + 329 + return false; 330 + } 293 331 #else /* CONFIG_USERFAULTFD */ 294 332 295 333 /* mm helpers */ ··· 451 415 return false; 452 416 } 453 417 454 - #endif /* CONFIG_USERFAULTFD */ 455 - 456 418 static inline bool userfaultfd_wp_use_markers(struct vm_area_struct *vma) 457 419 { 458 - /* Only wr-protect mode uses pte markers */ 459 - if (!userfaultfd_wp(vma)) 460 - return false; 461 - 462 - /* File-based uffd-wp always need markers */ 463 - if (!vma_is_anonymous(vma)) 464 - return true; 465 - 466 - /* 467 - * Anonymous uffd-wp only needs the markers if WP_UNPOPULATED 468 - * enabled (to apply markers on zero pages). 469 - */ 470 - return userfaultfd_wp_unpopulated(vma); 420 + return false; 471 421 } 472 422 473 423 /* ··· 462 440 */ 463 441 static inline bool pte_swp_uffd_wp_any(pte_t pte) 464 442 { 465 - #ifdef CONFIG_PTE_MARKER_UFFD_WP 466 - if (pte_present(pte)) 467 - return false; 468 - if (pte_swp_uffd_wp(pte)) 469 - return true; 470 - 471 - if (pte_is_uffd_wp_marker(pte)) 472 - return true; 473 - #endif 474 443 return false; 475 444 } 476 - 445 + #endif /* CONFIG_USERFAULTFD */ 477 446 #endif /* _LINUX_USERFAULTFD_K_H */
+4 -2
mm/memory.c
··· 1590 1590 { 1591 1591 bool was_installed = false; 1592 1592 1593 - #ifdef CONFIG_PTE_MARKER_UFFD_WP 1593 + if (!uffd_supports_wp_marker()) 1594 + return false; 1595 + 1594 1596 /* Zap on anonymous always means dropping everything */ 1595 1597 if (vma_is_anonymous(vma)) 1596 1598 return false; ··· 1609 1607 pte++; 1610 1608 addr += PAGE_SIZE; 1611 1609 } 1612 - #endif 1610 + 1613 1611 return was_installed; 1614 1612 } 1615 1613