Commit 202ee5df authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: add a new spinlock i_raw_lock to protect the ext4's raw inode

To avoid potential data races, use a spinlock which protects the raw
(on-disk) inode.
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
parent f5ccfe1d
...@@ -875,6 +875,8 @@ struct ext4_inode_info { ...@@ -875,6 +875,8 @@ struct ext4_inode_info {
struct inode vfs_inode; struct inode vfs_inode;
struct jbd2_inode *jinode; struct jbd2_inode *jinode;
spinlock_t i_raw_lock; /* protects updates to the raw inode */
/* /*
* File creation time. Its function is same as that of * File creation time. Its function is same as that of
* struct timespec i_{a,c,m}time in the generic inode. * struct timespec i_{a,c,m}time in the generic inode.
......
...@@ -4305,12 +4305,15 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -4305,12 +4305,15 @@ static int ext4_do_update_inode(handle_t *handle,
struct ext4_inode *raw_inode = ext4_raw_inode(iloc); struct ext4_inode *raw_inode = ext4_raw_inode(iloc);
struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_inode_info *ei = EXT4_I(inode);
struct buffer_head *bh = iloc->bh; struct buffer_head *bh = iloc->bh;
struct super_block *sb = inode->i_sb;
int err = 0, rc, block; int err = 0, rc, block;
int need_datasync = 0; int need_datasync = 0, set_large_file = 0;
uid_t i_uid; uid_t i_uid;
gid_t i_gid; gid_t i_gid;
/* For fields not not tracking in the in-memory inode, spin_lock(&ei->i_raw_lock);
/* For fields not tracked in the in-memory inode,
* initialise them to zero for new inodes. */ * initialise them to zero for new inodes. */
if (ext4_test_inode_state(inode, EXT4_STATE_NEW)) if (ext4_test_inode_state(inode, EXT4_STATE_NEW))
memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size); memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size);
...@@ -4348,8 +4351,10 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -4348,8 +4351,10 @@ static int ext4_do_update_inode(handle_t *handle,
EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode); EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode); EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
if (ext4_inode_blocks_set(handle, raw_inode, ei)) if (ext4_inode_blocks_set(handle, raw_inode, ei)) {
spin_unlock(&ei->i_raw_lock);
goto out_brelse; goto out_brelse;
}
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
raw_inode->i_flags = cpu_to_le32(ei->i_flags & 0xFFFFFFFF); raw_inode->i_flags = cpu_to_le32(ei->i_flags & 0xFFFFFFFF);
if (likely(!test_opt2(inode->i_sb, HURD_COMPAT))) if (likely(!test_opt2(inode->i_sb, HURD_COMPAT)))
...@@ -4361,24 +4366,11 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -4361,24 +4366,11 @@ static int ext4_do_update_inode(handle_t *handle,
need_datasync = 1; need_datasync = 1;
} }
if (ei->i_disksize > 0x7fffffffULL) { if (ei->i_disksize > 0x7fffffffULL) {
struct super_block *sb = inode->i_sb;
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE) || EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
EXT4_SB(sb)->s_es->s_rev_level == EXT4_SB(sb)->s_es->s_rev_level ==
cpu_to_le32(EXT4_GOOD_OLD_REV)) { cpu_to_le32(EXT4_GOOD_OLD_REV))
/* If this is the first large file set_large_file = 1;
* created, add a flag to the superblock.
*/
err = ext4_journal_get_write_access(handle,
EXT4_SB(sb)->s_sbh);
if (err)
goto out_brelse;
ext4_update_dynamic_rev(sb);
EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb);
}
} }
raw_inode->i_generation = cpu_to_le32(inode->i_generation); raw_inode->i_generation = cpu_to_le32(inode->i_generation);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
...@@ -4410,12 +4402,23 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -4410,12 +4402,23 @@ static int ext4_do_update_inode(handle_t *handle,
ext4_inode_csum_set(inode, raw_inode, ei); ext4_inode_csum_set(inode, raw_inode, ei);
spin_unlock(&ei->i_raw_lock);
BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
rc = ext4_handle_dirty_metadata(handle, NULL, bh); rc = ext4_handle_dirty_metadata(handle, NULL, bh);
if (!err) if (!err)
err = rc; err = rc;
ext4_clear_inode_state(inode, EXT4_STATE_NEW); ext4_clear_inode_state(inode, EXT4_STATE_NEW);
if (set_large_file) {
err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
if (err)
goto out_brelse;
ext4_update_dynamic_rev(sb);
EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb);
}
ext4_update_inode_fsync_trans(handle, inode, need_datasync); ext4_update_inode_fsync_trans(handle, inode, need_datasync);
out_brelse: out_brelse:
brelse(bh); brelse(bh);
......
...@@ -879,6 +879,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) ...@@ -879,6 +879,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
return NULL; return NULL;
ei->vfs_inode.i_version = 1; ei->vfs_inode.i_version = 1;
spin_lock_init(&ei->i_raw_lock);
INIT_LIST_HEAD(&ei->i_prealloc_list); INIT_LIST_HEAD(&ei->i_prealloc_list);
spin_lock_init(&ei->i_prealloc_lock); spin_lock_init(&ei->i_prealloc_lock);
ext4_es_init_tree(&ei->i_es_tree); ext4_es_init_tree(&ei->i_es_tree);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment