From: Amir Goldstein <amir73il@xxxxxxxxxxxx> Before every metadata buffer write, the journal API is called, namely, one of the ext4_journal_get_XXX_access() functions. We use these journal hooks to call the snapshot API, namely ext4_snapshot_get_XXX_access(), to COW the metadata buffer before it is modified for the first time. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxxxxx> Signed-off-by: Yongqiang Yang <xiaoqiangnk@xxxxxxxxx> --- fs/ext4/ext4_jbd2.c | 9 +++++++-- fs/ext4/ext4_jbd2.h | 15 +++++++++++---- fs/ext4/extents.c | 3 ++- fs/ext4/inode.c | 22 +++++++++++++++------- fs/ext4/move_extent.c | 3 ++- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 560020d..833969b 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -23,13 +23,16 @@ int __ext4_journal_get_undo_access(const char *where, unsigned int line, return err; } -int __ext4_journal_get_write_access(const char *where, unsigned int line, - handle_t *handle, struct buffer_head *bh) +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct buffer_head *bh, int exclude) { int err = 0; if (ext4_handle_valid(handle)) { err = jbd2_journal_get_write_access(handle, bh); + if (!err && !exclude) + err = ext4_snapshot_get_write_access(handle, inode, bh); if (err) ext4_journal_abort_handle(where, line, __func__, bh, handle, err); @@ -111,6 +114,8 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line, if (ext4_handle_valid(handle)) { err = jbd2_journal_get_create_access(handle, bh); + if (!err) + err = ext4_snapshot_get_create_access(handle, bh); if (err) ext4_journal_abort_handle(where, line, __func__, bh, handle, err); diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 8ffffb1..75662f7 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -132,9 +132,9 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line, int __ext4_journal_get_undo_access(const char *where, unsigned int line, handle_t *handle, struct buffer_head *bh); -int __ext4_journal_get_write_access(const char *where, unsigned int line, - handle_t *handle, struct buffer_head *bh); - +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct buffer_head *bh, int exclude); int __ext4_forget(const char *where, unsigned int line, handle_t *handle, int is_metadata, struct inode *inode, struct buffer_head *bh, ext4_fsblk_t blocknr); @@ -151,8 +151,15 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, #define ext4_journal_get_undo_access(handle, bh) \ __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh)) +#define ext4_journal_get_write_access_exclude(handle, bh) \ + __ext4_journal_get_write_access_inode(__func__, __LINE__, \ + (handle), NULL, (bh), 1) #define ext4_journal_get_write_access(handle, bh) \ - __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) + __ext4_journal_get_write_access_inode(__func__, __LINE__, \ + (handle), NULL, (bh), 0) +#define ext4_journal_get_write_access_inode(handle, inode, bh) \ + __ext4_journal_get_write_access_inode(__func__, __LINE__, \ + (handle), (inode), (bh), 0) #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \ __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \ (bh), (block_nr)) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 0c3ea93..c8cab3d 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -77,7 +77,8 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode, { if (path->p_bh) { /* path points to block */ - return ext4_journal_get_write_access(handle, path->p_bh); + return ext4_journal_get_write_access_inode(handle, + inode, path->p_bh); } /* path points to leaf/index in inode body */ /* we use in-core data, no need to protect them */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a597ff1..b848072 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -874,7 +874,8 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode, */ if (where->bh) { BUFFER_TRACE(where->bh, "get_write_access"); - err = ext4_journal_get_write_access(handle, where->bh); + err = ext4_journal_get_write_access_inode(handle, inode, + where->bh); if (err) goto err_out; } @@ -4172,7 +4173,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode, goto out_err; if (bh) { BUFFER_TRACE(bh, "retaking write access"); - err = ext4_journal_get_write_access(handle, bh); + err = ext4_journal_get_write_access_inode(handle, + inode, bh); if (unlikely(err)) goto out_err; } @@ -4223,7 +4225,8 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, if (this_bh) { /* For indirect block */ BUFFER_TRACE(this_bh, "get_write_access"); - err = ext4_journal_get_write_access(handle, this_bh); + err = ext4_journal_get_write_access_inode(handle, inode, + this_bh); /* Important: if we can't update the indirect pointers * to the blocks, we can't free them. */ if (err) @@ -4386,8 +4389,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, * pointed to by an indirect block: journal it */ BUFFER_TRACE(parent_bh, "get_write_access"); - if (!ext4_journal_get_write_access(handle, - parent_bh)){ + if (!ext4_journal_get_write_access_inode( + handle, inode, parent_bh)){ *p = 0; BUFFER_TRACE(parent_bh, "call ext4_handle_dirty_metadata"); @@ -4759,9 +4762,14 @@ has_buffer: int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc) { - /* We have all inode data except xattrs in memory here. */ - return __ext4_get_inode_loc(inode, iloc, + int in_mem = (!EXT4_SNAPSHOTS(inode->i_sb) && !ext4_test_inode_state(inode, EXT4_STATE_XATTR)); + + /* + * We have all inode's data except xattrs in memory here, + * but we must always read-in the entire inode block for COW. + */ + return __ext4_get_inode_loc(inode, iloc, in_mem); } void ext4_set_inode_flags(struct inode *inode) diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index b9f3e78..ad5409a 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -421,7 +421,8 @@ mext_insert_extents(handle_t *handle, struct inode *orig_inode, if (depth) { /* Register to journal */ - ret = ext4_journal_get_write_access(handle, orig_path->p_bh); + ret = ext4_journal_get_write_access_inode(handle, + orig_inode, orig_path->p_bh); if (ret) return ret; } -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html