[Devel] [RH7 PATCH] ext4: fix automatic csum calculation
Dmitry Monakhov
dmonakhov at openvz.org
Sun Aug 30 06:19:04 PDT 2015
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 <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;
+
}
--
1.7.1
More information about the Devel
mailing list