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

squashfs: add optional full compressed block caching

The commit 93e72b3c612adcaca1 ("squashfs: migrate from ll_rw_block usage
to BIO") removed caching of compressed blocks in SquashFS, causing fio
performance regression in workloads with repeated file reads. Without
caching, every read triggers disk I/O, severely impacting performance in
tools like fio.

This patch introduces a new CONFIG_SQUASHFS_COMP_CACHE_FULL Kconfig option
to enable caching of all compressed blocks, restoring performance to
pre-BIO migration levels. When enabled, all pages in a BIO are cached in
the page cache, reducing disk I/O for repeated reads. The fio test
results with this patch confirm the performance restoration:

For example, fio tests (iodepth=1, numjobs=1,
ioengine=psync) show a notable performance restoration:

Disable CONFIG_SQUASHFS_COMP_CACHE_FULL:
IOPS=815, BW=102MiB/s (107MB/s)(6113MiB/60001msec)
Enable CONFIG_SQUASHFS_COMP_CACHE_FULL:
IOPS=2223, BW=278MiB/s (291MB/s)(16.3GiB/59999msec)

The tradeoff is increased memory usage due to caching all compressed
blocks. The CONFIG_SQUASHFS_COMP_CACHE_FULL option allows users to enable
this feature selectively, balancing performance and memory usage for
workloads with frequent repeated reads.

Link: https://lkml.kernel.org/r/20250521072559.2389-1-chanho.min@lge.com
Signed-off-by: Chanho Min <chanho.min@lge.com>
Reviewed-by Phillip Lougher <phillip@squashfs.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Chanho Min and committed by
Andrew Morton
2e227ff5 4496e1c1

+49
+21
fs/squashfs/Kconfig
··· 149 149 150 150 If unsure, say N. 151 151 152 + config SQUASHFS_COMP_CACHE_FULL 153 + bool "Enable full caching of compressed blocks" 154 + depends on SQUASHFS 155 + default n 156 + help 157 + This option enables caching of all compressed blocks, Without caching, 158 + repeated reads of the same files trigger excessive disk I/O, significantly 159 + reducinng performance in workloads like fio-based benchmarks. 160 + 161 + For example, fio tests (iodepth=1, numjobs=1, ioengine=psync) show: 162 + With caching: IOPS=2223, BW=278MiB/s (291MB/s) 163 + Without caching: IOPS=815, BW=102MiB/s (107MB/s) 164 + 165 + Enabling this option restores performance to pre-regression levels by 166 + caching all compressed blocks in the page cache, reducing disk I/O for 167 + repeated reads. However, this increases memory usage, which may be a 168 + concern in memory-constrained environments. 169 + 170 + Enable this option if your workload involves frequent repeated reads and 171 + memory usage is not a limiting factor. If unsure, say N. 172 + 152 173 config SQUASHFS_ZLIB 153 174 bool "Include support for ZLIB compressed file systems" 154 175 depends on SQUASHFS
+28
fs/squashfs/block.c
··· 88 88 struct bio_vec *bv; 89 89 int idx = 0; 90 90 int err = 0; 91 + #ifdef CONFIG_SQUASHFS_COMP_CACHE_FULL 92 + struct page **cache_pages = kmalloc_array(page_count, 93 + sizeof(void *), GFP_KERNEL | __GFP_ZERO); 94 + #endif 91 95 92 96 bio_for_each_segment_all(bv, fullbio, iter_all) { 93 97 struct page *page = bv->bv_page; ··· 114 110 head_to_cache = page; 115 111 else if (idx == page_count - 1 && index + length != read_end) 116 112 tail_to_cache = page; 113 + #ifdef CONFIG_SQUASHFS_COMP_CACHE_FULL 114 + /* Cache all pages in the BIO for repeated reads */ 115 + else if (cache_pages) 116 + cache_pages[idx] = page; 117 + #endif 117 118 118 119 if (!bio || idx != end_idx) { 119 120 struct bio *new = bio_alloc_clone(bdev, fullbio, ··· 172 163 } 173 164 } 174 165 166 + #ifdef CONFIG_SQUASHFS_COMP_CACHE_FULL 167 + if (!cache_pages) 168 + goto out; 169 + 170 + for (idx = 0; idx < page_count; idx++) { 171 + if (!cache_pages[idx]) 172 + continue; 173 + int ret = add_to_page_cache_lru(cache_pages[idx], cache_mapping, 174 + (read_start >> PAGE_SHIFT) + idx, 175 + GFP_NOIO); 176 + 177 + if (!ret) { 178 + SetPageUptodate(cache_pages[idx]); 179 + unlock_page(cache_pages[idx]); 180 + } 181 + } 182 + kfree(cache_pages); 183 + out: 184 + #endif 175 185 return 0; 176 186 } 177 187