[PATCH 06/16] squashfs: convert squashfs_readpage_block() to folios

From: Tal Zussman

Date: Tue Nov 03 2026 - 08:24:26 EST


Convert squashfs_readpage_block() to folios. Use __filemap_get_folio()
instead of grab_cache_page_nowait(), and drop FGP_NOFS, which
FGP_NOWAIT already covers [1]. Add squashfs_pages_per_block(),
squashfs_block_first_index(), and squashfs_block_bytes() for the block
arithmetic the read paths keep open-coding, and use the first two here.

This removes one of the last callers of grab_cache_page_nowait() and
up to three calls to compound_head() per folio. No functional change.

[1] https://lore.kernel.org/linux-mm/20260830041901.2668-9-willy@xxxxxxxxxxxxx/

Signed-off-by: Tal Zussman <tz2294@xxxxxxxxxxxx>
---
fs/squashfs/file_direct.c | 95 ++++++++++++++++++++++----------------------
fs/squashfs/squashfs_fs_sb.h | 24 +++++++++++
2 files changed, 72 insertions(+), 47 deletions(-)

diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index 3b6aa4727c79..55286dab03d2 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -22,102 +22,103 @@
int squashfs_readpage_block(struct folio *folio, u64 block, int bsize,
int expected)
{
- struct page *target_page = &folio->page;
struct inode *inode = folio->mapping->host;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
loff_t file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
- int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
- loff_t start_index = folio->index & ~mask;
- loff_t end_index = start_index | mask;
+ unsigned int nr = squashfs_pages_per_block(msblk);
+ loff_t start_index = squashfs_block_first_index(msblk, folio->index);
+ loff_t end_index = start_index + nr - 1;
loff_t index;
- int i, pages, bytes, res = -ENOMEM;
- struct page **page, *last_page;
+ int i, nr_folios, bytes, res = -ENOMEM;
+ struct folio **folios, *last_folio;
struct squashfs_page_actor *actor;
- void *pageaddr;

if (end_index > file_end)
end_index = file_end;

- pages = end_index - start_index + 1;
+ nr_folios = end_index - start_index + 1;

- page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
- if (page == NULL)
+ folios = kmalloc_objs(*folios, nr_folios);
+ if (folios == NULL)
return res;

- /* Try to grab all the pages covered by the Squashfs block */
+ /* Try to grab all the folios covered by the Squashfs block */
for (i = 0, index = start_index; index <= end_index; index++) {
- page[i] = (index == folio->index) ? target_page :
- grab_cache_page_nowait(folio->mapping, index);
+ struct folio *f;

- if (page[i] == NULL)
+ if (index == folio->index)
+ f = folio;
+ else
+ f = __filemap_get_folio(folio->mapping, index,
+ FGP_LOCK | FGP_CREAT | FGP_NOWAIT,
+ mapping_gfp_mask(folio->mapping));
+
+ if (IS_ERR(f))
continue;

- if (PageUptodate(page[i])) {
- unlock_page(page[i]);
- put_page(page[i]);
+ if (folio_test_uptodate(f)) {
+ folio_unlock(f);
+ folio_put(f);
continue;
}

- i++;
+ folios[i++] = f;
}

- pages = i;
+ nr_folios = i;

/*
* Create a "page actor" which will kmap and kunmap the
- * page cache pages appropriately within the decompressor
+ * page cache folios appropriately within the decompressor.
*/
- actor = squashfs_page_actor_init_special(msblk, page, pages, expected,
- start_index << PAGE_SHIFT);
+ actor = squashfs_page_actor_init_folios(msblk, folios, nr_folios,
+ expected, start_index << PAGE_SHIFT);
if (actor == NULL)
goto mark_errored;

/* Decompress directly into the page cache buffers */
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);

- last_page = squashfs_page_actor_free(actor);
+ last_folio = squashfs_page_actor_free_folios(actor);

if (res < 0)
goto mark_errored;

- if (res != expected || IS_ERR(last_page)) {
+ if (res != expected || IS_ERR(last_folio)) {
res = -EIO;
goto mark_errored;
}

- /* Last page (if present) may have trailing bytes not filled */
+ /* Last folio (if present) may have trailing bytes not filled */
bytes = res % PAGE_SIZE;
- if (end_index == file_end && last_page && bytes) {
- pageaddr = kmap_local_page(last_page);
- memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
- kunmap_local(pageaddr);
- }
-
- /* Mark pages as uptodate, unlock and release */
- for (i = 0; i < pages; i++) {
- flush_dcache_page(page[i]);
- SetPageUptodate(page[i]);
- unlock_page(page[i]);
- if (page[i] != target_page)
- put_page(page[i]);
+ if (end_index == file_end && last_folio && bytes)
+ folio_zero_segment(last_folio, bytes, PAGE_SIZE);
+
+ /* Mark folios as uptodate, unlock and release */
+ for (i = 0; i < nr_folios; i++) {
+ flush_dcache_folio(folios[i]);
+ folio_end_read(folios[i], true);
+ if (folios[i] != folio)
+ folio_put(folios[i]);
}

- kfree(page);
+ kfree(folios);

return 0;

mark_errored:
- /* Decompression failed. Target_page is
- * dealt with by the caller
+ /*
+ * Decompression failed. The target folio is dealt with by the
+ * caller.
*/
- for (i = 0; i < pages; i++) {
- if (page[i] == NULL || page[i] == target_page)
+ for (i = 0; i < nr_folios; i++) {
+ if (folios[i] == folio)
continue;
- flush_dcache_page(page[i]);
- unlock_page(page[i]);
- put_page(page[i]);
+ flush_dcache_folio(folios[i]);
+ folio_unlock(folios[i]);
+ folio_put(folios[i]);
}

- kfree(page);
+ kfree(folios);
return res;
}
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index b87221ea9bdd..e9ba38afacf3 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -10,6 +10,7 @@
* squashfs_fs_sb.h
*/

+#include <linux/fs.h>
#include "squashfs_fs.h"

/*
@@ -79,4 +80,27 @@ struct squashfs_sb_info {
const struct squashfs_decompressor_thread_ops *thread_ops;
int max_thread_num;
};
+
+static inline unsigned int squashfs_pages_per_block(const struct squashfs_sb_info *msblk)
+{
+ return 1U << (msblk->block_log - PAGE_SHIFT);
+}
+
+/* Index of the first page of the block that contains @index */
+static inline pgoff_t squashfs_block_first_index(const struct squashfs_sb_info *msblk,
+ pgoff_t index)
+{
+ return round_down(index, squashfs_pages_per_block(msblk));
+}
+
+/* Number of bytes of file data in block @block */
+static inline unsigned int squashfs_block_bytes(struct inode *inode, u64 block)
+{
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ loff_t i_size = i_size_read(inode);
+
+ if (block == i_size >> msblk->block_log)
+ return i_size & (msblk->block_size - 1);
+ return msblk->block_size;
+}
#endif

--
2.39.5