diff --git a/mm/shmem.c b/mm/shmem.c index e96dc3beac95..e1b7cf4b5280 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1298,12 +1298,10 @@ void shmem_unlock_mapping(struct address_space *mapping) static struct folio *shmem_get_partial_folio(struct inode *inode, pgoff_t index) { + const gfp_t gfp = mapping_gfp_mask(inode->i_mapping); struct folio *folio; - /* - * At first avoid shmem_get_folio(,,,SGP_READ): that fails - * beyond i_size, and reports fallocated folios as holes. - */ +retry: folio = filemap_get_entry(inode->i_mapping, index); if (!folio) return folio; @@ -1314,13 +1312,14 @@ static struct folio *shmem_get_partial_folio(struct inode *inode, pgoff_t index) /* The folio has been swapped out */ folio_unlock(folio); folio_put(folio); + goto retry; } /* - * But read a folio back from swap if any of it is within i_size - * (although in some cases this is just a waste of time). + * Truncation can look up a large swap entry at or beyond i_size. + * Swap it in directly to bypass the size check in shmem_get_folio(). */ - folio = NULL; - shmem_get_folio(inode, index, 0, &folio, SGP_READ); + if (shmem_swapin_folio(inode, index, &folio, SGP_READ, gfp, NULL, NULL) == -EEXIST) + goto retry; return folio; }