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. 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 <nzz...@126.com> --- 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 Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel