[Devel] [PATCH RHEL7 COMMIT] pfcache/ext4: fix automatic csum calculation

Konstantin Khorenko khorenko at virtuozzo.com
Mon Aug 31 09:03:06 PDT 2015


The commit is pushed to "branch-rh7-3.10.0-229.7.2-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-229.7.2.vz7.6.4
------>
commit 17c90deb8c54a3feef19d557008fcb510bed8cd3
Author: Dmitry Monakhov <dmonakhov at openvz.org>
Date:   Mon Aug 31 20:03:06 2015 +0400

    pfcache/ext4: fix automatic csum calculation
    
    port from 2.6.32-x: 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.
    
    https://jira.sw.ru/browse/PSBM-39233
    
    Signed-off-by: Dmitry Monakhov <dmonakhov at 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;
+
 }
 



More information about the Devel mailing list