backport: diff-pfcache-ext4-fix-automatic-csum-calculation Bug#1) https://jira.sw.ru/browse/PSBM-23774 truncate_data_csum should clear it's state unconditionally Bug#2) BUG_ON fs/jbd2/transaction.c:1033 truncate_data_csum call chain looks like follows: ->generic_file_buffered_write_iter ->ext4_da_write_begin ->ext4_journal_start( ,,1) : reserve 1 journal block ->ext4_write_end ->ext4_update_data_csum ->ext4_truncate_data_csum ->ext4_xattr_set ->ext4_journal_start(,,20): require 20 blocks, but since journal already started it use existing handle ->jbd2_journal_dirty_metadata J_ASSERT_JH(jh, handle->h_buffer_credits > 0) -> FAILURE
Obviously it is illegal to modify xattr from random context. In order to fix that bug it is reasonable to call ext4_truncate_data_csum() only from proper context (where journal was not started yet.) This patch splits ext4_update_csum in two peaces: 1) check correct csum window position and drop csum if necessary (called from write_begin) 2) update in-memory csum state (called from write_end) Minor fix: do not calculate csum for empty files. Signed-off-by: Dmitry Monakhov <dmonak...@openvz.org> --- fs/ext4/ext4.h | 3 ++- fs/ext4/inode.c | 13 +++++++++++++ fs/ext4/pfcache.c | 41 +++++++++++++++++++++++------------------ fs/ext4/truncate.h | 3 +++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 7059994..fc9608e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2843,11 +2843,12 @@ extern long ext4_dump_pfcache(struct super_block *sb, struct pfcache_dump_request __user *dump); extern int ext4_load_data_csum(struct inode *inode); extern void ext4_start_data_csum(struct inode *inode); +extern void ext4_check_pos_data_csum(struct inode *inode, loff_t pos); extern void ext4_update_data_csum(struct inode *inode, loff_t pos, unsigned len, struct page* page); extern void ext4_commit_data_csum(struct inode *inode); extern void ext4_clear_data_csum(struct inode *inode); -extern int ext4_truncate_data_csum(struct inode *inode, loff_t end); +extern void ext4_truncate_data_csum(struct inode *inode, loff_t end); extern void ext4_load_dir_csum(struct inode *inode); extern void ext4_save_dir_csum(struct inode *inode); static inline int ext4_want_data_csum(struct inode *dir) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 1b3462c..78fc407 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -238,6 +238,8 @@ void ext4_evict_inode(struct inode *inode) * protection against it */ sb_start_intwrite(inode->i_sb); + if (inode->i_blocks && ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM)) + ext4_truncate_data_csum(inode, inode->i_size); handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, ext4_blocks_for_truncate(inode)+3); if (IS_ERR(handle)) { @@ -936,6 +938,10 @@ retry_grab: unlock_page(page); retry_journal: + /* Check csum window position before journal_start */ + if (ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM)) + ext4_check_pos_data_csum(inode, pos); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks); if (IS_ERR(handle)) { page_cache_release(page); @@ -2593,6 +2599,10 @@ retry_grab: * of file which has an already mapped buffer. */ retry_journal: + /* Check csum window position before journal_start */ + if (ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM)) + ext4_check_pos_data_csum(inode, pos); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, ext4_da_write_credits(inode, pos, len)); if (IS_ERR(handle)) { @@ -4640,6 +4650,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (error) goto err_out; } + if (ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM)) + ext4_truncate_data_csum(inode, attr->ia_size); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); if (IS_ERR(handle)) { error = PTR_ERR(handle); diff --git a/fs/ext4/pfcache.c b/fs/ext4/pfcache.c index bf45504..b9751ce 100644 --- a/fs/ext4/pfcache.c +++ b/fs/ext4/pfcache.c @@ -446,6 +446,8 @@ static int ext4_save_data_csum(struct inode *inode, u8 *csum) { int ret; + WARN_ON(journal_current_handle()); + if (ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM) && EXT4_I(inode)->i_data_csum_end < 0 && memcmp(EXT4_I(inode)->i_data_csum, csum, EXT4_DATA_CSUM_SIZE)) @@ -493,30 +495,30 @@ void ext4_save_dir_csum(struct inode *inode) EXT4_DIR_CSUM_VALUE_LEN, 0); } -int ext4_truncate_data_csum(struct inode *inode, loff_t pos) +void ext4_truncate_data_csum(struct inode *inode, loff_t pos) { - int ret = 0; if (!S_ISREG(inode->i_mode)) - return 0; + return; if (EXT4_I(inode)->i_data_csum_end < 0) { + WARN_ON(journal_current_handle()); ext4_xattr_set(inode, EXT4_XATTR_INDEX_TRUSTED, EXT4_DATA_CSUM_NAME, NULL, 0, 0); ext4_close_pfcache(inode); } + spin_lock(&inode->i_lock); + ext4_clear_data_csum(inode); + if (!pos && test_opt2(inode->i_sb, PFCACHE_CSUM)) + ext4_init_data_csum(inode); + spin_unlock(&inode->i_lock); +} - if (EXT4_I(inode)->i_data_csum_end < 0 || - EXT4_I(inode)->i_data_csum_end > pos) { - spin_lock(&inode->i_lock); - ext4_clear_data_csum(inode); - if (!pos && test_opt2(inode->i_sb, PFCACHE_CSUM)) - ext4_init_data_csum(inode); - else - ret = -1; - spin_unlock(&inode->i_lock); - } - return ret; +void ext4_check_pos_data_csum(struct inode *inode, loff_t pos) +{ + if ((pos & ~(loff_t)(SHA_MESSAGE_BYTES-1)) != + EXT4_I(inode)->i_data_csum_end) + ext4_truncate_data_csum(inode, pos); } static void sha_batch_transform(__u32 *digest, const char *data, unsigned rounds) @@ -535,14 +537,14 @@ void ext4_update_data_csum(struct inode *inode, loff_t pos, __u32 *digest = (__u32 *)EXT4_I(inode)->i_data_csum; u8 *kaddr, *data; + if (!len) + return; + len += pos & (SHA_MESSAGE_BYTES-1); len &= ~(SHA_MESSAGE_BYTES-1); pos &= ~(loff_t)(SHA_MESSAGE_BYTES-1); - if ((pos != EXT4_I(inode)->i_data_csum_end && - ext4_truncate_data_csum(inode, pos)) || !len) - return; - + BUG_ON(pos != EXT4_I(inode)->i_data_csum_end); EXT4_I(inode)->i_data_csum_end += len; kaddr = kmap_atomic(page); @@ -567,6 +569,9 @@ static int ext4_finish_data_csum(struct inode *inode, u8 *csum) if (end < 0) return 0; + if (!inode->i_size) + return -ENODATA; + tail = inode->i_size - end; if (tail >= SHA_MESSAGE_BYTES) return -EIO; diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h index 011ba66..bca59b8 100644 --- a/fs/ext4/truncate.h +++ b/fs/ext4/truncate.h @@ -11,6 +11,8 @@ static inline void ext4_truncate_failed_write(struct inode *inode) { truncate_inode_pages(inode->i_mapping, inode->i_size); + if (ext4_test_inode_state(inode, EXT4_STATE_PFCACHE_CSUM)) + ext4_truncate_data_csum(inode, inode->i_size); ext4_truncate(inode); } @@ -39,5 +41,6 @@ static inline unsigned long ext4_blocks_for_truncate(struct inode *inode) needed = EXT4_MAX_TRANS_DATA; return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed; + } -- 1.7.1 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel