]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ext4: add a new spinlock i_raw_lock to protect the ext4's raw inode
authorTheodore Ts'o <tytso@mit.edu>
Mon, 21 Apr 2014 18:37:55 +0000 (14:37 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 21 Apr 2014 18:37:55 +0000 (14:37 -0400)
To avoid potential data races, use a spinlock which protects the raw
(on-disk) inode.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Reviewed-by: Jan Kara <jack@suse.cz>
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/super.c

index 86c2cda208ea5b64ecbd5bf9e85f100663503d7d..1d08a1b51bdd806127b6582b6a8b622cee0765f6 100644 (file)
@@ -875,6 +875,8 @@ struct ext4_inode_info {
        struct inode vfs_inode;
        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
         * struct timespec i_{a,c,m}time in the generic inode.
index 7dfbcbba67d511185acbb1f4c598b0abee0a18cc..9b3c52fbe86de0b487694b1fccccfa28631ce410 100644 (file)
@@ -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_info *ei = EXT4_I(inode);
        struct buffer_head *bh = iloc->bh;
+       struct super_block *sb = inode->i_sb;
        int err = 0, rc, block;
-       int need_datasync = 0;
+       int need_datasync = 0, set_large_file = 0;
        uid_t i_uid;
        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. */
        if (ext4_test_inode_state(inode, EXT4_STATE_NEW))
                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,
        EXT4_INODE_SET_XTIME(i_atime, inode, 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;
+       }
        raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
        raw_inode->i_flags = cpu_to_le32(ei->i_flags & 0xFFFFFFFF);
        if (likely(!test_opt2(inode->i_sb, HURD_COMPAT)))
@@ -4361,24 +4366,11 @@ static int ext4_do_update_inode(handle_t *handle,
                need_datasync = 1;
        }
        if (ei->i_disksize > 0x7fffffffULL) {
-               struct super_block *sb = inode->i_sb;
                if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
                                EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
                                EXT4_SB(sb)->s_es->s_rev_level ==
-                               cpu_to_le32(EXT4_GOOD_OLD_REV)) {
-                       /* If this is the first large file
-                        * 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);
-               }
+                   cpu_to_le32(EXT4_GOOD_OLD_REV))
+                       set_large_file = 1;
        }
        raw_inode->i_generation = cpu_to_le32(inode->i_generation);
        if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
@@ -4410,12 +4402,23 @@ static int ext4_do_update_inode(handle_t *handle,
 
        ext4_inode_csum_set(inode, raw_inode, ei);
 
+       spin_unlock(&ei->i_raw_lock);
+
        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
        rc = ext4_handle_dirty_metadata(handle, NULL, bh);
        if (!err)
                err = rc;
        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);
 out_brelse:
        brelse(bh);
index c4895c195e00d5be0ccf7b69b7dd3119e1f89bd7..1f8cb1812723a1a1d4127780dba100a1b3310eaf 100644 (file)
@@ -879,6 +879,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
                return NULL;
 
        ei->vfs_inode.i_version = 1;
+       spin_lock_init(&ei->i_raw_lock);
        INIT_LIST_HEAD(&ei->i_prealloc_list);
        spin_lock_init(&ei->i_prealloc_lock);
        ext4_es_init_tree(&ei->i_es_tree);