On 11/04, Nanzhe Zhao wrote:
> 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]/

I think this is the patch series coming from 
[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