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

Configure Feed

Select the types of activity you want to include in your feed.

mm: add persistent huge zero folio

Many places in the kernel need to zero out larger chunks, but the maximum
segment that can be zeroed out at a time by ZERO_PAGE is limited by
PAGE_SIZE.

This is especially annoying in block devices and filesystems where
multiple ZERO_PAGEs are attached to the bio in different bvecs. With
multipage bvec support in block layer, it is much more efficient to send
out larger zero pages as a part of single bvec.

This concern was raised during the review of adding Large Block Size
support to XFS[1][2].

Usually huge_zero_folio is allocated on demand, and it will be deallocated
by the shrinker if there are no users of it left. At moment,
huge_zero_folio infrastructure refcount is tied to the process lifetime
that created it. This might not work for bio layer as the completions can
be async and the process that created the huge_zero_folio might no longer
be alive. And, one of the main points that came up during discussion is
to have something bigger than zero page as a drop-in replacement.

Add a config option PERSISTENT_HUGE_ZERO_FOLIO that will result in
allocating the huge zero folio during early init and never free the memory
by disabling the shrinker. This makes using the huge_zero_folio without
having to pass any mm struct and does not tie the lifetime of the zero
folio to anything, making it a drop-in replacement for ZERO_PAGE.

If PERSISTENT_HUGE_ZERO_FOLIO config option is enabled, then
mm_get_huge_zero_folio() will simply return the allocated page instead of
dynamically allocating a new PMD page.

Use this option carefully in resource constrained systems as it uses one
full PMD sized page for zeroing purposes.

[1] https://lore.kernel.org/linux-xfs/20231027051847.GA7885@lst.de/
[2] https://lore.kernel.org/linux-xfs/ZitIK5OnR7ZNY0IG@infradead.org/

Link: https://lkml.kernel.org/r/20250811084113.647267-4-kernel@pankajraghav.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Pankaj Raghav <p.raghav@samsung.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Co-developed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Luis Chamberalin <mcgrof@kernel.org>
Cc: Mariano Pache <npache@redhat.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Kiryl Shutsemau <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Pankaj Raghav and committed by
Andrew Morton
2d8bd804 2843408c

+62 -10
+16
include/linux/huge_mm.h
··· 495 495 struct folio *mm_get_huge_zero_folio(struct mm_struct *mm); 496 496 void mm_put_huge_zero_folio(struct mm_struct *mm); 497 497 498 + static inline struct folio *get_persistent_huge_zero_folio(void) 499 + { 500 + if (!IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) 501 + return NULL; 502 + 503 + if (unlikely(!huge_zero_folio)) 504 + return NULL; 505 + 506 + return huge_zero_folio; 507 + } 508 + 498 509 static inline bool thp_migration_supported(void) 499 510 { 500 511 return IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION); ··· 695 684 unsigned long cp_flags) 696 685 { 697 686 return 0; 687 + } 688 + 689 + static inline struct folio *get_persistent_huge_zero_folio(void) 690 + { 691 + return NULL; 698 692 } 699 693 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 700 694
+16
mm/Kconfig
··· 823 823 config ARCH_WANTS_THP_SWAP 824 824 def_bool n 825 825 826 + config PERSISTENT_HUGE_ZERO_FOLIO 827 + bool "Allocate a PMD sized folio for zeroing" 828 + depends on TRANSPARENT_HUGEPAGE 829 + help 830 + Enable this option to reduce the runtime refcounting overhead 831 + of the huge zero folio and expand the places in the kernel 832 + that can use huge zero folios. For instance, block I/O benefits 833 + from access to large folios for zeroing memory. 834 + 835 + With this option enabled, the huge zero folio is allocated 836 + once and never freed. One full huge page's worth of memory shall 837 + be used. 838 + 839 + Say Y if your system has lots of memory. Say N if you are 840 + memory constrained. 841 + 826 842 config MM_ID 827 843 def_bool n 828 844
+30 -10
mm/huge_memory.c
··· 248 248 249 249 struct folio *mm_get_huge_zero_folio(struct mm_struct *mm) 250 250 { 251 + if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) 252 + return huge_zero_folio; 253 + 251 254 if (test_bit(MMF_HUGE_ZERO_FOLIO, &mm->flags)) 252 255 return READ_ONCE(huge_zero_folio); 253 256 ··· 265 262 266 263 void mm_put_huge_zero_folio(struct mm_struct *mm) 267 264 { 265 + if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) 266 + return; 267 + 268 268 if (test_bit(MMF_HUGE_ZERO_FOLIO, &mm->flags)) 269 269 put_huge_zero_folio(); 270 270 } ··· 855 849 856 850 static int __init thp_shrinker_init(void) 857 851 { 858 - huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero"); 859 - if (!huge_zero_folio_shrinker) 860 - return -ENOMEM; 861 - 862 852 deferred_split_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE | 863 853 SHRINKER_MEMCG_AWARE | 864 854 SHRINKER_NONSLAB, 865 855 "thp-deferred_split"); 866 - if (!deferred_split_shrinker) { 867 - shrinker_free(huge_zero_folio_shrinker); 856 + if (!deferred_split_shrinker) 857 + return -ENOMEM; 858 + 859 + deferred_split_shrinker->count_objects = deferred_split_count; 860 + deferred_split_shrinker->scan_objects = deferred_split_scan; 861 + shrinker_register(deferred_split_shrinker); 862 + 863 + if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) { 864 + /* 865 + * Bump the reference of the huge_zero_folio and do not 866 + * initialize the shrinker. 867 + * 868 + * huge_zero_folio will always be NULL on failure. We assume 869 + * that get_huge_zero_folio() will most likely not fail as 870 + * thp_shrinker_init() is invoked early on during boot. 871 + */ 872 + if (!get_huge_zero_folio()) 873 + pr_warn("Allocating persistent huge zero folio failed\n"); 874 + return 0; 875 + } 876 + 877 + huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero"); 878 + if (!huge_zero_folio_shrinker) { 879 + shrinker_free(deferred_split_shrinker); 868 880 return -ENOMEM; 869 881 } 870 882 871 883 huge_zero_folio_shrinker->count_objects = shrink_huge_zero_folio_count; 872 884 huge_zero_folio_shrinker->scan_objects = shrink_huge_zero_folio_scan; 873 885 shrinker_register(huge_zero_folio_shrinker); 874 - 875 - deferred_split_shrinker->count_objects = deferred_split_count; 876 - deferred_split_shrinker->scan_objects = deferred_split_scan; 877 - shrinker_register(deferred_split_shrinker); 878 886 879 887 return 0; 880 888 }