mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Daeho Jeong <daeho43@gmail.com>
To: linux-kernel@vger.kernel.org,
	linux-f2fs-devel@lists.sourceforge.net, kernel-team@android.com
Cc: Daeho Jeong <daehojeong@google.com>
Subject: [PATCH 18/20] f2fs: support subpage blocks in compression
Date: Mon,  2 Nov 2026 11:39:50 -0800	[thread overview]
Message-ID: <20261102193952.3002018-19-daeho43@gmail.com> (raw)
In-Reply-To: <20261102193952.3002018-1-daeho43@gmail.com>

From: Daeho Jeong <daehojeong@google.com>

With subpage blocks, a cluster spans fewer folios than blocks, and
several compressed blocks share a bounce folio.

- Require a cluster to cover at least one page.
- Use block offsets and sizes for compressed data, keep each compressed
  block at its block offset within the cluster, and count pending
  compressed I/O in blocks so that a shared bounce folio is not
  decompressed or freed early.
- Submit only the written block of a shared bounce folio in
  f2fs_submit_page_write(), at its offset in the folio.
- Keep compressed files on the page based read and write paths, and
  compress a cluster only when all of its blocks are dirty and below
  EOF.
- A folio with per-block state may hold uptodate blocks newer than the
  on-disk cluster, so decompress into a bounce page and copy back only
  the blocks that are not uptodate.
- Cache compressed blocks one block at a time in the compress cache.
- Lock only the unlocked pages in cancel_cluster_writeback() to avoid a
  self-deadlock.
- Attach a folio state to multi-block folios when compressed writeback
  starts, as the raw write path does. Otherwise the write completion,
  which does not hold the folio lock, can detach the page private flags
  while a task is moving them into a new folio state.
- Take sbi from the dic to count the blocks of a compressed page in
  f2fs_finish_read_bio().
- Write the blocks of a multi-block folio in
  f2fs_write_single_data_page() as f2fs_write_cache_folios() does,
  taking the write_blocks_pending bias with a new helper shared by the
  two and dropping it with f2fs_ffs_put_write_pending().
- Remove the -EOPNOTSUPP checks.

The bounce folios are now freed when the last block of a cluster
completes, also when the block size equals PAGE_SIZE.

Signed-off-by: Daeho Jeong <daehojeong@google.com>
---
 fs/f2fs/compress.c | 520 +++++++++++++++++++++++++++++++--------------
 fs/f2fs/data.c     | 248 +++++++++++++--------
 fs/f2fs/f2fs.h     |  61 +++---
 fs/f2fs/file.c     |  65 +++---
 fs/f2fs/inode.c    |   2 +-
 fs/f2fs/super.c    |   7 +-
 6 files changed, 582 insertions(+), 321 deletions(-)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 0b0f31af6626..5b37ada5b111 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -57,14 +57,15 @@ struct f2fs_compress_ops {
 	bool (*is_level_valid)(int level);
 };
 
-static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
+static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t lblk)
 {
-	return index & (cc->cluster_size - 1);
+	return (lblk & (cc->cluster_size - 1)) >>
+		F2FS_LOG_BLKS_PER_PAGE(F2FS_I_SB(cc->inode));
 }
 
-static pgoff_t cluster_idx(struct compress_ctx *cc, pgoff_t index)
+static pgoff_t cluster_idx(struct compress_ctx *cc, pgoff_t lblk)
 {
-	return index >> cc->log_cluster_size;
+	return lblk >> cc->log_cluster_size;
 }
 
 static pgoff_t start_idx_of_cluster(struct compress_ctx *cc)
@@ -114,7 +115,9 @@ static void f2fs_drop_rpages(struct compress_ctx *cc, int len, bool unlock)
 
 static void f2fs_put_rpages(struct compress_ctx *cc)
 {
-	f2fs_drop_rpages(cc, cc->cluster_size, false);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+
+	f2fs_drop_rpages(cc, cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi), false);
 }
 
 static void f2fs_unlock_rpages(struct compress_ctx *cc, int len)
@@ -125,9 +128,11 @@ static void f2fs_unlock_rpages(struct compress_ctx *cc, int len)
 static void f2fs_put_rpages_wbc(struct compress_ctx *cc,
 		struct writeback_control *wbc, bool redirty, bool unlock)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	unsigned int i;
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		if (!cc->rpages[i])
 			continue;
 		if (redirty)
@@ -158,22 +163,24 @@ void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
 	cc->rpages = NULL;
 	cc->nr_rpages = 0;
 	cc->nr_cpages = 0;
-	cc->valid_nr_cpages = 0;
+	cc->valid_nr_cblocks = 0;
 	if (!reuse)
 		cc->cluster_idx = NULL_CLUSTER;
 }
 
 void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct folio *folio)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
 	unsigned int cluster_ofs;
+	pgoff_t lblk = f2fs_folio_lblk(sbi, folio);
 
-	if (!f2fs_cluster_can_merge_page(cc, folio->index))
-		f2fs_bug_on(F2FS_I_SB(cc->inode), 1);
+	if (!f2fs_cluster_can_merge_block(cc, lblk))
+		f2fs_bug_on(sbi, 1);
 
-	cluster_ofs = offset_in_cluster(cc, folio->index);
+	cluster_ofs = offset_in_cluster(cc, lblk);
 	cc->rpages[cluster_ofs] = folio_page(folio, 0);
 	cc->nr_rpages++;
-	cc->cluster_idx = cluster_idx(cc, folio->index);
+	cc->cluster_idx = cluster_idx(cc, lblk);
 }
 
 #ifdef CONFIG_F2FS_FS_LZO
@@ -184,7 +191,7 @@ static int lzo_init_compress_ctx(struct compress_ctx *cc)
 	if (!cc->private)
 		return -ENOMEM;
 
-	cc->clen = lzo1x_worst_compress(PAGE_SIZE << cc->log_cluster_size);
+	cc->clen = lzo1x_worst_compress(F2FS_BLKSIZE(F2FS_I_SB(cc->inode)) << cc->log_cluster_size);
 	return 0;
 }
 
@@ -220,10 +227,11 @@ static int lzo_decompress_pages(struct decompress_io_ctx *dic)
 		return -EIO;
 	}
 
-	if (dic->rlen != PAGE_SIZE << dic->log_cluster_size) {
+	if (dic->rlen != F2FS_BLKSIZE(dic->sbi) << dic->log_cluster_size) {
 		f2fs_err_ratelimited(dic->sbi,
 				"lzo invalid rlen:%zu, expected:%lu",
-				dic->rlen, PAGE_SIZE << dic->log_cluster_size);
+				dic->rlen, (unsigned long)F2FS_BLKSIZE(dic->sbi) <<
+						dic->log_cluster_size);
 		return -EIO;
 	}
 	return 0;
@@ -256,7 +264,7 @@ static int lz4_init_compress_ctx(struct compress_ctx *cc)
 	 * adapt worst compress case, because lz4 compressor can handle
 	 * output budget properly.
 	 */
-	cc->clen = cc->rlen - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+	cc->clen = cc->rlen - F2FS_BLKSIZE(F2FS_I_SB(cc->inode)) - COMPRESS_HEADER_SIZE;
 	return 0;
 }
 
@@ -300,10 +308,11 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
 		return -EIO;
 	}
 
-	if (ret != PAGE_SIZE << dic->log_cluster_size) {
+	if (ret != F2FS_BLKSIZE(dic->sbi) << dic->log_cluster_size) {
 		f2fs_err_ratelimited(dic->sbi,
 				"lz4 invalid ret:%d, expected:%lu",
-				ret, PAGE_SIZE << dic->log_cluster_size);
+				ret, (unsigned long)F2FS_BLKSIZE(dic->sbi) <<
+						dic->log_cluster_size);
 		return -EIO;
 	}
 	return 0;
@@ -358,7 +367,7 @@ static int zstd_init_compress_ctx(struct compress_ctx *cc)
 	cc->private = workspace;
 	cc->private2 = stream;
 
-	cc->clen = cc->rlen - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+	cc->clen = cc->rlen - F2FS_BLKSIZE(F2FS_I_SB(cc->inode)) - COMPRESS_HEADER_SIZE;
 	return 0;
 }
 
@@ -375,7 +384,7 @@ static int zstd_compress_pages(struct compress_ctx *cc)
 	zstd_in_buffer inbuf;
 	zstd_out_buffer outbuf;
 	int src_size = cc->rlen;
-	int dst_size = src_size - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+	int dst_size = src_size - F2FS_BLKSIZE(F2FS_I_SB(cc->inode)) - COMPRESS_HEADER_SIZE;
 	int ret;
 
 	inbuf.pos = 0;
@@ -419,7 +428,7 @@ static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
 	void *workspace;
 	unsigned int workspace_size;
 	unsigned int max_window_size =
-			MAX_COMPRESS_WINDOW_SIZE(dic->log_cluster_size);
+			MAX_COMPRESS_WINDOW_SIZE(dic->sbi, dic->log_cluster_size);
 
 	workspace_size = zstd_dstream_workspace_bound(max_window_size);
 
@@ -475,7 +484,7 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
 		f2fs_err_ratelimited(dic->sbi,
 				"%s ZSTD invalid rlen:%zu, expected:%lu",
 				__func__, dic->rlen,
-				PAGE_SIZE << dic->log_cluster_size);
+				(unsigned long)F2FS_BLKSIZE(dic->sbi) << dic->log_cluster_size);
 		return -EIO;
 	}
 
@@ -550,8 +559,6 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
 {
 	if (!f2fs_compressed_file(inode))
 		return true;
-	if (f2fs_has_subpage_blocks(F2FS_I_SB(inode)))
-		return false;
 	return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
 }
 
@@ -621,13 +628,23 @@ static void *f2fs_vmap(struct page **pages, unsigned int count)
 	return buf;
 }
 
+static void f2fs_vunmap(void *vaddr, unsigned int count)
+{
+	if (!vaddr)
+		return;
+	vm_unmap_ram((void *)((unsigned long)vaddr & PAGE_MASK), count);
+}
+
 static int f2fs_compress_pages(struct compress_ctx *cc)
 {
 	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
 	struct f2fs_inode_info *fi = F2FS_I(cc->inode);
 	const struct f2fs_compress_ops *cops =
 				f2fs_cops[fi->i_compress_algorithm];
-	unsigned int max_len, new_nr_cpages;
+	unsigned int max_len, new_cblocks;
+	unsigned int nr_rfolios, needed_folios;
+	unsigned int cbuf_blk_ofs = f2fs_has_subpage_blocks(sbi) ? 1 : 0;
+	void *cbuf_vaddr;
 	u32 chksum = 0;
 	int i, ret;
 
@@ -640,9 +657,12 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
 			goto out;
 	}
 
+	nr_rfolios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
+
 	max_len = COMPRESS_HEADER_SIZE + cc->clen;
-	cc->nr_cpages = DIV_ROUND_UP(max_len, PAGE_SIZE);
-	cc->valid_nr_cpages = cc->nr_cpages;
+	cc->valid_nr_cblocks = DIV_ROUND_UP(max_len, F2FS_BLKSIZE(sbi));
+	cc->nr_cpages = DIV_ROUND_UP(cbuf_blk_ofs + cc->valid_nr_cblocks,
+					F2FS_BLKS_PER_PAGE(sbi));
 
 	cc->cpages = page_array_alloc(sbi, cc->nr_cpages);
 	if (!cc->cpages) {
@@ -653,23 +673,24 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
 	for (i = 0; i < cc->nr_cpages; i++)
 		cc->cpages[i] = f2fs_compress_alloc_page();
 
-	cc->rbuf = f2fs_vmap(cc->rpages, cc->cluster_size);
+	cc->rbuf = f2fs_vmap(cc->rpages, nr_rfolios);
 	if (!cc->rbuf) {
 		ret = -ENOMEM;
 		goto out_free_cpages;
 	}
 
-	cc->cbuf = f2fs_vmap(cc->cpages, cc->nr_cpages);
-	if (!cc->cbuf) {
+	cbuf_vaddr = f2fs_vmap(cc->cpages, cc->nr_cpages);
+	if (!cbuf_vaddr) {
 		ret = -ENOMEM;
 		goto out_vunmap_rbuf;
 	}
+	cc->cbuf = cbuf_vaddr + (cbuf_blk_ofs << sbi->log_blocksize);
 
 	ret = cops->compress_pages(cc);
 	if (ret)
 		goto out_vunmap_cbuf;
 
-	max_len = PAGE_SIZE * (cc->cluster_size - 1) - COMPRESS_HEADER_SIZE;
+	max_len = F2FS_BLKSIZE(sbi) * (cc->cluster_size - 1) - COMPRESS_HEADER_SIZE;
 
 	if (cc->clen > max_len) {
 		ret = -EAGAIN;
@@ -685,17 +706,19 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
 	for (i = 0; i < COMPRESS_DATA_RESERVED_SIZE; i++)
 		cc->cbuf->reserved[i] = cpu_to_le32(0);
 
-	new_nr_cpages = DIV_ROUND_UP(cc->clen + COMPRESS_HEADER_SIZE, PAGE_SIZE);
+	new_cblocks = DIV_ROUND_UP(cc->clen + COMPRESS_HEADER_SIZE, F2FS_BLKSIZE(sbi));
 
-	/* zero out any unused part of the last page */
+	/* zero out any unused part of the last block */
 	memset(&cc->cbuf->cdata[cc->clen], 0,
-			(new_nr_cpages * PAGE_SIZE) -
+			(new_cblocks * F2FS_BLKSIZE(sbi)) -
 			(cc->clen + COMPRESS_HEADER_SIZE));
 
-	vm_unmap_ram(cc->cbuf, cc->nr_cpages);
-	vm_unmap_ram(cc->rbuf, cc->cluster_size);
+	f2fs_vunmap(cc->cbuf, cc->nr_cpages);
+	f2fs_vunmap(cc->rbuf, nr_rfolios);
 
-	for (i = new_nr_cpages; i < cc->nr_cpages; i++) {
+	needed_folios = DIV_ROUND_UP(cbuf_blk_ofs + new_cblocks,
+					F2FS_BLKS_PER_PAGE(sbi));
+	for (i = needed_folios; i < cc->nr_cpages; i++) {
 		f2fs_compress_free_page(cc->cpages[i]);
 		cc->cpages[i] = NULL;
 	}
@@ -703,16 +726,16 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
 	if (cops->destroy_compress_ctx)
 		cops->destroy_compress_ctx(cc);
 
-	cc->valid_nr_cpages = new_nr_cpages;
+	cc->valid_nr_cblocks = new_cblocks;
 
 	trace_f2fs_compress_pages_end(cc->inode, cc->cluster_idx,
 							cc->clen, ret);
 	return 0;
 
 out_vunmap_cbuf:
-	vm_unmap_ram(cc->cbuf, cc->nr_cpages);
+	f2fs_vunmap(cc->cbuf, cc->nr_cpages);
 out_vunmap_rbuf:
-	vm_unmap_ram(cc->rbuf, cc->cluster_size);
+	f2fs_vunmap(cc->rbuf, nr_rfolios);
 out_free_cpages:
 	for (i = 0; i < cc->nr_cpages; i++) {
 		if (cc->cpages[i])
@@ -741,7 +764,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 	const struct f2fs_compress_ops *cops =
 			f2fs_cops[fi->i_compress_algorithm];
 	bool bypass_callback = false;
-	int ret;
+	int i, ret;
 
 	trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx,
 				dic->cluster_size, fi->i_compress_algorithm);
@@ -758,9 +781,9 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 	}
 
 	dic->clen = le32_to_cpu(dic->cbuf->clen);
-	dic->rlen = PAGE_SIZE << dic->log_cluster_size;
+	dic->rlen = F2FS_BLKSIZE(sbi) << dic->log_cluster_size;
 
-	if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) {
+	if (dic->clen > F2FS_BLKSIZE(sbi) * dic->valid_nr_cblocks - COMPRESS_HEADER_SIZE) {
 		ret = -EFSCORRUPTED;
 
 		/* Avoid f2fs_commit_super in irq context */
@@ -787,6 +810,31 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 		}
 	}
 
+	/*
+	 * Folios with f2fs_folio_state were decompressed into bounce pages.
+	 * Copy only the blocks that are not uptodate, so that newer data in
+	 * the uptodate (possibly dirty) blocks is kept.
+	 */
+	for (i = 0; !ret && i < dic->nr_rpages; i++) {
+		struct page *rpage = dic->rpages[i];
+		struct folio *folio;
+		pgoff_t lblk;
+		unsigned int b;
+
+		if (!rpage || dic->tpages[i] == rpage)
+			continue;
+
+		folio = page_folio(rpage);
+		lblk = f2fs_folio_lblk(sbi, folio);
+		for (b = 0; b < f2fs_blocks_per_folio(sbi, folio); b++) {
+			size_t off = (size_t)b << sbi->log_blocksize;
+
+			if (!f2fs_ffs_test_blk_uptodate(folio, lblk + b))
+				memcpy_page(rpage, off, dic->tpages[i], off,
+					    F2FS_BLKSIZE(sbi));
+		}
+	}
+
 out_release:
 	f2fs_release_decomp_mem(dic, bypass_callback, false);
 
@@ -796,8 +844,8 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 	f2fs_decompress_end_io(dic, ret, in_task);
 }
 
-static void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
-		struct folio *folio, nid_t ino, block_t blkaddr);
+static void f2fs_cache_compressed_block(struct f2fs_sb_info *sbi,
+		struct folio *folio, size_t offset, nid_t ino, block_t blkaddr);
 
 /*
  * This is called when a page of a compressed cluster has been read from disk
@@ -805,29 +853,35 @@ static void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
  * page being waited on in the cluster, and if so, it decompresses the cluster
  * (or in the case of a failure, cleans up without actually decompressing).
  */
-void f2fs_end_read_compressed_page(struct folio *folio, bool failed,
-		block_t blkaddr, bool in_task)
+void f2fs_end_read_compressed_page(struct folio *folio, size_t offset,
+		bool failed, block_t blkaddr, unsigned int nr_blocks,
+		bool in_task)
 {
 	struct decompress_io_ctx *dic = folio->private;
 	struct f2fs_sb_info *sbi = dic->sbi;
+	int i;
 
-	dec_cache_count(sbi, F2FS_RD_DATA);
+	sub_cache_counts(sbi, F2FS_RD_DATA, nr_blocks);
 
 	if (failed)
 		WRITE_ONCE(dic->failed, true);
-	else if (blkaddr && in_task)
-		f2fs_cache_compressed_page(sbi, folio,
-					dic->inode->i_ino, blkaddr);
+	else if (blkaddr && in_task) {
+		for (i = 0; i < nr_blocks; i++)
+			f2fs_cache_compressed_block(sbi, folio,
+					offset + i * F2FS_BLKSIZE(sbi),
+					dic->inode->i_ino,
+					blkaddr + i);
+	}
 
-	if (atomic_dec_and_test(&dic->remaining_pages))
+	if (atomic_sub_and_test(nr_blocks, &dic->remaining_blocks))
 		f2fs_decompress_cluster(dic, in_task);
 }
 
-static bool is_page_in_cluster(struct compress_ctx *cc, pgoff_t index)
+static bool is_block_in_cluster(struct compress_ctx *cc, pgoff_t lblk)
 {
 	if (cc->cluster_idx == NULL_CLUSTER)
 		return true;
-	return cc->cluster_idx == cluster_idx(cc, index);
+	return cc->cluster_idx == cluster_idx(cc, lblk);
 }
 
 bool f2fs_cluster_is_empty(struct compress_ctx *cc)
@@ -837,19 +891,40 @@ bool f2fs_cluster_is_empty(struct compress_ctx *cc)
 
 static bool f2fs_cluster_is_full(struct compress_ctx *cc)
 {
-	return cc->cluster_size == cc->nr_rpages;
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
+	int i;
+
+	if (cc->nr_rpages != nr_folios)
+		return false;
+
+	/* PG_dirty is cleared for writeback; without ffs it covered every block */
+	for (i = 0; i < nr_folios; i++) {
+		struct folio *folio = page_folio(cc->rpages[i]);
+		unsigned int nr_blocks = f2fs_blocks_per_folio(sbi, folio);
+		unsigned int start, count;
+
+		if (f2fs_folio_has_ffs(folio) &&
+		    (!f2fs_ffs_dirty_blk_range(folio, 0, nr_blocks,
+					       &start, &count) ||
+		     count != nr_blocks))
+			return false;
+	}
+	return true;
 }
 
-bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index)
+bool f2fs_cluster_can_merge_block(struct compress_ctx *cc, pgoff_t lblk)
 {
 	if (f2fs_cluster_is_empty(cc))
 		return true;
-	return is_page_in_cluster(cc, index);
+	return is_block_in_cluster(cc, lblk);
 }
 
 bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
 				int index, int nr_pages, bool uptodate)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	unsigned long pgidx = page_folio(pages[index])->index;
 	int i = uptodate ? 0 : 1;
 
@@ -857,13 +932,13 @@ bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
 	 * when uptodate set to true, try to check all pages in cluster is
 	 * uptodate or not.
 	 */
-	if (uptodate && (pgidx % cc->cluster_size))
+	if (uptodate && (pgidx % nr_folios))
 		return false;
 
-	if (nr_pages - index < cc->cluster_size)
+	if (nr_pages - index < nr_folios)
 		return false;
 
-	for (; i < cc->cluster_size; i++) {
+	for (; i < nr_folios; i++) {
 		struct folio *folio = page_folio(pages[index + i]);
 
 		if (folio->index != pgidx + i)
@@ -877,17 +952,19 @@ bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
 
 static bool cluster_has_invalid_data(struct compress_ctx *cc)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
 	loff_t i_size = i_size_read(cc->inode);
-	unsigned nr_pages = DIV_ROUND_UP(i_size, PAGE_SIZE);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	int i;
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		struct page *page = cc->rpages[i];
 
-		f2fs_bug_on(F2FS_I_SB(cc->inode), !page);
+		f2fs_bug_on(sbi, !page);
 
-		/* beyond EOF */
-		if (page_folio(page)->index >= nr_pages)
+		/* any block beyond EOF */
+		if (folio_pos(page_folio(page)) +
+		    folio_size(page_folio(page)) - F2FS_BLKSIZE(sbi) >= i_size)
 			return true;
 	}
 	return false;
@@ -1014,17 +1091,17 @@ static int f2fs_decompressed_blocks(struct inode *inode,
 }
 
 /* return whether cluster is compressed one or not */
-int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
+int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t lblk)
 {
 	return __f2fs_cluster_blocks(inode,
-		index >> F2FS_I(inode)->i_log_cluster_size,
+		lblk >> F2FS_I(inode)->i_log_cluster_size,
 		CLUSTER_IS_COMPR);
 }
 
 /* return whether cluster contains non raw blocks or not */
-bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index)
+bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t lblk)
 {
-	unsigned int cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size;
+	unsigned int cluster_idx = lblk >> F2FS_I(inode)->i_log_cluster_size;
 
 	return f2fs_decompressed_blocks(inode, cluster_idx) !=
 		F2FS_I(inode)->i_cluster_size;
@@ -1045,34 +1122,57 @@ static bool cluster_may_compress(struct compress_ctx *cc)
 
 static void set_cluster_writeback(struct compress_ctx *cc)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	int i;
 
-	for (i = 0; i < cc->cluster_size; i++) {
-		if (cc->rpages[i])
-			set_page_writeback(cc->rpages[i]);
+	for (i = 0; i < nr_folios; i++) {
+		struct folio *folio;
+
+		if (!cc->rpages[i])
+			continue;
+
+		folio = page_folio(cc->rpages[i]);
+		set_page_writeback(cc->rpages[i]);
+		/*
+		 * Attach a folio state before the I/O, as the raw write path
+		 * does, so that the write completion, which does not hold the
+		 * folio lock, never detaches the page private flags kept in
+		 * folio->private.
+		 */
+		if (f2fs_is_multiblock_folio(sbi, folio))
+			f2fs_ffs_find_or_alloc(folio);
 	}
 }
 
 static void cancel_cluster_writeback(struct compress_ctx *cc,
-			struct compress_io_ctx *cic, int submitted)
+			struct compress_io_ctx *cic, int failed_blk)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	int i;
 
 	/* Wait for submitted IOs. */
-	if (submitted > 1) {
+	if (failed_blk > 1) {
 		f2fs_submit_merged_write(F2FS_I_SB(cc->inode), DATA);
-		while (atomic_read(&cic->pending_pages) !=
-					(cc->valid_nr_cpages - submitted + 1))
+		while (atomic_read(&cic->pending_blocks) !=
+					(cc->valid_nr_cblocks - failed_blk + 1))
 			f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
 	}
 
+	inode_add_dirty_blocks(cc->inode, failed_blk);
+
 	/* Cancel writeback and stay locked. */
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		struct folio *folio = page_folio(cc->rpages[i]);
+		unsigned int last_blk_in_folio =
+			((i + 1) << F2FS_LOG_BLKS_PER_PAGE(sbi)) - 1;
 
-		if (i < submitted) {
-			inode_inc_dirty_block(cc->inode);
+		if (last_blk_in_folio < failed_blk) {
 			folio_lock(folio);
+			/* f2fs_write_compressed_pages() cleared its dirty blocks */
+			if (f2fs_folio_has_ffs(folio))
+				f2fs_ffs_mark_subrange_dirty(folio, 0, PAGE_SIZE);
 		}
 		folio_clear_f2fs_gcing(folio);
 		if (folio_test_writeback(folio))
@@ -1082,21 +1182,26 @@ static void cancel_cluster_writeback(struct compress_ctx *cc,
 
 static void set_cluster_dirty(struct compress_ctx *cc)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	int i;
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		struct folio *folio;
 
 		if (!cc->rpages[i])
 			continue;
 		folio = page_folio(cc->rpages[i]);
+		/* The whole cluster is rewritten */
+		if (f2fs_folio_has_ffs(folio))
+			f2fs_ffs_mark_subrange_dirty(folio, 0, folio_size(folio));
 		folio_mark_dirty(folio);
 		folio_set_f2fs_gcing(folio);
 	}
 }
 
 static int prepare_compress_overwrite(struct compress_ctx *cc,
-		struct page **pagep, pgoff_t index, void **fsdata)
+		struct page **pagep, pgoff_t lblk, void **fsdata)
 {
 	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
 	struct address_space *mapping = cc->inode->i_mapping;
@@ -1115,9 +1220,13 @@ static int prepare_compress_overwrite(struct compress_ctx *cc,
 	if (ret)
 		return ret;
 
+	unsigned int nr_folios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
+
 	/* keep folio reference to avoid page reclaim */
-	for (i = 0; i < cc->cluster_size; i++) {
-		folio = f2fs_filemap_get_folio(mapping, start_idx + i,
+	for (i = 0; i < nr_folios; i++) {
+		pgoff_t f_index = f2fs_lblk_to_folio_index(sbi, start_idx) + i;
+
+		folio = f2fs_filemap_get_folio(mapping, f_index,
 				fgp_flag, GFP_NOFS);
 		if (IS_ERR(folio)) {
 			ret = PTR_ERR(folio);
@@ -1147,10 +1256,12 @@ static int prepare_compress_overwrite(struct compress_ctx *cc,
 			goto out;
 	}
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
+		pgoff_t f_index = f2fs_lblk_to_folio_index(sbi, start_idx) + i;
+
 		f2fs_bug_on(sbi, cc->rpages[i]);
 
-		folio = filemap_lock_folio(mapping, start_idx + i);
+		folio = filemap_lock_folio(mapping, f_index);
 		if (IS_ERR(folio)) {
 			/* folio could be truncated */
 			goto release_and_retry;
@@ -1171,7 +1282,7 @@ static int prepare_compress_overwrite(struct compress_ctx *cc,
 
 	if (likely(!ret)) {
 		*fsdata = cc->rpages;
-		*pagep = cc->rpages[offset_in_cluster(cc, index)];
+		*pagep = cc->rpages[offset_in_cluster(cc, lblk)];
 		return cc->cluster_size;
 	}
 
@@ -1184,23 +1295,23 @@ static int prepare_compress_overwrite(struct compress_ctx *cc,
 }
 
 int f2fs_prepare_compress_overwrite(struct inode *inode,
-		struct page **pagep, pgoff_t index, void **fsdata)
+		struct page **pagep, pgoff_t lblk, void **fsdata)
 {
 	struct compress_ctx cc = {
 		.inode = inode,
 		.log_cluster_size = F2FS_I(inode)->i_log_cluster_size,
 		.cluster_size = F2FS_I(inode)->i_cluster_size,
-		.cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size,
+		.cluster_idx = lblk >> F2FS_I(inode)->i_log_cluster_size,
 		.rpages = NULL,
 		.nr_rpages = 0,
 		.vi = NULL, /* can't write to fsverity files */
 	};
 
-	return prepare_compress_overwrite(&cc, pagep, index, fsdata);
+	return prepare_compress_overwrite(&cc, pagep, lblk, fsdata);
 }
 
 bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
-					pgoff_t index, unsigned copied)
+					pgoff_t lblk, unsigned int copied)
 
 {
 	struct compress_ctx cc = {
@@ -1210,7 +1321,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
 		.rpages = fsdata,
 	};
 	struct folio *folio = page_folio(cc.rpages[0]);
-	bool first_index = (index == folio->index);
+	bool first_index = (lblk == f2fs_folio_lblk(F2FS_I_SB(inode), folio));
 
 	if (copied)
 		set_cluster_dirty(&cc);
@@ -1223,12 +1334,15 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
 
 int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
 {
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
 	void *fsdata = NULL;
 	struct page *pagep;
 	struct page **rpages;
 	int log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
-	pgoff_t start_idx = from >> (PAGE_SHIFT + log_cluster_size) <<
+	pgoff_t start_idx = (from >> (sbi->log_blocksize + log_cluster_size)) <<
 							log_cluster_size;
+	unsigned int nr_folios = (1 << log_cluster_size) >>
+					F2FS_LOG_BLKS_PER_PAGE(sbi);
 	int i;
 	int err;
 
@@ -1252,9 +1366,9 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
 
 	rpages = fsdata;
 
-	for (i = (1 << log_cluster_size) - 1; i >= 0; i--) {
+	for (i = nr_folios - 1; i >= 0; i--) {
 		struct folio *folio = page_folio(rpages[i]);
-		loff_t start = (loff_t)folio->index << PAGE_SHIFT;
+		loff_t start = folio_pos(folio);
 		loff_t offset = from > start ? from - start : 0;
 
 		folio_zero_segment(folio, offset, folio_size(folio));
@@ -1266,14 +1380,15 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
 	f2fs_compress_write_end(inode, fsdata, start_idx, true);
 
 	err = filemap_write_and_wait_range(inode->i_mapping,
-			round_down(from, 1 << log_cluster_size << PAGE_SHIFT),
+			round_down(from, 1 << log_cluster_size << sbi->log_blocksize),
 			LLONG_MAX);
 	if (err)
 		return err;
 
 	truncate_pagecache(inode, from);
 
-	return f2fs_do_truncate_blocks(inode, round_up(from, PAGE_SIZE), lock);
+	return f2fs_do_truncate_blocks(inode, round_up(from, F2FS_BLKSIZE(sbi)),
+				       lock);
 }
 
 static int f2fs_write_compressed_pages(struct compress_ctx *cc,
@@ -1303,8 +1418,9 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 	struct node_info ni;
 	struct compress_io_ctx *cic;
 	struct f2fs_lock_context lc;
+	unsigned int nr_rfolios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 	pgoff_t start_idx = start_idx_of_cluster(cc);
-	unsigned int last_index = cc->cluster_size - 1;
+	unsigned int blk_idx, c_folio_idx, c_folio_ofs;
 	loff_t psize;
 	int i, err;
 	bool quota_inode = IS_NOQUOTA(inode);
@@ -1338,10 +1454,10 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 			goto out_put_dnode;
 	}
 
-	folio = page_folio(cc->rpages[last_index]);
+	folio = page_folio(cc->rpages[nr_rfolios - 1]);
 	psize = folio_next_pos(folio);
 
-	err = f2fs_get_node_info(fio.sbi, dn.nid, &ni, false);
+	err = f2fs_get_node_info(sbi, dn.nid, &ni, false);
 	if (err)
 		goto out_put_dnode;
 
@@ -1353,18 +1469,30 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 
 	cic->magic = F2FS_COMPRESSED_PAGE_MAGIC;
 	cic->inode = inode;
-	atomic_set(&cic->pending_pages, cc->valid_nr_cpages);
-	cic->rpages = page_array_alloc(sbi, cc->cluster_size);
+	atomic_set(&cic->pending_blocks, cc->valid_nr_cblocks);
+	cic->rpages = page_array_alloc(sbi, nr_rfolios);
 	if (!cic->rpages)
 		goto out_put_cic;
 
-	cic->nr_rpages = cc->cluster_size;
+	cic->nr_rpages = nr_rfolios;
+	cic->cpages = cc->cpages;
+	cic->nr_cpages = cc->nr_cpages;
+	cc->cpages = NULL;
+
+	for (i = 0; i < cic->nr_cpages; i++) {
+		pgoff_t fidx;
+
+		if (!cic->cpages[i])
+			break;
+
+		fidx = f2fs_has_subpage_blocks(sbi) ?
+				(f2fs_lblk_to_folio_index(sbi, start_idx) + i) :
+				(start_idx + 1 + i);
 
-	for (i = 0; i < cc->valid_nr_cpages; i++) {
-		f2fs_set_compressed_page(cc->cpages[i], inode,
-				page_folio(cc->rpages[i + 1])->index, cic);
-		fio.compressed_page = cc->cpages[i];
+		f2fs_set_compressed_page(cic->cpages[i], inode, fidx, cic);
+	}
 
+	for (i = 0; i < cc->valid_nr_cblocks; i++) {
 		fio.old_blkaddr = data_blkaddr(dn.inode, dn.node_entry,
 						dn.ofs_in_node + i + 1);
 
@@ -1374,14 +1502,15 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 
 	set_cluster_writeback(cc);
 
-	for (i = 0; i < cc->cluster_size; i++)
+	for (i = 0; i < nr_rfolios; i++)
 		cic->rpages[i] = cc->rpages[i];
 
 	for (i = 0; i < cc->cluster_size; i++, dn.ofs_in_node++) {
 		block_t blkaddr;
+		unsigned int r_idx = i >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 
 		blkaddr = f2fs_data_blkaddr(&dn);
-		fio.folio = page_folio(cc->rpages[i]);
+		fio.folio = page_folio(cc->rpages[r_idx]);
 		fio.old_blkaddr = blkaddr;
 
 		/* cluster header */
@@ -1397,7 +1526,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 		if (fio.compr_blocks && __is_valid_data_blkaddr(blkaddr))
 			fio.compr_blocks++;
 
-		if (i > cc->valid_nr_cpages) {
+		if (i > cc->valid_nr_cblocks) {
 			if (__is_valid_data_blkaddr(blkaddr)) {
 				f2fs_invalidate_blocks(sbi, blkaddr, 1);
 				f2fs_update_data_blkaddr(&dn, NEW_ADDR);
@@ -1407,9 +1536,13 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 
 		f2fs_bug_on(fio.sbi, blkaddr == NULL_ADDR);
 
-		fio.compressed_page = cc->cpages[i - 1];
+		blk_idx = f2fs_has_subpage_blocks(sbi) ? i : (i - 1);
+		c_folio_idx = blk_idx / F2FS_BLKS_PER_PAGE(sbi);
+		c_folio_ofs = blk_idx % F2FS_BLKS_PER_PAGE(sbi);
+
+		fio.compressed_page = cic->cpages[c_folio_idx];
+		fio.folio_offset = c_folio_ofs;
 
-		cc->cpages[i - 1] = NULL;
 		fio.submitted = 0;
 		f2fs_outplace_write_data(&dn, &fio);
 		if (unlikely(!fio.submitted)) {
@@ -1420,13 +1553,20 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 		(*submitted)++;
 unlock_continue:
 		inode_dec_dirty_block(cc->inode);
-		folio_unlock(fio.folio);
+		if ((i + 1) % F2FS_BLKS_PER_PAGE(sbi) == 0) {
+			f2fs_ffs_clear_subrange_dirty_and_test(fio.folio,
+							       0, PAGE_SIZE);
+			folio_unlock(fio.folio);
+		}
 	}
 
+	/* count pages like the raw write path */
+	*submitted = DIV_ROUND_UP(*submitted, F2FS_BLKS_PER_PAGE(sbi));
+
 	if (fio.compr_blocks)
 		f2fs_i_compr_blocks_update(inode, fio.compr_blocks - 1, false);
-	f2fs_i_compr_blocks_update(inode, cc->valid_nr_cpages, true);
-	add_compr_block_stat(inode, cc->valid_nr_cpages);
+	f2fs_i_compr_blocks_update(inode, cc->valid_nr_cblocks, true);
+	add_compr_block_stat(inode, cc->valid_nr_cblocks);
 
 	set_inode_flag(cc->inode, FI_APPEND_WRITE);
 
@@ -1442,13 +1582,18 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 	spin_unlock(&fi->i_size_lock);
 
 	f2fs_put_rpages(cc);
-	page_array_free(sbi, cc->cpages, cc->nr_cpages);
-	cc->cpages = NULL;
 	f2fs_destroy_compress_ctx(cc, false);
 	return 0;
 
 out_free_page_array:
-	page_array_free(sbi, cic->rpages, cc->cluster_size);
+	page_array_free(sbi, cic->rpages, nr_rfolios);
+
+	for (i = 0; i < cic->nr_cpages; i++) {
+		if (!cic->cpages[i])
+			continue;
+		f2fs_compress_free_page(cic->cpages[i]);
+	}
+	page_array_free(sbi, cic->cpages, cic->nr_cpages);
 out_put_cic:
 	kmem_cache_free(cic_entry_slab, cic);
 out_put_dnode:
@@ -1459,18 +1604,21 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
 	else
 		f2fs_unlock_op(sbi, &lc);
 out_free:
-	for (i = 0; i < cc->valid_nr_cpages; i++) {
-		f2fs_compress_free_page(cc->cpages[i]);
-		cc->cpages[i] = NULL;
+	if (cc->cpages) {
+		for (i = 0; i < cc->nr_cpages; i++) {
+			if (!cc->cpages[i])
+				continue;
+			f2fs_compress_free_page(cc->cpages[i]);
+		}
+		page_array_free(sbi, cc->cpages, cc->nr_cpages);
+		cc->cpages = NULL;
 	}
-	page_array_free(sbi, cc->cpages, cc->nr_cpages);
-	cc->cpages = NULL;
 	return -EAGAIN;
 }
 
-void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
+void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio,
+				unsigned int nr_blocks)
 {
-	struct page *page = &folio->page;
 	struct f2fs_sb_info *sbi = bio->bi_private;
 	struct compress_io_ctx *cic = folio->private;
 	enum count_type type = WB_DATA_TYPE(folio, true);
@@ -1479,13 +1627,17 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 	if (unlikely(bio->bi_status != BLK_STS_OK))
 		mapping_set_error(cic->inode->i_mapping, -EIO);
 
-	f2fs_compress_free_page(page);
-
-	if (atomic_dec_return(&cic->pending_pages)) {
-		dec_cache_count(sbi, type);
+	if (!atomic_sub_and_test(nr_blocks, &cic->pending_blocks)) {
+		sub_cache_counts(sbi, type, nr_blocks);
 		return;
 	}
 
+	for (i = 0; i < cic->nr_cpages; i++) {
+		if (cic->cpages[i])
+			f2fs_compress_free_page(cic->cpages[i]);
+	}
+	page_array_free(sbi, cic->cpages, cic->nr_cpages);
+
 	for (i = 0; i < cic->nr_rpages; i++) {
 		struct folio *rfolio;
 
@@ -1499,12 +1651,12 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 	kmem_cache_free(cic_entry_slab, cic);
 
 	/*
-	 * Make sure dec_cache_count() is the last access to sbi.
+	 * Make sure sub_cache_counts() is the last access to sbi.
 	 * Once it drops the F2FS_WB_CP_DATA counter to zero, the
 	 * unmount thread can proceed to destroy sbi and
 	 * sbi->page_array_slab.
 	 */
-	dec_cache_count(sbi, type);
+	sub_cache_counts(sbi, type, nr_blocks);
 }
 
 static int f2fs_write_raw_pages(struct compress_ctx *cc,
@@ -1517,10 +1669,12 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
 	struct f2fs_lock_context lc;
 	int submitted, compr_blocks, i;
 	int ret = 0;
+	unsigned int nr_folios = cc->cluster_size >>
+					F2FS_LOG_BLKS_PER_PAGE(sbi);
 
 	compr_blocks = f2fs_compressed_blocks(cc);
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		if (!cc->rpages[i])
 			continue;
 
@@ -1535,7 +1689,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
 	if (compr_blocks > 0)
 		f2fs_lock_op(sbi, &lc);
 
-	for (i = 0; i < cc->cluster_size; i++) {
+	for (i = 0; i < nr_folios; i++) {
 		struct folio *folio;
 
 		if (!cc->rpages[i])
@@ -1639,17 +1793,27 @@ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
 		bool pre_alloc)
 {
 	const struct f2fs_compress_ops *cops = f2fs_cops[dic->compress_algorithm];
+	unsigned int nr_rfolios = dic->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(dic->sbi);
+	unsigned int cbuf_blk_ofs = f2fs_has_subpage_blocks(dic->sbi) ? 1 : 0;
+	void *cbuf_vaddr;
 	int i;
 
 	if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
 		return 0;
 
-	dic->tpages = page_array_alloc(dic->sbi, dic->cluster_size);
+	dic->tpages = page_array_alloc(dic->sbi, nr_rfolios);
 	if (!dic->tpages)
 		return -ENOMEM;
 
-	for (i = 0; i < dic->cluster_size; i++) {
-		if (dic->rpages[i]) {
+	for (i = 0; i < nr_rfolios; i++) {
+		/*
+		 * A folio with f2fs_folio_state may have uptodate blocks newer
+		 * than the on-disk cluster (e.g., dirty blocks left after a
+		 * partial truncate). Decompress it into a bounce page instead,
+		 * see f2fs_decompress_cluster().
+		 */
+		if (dic->rpages[i] &&
+		    !f2fs_folio_has_ffs(page_folio(dic->rpages[i]))) {
 			dic->tpages[i] = dic->rpages[i];
 			continue;
 		}
@@ -1657,14 +1821,16 @@ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
 		dic->tpages[i] = f2fs_compress_alloc_page();
 	}
 
-	dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size);
+	dic->rbuf = f2fs_vmap(dic->tpages, nr_rfolios);
 	if (!dic->rbuf)
 		return -ENOMEM;
 
-	dic->cbuf = f2fs_vmap(dic->cpages, dic->nr_cpages);
-	if (!dic->cbuf)
+	cbuf_vaddr = f2fs_vmap(dic->cpages, dic->nr_cpages);
+	if (!cbuf_vaddr)
 		return -ENOMEM;
 
+	dic->cbuf = cbuf_vaddr + (cbuf_blk_ofs << dic->sbi->log_blocksize);
+
 	if (cops->init_decompress_ctx)
 		return cops->init_decompress_ctx(dic);
 
@@ -1675,6 +1841,7 @@ static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
 		bool bypass_destroy_callback, bool pre_alloc)
 {
 	const struct f2fs_compress_ops *cops = f2fs_cops[dic->compress_algorithm];
+	unsigned int nr_rfolios = dic->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(dic->sbi);
 
 	if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
 		return;
@@ -1682,11 +1849,15 @@ static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
 	if (!bypass_destroy_callback && cops->destroy_decompress_ctx)
 		cops->destroy_decompress_ctx(dic);
 
-	if (dic->cbuf)
-		vm_unmap_ram(dic->cbuf, dic->nr_cpages);
+	if (dic->cbuf) {
+		f2fs_vunmap(dic->cbuf, dic->nr_cpages);
+		dic->cbuf = NULL;
+	}
 
-	if (dic->rbuf)
-		vm_unmap_ram(dic->rbuf, dic->cluster_size);
+	if (dic->rbuf) {
+		f2fs_vunmap(dic->rbuf, nr_rfolios);
+		dic->rbuf = NULL;
+	}
 }
 
 static void f2fs_free_dic(struct decompress_io_ctx *dic,
@@ -1697,13 +1868,17 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
 	struct decompress_io_ctx *dic;
 	pgoff_t start_idx = start_idx_of_cluster(cc);
 	struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+	unsigned int nr_rfolios = cc->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
+	unsigned int cbuf_blk_ofs = f2fs_has_subpage_blocks(sbi) ? 1 : 0;
+	unsigned int nr_cpages = DIV_ROUND_UP(cbuf_blk_ofs + cc->valid_nr_cblocks,
+					F2FS_BLKS_PER_PAGE(sbi));
 	int i, ret;
 
 	dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO, false, sbi);
 	if (!dic)
 		return ERR_PTR(-ENOMEM);
 
-	dic->rpages = page_array_alloc(sbi, cc->cluster_size);
+	dic->rpages = page_array_alloc(sbi, nr_rfolios);
 	if (!dic->rpages) {
 		kmem_cache_free(dic_entry_slab, dic);
 		return ERR_PTR(-ENOMEM);
@@ -1713,33 +1888,38 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
 	dic->inode = cc->inode;
 	dic->sbi = sbi;
 	dic->compress_algorithm = F2FS_I(cc->inode)->i_compress_algorithm;
-	atomic_set(&dic->remaining_pages, cc->nr_cpages);
+	atomic_set(&dic->remaining_blocks, cc->valid_nr_cblocks);
 	dic->cluster_idx = cc->cluster_idx;
 	dic->cluster_size = cc->cluster_size;
 	dic->log_cluster_size = cc->log_cluster_size;
-	dic->nr_cpages = cc->nr_cpages;
+	dic->valid_nr_cblocks = cc->valid_nr_cblocks;
+	dic->nr_cpages = nr_cpages;
 	refcount_set(&dic->refcnt, 1);
 	dic->failed = false;
 	dic->vi = cc->vi;
 
-	for (i = 0; i < dic->cluster_size; i++)
+	for (i = 0; i < nr_rfolios; i++)
 		dic->rpages[i] = cc->rpages[i];
-	dic->nr_rpages = cc->cluster_size;
+	dic->nr_rpages = nr_rfolios;
 
-	dic->cpages = page_array_alloc(sbi, dic->nr_cpages);
+	dic->cpages = page_array_alloc(sbi, nr_cpages);
 	if (!dic->cpages) {
 		ret = -ENOMEM;
 		goto out_free;
 	}
 
-	for (i = 0; i < dic->nr_cpages; i++) {
+	for (i = 0; i < nr_cpages; i++) {
 		struct page *page;
+		pgoff_t fidx = f2fs_has_subpage_blocks(sbi) ?
+				(f2fs_lblk_to_folio_index(sbi, start_idx) + i) :
+				(start_idx + 1 + i);
 
 		page = f2fs_compress_alloc_page();
-		f2fs_set_compressed_page(page, cc->inode,
-					start_idx + i + 1, dic);
+		f2fs_set_compressed_page(page, cc->inode, fidx, dic);
 		dic->cpages[i] = page;
 	}
+	dic->valid_nr_cblocks = cc->valid_nr_cblocks;
+	dic->nr_cpages = nr_cpages;
 
 	ret = f2fs_prepare_decomp_mem(dic, true);
 	if (ret)
@@ -1758,18 +1938,19 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
 	int i;
 	/* use sbi in dic to avoid UFA of dic->inode*/
 	struct f2fs_sb_info *sbi = dic->sbi;
+	unsigned int nr_rfolios = dic->cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi);
 
 	f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
 
 	if (dic->tpages) {
-		for (i = 0; i < dic->cluster_size; i++) {
-			if (dic->rpages[i])
+		for (i = 0; i < nr_rfolios; i++) {
+			if (dic->tpages[i] == dic->rpages[i])
 				continue;
 			if (!dic->tpages[i])
 				continue;
 			f2fs_compress_free_page(dic->tpages[i]);
 		}
-		page_array_free(sbi, dic->tpages, dic->cluster_size);
+		page_array_free(sbi, dic->tpages, nr_rfolios);
 	}
 
 	if (dic->cpages) {
@@ -1812,15 +1993,19 @@ static void f2fs_verify_cluster(struct work_struct *work)
 	int i;
 
 	/* Verify, update, and unlock the decompressed pages. */
-	for (i = 0; i < dic->cluster_size; i++) {
+	for (i = 0; i < dic->nr_rpages; i++) {
 		struct page *rpage = dic->rpages[i];
 		struct folio *rfolio;
 
 		if (!rpage)
 			continue;
 		rfolio = page_folio(rpage);
-		if (fsverity_verify_folio(dic->vi, rfolio))
+		if (fsverity_verify_folio(dic->vi, rfolio)) {
+			if (f2fs_is_multiblock_folio(dic->sbi, rfolio))
+				f2fs_ffs_mark_subrange_uptodate(rfolio, 0,
+							folio_size(rfolio));
 			folio_mark_uptodate(rfolio);
+		}
 		folio_unlock(rfolio);
 	}
 
@@ -1849,16 +2034,20 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
 	}
 
 	/* Update and unlock the cluster's pagecache pages. */
-	for (i = 0; i < dic->cluster_size; i++) {
+	for (i = 0; i < dic->nr_rpages; i++) {
 		struct page *rpage = dic->rpages[i];
 
 		if (!rpage)
 			continue;
 
-		if (failed)
+		if (failed) {
 			ClearPageUptodate(rpage);
-		else
+		} else {
+			if (f2fs_is_multiblock_folio(dic->sbi, page_folio(rpage)))
+				f2fs_ffs_mark_subrange_uptodate(page_folio(rpage), 0,
+								folio_size(page_folio(rpage)));
 			SetPageUptodate(rpage);
+		}
 		unlock_page(rpage);
 	}
 
@@ -1916,8 +2105,8 @@ void f2fs_invalidate_compress_pages_range(struct f2fs_sb_info *sbi,
 	f2fs_drop_cache_range(COMPRESS_CACHE(sbi), blkaddr, len, false);
 }
 
-static void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
-		struct folio *folio, nid_t ino, block_t blkaddr)
+static void f2fs_cache_compressed_block(struct f2fs_sb_info *sbi,
+		struct folio *folio, size_t offset, nid_t ino, block_t blkaddr)
 {
 	struct f2fs_cached_block *entry;
 
@@ -1961,13 +2150,14 @@ static void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
 	}
 
 	entry->ino = ino;
-	memcpy(cache_address(entry), folio_address(folio), sbi->blocksize);
+	memcpy(cache_address(entry), folio_address(folio) + offset,
+							sbi->blocksize);
 	f2fs_cache_set_uptodate(entry);
 	f2fs_put_cache(entry, true);
 }
 
-bool f2fs_load_compressed_folio(struct f2fs_sb_info *sbi, struct folio *folio,
-								block_t blkaddr)
+bool f2fs_load_compressed_block(struct f2fs_sb_info *sbi, struct folio *folio,
+				size_t offset, block_t blkaddr)
 {
 	struct f2fs_cached_block *entry;
 	bool hitted = false;
@@ -1982,8 +2172,8 @@ bool f2fs_load_compressed_folio(struct f2fs_sb_info *sbi, struct folio *folio,
 		if (f2fs_is_compress_cache(entry) &&
 				f2fs_cache_test_uptodate(entry)) {
 			atomic_inc(&sbi->compress_page_hit);
-			memcpy(folio_address(folio),
-				cache_address(entry), folio_size(folio));
+			memcpy(folio_address(folio) + offset,
+				cache_address(entry), sbi->blocksize);
 			hitted = true;
 		}
 		f2fs_put_cache(entry, true);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index a7cc7231b572..869005dfb40b 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -150,12 +150,17 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
 		bool finished = true;
 		bool uptodate = bio->bi_status == BLK_STS_OK;
 
-		if (!folio_test_large(folio) &&
-		    f2fs_is_compressed_page(folio)) {
+		if (f2fs_is_compressed_page(folio)) {
+			struct decompress_io_ctx *dic = folio->private;
+
+			/* The cluster may be unlocked, so do not use its inode. */
+			nr_blocks = fi.length >> dic->sbi->log_blocksize;
 			if (ctx && !ctx->decompression_attempted)
-				f2fs_end_read_compressed_page(folio, true, 0,
+				f2fs_end_read_compressed_page(folio, fi.offset,
+							true, 0, nr_blocks,
 							in_task);
-			f2fs_put_folio_dic(folio, in_task);
+			while (nr_blocks--)
+				f2fs_put_folio_dic(folio, in_task);
 			continue;
 		}
 
@@ -265,14 +270,18 @@ static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx,
 
 	bio_for_each_folio_all(fi, ctx->bio) {
 		struct folio *folio = fi.folio;
+		struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
+		unsigned int nr_blocks = fi.length >> sbi->log_blocksize;
 
-		if (f2fs_is_compressed_page(folio))
-			f2fs_end_read_compressed_page(folio, false, blkaddr,
-						      in_task);
-		else
+		if (f2fs_is_compressed_page(folio)) {
+			f2fs_end_read_compressed_page(folio, fi.offset,
+						      false, blkaddr,
+						      nr_blocks, in_task);
+		} else {
 			all_compressed = false;
+		}
 
-		blkaddr++;
+		blkaddr += nr_blocks;
 	}
 
 	ctx->decompression_attempted = true;
@@ -425,7 +434,7 @@ static void f2fs_write_end_bio(struct bio *bio)
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 		if (f2fs_is_compressed_page(folio)) {
-			f2fs_compress_write_end_io(bio, folio);
+			f2fs_compress_write_end_io(bio, folio, nr_blocks);
 			continue;
 		}
 #endif
@@ -1337,9 +1346,14 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
 		io->fio = *fio;
 	}
 
-	bio_offset = bio_folio == fio->folio ? F2FS_FIO_BIO_OFFSET(fio) : 0;
-	bio_len = bio_folio == fio->folio ? F2FS_FIO_BIO_SIZE(fio) :
-					    folio_size(bio_folio);
+	/* A compressed page holds several blocks with subpage blocks. */
+	if (bio_folio == fio->folio || fio->compressed_page) {
+		bio_offset = F2FS_FIO_BIO_OFFSET(fio);
+		bio_len = F2FS_FIO_BIO_SIZE(fio);
+	} else {
+		bio_offset = 0;
+		bio_len = folio_size(bio_folio);
+	}
 
 	if (!bio_add_folio(io->bio, bio_folio, bio_len, bio_offset)) {
 		__submit_merged_bio(io);
@@ -2728,7 +2742,7 @@ static int f2fs_read_single_page(struct inode *inode, struct fsverity_info *vi,
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
-				unsigned nr_pages, sector_t *last_block_in_bio,
+				unsigned int nr_blocks, sector_t *last_block_in_bio,
 				struct readahead_control *rac, bool for_write)
 {
 	struct dnode_of_data dn;
@@ -2765,7 +2779,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 			continue;
 
 		folio = page_folio(page);
-		if ((sector_t)folio->index >= last_block_in_file) {
+		if ((sector_t)f2fs_folio_lblk(sbi, folio) >= last_block_in_file) {
 			folio_zero_segment(folio, 0, folio_size(folio));
 			if (!folio_test_uptodate(folio))
 				folio_mark_uptodate(folio);
@@ -2811,14 +2825,14 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 			ret = -EFAULT;
 			goto out_put_dnode;
 		}
-		cc->nr_cpages++;
+		cc->valid_nr_cblocks++;
 
 		if (!from_dnode && i >= ei.c_len)
 			break;
 	}
 
 	/* nothing to decompress */
-	if (cc->nr_cpages == 0) {
+	if (cc->valid_nr_cblocks == 0) {
 		ret = 0;
 		goto out_put_dnode;
 	}
@@ -2829,8 +2843,12 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 		goto out_put_dnode;
 	}
 
-	for (i = 0; i < cc->nr_cpages; i++) {
-		struct folio *folio = page_folio(dic->cpages[i]);
+	for (i = 0; i < cc->valid_nr_cblocks; i++) {
+		unsigned int blk_idx = f2fs_has_subpage_blocks(sbi) ? (1 + i) : i;
+		unsigned int c_folio_idx = blk_idx / F2FS_BLKS_PER_PAGE(sbi);
+		unsigned int c_folio_ofs = blk_idx % F2FS_BLKS_PER_PAGE(sbi);
+		struct folio *folio = page_folio(dic->cpages[c_folio_idx]);
+		size_t offset = c_folio_ofs * blocksize;
 		block_t blkaddr;
 		struct bio_post_read_ctx *ctx;
 
@@ -2840,8 +2858,8 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 
 		f2fs_wait_on_block_writeback(inode, blkaddr);
 
-		if (f2fs_load_compressed_folio(sbi, folio, blkaddr)) {
-			if (atomic_dec_and_test(&dic->remaining_pages)) {
+		if (f2fs_load_compressed_block(sbi, folio, offset, blkaddr)) {
+			if (atomic_dec_and_test(&dic->remaining_blocks)) {
 				f2fs_decompress_cluster(dic, true);
 				break;
 			}
@@ -2850,7 +2868,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 
 		if (bio && (!page_is_mergeable(sbi, bio,
 					*last_block_in_bio, blkaddr) ||
-		    !f2fs_crypt_mergeable_bio(bio, inode, folio->index, NULL))) {
+		    !f2fs_crypt_mergeable_bio(bio, inode, start_idx + 1 + i, NULL))) {
 submit_and_realloc:
 			f2fs_submit_read_bio(sbi, bio, DATA);
 			bio = NULL;
@@ -2858,11 +2876,11 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
 
 		if (!bio)
 			bio = f2fs_grab_read_bio(inode, cc->vi, blkaddr,
-						 nr_pages - i,
+						 nr_blocks - i,
 						 f2fs_ra_op_flags(rac),
-						 folio->index, for_write);
+						 start_idx + 1 + i, for_write);
 
-		if (!bio_add_folio(bio, folio, blocksize, 0))
+		if (!bio_add_folio(bio, folio, blocksize, offset))
 			goto submit_and_realloc;
 
 		ctx = get_post_read_ctx(bio);
@@ -3525,7 +3543,7 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 		.rpages = NULL,
 		.cpages = NULL,
 		.nr_rpages = 0,
-		.nr_cpages = 0,
+		.valid_nr_cblocks = 0,
 	};
 	pgoff_t nc_cluster_idx = NULL_CLUSTER;
 	pgoff_t index;
@@ -3535,14 +3553,19 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 	unsigned max_nr_pages = nr_pages;
 	int ret = 0;
 
-	if (f2fs_has_multiblock_folios(inode))
+	if (mapping_large_folio_support(inode->i_mapping) ||
+	    (f2fs_has_subpage_blocks(F2FS_I_SB(inode)) &&
+	     !f2fs_compressed_file(inode)))
 		return f2fs_read_data_large_folio(inode, vi, rac, folio);
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	if (f2fs_compressed_file(inode)) {
+		unsigned int nr_rfolios = cc.cluster_size >>
+				F2FS_LOG_BLKS_PER_PAGE(F2FS_I_SB(inode));
+
 		index = rac ? readahead_index(rac) : folio->index;
-		max_nr_pages = round_up(index + nr_pages, cc.cluster_size) -
-				round_down(index, cc.cluster_size);
+		max_nr_pages = round_up(index + nr_pages, nr_rfolios) -
+				round_down(index, nr_rfolios);
 	}
 #endif
 
@@ -3564,16 +3587,16 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 		f2fs_update_read_folio_count(F2FS_I_SB(inode), folio);
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
-		index = folio->index;
+		pgoff_t f_lblk = f2fs_folio_lblk(F2FS_I_SB(inode), folio);
 
 		if (!f2fs_compressed_file(inode))
 			goto read_single_page;
 
 		/* there are remained compressed pages, submit them */
-		if (!f2fs_cluster_can_merge_page(&cc, index)) {
+		if (!f2fs_cluster_can_merge_block(&cc, f_lblk)) {
 			cc.vi = vi;
 			ret = f2fs_read_multi_pages(&cc, &bio,
-						max_nr_pages,
+						max_nr_pages * F2FS_BLKS_PER_PAGE(F2FS_I_SB(inode)),
 						&last_block_in_bio,
 						rac, false);
 			f2fs_destroy_compress_ctx(&cc, false);
@@ -3581,15 +3604,15 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 				goto set_error_page;
 		}
 		if (cc.cluster_idx == NULL_CLUSTER) {
-			if (nc_cluster_idx == index >> cc.log_cluster_size)
+			if (nc_cluster_idx == f_lblk >> cc.log_cluster_size)
 				goto read_single_page;
 
-			ret = f2fs_is_compressed_cluster(inode, index);
+			ret = f2fs_is_compressed_cluster(inode, f_lblk);
 			if (ret < 0)
 				goto set_error_page;
 			else if (!ret) {
 				nc_cluster_idx =
-					index >> cc.log_cluster_size;
+					f_lblk >> cc.log_cluster_size;
 				goto read_single_page;
 			}
 
@@ -3605,15 +3628,21 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 read_single_page:
 #endif
 
-		ret = f2fs_read_single_page(inode, vi, folio, max_nr_pages,
-					    &map, &bio, &last_block_in_bio,
-					    rac);
-		if (ret) {
+		if (f2fs_is_multiblock_folio(F2FS_I_SB(inode), folio)) {
+			ret = f2fs_read_data_large_folio(inode, vi, NULL, folio);
+			if (ret)
+				continue;
+		} else {
+			ret = f2fs_read_single_page(inode, vi, folio,
+						    max_nr_pages, &map, &bio,
+						    &last_block_in_bio, rac);
+			if (ret) {
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 set_error_page:
 #endif
-			folio_zero_segment(folio, 0, folio_size(folio));
-			folio_unlock(folio);
+				folio_zero_segment(folio, 0, folio_size(folio));
+				folio_unlock(folio);
+			}
 		}
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 next_page:
@@ -3625,7 +3654,8 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
 			if (nr_pages == 1 && !f2fs_cluster_is_empty(&cc)) {
 				cc.vi = vi;
 				ret = f2fs_read_multi_pages(&cc, &bio,
-							max_nr_pages,
+							max_nr_pages *
+							F2FS_BLKS_PER_PAGE(F2FS_I_SB(inode)),
 							&last_block_in_bio,
 							rac, false);
 				f2fs_destroy_compress_ctx(&cc, false);
@@ -3934,7 +3964,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
 static int f2fs_write_folio_dirty_range(struct folio *folio, int *submitted,
 					struct writeback_control *wbc,
 					enum iostat_type io_type,
-					u64 start, u64 end)
+					u64 start, u64 end,
+					int compr_blocks)
 {
 	struct inode *inode = folio->mapping->host;
 	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
@@ -3967,6 +3998,7 @@ static int f2fs_write_folio_dirty_range(struct folio *folio, int *submitted,
 			.folio_blkcnt = 1,
 			.encrypted_page = NULL,
 			.submitted = 0,
+			.compr_blocks = (i == 0) ? compr_blocks : 0,
 			.need_lock = LOCK_DONE,
 			.meta_gc = f2fs_meta_inode_gc_required(inode) ? 1 : 0,
 			.io_type = io_type,
@@ -3977,8 +4009,12 @@ static int f2fs_write_folio_dirty_range(struct folio *folio, int *submitted,
 			struct f2fs_folio_state *ffs =
 				(struct f2fs_folio_state *)folio->private;
 
-			/* Write only dirty blocks */
-			if (!f2fs_ffs_test_blk_dirty(folio, data_idx))
+			/*
+			 * Write only dirty blocks, or all blocks of a
+			 * compressed cluster.
+			 */
+			if (!compr_blocks &&
+			    !f2fs_ffs_test_blk_dirty(folio, data_idx))
 				continue;
 			atomic_inc(&ffs->write_blocks_pending);
 		}
@@ -4053,6 +4089,9 @@ static int f2fs_write_folio_dirty_range(struct folio *folio, int *submitted,
 
 		fio.version = ni.version;
 
+		if (fio.compr_blocks && fio.old_blkaddr == COMPRESS_ADDR)
+			f2fs_i_compr_blocks_update(inode, fio.compr_blocks - 1, false);
+
 		f2fs_outplace_write_data(&dn, &fio);
 		f2fs_put_dnode(&dn);
 		/* The outplace write path failed to submit this block. */
@@ -4139,6 +4178,22 @@ static enum f2fs_wb_action f2fs_wb_folio_action(struct f2fs_sb_info *sbi,
 	return F2FS_WB_WRITE;
 }
 
+/*
+ * Hold a bias on write_blocks_pending while the blocks of a multi-block
+ * folio are submitted, so that their completions cannot end the folio
+ * writeback early. Without ffs, the folio dirty flag covered @len bytes.
+ */
+static void f2fs_folio_get_write_bias(struct folio *folio, size_t len)
+{
+	struct f2fs_folio_state *ffs;
+
+	if (!f2fs_folio_has_ffs(folio))
+		f2fs_ffs_mark_subrange_dirty(folio, 0, len);
+	ffs = folio->private;
+	WARN_ON_ONCE(atomic_read(&ffs->write_blocks_pending) != 0);
+	atomic_inc(&ffs->write_blocks_pending);
+}
+
 int f2fs_write_single_data_page(struct folio *folio, int *submitted,
 				struct bio **bio,
 				sector_t *last_block,
@@ -4157,6 +4212,7 @@ int f2fs_write_single_data_page(struct folio *folio, int *submitted,
 	bool need_balance_fs = false;
 	bool quota_inode = IS_NOQUOTA(inode);
 	int err = 0;
+	int local_submitted = 0;
 	struct f2fs_io_info fio = {
 		.sbi = sbi,
 		.ino = inode->i_ino,
@@ -4203,6 +4259,42 @@ int f2fs_write_single_data_page(struct folio *folio, int *submitted,
 
 	folio_zero_segment(folio, offset, folio_size(folio));
 write:
+	if (f2fs_is_multiblock_folio(sbi, folio)) {
+		struct f2fs_lock_context lc;
+		unsigned int to_blk = f2fs_blocks_per_folio(sbi, folio);
+		u64 pos = folio_pos(folio);
+
+		if (!compr_blocks && !f2fs_verity_in_progress(inode))
+			to_blk = min_t(unsigned int,
+					F2FS_BLK_ALIGN(sbi, i_size - pos),
+					to_blk);
+
+		/* Deadlock due to between page->lock and f2fs_lock_op */
+		if (!compr_blocks && !f2fs_trylock_op(sbi, &lc)) {
+			err = -EAGAIN;
+			goto redirty_out;
+		}
+
+		folio_start_writeback(folio);
+		f2fs_folio_get_write_bias(folio, F2FS_BLK_TO_BYTES(sbi, to_blk));
+
+		err = f2fs_write_folio_dirty_range(folio, &local_submitted,
+					wbc, io_type, pos,
+					pos + F2FS_BLK_TO_BYTES(sbi, to_blk),
+					compr_blocks);
+
+		if (!compr_blocks)
+			f2fs_unlock_op(sbi, &lc);
+
+		if (!err)
+			f2fs_ffs_clear_subrange_dirty_and_test(folio, 0,
+					F2FS_BLK_TO_BYTES(sbi, to_blk));
+
+		f2fs_ffs_put_write_pending(folio, 1);
+
+		goto done;
+	}
+
 	if (f2fs_cp_controlled_inode(inode)) {
 		struct f2fs_lock_context lc;
 
@@ -4272,7 +4364,9 @@ int f2fs_write_single_data_page(struct folio *folio, int *submitted,
 	}
 
 	if (submitted)
-		*submitted = fio.submitted;
+		*submitted = f2fs_is_multiblock_folio(sbi, folio) ?
+			DIV_ROUND_UP(local_submitted, F2FS_BLKS_PER_PAGE(sbi)) :
+			fio.submitted;
 
 	return 0;
 
@@ -4317,10 +4411,10 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
 		.rpages = NULL,
 		.nr_rpages = 0,
 		.cpages = NULL,
-		.valid_nr_cpages = 0,
+		.valid_nr_cblocks = 0,
 		.rbuf = NULL,
 		.cbuf = NULL,
-		.rlen = PAGE_SIZE * F2FS_I(inode)->i_cluster_size,
+		.rlen = F2FS_BLKSIZE(sbi) * F2FS_I(inode)->i_cluster_size,
 		.private = NULL,
 	};
 #endif
@@ -4409,6 +4503,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
 				void *fsdata = NULL;
 				struct page *pagep;
 				int ret2;
+				pgoff_t f_lblk = f2fs_folio_lblk(sbi, folio);
 
 				ret = f2fs_init_compress_ctx(&cc);
 				if (ret) {
@@ -4416,8 +4511,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
 					break;
 				}
 
-				if (!f2fs_cluster_can_merge_page(&cc,
-								folio->index)) {
+				if (!f2fs_cluster_can_merge_block(&cc, f_lblk)) {
 					ret = f2fs_write_multi_pages(&cc,
 						&submitted, wbc, io_type);
 					if (!ret)
@@ -4437,14 +4531,14 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
 
 				ret2 = f2fs_prepare_compress_overwrite(
 							inode, &pagep,
-							folio->index, &fsdata);
+							f_lblk, &fsdata);
 				if (ret2 < 0) {
 					ret = ret2;
 					done = 1;
 					break;
 				} else if (ret2 &&
 					(!f2fs_compress_write_end(inode,
-						fsdata, folio->index, 1) ||
+						fsdata, f_lblk, 1) ||
 					 !f2fs_all_cluster_page_ready(&cc,
 						pages, i, nr_pages,
 						false))) {
@@ -4599,7 +4693,6 @@ static int f2fs_write_cache_folios(struct address_space *mapping,
 		clear_inode_flag(inode, FI_HOT_DATA);
 
 	while ((folio = writeback_iter(mapping, wbc, folio, &err))) {
-		struct f2fs_folio_state *ffs = NULL;
 		u64 isize;
 		size_t poff;
 		pgoff_t end_index;
@@ -4623,16 +4716,6 @@ static int f2fs_write_cache_folios(struct address_space *mapping,
 			goto retry_out;
 		}
 
-		if (f2fs_has_subpage_blocks(sbi) &&
-		    f2fs_compressed_file(inode)) {
-			WARN_ON_ONCE(1);
-			mapping_set_error(mapping, -EOPNOTSUPP);
-			folio_clear_dirty_for_io(folio);
-			inode_sub_dirty_blocks(inode, f2fs_blocks_per_folio(sbi, folio));
-			err = -EOPNOTSUPP;
-			goto retry_out;
-		}
-
 		/*
 		 * Writing back one large folio performs multiple block allocations, so
 		 * best-effort balance the filesystem before writing it back.
@@ -4754,25 +4837,12 @@ static int f2fs_write_cache_folios(struct address_space *mapping,
 		 * folio has no ffs, so its whole folio is written.
 		 */
 		if (f2fs_is_multiblock_folio(sbi, folio)) {
-			if (!f2fs_folio_has_ffs(folio))
-				f2fs_ffs_mark_subrange_dirty(folio, 0, end_pos - pos);
-			ffs = (struct f2fs_folio_state *)folio->private;
-			/*
-			 * Multiblock folios can be submitted as multiple bios,
-			 * which may complete while we are still submitting the
-			 * remaining dirty ranges.  Hold a bias on
-			 * write_blocks_pending to prevent folio_end_writeback()
-			 * from being called early.
-			 */
-			if (!bias_added) {
-				WARN_ON_ONCE(atomic_read(&ffs->write_blocks_pending) != 0);
-				atomic_inc(&ffs->write_blocks_pending);
-				bias_added = true;
-			}
+			f2fs_folio_get_write_bias(folio, end_pos - pos);
+			bias_added = true;
 		}
 
 		err = f2fs_write_folio_dirty_range(folio, &submitted, wbc, io_type,
-						   pos, end_pos);
+						   pos, end_pos, 0);
 		folio_submitted += submitted;
 		nwritten += submitted;
 		if (err)
@@ -4928,7 +4998,8 @@ static int __f2fs_write_data_pages(struct address_space *mapping,
 	account_writeback(inode, true);
 
 	blk_start_plug(&plug);
-	if (f2fs_has_multiblock_folios(inode))
+	if (mapping_large_folio_support(mapping) ||
+	    (f2fs_has_subpage_blocks(sbi) && !f2fs_compressed_file(inode)))
 		ret = f2fs_write_cache_folios(mapping, wbc, io_type);
 	else
 		ret = f2fs_write_cache_pages(mapping, wbc, io_type);
@@ -5267,10 +5338,6 @@ static int prepare_large_folio_write_begin(struct inode *inode,
 	size_t need_off = ori_off;
 	int err = 0;
 
-	if (f2fs_has_subpage_blocks(sbi) &&
-	    f2fs_compressed_file(inode))
-		return -EOPNOTSUPP;
-
 	/*
 	 * Reserve blocks without f2fs_balance_fs() since the folio is locked;
 	 * the caller balances after unlocking it if *need_balance is set.
@@ -5464,12 +5531,6 @@ static int f2fs_write_begin(const struct kiocb *iocb,
 		goto fail;
 	}
 
-	if (f2fs_has_subpage_blocks(sbi) &&
-	    f2fs_compressed_file(inode)) {
-		err = -EOPNOTSUPP;
-		goto fail;
-	}
-
 	/*
 	 * We should check this at this moment to avoid deadlock on inode page
 	 * and #0 page. The locking rule for inline_data conversion should be:
@@ -5490,11 +5551,13 @@ static int f2fs_write_begin(const struct kiocb *iocb,
 
 		*fsdata = NULL;
 
+		pgoff_t f_lblk = F2FS_BYTES_TO_BLK(sbi, pos);
+
 		if (len == PAGE_SIZE && !(f2fs_is_atomic_file(inode)))
 			goto repeat;
 
 		ret = f2fs_prepare_compress_overwrite(inode, &page,
-							index, fsdata);
+							f_lblk, fsdata);
 		if (ret < 0) {
 			err = ret;
 			goto fail;
@@ -5670,7 +5733,8 @@ static int f2fs_write_end(const struct kiocb *iocb,
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	/* overwrite compressed file */
 	if (f2fs_compressed_file(inode) && fsdata) {
-		f2fs_compress_write_end(inode, fsdata, folio->index, copied);
+		f2fs_compress_write_end(inode, fsdata,
+				f2fs_folio_lblk(F2FS_I_SB(inode), folio), copied);
 		f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
 
 		if (pos + copied > i_size_read(inode) &&
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 8e38d46c446c..0a11c61ee3cf 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1696,7 +1696,7 @@ struct compress_ctx {
 	unsigned int nr_rpages;		/* total page number in rpages */
 	struct page **cpages;		/* pages store compressed data in cluster */
 	unsigned int nr_cpages;		/* total page number in cpages */
-	unsigned int valid_nr_cpages;	/* valid page number in cpages */
+	unsigned int valid_nr_cblocks;	/* valid block number in cpages */
 	void *rbuf;			/* virtual mapped address on rpages */
 	struct compress_data *cbuf;	/* virtual mapped address on cpages */
 	size_t rlen;			/* valid data length in rbuf */
@@ -1712,7 +1712,9 @@ struct compress_io_ctx {
 	struct inode *inode;		/* inode the context belong to */
 	struct page **rpages;		/* pages store raw data in cluster */
 	unsigned int nr_rpages;		/* total page number in rpages */
-	atomic_t pending_pages;		/* in-flight compressed page count */
+	atomic_t pending_blocks;	/* in-flight compressed block count */
+	struct page **cpages;		/* bounce pages store compressed data */
+	unsigned int nr_cpages;		/* total page number in cpages */
 };
 
 /* Context for decompressing one cluster on the read IO path */
@@ -1727,6 +1729,7 @@ struct decompress_io_ctx {
 	unsigned int nr_rpages;		/* total page number in rpages */
 	struct page **cpages;		/* pages store compressed data in cluster */
 	unsigned int nr_cpages;		/* total page number in cpages */
+	unsigned int valid_nr_cblocks;	/* valid compressed block number */
 	struct page **tpages;		/* temp pages to pad holes in cluster */
 	void *rbuf;			/* virtual mapped address on rpages */
 	struct compress_data *cbuf;	/* virtual mapped address on cpages */
@@ -1734,16 +1737,16 @@ struct decompress_io_ctx {
 	size_t clen;			/* valid data length in cbuf */
 
 	/*
-	 * The number of compressed pages remaining to be read in this cluster.
-	 * This is initially nr_cpages.  It is decremented by 1 each time a page
-	 * has been read (or failed to be read).  When it reaches 0, the cluster
-	 * is decompressed (or an error is reported).
+	 * The number of compressed blocks remaining to be read in this cluster.
+	 * This is initially valid_nr_cblocks.  It is decremented by the number
+	 * of blocks each time they have been read (or failed to be read).  When
+	 * it reaches 0, the cluster is decompressed (or an error is reported).
 	 *
 	 * If an error occurs before all the pages have been submitted for I/O,
 	 * then this will never reach 0.  In this case the I/O submitter is
 	 * responsible for calling f2fs_decompress_end_io() instead.
 	 */
-	atomic_t remaining_pages;
+	atomic_t remaining_blocks;
 
 	/*
 	 * Number of references to this decompress_io_ctx.
@@ -1770,7 +1773,7 @@ struct decompress_io_ctx {
 #define NULL_CLUSTER			((unsigned int)(~0))
 #define MIN_COMPRESS_LOG_SIZE		2
 #define MAX_COMPRESS_LOG_SIZE		8
-#define MAX_COMPRESS_WINDOW_SIZE(log_size)	((PAGE_SIZE) << (log_size))
+#define MAX_COMPRESS_WINDOW_SIZE(sbi, log_size)	((F2FS_BLKSIZE(sbi)) << (log_size))
 
 struct f2fs_gc_kthread {
 	struct task_struct *f2fs_gc_task;
@@ -2338,6 +2341,12 @@ static inline unsigned int f2fs_blocks_per_folio(struct f2fs_sb_info *sbi,
 
 #define F2FS_LOG_BLKS_PER_PAGE(sbi)	(PAGE_SHIFT - (sbi)->log_blocksize)
 
+static inline unsigned int min_compress_log_size(struct f2fs_sb_info *sbi)
+{
+	return max_t(unsigned int, MIN_COMPRESS_LOG_SIZE,
+			F2FS_LOG_BLKS_PER_PAGE(sbi));
+}
+
 static inline bool f2fs_has_subpage_blocks(struct f2fs_sb_info *sbi)
 {
 	return F2FS_BLKSIZE(sbi) < PAGE_SIZE;
@@ -4990,20 +4999,22 @@ enum cluster_check_type {
 bool f2fs_is_compressed_page(struct folio *folio);
 struct folio *f2fs_compress_control_folio(struct folio *folio);
 int f2fs_prepare_compress_overwrite(struct inode *inode,
-			struct page **pagep, pgoff_t index, void **fsdata);
+			struct page **pagep, pgoff_t lblk, void **fsdata);
 bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
-					pgoff_t index, unsigned copied);
+					pgoff_t lblk, unsigned int copied);
 int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
-void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio);
+void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio,
+				unsigned int nr_blocks);
 bool f2fs_is_compress_backend_ready(struct inode *inode);
 bool f2fs_is_compress_level_valid(int alg, int lvl);
 int __init f2fs_init_compress_mempool(void);
 void f2fs_destroy_compress_mempool(void);
 void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
-void f2fs_end_read_compressed_page(struct folio *folio, bool failed,
-				block_t blkaddr, bool in_task);
+void f2fs_end_read_compressed_page(struct folio *folio, size_t offset,
+				bool failed, block_t blkaddr,
+				unsigned int nr_blocks, bool in_task);
 bool f2fs_cluster_is_empty(struct compress_ctx *cc);
-bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
+bool f2fs_cluster_can_merge_block(struct compress_ctx *cc, pgoff_t lblk);
 bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
 				int index, int nr_pages, bool uptodate);
 bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
@@ -5012,13 +5023,13 @@ int f2fs_write_multi_pages(struct compress_ctx *cc,
 						int *submitted,
 						struct writeback_control *wbc,
 						enum iostat_type io_type);
-int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index);
-bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index);
+int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t lblk);
+bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t lblk);
 void f2fs_update_read_extent_tree_range_compressed(struct inode *inode,
 				pgoff_t fofs, block_t blkaddr,
 				unsigned int llen, unsigned int c_len);
 int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
-				unsigned nr_pages, sector_t *last_block_in_bio,
+				unsigned int nr_blocks, sector_t *last_block_in_bio,
 				struct readahead_control *rac, bool for_write);
 struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);
 void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
@@ -5036,8 +5047,8 @@ int __init f2fs_init_compress_cache(void);
 void f2fs_destroy_compress_cache(void);
 void f2fs_invalidate_compress_pages_range(struct f2fs_sb_info *sbi,
 					block_t blkaddr, unsigned int len);
-bool f2fs_load_compressed_folio(struct f2fs_sb_info *sbi, struct folio *folio,
-								block_t blkaddr);
+bool f2fs_load_compressed_block(struct f2fs_sb_info *sbi, struct folio *folio,
+				size_t offset, block_t blkaddr);
 void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi, nid_t ino);
 #define inc_compr_inode_stat(inode)					\
 	do {								\
@@ -5071,7 +5082,8 @@ static inline void f2fs_destroy_compress_mempool(void) { }
 static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic,
 				bool in_task) { }
 static inline void f2fs_end_read_compressed_page(struct folio *folio,
-				bool failed, block_t blkaddr, bool in_task)
+				size_t offset, bool failed, block_t blkaddr,
+				unsigned int nr_blocks, bool in_task)
 {
 	WARN_ON_ONCE(1);
 }
@@ -5089,17 +5101,18 @@ static inline int __init f2fs_init_compress_cache(void) { return 0; }
 static inline void f2fs_destroy_compress_cache(void) { }
 static inline void f2fs_invalidate_compress_pages_range(struct f2fs_sb_info *sbi,
 				block_t blkaddr, unsigned int len) { }
-static inline bool f2fs_load_compressed_folio(struct f2fs_sb_info *sbi,
-		struct folio *folio, block_t blkaddr) { return false; }
+static inline bool f2fs_load_compressed_block(struct f2fs_sb_info *sbi,
+		struct folio *folio, size_t offset,
+		block_t blkaddr) { return false; }
 static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi,
 							nid_t ino) { }
 #define inc_compr_inode_stat(inode)		do { } while (0)
 static inline int f2fs_is_compressed_cluster(
 				struct inode *inode,
-				pgoff_t index) { return 0; }
+				pgoff_t lblk) { return 0; }
 static inline bool f2fs_is_sparse_cluster(
 				struct inode *inode,
-				pgoff_t index) { return true; }
+				pgoff_t lblk) { return true; }
 static inline void f2fs_update_read_extent_tree_range_compressed(
 				struct inode *inode,
 				pgoff_t fofs, block_t blkaddr,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 67ed6be8952f..952896640e72 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -178,7 +178,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	if (f2fs_compressed_file(inode)) {
-		int ret = f2fs_is_compressed_cluster(inode, pidx);
+		int ret = f2fs_is_compressed_cluster(inode, bidx);
 
 		if (ret < 0) {
 			err = ret;
@@ -1019,7 +1019,8 @@ int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
 	 */
 	if (f2fs_compressed_file(inode))
 		free_from = round_up(from,
-				F2FS_I(inode)->i_cluster_size << PAGE_SHIFT);
+				F2FS_BLK_TO_BYTES(F2FS_I_SB(inode),
+					F2FS_I(inode)->i_cluster_size));
 #endif
 
 	err = f2fs_do_truncate_blocks(inode, free_from, lock);
@@ -2298,13 +2299,6 @@ static long f2fs_fallocate(struct file *file, int mode,
 		goto out;
 	}
 
-	if (f2fs_has_subpage_blocks(F2FS_I_SB(inode))) {
-		if (f2fs_compressed_file(inode)) {
-			ret = -EOPNOTSUPP;
-			goto out;
-		}
-	}
-
 	ret = file_modified(file);
 	if (ret)
 		goto out;
@@ -4447,9 +4441,6 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
 	int ret;
 	int writecount;
 
-	if (f2fs_has_subpage_blocks(sbi))
-		return -EOPNOTSUPP;
-
 	if (!f2fs_sb_has_compression(sbi))
 		return -EOPNOTSUPP;
 
@@ -4645,9 +4636,6 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
 	unsigned int reserved_blocks = 0;
 	int ret;
 
-	if (f2fs_has_subpage_blocks(sbi))
-		return -EOPNOTSUPP;
-
 	if (!f2fs_sb_has_compression(sbi))
 		return -EOPNOTSUPP;
 
@@ -4969,9 +4957,6 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
 	struct f2fs_comp_option option;
 	int ret = 0;
 
-	if (f2fs_has_subpage_blocks(sbi))
-		return -EOPNOTSUPP;
-
 	if (!f2fs_sb_has_compression(sbi))
 		return -EOPNOTSUPP;
 
@@ -4982,7 +4967,7 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
 				sizeof(option)))
 		return -EFAULT;
 
-	if (option.log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
+	if (option.log_cluster_size < min_compress_log_size(sbi) ||
 		option.log_cluster_size > MAX_COMPRESS_LOG_SIZE ||
 		option.algorithm >= COMPRESS_MAX)
 		return -EINVAL;
@@ -5076,16 +5061,22 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
 		off = offset_in_folio(folio, (loff_t)redirty_idx << PAGE_SHIFT);
 		dlen = (size_t)(next_idx - redirty_idx) << PAGE_SHIFT;
 
-		if (folio_test_large(folio)) {
+		if (f2fs_is_multiblock_folio(F2FS_I_SB(inode), folio)) {
+			loff_t eof = i_size_read(inode) - folio_pos(folio);
+
 			/*
-			 * A compressed inode can keep using large folios, in
+			 * folio_mark_dirty() does not dirty the blocks of a folio
+			 * state.  A compressed inode can keep using large folios, in
 			 * which case the compression flag only works as a hint
 			 * until the inode is evicted.  Update the subpage dirty
 			 * bitmap as well, otherwise f2fs_write_cache_folios()
-			 * finds no dirty range and skips the whole folio.
+			 * finds no dirty range and skips the whole folio.  Do
+			 * not dirty the blocks past EOF.
 			 */
-			f2fs_ffs_find_or_alloc(folio);
-			f2fs_ffs_mark_subrange_dirty(folio, off, dlen);
+			if ((loff_t)(off + dlen) > eof)
+				dlen = eof > (loff_t)off ? eof - off : 0;
+			if (dlen)
+				f2fs_ffs_mark_subrange_dirty(folio, off, dlen);
 		}
 
 		folio_mark_dirty(folio);
@@ -5103,12 +5094,9 @@ static int f2fs_ioc_decompress_file(struct file *filp)
 	struct inode *inode = file_inode(filp);
 	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
 	struct f2fs_inode_info *fi = F2FS_I(inode);
-	pgoff_t page_idx = 0, last_idx, cluster_idx;
+	pgoff_t lblk = 0, last_idx, cluster_idx;
 	int ret;
 
-	if (f2fs_has_subpage_blocks(sbi))
-		return -EOPNOTSUPP;
-
 	if (!f2fs_sb_has_compression(sbi) ||
 			F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
 		return -EOPNOTSUPP;
@@ -5145,12 +5133,14 @@ static int f2fs_ioc_decompress_file(struct file *filp)
 	last_idx >>= fi->i_log_cluster_size;
 
 	for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
-		page_idx = cluster_idx << fi->i_log_cluster_size;
+		lblk = cluster_idx << fi->i_log_cluster_size;
 
-		if (!f2fs_is_compressed_cluster(inode, page_idx))
+		if (!f2fs_is_compressed_cluster(inode, lblk))
 			continue;
 
-		ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
+		ret = redirty_blocks(inode,
+			f2fs_lblk_to_folio_index(sbi, lblk),
+			fi->i_cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi));
 		if (ret < 0)
 			break;
 
@@ -5187,12 +5177,9 @@ static int f2fs_ioc_compress_file(struct file *filp)
 	struct inode *inode = file_inode(filp);
 	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
 	struct f2fs_inode_info *fi = F2FS_I(inode);
-	pgoff_t page_idx = 0, last_idx, cluster_idx;
+	pgoff_t lblk = 0, last_idx, cluster_idx;
 	int ret;
 
-	if (f2fs_has_subpage_blocks(sbi))
-		return -EOPNOTSUPP;
-
 	if (!f2fs_sb_has_compression(sbi) ||
 			F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
 		return -EOPNOTSUPP;
@@ -5235,12 +5222,14 @@ static int f2fs_ioc_compress_file(struct file *filp)
 	last_idx >>= fi->i_log_cluster_size;
 
 	for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
-		page_idx = cluster_idx << fi->i_log_cluster_size;
+		lblk = cluster_idx << fi->i_log_cluster_size;
 
-		if (f2fs_is_sparse_cluster(inode, page_idx))
+		if (f2fs_is_sparse_cluster(inode, lblk))
 			continue;
 
-		ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
+		ret = redirty_blocks(inode,
+			f2fs_lblk_to_folio_index(sbi, lblk),
+			fi->i_cluster_size >> F2FS_LOG_BLKS_PER_PAGE(sbi));
 		if (ret < 0)
 			break;
 
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 5c6a64b31b05..aad5fbbcc5a2 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -225,7 +225,7 @@ static bool sanity_check_compress_inode(struct inode *inode,
 			SECTOR_TO_BLOCK(sbi, inode->i_blocks));
 		return false;
 	}
-	if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
+	if (ri->i_log_cluster_size < min_compress_log_size(sbi) ||
 		ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE) {
 		f2fs_warn(sbi,
 			"%s: inode (ino=%llx) has unsupported log cluster size: %u, run fsck to fix",
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index fa74564adcfb..b2669057b7d2 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1447,6 +1447,11 @@ static int f2fs_check_compression(struct fs_context *fc,
 		ctx_clear_opt_mask(ctx, F2FS_MOUNT_COMPRESS_CACHE);
 		return 0;
 	}
+	if ((ctx->spec_mask & F2FS_SPEC_compress_log_size) &&
+	    F2FS_CTX_INFO(ctx).compress_log_size < min_compress_log_size(sbi)) {
+		f2fs_err(sbi, "Compress cluster log size is out of range");
+		return -EINVAL;
+	}
 	if (ctx->spec_mask & F2FS_SPEC_compress_extension) {
 		cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
 		for (i = 0; i < F2FS_CTX_INFO(ctx).compress_ext_cnt; i++) {
@@ -2630,7 +2635,7 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount)
 	F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
 	if (f2fs_sb_has_compression(sbi)) {
 		F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZ4;
-		F2FS_OPTION(sbi).compress_log_size = MIN_COMPRESS_LOG_SIZE;
+		F2FS_OPTION(sbi).compress_log_size = min_compress_log_size(sbi);
 		F2FS_OPTION(sbi).compress_ext_cnt = 0;
 		F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
 	}
-- 
2.56.0.317.g454f8302e1-goog


  parent reply	other threads:[~2026-11-02 19:40 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 19:39 [PATCH 00/20] f2fs: support block size smaller than PAGE_SIZE Daeho Jeong
2026-11-02 19:39 ` [PATCH 01/20] f2fs: use block-based index and size calculations in remaining paths Daeho Jeong
2026-11-03  7:04   ` [f2fs-dev] " Chao Yu
2026-11-02 19:39 ` [PATCH 02/20] f2fs: support subpage block size superblock and mount Daeho Jeong
2026-11-03  9:26   ` [f2fs-dev] " Chao Yu
2026-11-02 19:39 ` [PATCH 03/20] f2fs: extend f2fs_folio_state for subpage blocks Daeho Jeong
2026-11-02 19:39 ` [PATCH 04/20] f2fs: support subpage blocks in directory operations Daeho Jeong
2026-11-02 19:39 ` [PATCH 05/20] f2fs: convert inode dirty page counters to dirty block counters Daeho Jeong
2026-11-02 19:39 ` [PATCH 06/20] f2fs: support subpage blocks in data read path and GC Daeho Jeong
2026-11-02 19:39 ` [PATCH 07/20] f2fs: support subpage blocks in data write path and writeback Daeho Jeong
2026-11-02 19:39 ` [PATCH 08/20] f2fs: adapt range operations and mmap for subpage blocks Daeho Jeong
2026-11-02 19:39 ` [PATCH 09/20] f2fs: support subpage blocks in pinned files Daeho Jeong
2026-11-02 19:39 ` [PATCH 10/20] f2fs: support subpage blocks in collapse and insert range Daeho Jeong
2026-11-02 19:39 ` [PATCH 11/20] f2fs: support subpage blocks in move range Daeho Jeong
2026-11-02 19:39 ` [PATCH 12/20] f2fs: support subpage blocks in defragmentation Daeho Jeong
2026-11-02 19:39 ` [PATCH 13/20] f2fs: support subpage blocks in swapfile activation Daeho Jeong
2026-11-02 19:39 ` [PATCH 14/20] f2fs: support subpage blocks in fs-verity enablement Daeho Jeong
2026-11-02 19:39 ` [PATCH 15/20] f2fs: support subpage blocks in atomic writes Daeho Jeong
2026-11-02 19:39 ` [PATCH 16/20] f2fs: preserve inline data in subpage block mode Daeho Jeong
2026-11-02 19:39 ` [PATCH 17/20] f2fs: support subpage blocks in online resize Daeho Jeong
2026-11-02 19:39 ` Daeho Jeong [this message]
2026-11-02 19:39 ` [PATCH 19/20] f2fs: count compress cache entries in pages for its memory limit Daeho Jeong
2026-11-02 19:39 ` [PATCH 20/20] f2fs: enable subpage block size support Daeho Jeong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261102193952.3002018-19-daeho43@gmail.com \
    --to=daeho43@gmail.com \
    --cc=daehojeong@google.com \
    --cc=kernel-team@android.com \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®