]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/ext4/inline.c
ext4: fix deadlock between inline_data and ext4_expand_extra_isize_ea()
[karo-tx-linux.git] / fs / ext4 / inline.c
index 437df6a1a8417cec309c699cfef2166f02b6930a..99a5312ced5205e05d35b72fc27a23bab546cc94 100644 (file)
@@ -381,7 +381,7 @@ out:
 static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
                                    unsigned int len)
 {
-       int ret, size;
+       int ret, size, no_expand;
        struct ext4_inode_info *ei = EXT4_I(inode);
 
        if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
@@ -391,15 +391,14 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
        if (size < len)
                return -ENOSPC;
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
 
        if (ei->i_inline_off)
                ret = ext4_update_inline_data(handle, inode, len);
        else
                ret = ext4_create_inline_data(handle, inode, len);
 
-       up_write(&EXT4_I(inode)->xattr_sem);
-
+       ext4_write_unlock_xattr(inode, &no_expand);
        return ret;
 }
 
@@ -533,7 +532,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
                                              struct inode *inode,
                                              unsigned flags)
 {
-       int ret, needed_blocks;
+       int ret, needed_blocks, no_expand;
        handle_t *handle = NULL;
        int retries = 0, sem_held = 0;
        struct page *page = NULL;
@@ -573,7 +572,7 @@ retry:
                goto out;
        }
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
        sem_held = 1;
        /* If some one has already done this for us, just exit. */
        if (!ext4_has_inline_data(inode)) {
@@ -610,7 +609,7 @@ retry:
                put_page(page);
                page = NULL;
                ext4_orphan_add(handle, inode);
-               up_write(&EXT4_I(inode)->xattr_sem);
+               ext4_write_unlock_xattr(inode, &no_expand);
                sem_held = 0;
                ext4_journal_stop(handle);
                handle = NULL;
@@ -636,7 +635,7 @@ out:
                put_page(page);
        }
        if (sem_held)
-               up_write(&EXT4_I(inode)->xattr_sem);
+               ext4_write_unlock_xattr(inode, &no_expand);
        if (handle)
                ext4_journal_stop(handle);
        brelse(iloc.bh);
@@ -729,7 +728,7 @@ convert:
 int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
                               unsigned copied, struct page *page)
 {
-       int ret;
+       int ret, no_expand;
        void *kaddr;
        struct ext4_iloc iloc;
 
@@ -747,7 +746,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
                goto out;
        }
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
        BUG_ON(!ext4_has_inline_data(inode));
 
        kaddr = kmap_atomic(page);
@@ -757,7 +756,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
        /* clear page dirty so that writepages wouldn't work for us. */
        ClearPageDirty(page);
 
-       up_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_unlock_xattr(inode, &no_expand);
        brelse(iloc.bh);
 out:
        return copied;
@@ -768,7 +767,7 @@ ext4_journalled_write_inline_data(struct inode *inode,
                                  unsigned len,
                                  struct page *page)
 {
-       int ret;
+       int ret, no_expand;
        void *kaddr;
        struct ext4_iloc iloc;
 
@@ -778,11 +777,11 @@ ext4_journalled_write_inline_data(struct inode *inode,
                return NULL;
        }
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
        kaddr = kmap_atomic(page);
        ext4_write_inline_data(inode, &iloc, kaddr, 0, len);
        kunmap_atomic(kaddr);
-       up_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_unlock_xattr(inode, &no_expand);
 
        return iloc.bh;
 }
@@ -1259,7 +1258,7 @@ out:
 int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
                              struct inode *dir, struct inode *inode)
 {
-       int ret, inline_size;
+       int ret, inline_size, no_expand;
        void *inline_start;
        struct ext4_iloc iloc;
 
@@ -1267,7 +1266,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
        if (ret)
                return ret;
 
-       down_write(&EXT4_I(dir)->xattr_sem);
+       ext4_write_lock_xattr(dir, &no_expand);
        if (!ext4_has_inline_data(dir))
                goto out;
 
@@ -1313,7 +1312,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
 
 out:
        ext4_mark_inode_dirty(handle, dir);
-       up_write(&EXT4_I(dir)->xattr_sem);
+       ext4_write_unlock_xattr(dir, &no_expand);
        brelse(iloc.bh);
        return ret;
 }
@@ -1673,7 +1672,7 @@ int ext4_delete_inline_entry(handle_t *handle,
                             struct buffer_head *bh,
                             int *has_inline_data)
 {
-       int err, inline_size;
+       int err, inline_size, no_expand;
        struct ext4_iloc iloc;
        void *inline_start;
 
@@ -1681,7 +1680,7 @@ int ext4_delete_inline_entry(handle_t *handle,
        if (err)
                return err;
 
-       down_write(&EXT4_I(dir)->xattr_sem);
+       ext4_write_lock_xattr(dir, &no_expand);
        if (!ext4_has_inline_data(dir)) {
                *has_inline_data = 0;
                goto out;
@@ -1715,7 +1714,7 @@ int ext4_delete_inline_entry(handle_t *handle,
 
        ext4_show_inline_dir(dir, iloc.bh, inline_start, inline_size);
 out:
-       up_write(&EXT4_I(dir)->xattr_sem);
+       ext4_write_unlock_xattr(dir, &no_expand);
        brelse(iloc.bh);
        if (err != -ENOENT)
                ext4_std_error(dir->i_sb, err);
@@ -1814,11 +1813,11 @@ out:
 
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
-       int ret;
+       int ret, no_expand;
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
        ret = ext4_destroy_inline_data_nolock(handle, inode);
-       up_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_unlock_xattr(inode, &no_expand);
 
        return ret;
 }
@@ -1903,7 +1902,7 @@ out:
 void ext4_inline_data_truncate(struct inode *inode, int *has_inline)
 {
        handle_t *handle;
-       int inline_size, value_len, needed_blocks;
+       int inline_size, value_len, needed_blocks, no_expand;
        size_t i_size;
        void *value = NULL;
        struct ext4_xattr_ibody_find is = {
@@ -1920,7 +1919,7 @@ void ext4_inline_data_truncate(struct inode *inode, int *has_inline)
        if (IS_ERR(handle))
                return;
 
-       down_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_lock_xattr(inode, &no_expand);
        if (!ext4_has_inline_data(inode)) {
                *has_inline = 0;
                ext4_journal_stop(handle);
@@ -1978,7 +1977,7 @@ out_error:
        up_write(&EXT4_I(inode)->i_data_sem);
 out:
        brelse(is.iloc.bh);
-       up_write(&EXT4_I(inode)->xattr_sem);
+       ext4_write_unlock_xattr(inode, &no_expand);
        kfree(value);
        if (inode->i_nlink)
                ext4_orphan_del(handle, inode);
@@ -1994,7 +1993,7 @@ out:
 
 int ext4_convert_inline_data(struct inode *inode)
 {
-       int error, needed_blocks;
+       int error, needed_blocks, no_expand;
        handle_t *handle;
        struct ext4_iloc iloc;
 
@@ -2016,15 +2015,10 @@ int ext4_convert_inline_data(struct inode *inode)
                goto out_free;
        }
 
-       down_write(&EXT4_I(inode)->xattr_sem);
-       if (!ext4_has_inline_data(inode)) {
-               up_write(&EXT4_I(inode)->xattr_sem);
-               goto out;
-       }
-
-       error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
-       up_write(&EXT4_I(inode)->xattr_sem);
-out:
+       ext4_write_lock_xattr(inode, &no_expand);
+       if (ext4_has_inline_data(inode))
+               error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
+       ext4_write_unlock_xattr(inode, &no_expand);
        ext4_journal_stop(handle);
 out_free:
        brelse(iloc.bh);