mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Chao Yu <chao@kernel.org>
To: Daeho Jeong <daeho43@gmail.com>
Cc: chao@kernel.org, linux-kernel@vger.kernel.org,
	linux-f2fs-devel@lists.sourceforge.net, kernel-team@android.com,
	Daeho Jeong <daehojeong@google.com>
Subject: Re: [f2fs-dev] [PATCH 18/20] f2fs: support subpage blocks in compression
Date: Wed, 11 Nov 2026 10:07:14 +0800	[thread overview]
Message-ID: <9f9c0997-2cf5-4648-9e5b-1820f297b4d3@kernel.org> (raw)
In-Reply-To: <CACOAw_x9dJ=qPP+s4aFhMZtqpwf1JM3nCm2H5PqF+VZk7t6PZQ@mail.gmail.com>

On 11/11/26 02:45, Daeho Jeong wrote:
> On Mon, Nov 9, 2026 at 7:04 PM Chao Yu <chao@kernel.org> wrote:
>>
>> On 11/3/26 03:39, Daeho Jeong wrote:
>>> 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.
>>
>> 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));
>> }
>>
>>         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",
>>
>> It seems we limited the min compress log size w/ F2FS_LOG_BLKS_PER_PAGE, e.g. 16k
>> page size, min compress cluster size is 16k, and for 64k page size, it is 64k, it
>> will deny to load any compress inode that cluster size is smaller than 64k when we
>> upgrade the kernel version, I think we need to declare it in commit message and document.
>>
> 
> Hi Chao,
> 
> Right. To be precise, the minimum changes only when a page has more than
> four blocks, since MIN_COMPRESS_LOG_SIZE is already 2: with 4KB blocks,
> it stays 16KB on a 16KB page kernel and becomes 64KB only on a 64KB page
> kernel. A 4KB block image could not be mounted on a 64KB page kernel
> before this series, so nothing changes for the existing users.

Hi Daeho,

Yes, I see.

> 
> But failing the sanity check is wrong, since it reports a valid inode as
> a corrupted one (SBI_NEED_FSCK, ERROR_CORRUPTED_INODE and "run fsck to
> fix"). In v2, I'll keep the on-disk range check as it is, and make

Ah, yes, we need to keep on-disk cluster size check as it is.

> f2fs_is_compress_backend_ready() return false for a cluster smaller than
> a page, as for an unsupported compression algorithm. Then such a file can
> still be looked up and removed, and opening it fails with -EOPNOTSUPP.
> The mount option and F2FS_IOC_SET_COMPRESS_OPTION still reject a
> compress_log_size smaller than a page.

Agreed, it makes sense.

Thanks,

> 
> I'll describe it in the commit message and in
> Documentation/filesystems/f2fs.rst.
> 
>>>
>>> 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));
>>
>> F2FS_BLK_ALIGN(sbi, max_len)?
> 
> Will fix it.
> 
>>
>>> +     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);
>>
>> f2fs_ffs_mark_subrange_dirty(folio, 0, folio_size(folio));
> 
> Will fix it.
> 
>>
>>>               }
>>>               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) :
>>
>> It's better to explain why the start iv value is different in between subpage
>> blocks and non subpage blocks case. I guess it's due to we still hold the first
>> unused block in cpage (the special block is tagged as COMPRESS_ADDR), right?
> 
> Right, I'll move it into a helper with a comment, and use it in f2fs_alloc_dic()
> as well.
> 
> Thanks,
> 
>>
>>> +                             (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)),
>>
>> max_nr_pages << F2FS_LOG_BLKS_PER_PAGE ?
>>
>>>                                               &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)),
>>
>> max_nr_pages << F2FS_LOG_BLKS_PER_PAGE ?
>>
>>>                                                       &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;
>>>       }
>>


  reply	other threads:[~2026-11-11  2:07 UTC|newest]

Thread overview: 43+ 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-04  9:19   ` [f2fs-dev] " Chao Yu
2026-11-06 20:53     ` Daeho Jeong
2026-11-02 19:39 ` [PATCH 04/20] f2fs: support subpage blocks in directory operations Daeho Jeong
2026-11-04 11:13   ` [f2fs-dev] " Chao Yu
2026-11-02 19:39 ` [PATCH 05/20] f2fs: convert inode dirty page counters to dirty block counters Daeho Jeong
2026-11-04 11:24   ` [f2fs-dev] " Chao Yu
2026-11-06 21:29     ` Daeho Jeong
2026-11-02 19:39 ` [PATCH 06/20] f2fs: support subpage blocks in data read path and GC Daeho Jeong
2026-11-05  7:55   ` [f2fs-dev] " Chao Yu
2026-11-02 19:39 ` [PATCH 07/20] f2fs: support subpage blocks in data write path and writeback Daeho Jeong
2026-11-06  2:52   ` [f2fs-dev] " Chao Yu
2026-11-07 23:14     ` Daeho Jeong
2026-11-10 11:19       ` Chao Yu
2026-11-10 19:50         ` Daeho Jeong
2026-11-02 19:39 ` [PATCH 08/20] f2fs: adapt range operations and mmap for subpage blocks Daeho Jeong
2026-11-06  3:54   ` [f2fs-dev] " Chao Yu
2026-11-07 23:24     ` Daeho Jeong
2026-11-10 11:35       ` Chao Yu
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-09  2:56   ` [f2fs-dev] " Chao Yu
2026-11-10 11:49   ` Chao Yu
2026-11-10 21:28     ` Daeho Jeong
2026-11-02 19:39 ` [PATCH 16/20] f2fs: preserve inline data in subpage block mode Daeho Jeong
2026-11-09  3:18   ` [f2fs-dev] " Chao Yu
2026-11-02 19:39 ` [PATCH 17/20] f2fs: support subpage blocks in online resize Daeho Jeong
2026-11-02 19:39 ` [PATCH 18/20] f2fs: support subpage blocks in compression Daeho Jeong
2026-11-10  3:04   ` [f2fs-dev] " Chao Yu
2026-11-10 18:45     ` Daeho Jeong
2026-11-11  2:07       ` Chao Yu [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=9f9c0997-2cf5-4648-9e5b-1820f297b4d3@kernel.org \
    --to=chao@kernel.org \
    --cc=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®