From: Chao Yu <chao@kernel.org>
To: Daeho Jeong <daeho43@gmail.com>,
linux-kernel@vger.kernel.org,
linux-f2fs-devel@lists.sourceforge.net, kernel-team@android.com
Cc: chao@kernel.org, Daeho Jeong <daehojeong@google.com>
Subject: Re: [f2fs-dev] [PATCH 18/20] f2fs: support subpage blocks in compression
Date: Tue, 10 Nov 2026 11:04:53 +0800 [thread overview]
Message-ID: <4e4fcefa-5a23-4210-83bf-ff1227a6aa39@kernel.org> (raw)
In-Reply-To: <20261102193952.3002018-19-daeho43@gmail.com>
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.
>
> 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)?
> + 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));
> }
> 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?
> + (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;
> }
next prev parent reply other threads:[~2026-11-10 3:04 UTC|newest]
Thread overview: 36+ 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-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-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-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 ` 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=4e4fcefa-5a23-4210-83bf-ff1227a6aa39@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®