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
