diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 36fa20e81a3a..5fd62a0f28b6 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1166,10 +1166,6 @@ struct ext4_inode_info { #define EXT4_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT4_ERROR_FS 0x0002 /* Errors detected */ #define EXT4_ORPHAN_FS 0x0004 /* Orphans being recovered */ -#define EXT4_FC_INELIGIBLE 0x0008 /* Fast commit ineligible */ -#define EXT4_FC_COMMITTING 0x0010 /* File system underoing a fast - * commit. - */ #define EXT4_FC_REPLAY 0x0020 /* Fast commit replay ongoing */ /* @@ -1431,6 +1427,10 @@ struct ext4_super_block { */ #define EXT4_MF_MNTDIR_SAMPLED 0x0001 #define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ +#define EXT4_MF_FC_INELIGIBLE 0x0004 /* Fast commit ineligible */ +#define EXT4_MF_FC_COMMITTING 0x0008 /* File system underoing a fast + * commit. + */ #ifdef CONFIG_FS_ENCRYPTION #define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_policy.policy != NULL) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 559100f3e23c..57cfa28919a9 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -1471,16 +1471,16 @@ static int ext4_ext_search_left(struct inode *inode, } /* - * search the closest allocated block to the right for *logical - * and returns it at @logical + it's physical address at @phys - * if *logical is the largest allocated block, the function - * returns 0 at @phys - * return value contains 0 (success) or error code + * Search the closest allocated block to the right for *logical + * and returns it at @logical + it's physical address at @phys. + * If not exists, return 0 and @phys is set to 0. We will return + * 1 which means we found an allocated block and ret_ex is valid. + * Or return a (< 0) error code. */ static int ext4_ext_search_right(struct inode *inode, struct ext4_ext_path *path, ext4_lblk_t *logical, ext4_fsblk_t *phys, - struct ext4_extent **ret_ex) + struct ext4_extent *ret_ex) { struct buffer_head *bh = NULL; struct ext4_extent_header *eh; @@ -1574,10 +1574,11 @@ got_index: found_extent: *logical = le32_to_cpu(ex->ee_block); *phys = ext4_ext_pblock(ex); - *ret_ex = ex; + if (ret_ex) + *ret_ex = *ex; if (bh) put_bh(bh); - return 0; + return 1; } /* @@ -2868,8 +2869,8 @@ again: */ lblk = ex_end + 1; err = ext4_ext_search_right(inode, path, &lblk, &pblk, - &ex); - if (err) + NULL); + if (err < 0) goto out; if (pblk) { partial.pclu = EXT4_B2C(sbi, pblk); @@ -4039,7 +4040,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags) { struct ext4_ext_path *path = NULL; - struct ext4_extent newex, *ex, *ex2; + struct ext4_extent newex, *ex, ex2; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); ext4_fsblk_t newblock = 0, pblk; int err = 0, depth, ret; @@ -4175,15 +4176,14 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, if (err) goto out; ar.lright = map->m_lblk; - ex2 = NULL; err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2); - if (err) + if (err < 0) goto out; /* Check if the extent after searching to the right implies a * cluster we can use. */ - if ((sbi->s_cluster_ratio > 1) && ex2 && - get_implied_cluster_alloc(inode->i_sb, map, ex2, path)) { + if ((sbi->s_cluster_ratio > 1) && err && + get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) { ar.len = allocated = map->m_len; newblock = map->m_pblk; goto got_allocated_blocks; diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index 447c8d93f480..8d43058386c3 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -269,7 +269,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason) (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)) return; - sbi->s_mount_state |= EXT4_FC_INELIGIBLE; + sbi->s_mount_flags |= EXT4_MF_FC_INELIGIBLE; WARN_ON(reason >= EXT4_FC_REASON_MAX); sbi->s_fc_stats.fc_ineligible_reason_count[reason]++; } @@ -292,7 +292,7 @@ void ext4_fc_start_ineligible(struct super_block *sb, int reason) } /* - * Stop a fast commit ineligible update. We set EXT4_FC_INELIGIBLE flag here + * Stop a fast commit ineligible update. We set EXT4_MF_FC_INELIGIBLE flag here * to ensure that after stopping the ineligible update, at least one full * commit takes place. */ @@ -302,13 +302,13 @@ void ext4_fc_stop_ineligible(struct super_block *sb) (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)) return; - EXT4_SB(sb)->s_mount_state |= EXT4_FC_INELIGIBLE; + EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FC_INELIGIBLE; atomic_dec(&EXT4_SB(sb)->s_fc_ineligible_updates); } static inline int ext4_fc_is_ineligible(struct super_block *sb) { - return (EXT4_SB(sb)->s_mount_state & EXT4_FC_INELIGIBLE) || + return (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FC_INELIGIBLE) || atomic_read(&EXT4_SB(sb)->s_fc_ineligible_updates); } @@ -358,7 +358,7 @@ static int ext4_fc_track_template( spin_lock(&sbi->s_fc_lock); if (list_empty(&EXT4_I(inode)->i_fc_list)) list_add_tail(&EXT4_I(inode)->i_fc_list, - (sbi->s_mount_state & EXT4_FC_COMMITTING) ? + (sbi->s_mount_flags & EXT4_MF_FC_COMMITTING) ? &sbi->s_fc_q[FC_Q_STAGING] : &sbi->s_fc_q[FC_Q_MAIN]); spin_unlock(&sbi->s_fc_lock); @@ -411,7 +411,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update) node->fcd_name.len = dentry->d_name.len; spin_lock(&sbi->s_fc_lock); - if (sbi->s_mount_state & EXT4_FC_COMMITTING) + if (sbi->s_mount_flags & EXT4_MF_FC_COMMITTING) list_add_tail(&node->fcd_list, &sbi->s_fc_dentry_q[FC_Q_STAGING]); else @@ -846,7 +846,7 @@ static int ext4_fc_submit_inode_data_all(journal_t *journal) int ret = 0; spin_lock(&sbi->s_fc_lock); - sbi->s_mount_state |= EXT4_FC_COMMITTING; + sbi->s_mount_flags |= EXT4_MF_FC_COMMITTING; list_for_each(pos, &sbi->s_fc_q[FC_Q_MAIN]) { ei = list_entry(pos, struct ext4_inode_info, i_fc_list); ext4_set_inode_state(&ei->vfs_inode, EXT4_STATE_FC_COMMITTING); @@ -964,7 +964,6 @@ static int ext4_fc_commit_dentry_updates(journal_t *journal, u32 *crc) fc_dentry->fcd_parent, fc_dentry->fcd_ino, fc_dentry->fcd_name.len, fc_dentry->fcd_name.name, crc)) { - spin_lock(&sbi->s_fc_lock); ret = -ENOSPC; goto lock_and_exit; } @@ -1191,8 +1190,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full) list_splice_init(&sbi->s_fc_q[FC_Q_STAGING], &sbi->s_fc_q[FC_Q_STAGING]); - sbi->s_mount_state &= ~EXT4_FC_COMMITTING; - sbi->s_mount_state &= ~EXT4_FC_INELIGIBLE; + sbi->s_mount_flags &= ~EXT4_MF_FC_COMMITTING; + sbi->s_mount_flags &= ~EXT4_MF_FC_INELIGIBLE; if (full) sbi->s_fc_bytes = 0; @@ -1617,8 +1616,10 @@ static int ext4_fc_replay_add_range(struct super_block *sb, if (ret == 0) { /* Range is not mapped */ path = ext4_find_extent(inode, cur, NULL, 0); - if (!path) - continue; + if (IS_ERR(path)) { + iput(inode); + return 0; + } memset(&newex, 0, sizeof(newex)); newex.ee_block = cpu_to_le32(cur); ext4_ext_store_pblock( @@ -2078,6 +2079,8 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, void ext4_fc_init(struct super_block *sb, journal_t *journal) { + int num_fc_blocks; + /* * We set replay callback even if fast commit disabled because we may * could still have fast commit blocks that need to be replayed even if @@ -2087,7 +2090,15 @@ void ext4_fc_init(struct super_block *sb, journal_t *journal) if (!test_opt2(sb, JOURNAL_FAST_COMMIT)) return; journal->j_fc_cleanup_callback = ext4_fc_cleanup; - if (jbd2_fc_init(journal, EXT4_NUM_FC_BLKS)) { + if (!buffer_uptodate(journal->j_sb_buffer) + && ext4_read_bh_lock(journal->j_sb_buffer, REQ_META | REQ_PRIO, + true)) { + ext4_msg(sb, KERN_ERR, "I/O error on journal"); + return; + } + num_fc_blocks = be32_to_cpu(journal->j_superblock->s_num_fc_blks); + if (jbd2_fc_init(journal, num_fc_blocks ? num_fc_blocks : + EXT4_NUM_FC_BLKS)) { pr_warn("Error while enabling fast commits, turning off."); ext4_clear_feature_fast_commit(sb); } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 739dc8ed4d21..afb48a980c19 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1931,7 +1931,7 @@ static int __ext4_journalled_writepage(struct page *page, } if (ret == 0) ret = err; - err = ext4_jbd2_inode_add_write(handle, inode, 0, len); + err = ext4_jbd2_inode_add_write(handle, inode, page_offset(page), len); if (ret == 0) ret = err; EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid; @@ -3331,10 +3331,12 @@ static bool ext4_inode_datasync_dirty(struct inode *inode) if (journal) { if (jbd2_transaction_committed(journal, - EXT4_I(inode)->i_datasync_tid)) - return true; - return atomic_read(&EXT4_SB(inode->i_sb)->s_fc_subtid) >= - EXT4_I(inode)->i_fc_committed_subtid; + EXT4_I(inode)->i_datasync_tid)) + return false; + if (test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT)) + return atomic_read(&EXT4_SB(inode->i_sb)->s_fc_subtid) < + EXT4_I(inode)->i_fc_committed_subtid; + return true; } /* Any metadata buffers to write? */ @@ -6181,7 +6183,8 @@ retry_alloc: if (ext4_walk_page_buffers(handle, page_buffers(page), 0, len, NULL, write_end_fn)) goto out_error; - if (ext4_jbd2_inode_add_write(handle, inode, 0, len)) + if (ext4_jbd2_inode_add_write(handle, inode, + page_offset(page), len)) goto out_error; ext4_set_inode_state(inode, EXT4_STATE_JDATA); } else { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b370c3c10073..6f622c69f941 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4771,8 +4771,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_MAIN]); INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_STAGING]); sbi->s_fc_bytes = 0; - sbi->s_mount_state &= ~EXT4_FC_INELIGIBLE; - sbi->s_mount_state &= ~EXT4_FC_COMMITTING; + sbi->s_mount_flags &= ~EXT4_MF_FC_INELIGIBLE; + sbi->s_mount_flags &= ~EXT4_MF_FC_COMMITTING; spin_lock_init(&sbi->s_fc_lock); memset(&sbi->s_fc_stats, 0, sizeof(sbi->s_fc_stats)); sbi->s_fc_replay_state.fc_regions = NULL; diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index 5ff33d18996a..4e27fe6ed3ae 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c @@ -315,6 +315,7 @@ EXT4_ATTR_FEATURE(casefold); EXT4_ATTR_FEATURE(verity); #endif EXT4_ATTR_FEATURE(metadata_csum_seed); +EXT4_ATTR_FEATURE(fast_commit); static struct attribute *ext4_feat_attrs[] = { ATTR_LIST(lazy_itable_init), @@ -331,6 +332,7 @@ static struct attribute *ext4_feat_attrs[] = { ATTR_LIST(verity), #endif ATTR_LIST(metadata_csum_seed), + ATTR_LIST(fast_commit), NULL, }; ATTRIBUTE_GROUPS(ext4_feat); diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index fb3d71ad6eea..1d5566af48ac 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -263,7 +263,10 @@ typedef struct journal_superblock_s /* 0x0050 */ __u8 s_checksum_type; /* checksum type */ __u8 s_padding2[3]; - __u32 s_padding[42]; +/* 0x0054 */ + __be32 s_num_fc_blks; /* Number of fast commit blocks */ +/* 0x0058 */ + __u32 s_padding[41]; __be32 s_checksum; /* crc32c(superblock) */ /* 0x0100 */ @@ -1253,7 +1256,7 @@ struct journal_s */ void (*j_fc_cleanup_callback)(struct journal_s *journal, int); - /* + /** * @j_fc_replay_callback: * * File-system specific function that performs replay of a fast