From 22c7255577e1efbca5186fa3a3afadf714743647 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Tue, 30 Jun 2026 19:13:20 +0100 Subject: [PATCH] foo Not-Signed-off-by: Pedro Falcato --- fs/open.c | 15 ++-------- include/linux/pagemap.h | 1 + mm/fadvise.c | 2 +- mm/internal.h | 3 +- mm/truncate.c | 63 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/fs/open.c b/fs/open.c index be7b55260a75..8feaf87c06b8 100644 --- a/fs/open.c +++ b/fs/open.c @@ -985,18 +985,9 @@ static int do_dentry_open(struct file *f, * cache will fail. */ if (filemap_nr_thps(inode->i_mapping)) { - struct address_space *mapping = inode->i_mapping; - - filemap_invalidate_lock(inode->i_mapping); - /* - * unmap_mapping_range just need to be called once - * here, because the private pages is not need to be - * unmapped mapping (e.g. data segment of dynamic - * shared libraries here). - */ - unmap_mapping_range(mapping, 0, 0, 0); - truncate_inode_pages(mapping, 0); - filemap_invalidate_unlock(inode->i_mapping); + error = filemap_truncate_thps(inode); + if (error) + goto cleanup_all; } } diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 68a5f1ff3301..401b03970f68 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -67,6 +67,7 @@ static inline int filemap_write_and_wait(struct address_space *mapping) { return filemap_write_and_wait_range(mapping, 0, LLONG_MAX); } +int filemap_truncate_thps(struct inode *inode); /** * filemap_set_wb_err - set a writeback error on an address_space diff --git a/mm/fadvise.c b/mm/fadvise.c index 588fe76c5a14..c44a7a11eee2 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c @@ -156,7 +156,7 @@ int generic_fadvise(struct file *file, loff_t offset, loff_t len, int advice) lru_add_drain(); mapping_try_invalidate(mapping, start_index, end_index, - &nr_failed); + &nr_failed, NULL); /* * The failures may be due to the folio being diff --git a/mm/internal.h b/mm/internal.h index 3bfc1dc2d7ea..83e3bbbe18a6 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -407,7 +407,8 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end); long mapping_evict_folio(struct address_space *mapping, struct folio *folio); unsigned long mapping_try_invalidate(struct address_space *mapping, - pgoff_t start, pgoff_t end, unsigned long *nr_failed); + pgoff_t start, pgoff_t end, unsigned long *nr_failed, + pgoff_t *first_fail); /** * folio_evictable - Test whether a folio is evictable. diff --git a/mm/truncate.c b/mm/truncate.c index fb5c20b57bd4..3efcadd2be4f 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -490,12 +490,14 @@ EXPORT_SYMBOL(truncate_inode_pages_final); * @start: the offset 'from' which to invalidate * @end: the offset 'to' which to invalidate (inclusive) * @nr_failed: How many folio invalidations failed + * @first_fail: What was the first offset to fail invalidation? * * This function is similar to invalidate_mapping_pages(), except that it * returns the number of folios which could not be evicted in @nr_failed. */ unsigned long mapping_try_invalidate(struct address_space *mapping, - pgoff_t start, pgoff_t end, unsigned long *nr_failed) + pgoff_t start, pgoff_t end, unsigned long *nr_failed, + pgoff_t *first_fail) { pgoff_t indices[PAGEVEC_SIZE]; struct folio_batch fbatch; @@ -504,6 +506,7 @@ unsigned long mapping_try_invalidate(struct address_space *mapping, unsigned long count = 0; int i; bool xa_has_values = false; + bool has_failed = false; folio_batch_init(&fbatch); while (find_lock_entries(mapping, &index, end, &fbatch, indices)) { @@ -529,6 +532,9 @@ unsigned long mapping_try_invalidate(struct address_space *mapping, /* Likely in the lru cache of a remote CPU */ if (nr_failed) (*nr_failed)++; + if (!has_failed && first_fail) + *first_fail = folio_pgoff(folio); + has_failed = true; } count += ret; } @@ -560,7 +566,7 @@ unsigned long mapping_try_invalidate(struct address_space *mapping, unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end) { - return mapping_try_invalidate(mapping, start, end, NULL); + return mapping_try_invalidate(mapping, start, end, NULL, NULL); } EXPORT_SYMBOL(invalidate_mapping_pages); @@ -864,3 +870,56 @@ void truncate_pagecache_range(struct inode *inode, loff_t lstart, loff_t lend) truncate_inode_pages_range(mapping, lstart, lend); } EXPORT_SYMBOL(truncate_pagecache_range); + +int filemap_truncate_thps(struct inode *inode) +{ + struct address_space *mapping = inode->i_mapping; + pgoff_t start_index = 0, first_fail; + unsigned long nr_failed = 0; + int err; + + while (filemap_nr_thps(mapping)) { + nr_failed = 0; + first_fail = 0; + filemap_invalidate_lock(mapping); + /* + * unmap_mapping_range just need to be called once + * here, because the private pages is not need to be + * unmapped mapping (e.g. data segment of dynamic + * shared libraries here). + */ + unmap_mapping_range(mapping, 0, 0, 0); + lru_add_drain(); + mapping_try_invalidate(mapping, start_index, LLONG_MAX, + &nr_failed, &first_fail); + filemap_invalidate_unlock(mapping); + if (!nr_failed) + break; + /* + * The failures may be due to the folio being + * in the LRU cache of a remote CPU. Drain all + * caches, do writeback and try again. + */ + lru_add_drain_all(); + /* + * We now know that up to first_fail, there are no THPs. Start + * from there to ensure forward progress. + */ + start_index = first_fail; + + /* + * Attempt to writeback. If it fails, it's ok to fail the open. There's not + * much we can do in that case. + */ + err = filemap_write_and_wait_range(mapping, start_index, LLONG_MAX); + if (err) + return err; + } + + /* + * It should not be possible to hit this case after the above loop + * completes. + */ + WARN_ON_ONCE(filemap_nr_thps(mapping)); + return 0; +} -- 2.55.0