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 folio_expected_ref_count() for reference count calculation

Patch series " JFS: Implement migrate_folio for jfs_metapage_aops" v5.

This patchset addresses a warning that occurs during memory compaction due
to JFS's missing migrate_folio operation. The warning was introduced by
commit 7ee3647243e5 ("migrate: Remove call to ->writepage") which added
explicit warnings when filesystem don't implement migrate_folio.

The syzbot reported following [1]:
jfs_metapage_aops does not implement migrate_folio
WARNING: CPU: 1 PID: 5861 at mm/migrate.c:955 fallback_migrate_folio mm/migrate.c:953 [inline]
WARNING: CPU: 1 PID: 5861 at mm/migrate.c:955 move_to_new_folio+0x70e/0x840 mm/migrate.c:1007
Modules linked in:
CPU: 1 UID: 0 PID: 5861 Comm: syz-executor280 Not tainted 6.15.0-rc1-next-20250411-syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2025
RIP: 0010:fallback_migrate_folio mm/migrate.c:953 [inline]
RIP: 0010:move_to_new_folio+0x70e/0x840 mm/migrate.c:1007

To fix this issue, this series implement metapage_migrate_folio() for JFS
which handles both single and multiple metapages per page configurations.

While most filesystems leverage existing migration implementations like
filemap_migrate_folio(), buffer_migrate_folio_norefs() or
buffer_migrate_folio() (which internally used folio_expected_refs()),
JFS's metapage architecture requires special handling of its private data
during migration. To support this, this series introduce the
folio_expected_ref_count(), which calculates external references to a
folio from page/swap cache, private data, and page table mappings.

This standardized implementation replaces the previous ad-hoc
folio_expected_refs() function and enables JFS to accurately determine
whether a folio has unexpected references before attempting migration.




Implement folio_expected_ref_count() to calculate expected folio reference
counts from:
- Page/swap cache (1 per page)
- Private data (1)
- Page table mappings (1 per map)

While originally needed for page migration operations, this improved
implementation standardizes reference counting by consolidating all
refcount contributors into a single, reusable function that can benefit
any subsystem needing to detect unexpected references to folios.

The folio_expected_ref_count() returns the sum of these external
references without including any reference the caller itself might hold.
Callers comparing against the actual folio_ref_count() must account for
their own references separately.

Link: https://syzkaller.appspot.com/bug?extid=8bb6fd945af4e0ad9299 [1]
Link: https://lkml.kernel.org/r/20250430100150.279751-1-shivankg@amd.com
Link: https://lkml.kernel.org/r/20250430100150.279751-2-shivankg@amd.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Shivank Garg <shivankg@amd.com>
Suggested-by: Matthew Wilcox <willy@infradead.org>
Co-developed-by: David Hildenbrand <david@redhat.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: Donet Tom <donettom@linux.ibm.com>
Cc: Jane Chu <jane.chu@oracle.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Shivank Garg and committed by
Andrew Morton
86ebd502 60309008

+59 -18
+55
include/linux/mm.h
··· 2114 2114 return test_bit(FOLIO_MM_IDS_SHARED_BITNUM, &folio->_mm_ids); 2115 2115 } 2116 2116 2117 + /** 2118 + * folio_expected_ref_count - calculate the expected folio refcount 2119 + * @folio: the folio 2120 + * 2121 + * Calculate the expected folio refcount, taking references from the pagecache, 2122 + * swapcache, PG_private and page table mappings into account. Useful in 2123 + * combination with folio_ref_count() to detect unexpected references (e.g., 2124 + * GUP or other temporary references). 2125 + * 2126 + * Does currently not consider references from the LRU cache. If the folio 2127 + * was isolated from the LRU (which is the case during migration or split), 2128 + * the LRU cache does not apply. 2129 + * 2130 + * Calling this function on an unmapped folio -- !folio_mapped() -- that is 2131 + * locked will return a stable result. 2132 + * 2133 + * Calling this function on a mapped folio will not result in a stable result, 2134 + * because nothing stops additional page table mappings from coming (e.g., 2135 + * fork()) or going (e.g., munmap()). 2136 + * 2137 + * Calling this function without the folio lock will also not result in a 2138 + * stable result: for example, the folio might get dropped from the swapcache 2139 + * concurrently. 2140 + * 2141 + * However, even when called without the folio lock or on a mapped folio, 2142 + * this function can be used to detect unexpected references early (for example, 2143 + * if it makes sense to even lock the folio and unmap it). 2144 + * 2145 + * The caller must add any reference (e.g., from folio_try_get()) it might be 2146 + * holding itself to the result. 2147 + * 2148 + * Returns the expected folio refcount. 2149 + */ 2150 + static inline int folio_expected_ref_count(const struct folio *folio) 2151 + { 2152 + const int order = folio_order(folio); 2153 + int ref_count = 0; 2154 + 2155 + if (WARN_ON_ONCE(folio_test_slab(folio))) 2156 + return 0; 2157 + 2158 + if (folio_test_anon(folio)) { 2159 + /* One reference per page from the swapcache. */ 2160 + ref_count += folio_test_swapcache(folio) << order; 2161 + } else if (!((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS)) { 2162 + /* One reference per page from the pagecache. */ 2163 + ref_count += !!folio->mapping << order; 2164 + /* One reference from PG_private. */ 2165 + ref_count += folio_test_private(folio); 2166 + } 2167 + 2168 + /* One reference per page table mapping. */ 2169 + return ref_count + folio_mapcount(folio); 2170 + } 2171 + 2117 2172 #ifndef HAVE_ARCH_MAKE_FOLIO_ACCESSIBLE 2118 2173 static inline int arch_make_folio_accessible(struct folio *folio) 2119 2174 {
+4 -18
mm/migrate.c
··· 445 445 } 446 446 #endif 447 447 448 - static int folio_expected_refs(struct address_space *mapping, 449 - struct folio *folio) 450 - { 451 - int refs = 1; 452 - if (!mapping) 453 - return refs; 454 - 455 - refs += folio_nr_pages(folio); 456 - if (folio_test_private(folio)) 457 - refs++; 458 - 459 - return refs; 460 - } 461 - 462 448 /* 463 449 * Replace the folio in the mapping. 464 450 * ··· 587 601 int folio_migrate_mapping(struct address_space *mapping, 588 602 struct folio *newfolio, struct folio *folio, int extra_count) 589 603 { 590 - int expected_count = folio_expected_refs(mapping, folio) + extra_count; 604 + int expected_count = folio_expected_ref_count(folio) + extra_count + 1; 591 605 592 606 if (folio_ref_count(folio) != expected_count) 593 607 return -EAGAIN; ··· 604 618 struct folio *dst, struct folio *src) 605 619 { 606 620 XA_STATE(xas, &mapping->i_pages, folio_index(src)); 607 - int rc, expected_count = folio_expected_refs(mapping, src); 621 + int rc, expected_count = folio_expected_ref_count(src) + 1; 608 622 609 623 if (folio_ref_count(src) != expected_count) 610 624 return -EAGAIN; ··· 735 749 struct folio *src, void *src_private, 736 750 enum migrate_mode mode) 737 751 { 738 - int rc, expected_count = folio_expected_refs(mapping, src); 752 + int rc, expected_count = folio_expected_ref_count(src) + 1; 739 753 740 754 /* Check whether src does not have extra refs before we do more work */ 741 755 if (folio_ref_count(src) != expected_count) ··· 823 837 return migrate_folio(mapping, dst, src, mode); 824 838 825 839 /* Check whether page does not have extra refs before we do more work */ 826 - expected_count = folio_expected_refs(mapping, src); 840 + expected_count = folio_expected_ref_count(src) + 1; 827 841 if (folio_ref_count(src) != expected_count) 828 842 return -EAGAIN; 829 843