Dear Mr Kim:
I had mispelled f2fs's mailling list address in my first send patch series and 
I'm totally very sorry for that.
I have a resent patch series that was visible in f2fs's mailling list.Could we 
migrate our discussion there?
My resent patch series is 
https://lore.kernel.org/linux-f2fs-devel/[email protected]/

Thanks !











At 2025-11-01 05:28:40, "Jaegeuk Kim" <[email protected]> wrote:
>Hi Nanzhe,
>
>On 08/13, Nanzhe Zhao wrote:
>> This commit enables large folios support for F2FS's buffered read and
>> write paths.
>> 
>> We introduce a helper function `f2fs_set_iomap` to handle all the logic
>> that converts a f2fs_map_blocks to iomap.
>> 
>> Currently, compressed files, encrypted files, and fsverity are not
>> supported with iomap large folios.
>
>If we cannot support the encrypted files, we'll lose the gain a lot. Any
>idea on this? And, how about applying the folio->private stuffs and supporting
>the buffered read path on non-compressed and encrypted/plain files without
>iomap conversion?
>
>> 
>> Since F2FS requires `f2fs_iomap_folio_state` (or a similar equivalent
>> mechanism) to correctly support the iomap framework, when
>> `CONFIG_F2FS_IOMAP_FOLIO_STATE` is not enabled, we will not use the
>> iomap buffered read/write paths.
>> 
>> Note: Since holes reported by f2fs_map_blocks come in two types
>> (NULL_ADDR and unmapped dnodes).
>> They requiring different handle logic to set iomap.length,
>> So we add a new block state flag for f2fs_map_blocks
>> 
>> Signed-off-by: Nanzhe Zhao <[email protected]>
>> ---
>>  fs/f2fs/data.c   | 286 +++++++++++++++++++++++++++++++++++++++++++----
>>  fs/f2fs/f2fs.h   | 120 +++++++++++++-------
>>  fs/f2fs/file.c   |  33 +++++-
>>  fs/f2fs/inline.c |  15 ++-
>>  fs/f2fs/inode.c  |  27 +++++
>>  fs/f2fs/namei.c  |   7 ++
>>  fs/f2fs/super.c  |   3 +
>>  7 files changed, 425 insertions(+), 66 deletions(-)
>> 
>> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
>> index 37eaf431ab42..243c6305b0c5 100644
>> --- a/fs/f2fs/data.c
>> +++ b/fs/f2fs/data.c
>> @@ -1149,6 +1149,9 @@ void f2fs_update_data_blkaddr(struct dnode_of_data 
>> *dn, block_t blkaddr)
>>  {
>>      f2fs_set_data_blkaddr(dn, blkaddr);
>>      f2fs_update_read_extent_cache(dn);
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    f2fs_iomap_seq_inc(dn->inode);
>> +#endif
>>  }
>>  
>>  /* dn->ofs_in_node will be returned with up-to-date last block pointer */
>> @@ -1182,6 +1185,9 @@ int f2fs_reserve_new_blocks(struct dnode_of_data *dn, 
>> blkcnt_t count)
>>  
>>      if (folio_mark_dirty(dn->node_folio))
>>              dn->node_changed = true;
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    f2fs_iomap_seq_inc(dn->inode);
>> +#endif
>>      return 0;
>>  }
>>  
>> @@ -1486,6 +1492,7 @@ static int f2fs_map_no_dnode(struct inode *inode,
>>              *map->m_next_pgofs = f2fs_get_next_page_offset(dn, pgoff);
>>      if (map->m_next_extent)
>>              *map->m_next_extent = f2fs_get_next_page_offset(dn, pgoff);
>> +    map->m_flags |= F2FS_MAP_NODNODE;
>>      return 0;
>>  }
>>  
>> @@ -1702,7 +1709,9 @@ int f2fs_map_blocks(struct inode *inode, struct 
>> f2fs_map_blocks *map, int flag)
>>              if (blkaddr == NEW_ADDR)
>>                      map->m_flags |= F2FS_MAP_DELALLOC;
>>              /* DIO READ and hole case, should not map the blocks. */
>> -            if (!(flag == F2FS_GET_BLOCK_DIO && is_hole && 
>> !map->m_may_create))
>> +            if (!(flag == F2FS_GET_BLOCK_DIO && is_hole &&
>> +                  !map->m_may_create) &&
>> +                !(flag == F2FS_GET_BLOCK_IOMAP && is_hole))
>>                      map->m_flags |= F2FS_MAP_MAPPED;
>>  
>>              map->m_pblk = blkaddr;
>> @@ -1736,6 +1745,10 @@ int f2fs_map_blocks(struct inode *inode, struct 
>> f2fs_map_blocks *map, int flag)
>>                      goto sync_out;
>>  
>>              map->m_len += dn.ofs_in_node - ofs_in_node;
>> +            /* Since we successfully reserved blocks, we can update the 
>> pblk now.
>> +             * No need to perform inefficient look up in write_begin again
>> +             */
>> +            map->m_pblk = dn.data_blkaddr;
>>              if (prealloc && dn.ofs_in_node != last_ofs_in_node + 1) {
>>                      err = -ENOSPC;
>>                      goto sync_out;
>> @@ -4255,9 +4268,6 @@ static int f2fs_iomap_begin(struct inode *inode, 
>> loff_t offset, loff_t length,
>>      err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DIO);
>>      if (err)
>>              return err;
>> -
>> -    iomap->offset = F2FS_BLK_TO_BYTES(map.m_lblk);
>> -
>>      /*
>>       * When inline encryption is enabled, sometimes I/O to an encrypted file
>>       * has to be broken up to guarantee DUN contiguity.  Handle this by
>> @@ -4272,28 +4282,44 @@ static int f2fs_iomap_begin(struct inode *inode, 
>> loff_t offset, loff_t length,
>>      if (WARN_ON_ONCE(map.m_pblk == COMPRESS_ADDR))
>>              return -EINVAL;
>>  
>> -    if (map.m_flags & F2FS_MAP_MAPPED) {
>> -            if (WARN_ON_ONCE(map.m_pblk == NEW_ADDR))
>> -                    return -EINVAL;
>> -
>> -            iomap->length = F2FS_BLK_TO_BYTES(map.m_len);
>> -            iomap->type = IOMAP_MAPPED;
>> -            iomap->flags |= IOMAP_F_MERGED;
>> -            iomap->bdev = map.m_bdev;
>> -            iomap->addr = F2FS_BLK_TO_BYTES(map.m_pblk);
>> -
>> -            if (flags & IOMAP_WRITE && map.m_last_pblk)
>> -                    iomap->private = (void *)map.m_last_pblk;
>> +    return f2fs_set_iomap(inode, &map, iomap, flags, offset, length, false);
>> +}
>> +int f2fs_set_iomap(struct inode *inode, struct f2fs_map_blocks *map,
>> +               struct iomap *iomap, unsigned int flags, loff_t offset,
>> +               loff_t length, bool dio)
>> +{
>> +    iomap->offset = F2FS_BLK_TO_BYTES(map->m_lblk);
>> +    if (map->m_flags & F2FS_MAP_MAPPED) {
>> +            if (dio) {
>> +                    if (WARN_ON_ONCE(map->m_pblk == NEW_ADDR))
>> +                            return -EINVAL;
>> +            }
>> +            iomap->length = F2FS_BLK_TO_BYTES(map->m_len);
>> +            iomap->bdev = map->m_bdev;
>> +            if (map->m_pblk != NEW_ADDR) {
>> +                    iomap->type = IOMAP_MAPPED;
>> +                    iomap->flags |= IOMAP_F_MERGED;
>> +                    iomap->addr = F2FS_BLK_TO_BYTES(map->m_pblk);
>> +            } else {
>> +                    iomap->type = IOMAP_UNWRITTEN;
>> +                    iomap->addr = IOMAP_NULL_ADDR;
>> +            }
>> +            if (flags & IOMAP_WRITE && map->m_last_pblk)
>> +                    iomap->private = (void *)map->m_last_pblk;
>>      } else {
>> -            if (flags & IOMAP_WRITE)
>> +            if (dio && flags & IOMAP_WRITE)
>>                      return -ENOTBLK;
>>  
>> -            if (map.m_pblk == NULL_ADDR) {
>> -                    iomap->length = F2FS_BLK_TO_BYTES(next_pgofs) -
>> -                                                    iomap->offset;
>> +            if (map->m_pblk == NULL_ADDR) {
>> +                    if (map->m_flags & F2FS_MAP_NODNODE)
>> +                            iomap->length =
>> +                                    F2FS_BLK_TO_BYTES(*map->m_next_pgofs) -
>> +                                    iomap->offset;
>> +                    else
>> +                            iomap->length = F2FS_BLK_TO_BYTES(map->m_len);
>>                      iomap->type = IOMAP_HOLE;
>> -            } else if (map.m_pblk == NEW_ADDR) {
>> -                    iomap->length = F2FS_BLK_TO_BYTES(map.m_len);
>> +            } else if (map->m_pblk == NEW_ADDR) {
>> +                    iomap->length = F2FS_BLK_TO_BYTES(map->m_len);
>>                      iomap->type = IOMAP_UNWRITTEN;
>>              } else {
>>                      f2fs_bug_on(F2FS_I_SB(inode), 1);
>> @@ -4301,7 +4327,7 @@ static int f2fs_iomap_begin(struct inode *inode, 
>> loff_t offset, loff_t length,
>>              iomap->addr = IOMAP_NULL_ADDR;
>>      }
>>  
>> -    if (map.m_flags & F2FS_MAP_NEW)
>> +    if (map->m_flags & F2FS_MAP_NEW)
>>              iomap->flags |= IOMAP_F_NEW;
>>      if ((inode->i_state & I_DIRTY_DATASYNC) ||
>>          offset + length > i_size_read(inode))
>> @@ -4313,3 +4339,217 @@ static int f2fs_iomap_begin(struct inode *inode, 
>> loff_t offset, loff_t length,
>>  const struct iomap_ops f2fs_iomap_ops = {
>>      .iomap_begin    = f2fs_iomap_begin,
>>  };
>> +
>> +/* iomap buffered-io */
>> +static int f2fs_buffered_read_iomap_begin(struct inode *inode, loff_t 
>> offset,
>> +                                      loff_t length, unsigned int flags,
>> +                                      struct iomap *iomap,
>> +                                      struct iomap *srcmap)
>> +{
>> +    pgoff_t next_pgofs = 0;
>> +    int err;
>> +    struct f2fs_map_blocks map = {};
>> +
>> +    map.m_lblk = F2FS_BYTES_TO_BLK(offset);
>> +    map.m_len = F2FS_BYTES_TO_BLK(offset + length - 1) - map.m_lblk + 1;
>> +    map.m_next_pgofs = &next_pgofs;
>> +    map.m_seg_type =
>> +            f2fs_rw_hint_to_seg_type(F2FS_I_SB(inode), inode->i_write_hint);
>> +    map.m_may_create = false;
>> +    if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_IS_SHUTDOWN))
>> +            return -EIO;
>> +    /*
>> +     * If the blocks being overwritten are already allocated,
>> +     * f2fs_map_lock and f2fs_balance_fs are not necessary.
>> +     */
>> +    if (flags & IOMAP_WRITE)
>> +            return -EINVAL;
>> +
>> +    err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_IOMAP);
>> +    if (err)
>> +            return err;
>> +
>> +    if (WARN_ON_ONCE(map.m_pblk == COMPRESS_ADDR))
>> +            return -EINVAL;
>> +
>> +    return f2fs_set_iomap(inode, &map, iomap, flags, offset, length, false);
>> +}
>> +
>> +const struct iomap_ops f2fs_buffered_read_iomap_ops = {
>> +    .iomap_begin = f2fs_buffered_read_iomap_begin,
>> +};
>> +
>> +static void f2fs_iomap_readahead(struct readahead_control *rac)
>> +{
>> +    struct inode *inode = rac->mapping->host;
>> +
>> +    if (!f2fs_is_compress_backend_ready(inode))
>> +            return;
>> +
>> +    /* If the file has inline data, skip readahead */
>> +    if (f2fs_has_inline_data(inode))
>> +            return;
>> +    iomap_readahead(rac, &f2fs_buffered_read_iomap_ops);
>> +}
>> +
>> +static int f2fs_buffered_write_iomap_begin(struct inode *inode, loff_t 
>> offset,
>> +                                       loff_t length, unsigned flags,
>> +                                       struct iomap *iomap,
>> +                                       struct iomap *srcmap)
>> +{
>> +    struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>> +    struct f2fs_map_blocks map = {};
>> +    struct folio *ifolio = NULL;
>> +    int err = 0;
>> +
>> +    iomap->offset = offset;
>> +    iomap->bdev = sbi->sb->s_bdev;
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    iomap->validity_cookie = f2fs_iomap_seq_read(inode);
>> +#endif
>> +    if (f2fs_has_inline_data(inode)) {
>> +            if (offset + length <= MAX_INLINE_DATA(inode)) {
>> +                    ifolio = f2fs_get_inode_folio(sbi, inode->i_ino);
>> +                    if (IS_ERR(ifolio)) {
>> +                            err = PTR_ERR(ifolio);
>> +                            goto failed;
>> +                    }
>> +                    set_inode_flag(inode, FI_DATA_EXIST);
>> +                    f2fs_iomap_prepare_read_inline(inode, ifolio, iomap,
>> +                                                   offset, length);
>> +                    if (inode->i_nlink)
>> +                            folio_set_f2fs_inline(ifolio);
>> +
>> +                    f2fs_folio_put(ifolio, 1);
>> +                    goto out;
>> +            }
>> +    }
>> +    block_t start_blk = F2FS_BYTES_TO_BLK(offset);
>> +    block_t len_blks =
>> +            F2FS_BYTES_TO_BLK(offset + length - 1) - start_blk + 1;
>> +    err = f2fs_map_blocks_iomap(inode, start_blk, len_blks, &map);
>> +    if (map.m_pblk == NULL_ADDR) {
>> +            err = f2fs_map_blocks_preallocate(inode, map.m_lblk, len_blks,
>> +                                              &map);
>> +            if (err)
>> +                    goto failed;
>> +    }
>> +    if (WARN_ON_ONCE(map.m_pblk == COMPRESS_ADDR))
>> +            return -EIO; // Should not happen for buffered write prep
>> +    err = f2fs_set_iomap(inode, &map, iomap, flags, offset, length, false);
>> +    if (err)
>> +            return err;
>> +failed:
>> +    f2fs_write_failed(inode, offset + length);
>> +out:
>> +    return err;
>> +}
>> +
>> +static int f2fs_buffered_write_atomic_iomap_begin(struct inode *inode,
>> +                                              loff_t offset, loff_t length,
>> +                                              unsigned flags,
>> +                                              struct iomap *iomap,
>> +                                              struct iomap *srcmap)
>> +{
>> +    struct inode *cow_inode = F2FS_I(inode)->cow_inode;
>> +    struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>> +    struct f2fs_map_blocks map = {};
>> +    int err = 0;
>> +
>> +    iomap->offset = offset;
>> +    iomap->bdev = sbi->sb->s_bdev;
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    iomap->validity_cookie = f2fs_iomap_seq_read(inode);
>> +#endif
>> +    block_t start_blk = F2FS_BYTES_TO_BLK(offset);
>> +    block_t len_blks =
>> +            F2FS_BYTES_TO_BLK(offset + length - 1) - start_blk + 1;
>> +    err = f2fs_map_blocks_iomap(cow_inode, start_blk, len_blks, &map);
>> +    if (err)
>> +            return err;
>> +    if (map.m_pblk == NULL_ADDR &&
>> +        is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) {
>> +            err = f2fs_map_blocks_preallocate(cow_inode, map.m_lblk,
>> +                                              map.m_len, &map);
>> +            if (err)
>> +                    return err;
>> +            inc_atomic_write_cnt(inode);
>> +            goto out;
>> +    } else if (map.m_pblk != NULL_ADDR) {
>> +            goto out;
>> +    }
>> +    err = f2fs_map_blocks_iomap(inode, start_blk, len_blks, &map);
>> +    if (err)
>> +            return err;
>> +out:
>> +    if (WARN_ON_ONCE(map.m_pblk == COMPRESS_ADDR))
>> +            return -EIO;
>> +
>> +    return f2fs_set_iomap(inode, &map, iomap, flags, offset, length, false);
>> +}
>> +
>> +static int f2fs_buffered_write_iomap_end(struct inode *inode, loff_t pos,
>> +                                     loff_t length, ssize_t written,
>> +                                     unsigned flags, struct iomap *iomap)
>> +{
>> +    return written;
>> +}
>> +
>> +const struct iomap_ops f2fs_buffered_write_iomap_ops = {
>> +    .iomap_begin = f2fs_buffered_write_iomap_begin,
>> +    .iomap_end = f2fs_buffered_write_iomap_end,
>> +};
>> +
>> +const struct iomap_ops f2fs_buffered_write_atomic_iomap_ops = {
>> +    .iomap_begin = f2fs_buffered_write_atomic_iomap_begin,
>> +};
>> +
>> +const struct address_space_operations f2fs_iomap_aops = {
>> +    .read_folio = f2fs_read_data_folio,
>> +    .readahead = f2fs_iomap_readahead,
>> +    .write_begin = f2fs_write_begin,
>> +    .write_end = f2fs_write_end,
>> +    .writepages = f2fs_write_data_pages,
>> +    .dirty_folio = f2fs_dirty_data_folio,
>> +    .invalidate_folio = f2fs_invalidate_folio,
>> +    .release_folio = f2fs_release_folio,
>> +    .migrate_folio = filemap_migrate_folio,
>> +    .is_partially_uptodate = iomap_is_partially_uptodate,
>> +    .error_remove_folio = generic_error_remove_folio,
>> +};
>> +
>> +static void f2fs_iomap_put_folio(struct inode *inode, loff_t pos,
>> +                             unsigned copied, struct folio *folio)
>> +{
>> +    if (!copied)
>> +            goto unlock_out;
>> +    if (f2fs_is_atomic_file(inode))
>> +            folio_set_f2fs_atomic(folio);
>> +
>> +    if (pos + copied > i_size_read(inode) &&
>> +        !f2fs_verity_in_progress(inode)) {
>> +            if (f2fs_is_atomic_file(inode))
>> +                    f2fs_i_size_write(F2FS_I(inode)->cow_inode,
>> +                                      pos + copied);
>> +    }
>> +unlock_out:
>> +    folio_unlock(folio);
>> +    folio_put(folio);
>> +    f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>> +}
>> +
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +static bool f2fs_iomap_valid(struct inode *inode, const struct iomap *iomap)
>> +{
>> +    return iomap->validity_cookie == f2fs_iomap_seq_read(inode);
>> +}
>> +#else
>> +static bool f2fs_iomap_valid(struct inode *inode, const struct iomap *iomap)
>> +{
>> +    return 1;
>> +}
>> +#endif
>> +const struct iomap_write_ops f2fs_iomap_write_ops = {
>> +    .put_folio = f2fs_iomap_put_folio,
>> +    .iomap_valid = f2fs_iomap_valid
>> +};
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index ac9a6ac13e1f..1cf12b76b09a 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -762,6 +762,7 @@ struct extent_tree_info {
>>  #define F2FS_MAP_NEW                (1U << 0)
>>  #define F2FS_MAP_MAPPED             (1U << 1)
>>  #define F2FS_MAP_DELALLOC   (1U << 2)
>> +#define F2FS_MAP_NODNODE    (1U << 3)
>>  #define F2FS_MAP_FLAGS              (F2FS_MAP_NEW | F2FS_MAP_MAPPED |\
>>                              F2FS_MAP_DELALLOC)
>>  
>> @@ -837,49 +838,53 @@ enum {
>>  
>>  /* used for f2fs_inode_info->flags */
>>  enum {
>> -    FI_NEW_INODE,           /* indicate newly allocated inode */
>> -    FI_DIRTY_INODE,         /* indicate inode is dirty or not */
>> -    FI_AUTO_RECOVER,        /* indicate inode is recoverable */
>> -    FI_DIRTY_DIR,           /* indicate directory has dirty pages */
>> -    FI_INC_LINK,            /* need to increment i_nlink */
>> -    FI_ACL_MODE,            /* indicate acl mode */
>> -    FI_NO_ALLOC,            /* should not allocate any blocks */
>> -    FI_FREE_NID,            /* free allocated nide */
>> -    FI_NO_EXTENT,           /* not to use the extent cache */
>> -    FI_INLINE_XATTR,        /* used for inline xattr */
>> -    FI_INLINE_DATA,         /* used for inline data*/
>> -    FI_INLINE_DENTRY,       /* used for inline dentry */
>> -    FI_APPEND_WRITE,        /* inode has appended data */
>> -    FI_UPDATE_WRITE,        /* inode has in-place-update data */
>> -    FI_NEED_IPU,            /* used for ipu per file */
>> -    FI_ATOMIC_FILE,         /* indicate atomic file */
>> -    FI_DATA_EXIST,          /* indicate data exists */
>> -    FI_SKIP_WRITES,         /* should skip data page writeback */
>> -    FI_OPU_WRITE,           /* used for opu per file */
>> -    FI_DIRTY_FILE,          /* indicate regular/symlink has dirty pages */
>> -    FI_PREALLOCATED_ALL,    /* all blocks for write were preallocated */
>> -    FI_HOT_DATA,            /* indicate file is hot */
>> -    FI_EXTRA_ATTR,          /* indicate file has extra attribute */
>> -    FI_PROJ_INHERIT,        /* indicate file inherits projectid */
>> -    FI_PIN_FILE,            /* indicate file should not be gced */
>> -    FI_VERITY_IN_PROGRESS,  /* building fs-verity Merkle tree */
>> -    FI_COMPRESSED_FILE,     /* indicate file's data can be compressed */
>> -    FI_COMPRESS_CORRUPT,    /* indicate compressed cluster is corrupted */
>> -    FI_MMAP_FILE,           /* indicate file was mmapped */
>> -    FI_ENABLE_COMPRESS,     /* enable compression in "user" compression 
>> mode */
>> -    FI_COMPRESS_RELEASED,   /* compressed blocks were released */
>> -    FI_ALIGNED_WRITE,       /* enable aligned write */
>> -    FI_COW_FILE,            /* indicate COW file */
>> -    FI_ATOMIC_COMMITTED,    /* indicate atomic commit completed except disk 
>> sync */
>> -    FI_ATOMIC_DIRTIED,      /* indicate atomic file is dirtied */
>> -    FI_ATOMIC_REPLACE,      /* indicate atomic replace */
>> -    FI_OPENED_FILE,         /* indicate file has been opened */
>> -    FI_DONATE_FINISHED,     /* indicate page donation of file has been 
>> finished */
>> -    FI_MAX,                 /* max flag, never be used */
>> +    FI_NEW_INODE, /* indicate newly allocated inode */
>> +    FI_DIRTY_INODE, /* indicate inode is dirty or not */
>> +    FI_AUTO_RECOVER, /* indicate inode is recoverable */
>> +    FI_DIRTY_DIR, /* indicate directory has dirty pages */
>> +    FI_INC_LINK, /* need to increment i_nlink */
>> +    FI_ACL_MODE, /* indicate acl mode */
>> +    FI_NO_ALLOC, /* should not allocate any blocks */
>> +    FI_FREE_NID, /* free allocated nide */
>> +    FI_NO_EXTENT, /* not to use the extent cache */
>> +    FI_INLINE_XATTR, /* used for inline xattr */
>> +    FI_INLINE_DATA, /* used for inline data*/
>> +    FI_INLINE_DENTRY, /* used for inline dentry */
>> +    FI_APPEND_WRITE, /* inode has appended data */
>> +    FI_UPDATE_WRITE, /* inode has in-place-update data */
>> +    FI_NEED_IPU, /* used for ipu per file */
>> +    FI_ATOMIC_FILE, /* indicate atomic file */
>> +    FI_DATA_EXIST, /* indicate data exists */
>> +    FI_SKIP_WRITES, /* should skip data page writeback */
>> +    FI_OPU_WRITE, /* used for opu per file */
>> +    FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */
>> +    FI_PREALLOCATED_ALL, /* all blocks for write were preallocated */
>> +    FI_HOT_DATA, /* indicate file is hot */
>> +    FI_EXTRA_ATTR, /* indicate file has extra attribute */
>> +    FI_PROJ_INHERIT, /* indicate file inherits projectid */
>> +    FI_PIN_FILE, /* indicate file should not be gced */
>> +    FI_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */
>> +    FI_COMPRESSED_FILE, /* indicate file's data can be compressed */
>> +    FI_COMPRESS_CORRUPT, /* indicate compressed cluster is corrupted */
>> +    FI_MMAP_FILE, /* indicate file was mmapped */
>> +    FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */
>> +    FI_COMPRESS_RELEASED, /* compressed blocks were released */
>> +    FI_ALIGNED_WRITE, /* enable aligned write */
>> +    FI_COW_FILE, /* indicate COW file */
>> +    FI_ATOMIC_COMMITTED, /* indicate atomic commit completed except disk 
>> sync */
>> +    FI_ATOMIC_DIRTIED, /* indicate atomic file is dirtied */
>> +    FI_ATOMIC_REPLACE, /* indicate atomic replace */
>> +    FI_OPENED_FILE, /* indicate file has been opened */
>> +    FI_DONATE_FINISHED, /* indicate page donation of file has been finished 
>> */
>> +    FI_IOMAP, /* indicate whether this inode should enable iomap*/
>> +    FI_MAX, /* max flag, never be used */
>>  };
>>  
>>  struct f2fs_inode_info {
>>      struct inode vfs_inode;         /* serve a vfs inode */
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    atomic64_t i_iomap_seq; /* for iomap_valid sequence number */
>> +#endif
>>      unsigned long i_flags;          /* keep an inode flags for ioctl */
>>      unsigned char i_advise;         /* use to give file attribute hints */
>>      unsigned char i_dir_level;      /* use for dentry level for large dir */
>> @@ -2814,6 +2819,16 @@ static inline void inc_page_count(struct f2fs_sb_info 
>> *sbi, int count_type)
>>              set_sbi_flag(sbi, SBI_IS_DIRTY);
>>  }
>>  
>> +static inline void inc_page_count_multiple(struct f2fs_sb_info *sbi,
>> +                                       int count_type, int npages)
>> +{
>> +    atomic_add(npages, &sbi->nr_pages[count_type]);
>> +
>> +    if (count_type == F2FS_DIRTY_DENTS || count_type == F2FS_DIRTY_NODES ||
>> +        count_type == F2FS_DIRTY_META || count_type == F2FS_DIRTY_QDATA ||
>> +        count_type == F2FS_DIRTY_IMETA)
>> +            set_sbi_flag(sbi, SBI_IS_DIRTY);
>> +}
>>  static inline void inode_inc_dirty_pages(struct inode *inode)
>>  {
>>      atomic_inc(&F2FS_I(inode)->dirty_pages);
>> @@ -3657,6 +3672,10 @@ static inline bool f2fs_is_cow_file(struct inode 
>> *inode)
>>      return is_inode_flag_set(inode, FI_COW_FILE);
>>  }
>>  
>> +static inline bool f2fs_iomap_inode(struct inode *inode)
>> +{
>> +    return is_inode_flag_set(inode, FI_IOMAP);
>> +}
>>  static inline void *inline_data_addr(struct inode *inode, struct folio 
>> *folio)
>>  {
>>      __le32 *addr = get_dnode_addr(inode, folio);
>> @@ -3880,7 +3899,17 @@ int f2fs_write_inode(struct inode *inode, struct 
>> writeback_control *wbc);
>>  void f2fs_remove_donate_inode(struct inode *inode);
>>  void f2fs_evict_inode(struct inode *inode);
>>  void f2fs_handle_failed_inode(struct inode *inode);
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +static inline void f2fs_iomap_seq_inc(struct inode *inode)
>> +{
>> +    atomic64_inc(&F2FS_I(inode)->i_iomap_seq);
>> +}
>>  
>> +static inline u64 f2fs_iomap_seq_read(struct inode *inode)
>> +{
>> +    return atomic64_read(&F2FS_I(inode)->i_iomap_seq);
>> +}
>> +#endif
>>  /*
>>   * namei.c
>>   */
>> @@ -4248,6 +4277,9 @@ int f2fs_write_single_data_page(struct folio *folio, 
>> int *submitted,
>>                              enum iostat_type io_type,
>>                              int compr_blocks, bool allow_balance);
>>  void f2fs_write_failed(struct inode *inode, loff_t to);
>> +int f2fs_set_iomap(struct inode *inode, struct f2fs_map_blocks *map,
>> +               struct iomap *iomap, unsigned int flags, loff_t offset,
>> +               loff_t length, bool dio);
>>  void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t 
>> length);
>>  bool f2fs_release_folio(struct folio *folio, gfp_t wait);
>>  bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
>> @@ -4258,6 +4290,11 @@ int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi);
>>  void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi);
>>  extern const struct iomap_ops f2fs_iomap_ops;
>>  
>> +extern const struct iomap_write_ops f2fs_iomap_write_ops;
>> +extern const struct iomap_ops f2fs_buffered_read_iomap_ops;
>> +extern const struct iomap_ops f2fs_buffered_write_iomap_ops;
>> +extern const struct iomap_ops f2fs_buffered_write_atomic_iomap_ops;
>> +
>>  /*
>>   * gc.c
>>   */
>> @@ -4540,6 +4577,7 @@ extern const struct file_operations 
>> f2fs_dir_operations;
>>  extern const struct file_operations f2fs_file_operations;
>>  extern const struct inode_operations f2fs_file_inode_operations;
>>  extern const struct address_space_operations f2fs_dblock_aops;
>> +extern const struct address_space_operations f2fs_iomap_aops;
>>  extern const struct address_space_operations f2fs_node_aops;
>>  extern const struct address_space_operations f2fs_meta_aops;
>>  extern const struct inode_operations f2fs_dir_inode_operations;
>> @@ -4578,7 +4616,9 @@ int f2fs_read_inline_dir(struct file *file, struct 
>> dir_context *ctx,
>>  int f2fs_inline_data_fiemap(struct inode *inode,
>>                      struct fiemap_extent_info *fieinfo,
>>                      __u64 start, __u64 len);
>> -
>> +void f2fs_iomap_prepare_read_inline(struct inode *inode, struct folio 
>> *ifolio,
>> +                                struct iomap *iomap, loff_t pos,
>> +                                loff_t length);
>>  /*
>>   * shrinker.c
>>   */
>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>> index 42faaed6a02d..6c5b3e632f2b 100644
>> --- a/fs/f2fs/file.c
>> +++ b/fs/f2fs/file.c
>> @@ -4965,7 +4965,14 @@ static int f2fs_preallocate_blocks(struct kiocb 
>> *iocb, struct iov_iter *iter,
>>              if (ret)
>>                      return ret;
>>      }
>> -
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    /* Buffered write can convert inline file to large normal file
>> +     * when convert success, we uses mapping set large folios here
>> +     */
>> +    if (f2fs_should_use_buffered_iomap(inode))
>> +            mapping_set_large_folios(inode->i_mapping);
>> +    set_inode_flag(inode, FI_IOMAP);
>> +#endif
>>      /* Do not preallocate blocks that will be written partially in 4KB. */
>>      map.m_lblk = F2FS_BLK_ALIGN(pos);
>>      map.m_len = F2FS_BYTES_TO_BLK(pos + count);
>> @@ -4994,6 +5001,24 @@ static int f2fs_preallocate_blocks(struct kiocb 
>> *iocb, struct iov_iter *iter,
>>      return map.m_len;
>>  }
>>  
>> +static ssize_t f2fs_iomap_buffered_write(struct kiocb *iocb, struct 
>> iov_iter *i)
>> +{
>> +    struct file *file = iocb->ki_filp;
>> +    struct inode *inode = file_inode(file);
>> +    ssize_t ret;
>> +
>> +    if (f2fs_is_atomic_file(inode)) {
>> +            ret = iomap_file_buffered_write(iocb, i,
>> +                                            
>> &f2fs_buffered_write_atomic_iomap_ops,
>> +                                            &f2fs_iomap_write_ops, NULL);
>> +    } else {
>> +            ret = iomap_file_buffered_write(iocb, i,
>> +                                            &f2fs_buffered_write_iomap_ops,
>> +                                            &f2fs_iomap_write_ops, NULL);
>> +    }
>> +    return ret;
>> +}
>> +
>>  static ssize_t f2fs_buffered_write_iter(struct kiocb *iocb,
>>                                      struct iov_iter *from)
>>  {
>> @@ -5004,7 +5029,11 @@ static ssize_t f2fs_buffered_write_iter(struct kiocb 
>> *iocb,
>>      if (iocb->ki_flags & IOCB_NOWAIT)
>>              return -EOPNOTSUPP;
>>  
>> -    ret = generic_perform_write(iocb, from);
>> +    if (f2fs_iomap_inode(inode)) {
>> +            ret = f2fs_iomap_buffered_write(iocb, from);
>> +    } else {
>> +            ret = generic_perform_write(iocb, from);
>> +    }
>>  
>>      if (ret > 0) {
>>              f2fs_update_iostat(F2FS_I_SB(inode), inode,
>> diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
>> index 58ac831ef704..bda338b4fc22 100644
>> --- a/fs/f2fs/inline.c
>> +++ b/fs/f2fs/inline.c
>> @@ -13,7 +13,7 @@
>>  #include "f2fs.h"
>>  #include "node.h"
>>  #include <trace/events/f2fs.h>
>> -
>> +#include <linux/iomap.h>
>>  static bool support_inline_data(struct inode *inode)
>>  {
>>      if (f2fs_used_in_atomic_write(inode))
>> @@ -832,3 +832,16 @@ int f2fs_inline_data_fiemap(struct inode *inode,
>>      f2fs_folio_put(ifolio, true);
>>      return err;
>>  }
>> +/* fill iomap struct for inline data case for
>> + *iomap buffered write
>> + */
>> +void f2fs_iomap_prepare_read_inline(struct inode *inode, struct folio 
>> *ifolio,
>> +                                struct iomap *iomap, loff_t pos,
>> +                                loff_t length)
>> +{
>> +    iomap->addr = IOMAP_NULL_ADDR;
>> +    iomap->length = length;
>> +    iomap->type = IOMAP_INLINE;
>> +    iomap->flags = 0;
>> +    iomap->inline_data = inline_data_addr(inode, ifolio);
>> +}
>> diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
>> index 8c4eafe9ffac..29378270d561 100644
>> --- a/fs/f2fs/inode.c
>> +++ b/fs/f2fs/inode.c
>> @@ -23,6 +23,24 @@
>>  extern const struct address_space_operations f2fs_compress_aops;
>>  #endif
>>  
>> +bool f2fs_should_use_buffered_iomap(struct inode *inode)
>> +{
>> +    if (!S_ISREG(inode->i_mode))
>> +            return false;
>> +    if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
>> +            return false;
>> +    if (inode->i_mapping == NODE_MAPPING(F2FS_I_SB(inode)))
>> +            return false;
>> +    if (inode->i_mapping == META_MAPPING(F2FS_I_SB(inode)))
>> +            return false;
>> +    if (f2fs_encrypted_file(inode))
>> +            return false;
>> +    if (fsverity_active(inode))
>> +            return false;
>> +    if (f2fs_compressed_file(inode))
>> +            return false;
>> +    return true;
>> +}
>>  void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
>>  {
>>      if (is_inode_flag_set(inode, FI_NEW_INODE))
>> @@ -611,7 +629,16 @@ struct inode *f2fs_iget(struct super_block *sb, 
>> unsigned long ino)
>>      } else if (S_ISREG(inode->i_mode)) {
>>              inode->i_op = &f2fs_file_inode_operations;
>>              inode->i_fop = &f2fs_file_operations;
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +            if (f2fs_should_use_buffered_iomap(inode)) {
>> +                    mapping_set_large_folios(inode->i_mapping);
>> +                    set_inode_flag(inode, FI_IOMAP);
>> +                    inode->i_mapping->a_ops = &f2fs_iomap_aops;
>> +            } else
>> +                    inode->i_mapping->a_ops = &f2fs_dblock_aops;
>> +#else
>>              inode->i_mapping->a_ops = &f2fs_dblock_aops;
>> +#endif
>>      } else if (S_ISDIR(inode->i_mode)) {
>>              inode->i_op = &f2fs_dir_inode_operations;
>>              inode->i_fop = &f2fs_dir_operations;
>> diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
>> index b882771e4699..2d995860c488 100644
>> --- a/fs/f2fs/namei.c
>> +++ b/fs/f2fs/namei.c
>> @@ -328,6 +328,13 @@ static struct inode *f2fs_new_inode(struct mnt_idmap 
>> *idmap,
>>      f2fs_init_extent_tree(inode);
>>  
>>      trace_f2fs_new_inode(inode, 0);
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    if (f2fs_should_use_buffered_iomap(inode)) {
>> +            set_inode_flag(inode, FI_IOMAP);
>> +            mapping_set_large_folios(inode->i_mapping);
>> +    }
>> +#endif
>> +
>>      return inode;
>>  
>>  fail:
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index 2000880b7dca..35a42d6214fe 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -1719,6 +1719,9 @@ static struct inode *f2fs_alloc_inode(struct 
>> super_block *sb)
>>      init_once((void *) fi);
>>  
>>      /* Initialize f2fs-specific inode info */
>> +#ifdef CONFIG_F2FS_IOMAP_FOLIO_STATE
>> +    atomic64_set(&fi->i_iomap_seq, 0);
>> +#endif
>>      atomic_set(&fi->dirty_pages, 0);
>>      atomic_set(&fi->i_compr_blocks, 0);
>>      atomic_set(&fi->open_count, 0);
>> -- 
>> 2.34.1
>> 

_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to