aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2017-01-11 21:50:46 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-12 00:41:41 -0500
commitda1e40237f8f3516581b534c484c236a79ccfd14 (patch)
tree27da64666bd1563e07dfe9ff3947c5ce6eaa07ed /fs
parent719f1765b02c6a65eff5db3765c87444a1156cb9 (diff)
ext4: fix deadlock between inline_data and ext4_expand_extra_isize_ea()
commit c755e251357a0cee0679081f08c3f4ba797a8009 upstream. The xattr_sem deadlock problems fixed in commit 2e81a4eeedca: "ext4: avoid deadlock when expanding inode size" didn't include the use of xattr_sem in fs/ext4/inline.c. With the addition of project quota which added a new extra inode field, this exposed deadlocks in the inline_data code similar to the ones fixed by 2e81a4eeedca. The deadlock can be reproduced via: dmesg -n 7 mke2fs -t ext4 -O inline_data -Fq -I 256 /dev/vdc 32768 mount -t ext4 -o debug_want_extra_isize=24 /dev/vdc /vdc mkdir /vdc/a umount /vdc mount -t ext4 /dev/vdc /vdc echo foo > /vdc/a/foo and looks like this: [ 11.158815] [ 11.160276] ============================================= [ 11.161960] [ INFO: possible recursive locking detected ] [ 11.161960] 4.10.0-rc3-00015-g011b30a8a3cf #160 Tainted: G W [ 11.161960] --------------------------------------------- [ 11.161960] bash/2519 is trying to acquire lock: [ 11.161960] (&ei->xattr_sem){++++..}, at: [<c1225a4b>] ext4_expand_extra_isize_ea+0x3d/0x4cd [ 11.161960] [ 11.161960] but task is already holding lock: [ 11.161960] (&ei->xattr_sem){++++..}, at: [<c1227941>] ext4_try_add_inline_entry+0x3a/0x152 [ 11.161960] [ 11.161960] other info that might help us debug this: [ 11.161960] Possible unsafe locking scenario: [ 11.161960] [ 11.161960] CPU0 [ 11.161960] ---- [ 11.161960] lock(&ei->xattr_sem); [ 11.161960] lock(&ei->xattr_sem); [ 11.161960] [ 11.161960] *** DEADLOCK *** [ 11.161960] [ 11.161960] May be due to missing lock nesting notation [ 11.161960] [ 11.161960] 4 locks held by bash/2519: [ 11.161960] #0: (sb_writers#3){.+.+.+}, at: [<c11a2414>] mnt_want_write+0x1e/0x3e [ 11.161960] #1: (&type->i_mutex_dir_key){++++++}, at: [<c119508b>] path_openat+0x338/0x67a [ 11.161960] #2: (jbd2_handle){++++..}, at: [<c123314a>] start_this_handle+0x582/0x622 [ 11.161960] #3: (&ei->xattr_sem){++++..}, at: [<c1227941>] ext4_try_add_inline_entry+0x3a/0x152 [ 11.161960] [ 11.161960] stack backtrace: [ 11.161960] CPU: 0 PID: 2519 Comm: bash Tainted: G W 4.10.0-rc3-00015-g011b30a8a3cf #160 [ 11.161960] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014 [ 11.161960] Call Trace: [ 11.161960] dump_stack+0x72/0xa3 [ 11.161960] __lock_acquire+0xb7c/0xcb9 [ 11.161960] ? kvm_clock_read+0x1f/0x29 [ 11.161960] ? __lock_is_held+0x36/0x66 [ 11.161960] ? __lock_is_held+0x36/0x66 [ 11.161960] lock_acquire+0x106/0x18a [ 11.161960] ? ext4_expand_extra_isize_ea+0x3d/0x4cd [ 11.161960] down_write+0x39/0x72 [ 11.161960] ? ext4_expand_extra_isize_ea+0x3d/0x4cd [ 11.161960] ext4_expand_extra_isize_ea+0x3d/0x4cd [ 11.161960] ? _raw_read_unlock+0x22/0x2c [ 11.161960] ? jbd2_journal_extend+0x1e2/0x262 [ 11.161960] ? __ext4_journal_get_write_access+0x3d/0x60 [ 11.161960] ext4_mark_inode_dirty+0x17d/0x26d [ 11.161960] ? ext4_add_dirent_to_inline.isra.12+0xa5/0xb2 [ 11.161960] ext4_add_dirent_to_inline.isra.12+0xa5/0xb2 [ 11.161960] ext4_try_add_inline_entry+0x69/0x152 [ 11.161960] ext4_add_entry+0xa3/0x848 [ 11.161960] ? __brelse+0x14/0x2f [ 11.161960] ? _raw_spin_unlock_irqrestore+0x44/0x4f [ 11.161960] ext4_add_nondir+0x17/0x5b [ 11.161960] ext4_create+0xcf/0x133 [ 11.161960] ? ext4_mknod+0x12f/0x12f [ 11.161960] lookup_open+0x39e/0x3fb [ 11.161960] ? __wake_up+0x1a/0x40 [ 11.161960] ? lock_acquire+0x11e/0x18a [ 11.161960] path_openat+0x35c/0x67a [ 11.161960] ? sched_clock_cpu+0xd7/0xf2 [ 11.161960] do_filp_open+0x36/0x7c [ 11.161960] ? _raw_spin_unlock+0x22/0x2c [ 11.161960] ? __alloc_fd+0x169/0x173 [ 11.161960] do_sys_open+0x59/0xcc [ 11.161960] SyS_open+0x1d/0x1f [ 11.161960] do_int80_syscall_32+0x4f/0x61 [ 11.161960] entry_INT80_32+0x2f/0x2f [ 11.161960] EIP: 0xb76ad469 [ 11.161960] EFLAGS: 00000286 CPU: 0 [ 11.161960] EAX: ffffffda EBX: 08168ac8 ECX: 00008241 EDX: 000001b6 [ 11.161960] ESI: b75e46bc EDI: b7755000 EBP: bfbdb108 ESP: bfbdafc0 [ 11.161960] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Reported-by: George Spelvin <linux@sciencehorizons.net> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/inline.c66
-rw-r--r--fs/ext4/xattr.c30
-rw-r--r--fs/ext4/xattr.h32
3 files changed, 74 insertions, 54 deletions
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d8ca4b9f9dd6..028329721bbe 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -376,7 +376,7 @@ out:
376static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, 376static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
377 unsigned int len) 377 unsigned int len)
378{ 378{
379 int ret, size; 379 int ret, size, no_expand;
380 struct ext4_inode_info *ei = EXT4_I(inode); 380 struct ext4_inode_info *ei = EXT4_I(inode);
381 381
382 if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) 382 if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
@@ -386,15 +386,14 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
386 if (size < len) 386 if (size < len)
387 return -ENOSPC; 387 return -ENOSPC;
388 388
389 down_write(&EXT4_I(inode)->xattr_sem); 389 ext4_write_lock_xattr(inode, &no_expand);
390 390
391 if (ei->i_inline_off) 391 if (ei->i_inline_off)
392 ret = ext4_update_inline_data(handle, inode, len); 392 ret = ext4_update_inline_data(handle, inode, len);
393 else 393 else
394 ret = ext4_create_inline_data(handle, inode, len); 394 ret = ext4_create_inline_data(handle, inode, len);
395 395
396 up_write(&EXT4_I(inode)->xattr_sem); 396 ext4_write_unlock_xattr(inode, &no_expand);
397
398 return ret; 397 return ret;
399} 398}
400 399
@@ -523,7 +522,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
523 struct inode *inode, 522 struct inode *inode,
524 unsigned flags) 523 unsigned flags)
525{ 524{
526 int ret, needed_blocks; 525 int ret, needed_blocks, no_expand;
527 handle_t *handle = NULL; 526 handle_t *handle = NULL;
528 int retries = 0, sem_held = 0; 527 int retries = 0, sem_held = 0;
529 struct page *page = NULL; 528 struct page *page = NULL;
@@ -563,7 +562,7 @@ retry:
563 goto out; 562 goto out;
564 } 563 }
565 564
566 down_write(&EXT4_I(inode)->xattr_sem); 565 ext4_write_lock_xattr(inode, &no_expand);
567 sem_held = 1; 566 sem_held = 1;
568 /* If some one has already done this for us, just exit. */ 567 /* If some one has already done this for us, just exit. */
569 if (!ext4_has_inline_data(inode)) { 568 if (!ext4_has_inline_data(inode)) {
@@ -600,7 +599,7 @@ retry:
600 put_page(page); 599 put_page(page);
601 page = NULL; 600 page = NULL;
602 ext4_orphan_add(handle, inode); 601 ext4_orphan_add(handle, inode);
603 up_write(&EXT4_I(inode)->xattr_sem); 602 ext4_write_unlock_xattr(inode, &no_expand);
604 sem_held = 0; 603 sem_held = 0;
605 ext4_journal_stop(handle); 604 ext4_journal_stop(handle);
606 handle = NULL; 605 handle = NULL;
@@ -626,7 +625,7 @@ out:
626 put_page(page); 625 put_page(page);
627 } 626 }
628 if (sem_held) 627 if (sem_held)
629 up_write(&EXT4_I(inode)->xattr_sem); 628 ext4_write_unlock_xattr(inode, &no_expand);
630 if (handle) 629 if (handle)
631 ext4_journal_stop(handle); 630 ext4_journal_stop(handle);
632 brelse(iloc.bh); 631 brelse(iloc.bh);
@@ -719,7 +718,7 @@ convert:
719int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, 718int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
720 unsigned copied, struct page *page) 719 unsigned copied, struct page *page)
721{ 720{
722 int ret; 721 int ret, no_expand;
723 void *kaddr; 722 void *kaddr;
724 struct ext4_iloc iloc; 723 struct ext4_iloc iloc;
725 724
@@ -737,7 +736,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
737 goto out; 736 goto out;
738 } 737 }
739 738
740 down_write(&EXT4_I(inode)->xattr_sem); 739 ext4_write_lock_xattr(inode, &no_expand);
741 BUG_ON(!ext4_has_inline_data(inode)); 740 BUG_ON(!ext4_has_inline_data(inode));
742 741
743 kaddr = kmap_atomic(page); 742 kaddr = kmap_atomic(page);
@@ -747,7 +746,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
747 /* clear page dirty so that writepages wouldn't work for us. */ 746 /* clear page dirty so that writepages wouldn't work for us. */
748 ClearPageDirty(page); 747 ClearPageDirty(page);
749 748
750 up_write(&EXT4_I(inode)->xattr_sem); 749 ext4_write_unlock_xattr(inode, &no_expand);
751 brelse(iloc.bh); 750 brelse(iloc.bh);
752out: 751out:
753 return copied; 752 return copied;
@@ -758,7 +757,7 @@ ext4_journalled_write_inline_data(struct inode *inode,
758 unsigned len, 757 unsigned len,
759 struct page *page) 758 struct page *page)
760{ 759{
761 int ret; 760 int ret, no_expand;
762 void *kaddr; 761 void *kaddr;
763 struct ext4_iloc iloc; 762 struct ext4_iloc iloc;
764 763
@@ -768,11 +767,11 @@ ext4_journalled_write_inline_data(struct inode *inode,
768 return NULL; 767 return NULL;
769 } 768 }
770 769
771 down_write(&EXT4_I(inode)->xattr_sem); 770 ext4_write_lock_xattr(inode, &no_expand);
772 kaddr = kmap_atomic(page); 771 kaddr = kmap_atomic(page);
773 ext4_write_inline_data(inode, &iloc, kaddr, 0, len); 772 ext4_write_inline_data(inode, &iloc, kaddr, 0, len);
774 kunmap_atomic(kaddr); 773 kunmap_atomic(kaddr);
775 up_write(&EXT4_I(inode)->xattr_sem); 774 ext4_write_unlock_xattr(inode, &no_expand);
776 775
777 return iloc.bh; 776 return iloc.bh;
778} 777}
@@ -1249,7 +1248,7 @@ out:
1249int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, 1248int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
1250 struct inode *dir, struct inode *inode) 1249 struct inode *dir, struct inode *inode)
1251{ 1250{
1252 int ret, inline_size; 1251 int ret, inline_size, no_expand;
1253 void *inline_start; 1252 void *inline_start;
1254 struct ext4_iloc iloc; 1253 struct ext4_iloc iloc;
1255 1254
@@ -1257,7 +1256,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
1257 if (ret) 1256 if (ret)
1258 return ret; 1257 return ret;
1259 1258
1260 down_write(&EXT4_I(dir)->xattr_sem); 1259 ext4_write_lock_xattr(dir, &no_expand);
1261 if (!ext4_has_inline_data(dir)) 1260 if (!ext4_has_inline_data(dir))
1262 goto out; 1261 goto out;
1263 1262
@@ -1303,7 +1302,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
1303 1302
1304out: 1303out:
1305 ext4_mark_inode_dirty(handle, dir); 1304 ext4_mark_inode_dirty(handle, dir);
1306 up_write(&EXT4_I(dir)->xattr_sem); 1305 ext4_write_unlock_xattr(dir, &no_expand);
1307 brelse(iloc.bh); 1306 brelse(iloc.bh);
1308 return ret; 1307 return ret;
1309} 1308}
@@ -1663,7 +1662,7 @@ int ext4_delete_inline_entry(handle_t *handle,
1663 struct buffer_head *bh, 1662 struct buffer_head *bh,
1664 int *has_inline_data) 1663 int *has_inline_data)
1665{ 1664{
1666 int err, inline_size; 1665 int err, inline_size, no_expand;
1667 struct ext4_iloc iloc; 1666 struct ext4_iloc iloc;
1668 void *inline_start; 1667 void *inline_start;
1669 1668
@@ -1671,7 +1670,7 @@ int ext4_delete_inline_entry(handle_t *handle,
1671 if (err) 1670 if (err)
1672 return err; 1671 return err;
1673 1672
1674 down_write(&EXT4_I(dir)->xattr_sem); 1673 ext4_write_lock_xattr(dir, &no_expand);
1675 if (!ext4_has_inline_data(dir)) { 1674 if (!ext4_has_inline_data(dir)) {
1676 *has_inline_data = 0; 1675 *has_inline_data = 0;
1677 goto out; 1676 goto out;
@@ -1705,7 +1704,7 @@ int ext4_delete_inline_entry(handle_t *handle,
1705 1704
1706 ext4_show_inline_dir(dir, iloc.bh, inline_start, inline_size); 1705 ext4_show_inline_dir(dir, iloc.bh, inline_start, inline_size);
1707out: 1706out:
1708 up_write(&EXT4_I(dir)->xattr_sem); 1707 ext4_write_unlock_xattr(dir, &no_expand);
1709 brelse(iloc.bh); 1708 brelse(iloc.bh);
1710 if (err != -ENOENT) 1709 if (err != -ENOENT)
1711 ext4_std_error(dir->i_sb, err); 1710 ext4_std_error(dir->i_sb, err);
@@ -1804,11 +1803,11 @@ out:
1804 1803
1805int ext4_destroy_inline_data(handle_t *handle, struct inode *inode) 1804int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
1806{ 1805{
1807 int ret; 1806 int ret, no_expand;
1808 1807
1809 down_write(&EXT4_I(inode)->xattr_sem); 1808 ext4_write_lock_xattr(inode, &no_expand);
1810 ret = ext4_destroy_inline_data_nolock(handle, inode); 1809 ret = ext4_destroy_inline_data_nolock(handle, inode);
1811 up_write(&EXT4_I(inode)->xattr_sem); 1810 ext4_write_unlock_xattr(inode, &no_expand);
1812 1811
1813 return ret; 1812 return ret;
1814} 1813}
@@ -1893,7 +1892,7 @@ out:
1893void ext4_inline_data_truncate(struct inode *inode, int *has_inline) 1892void ext4_inline_data_truncate(struct inode *inode, int *has_inline)
1894{ 1893{
1895 handle_t *handle; 1894 handle_t *handle;
1896 int inline_size, value_len, needed_blocks; 1895 int inline_size, value_len, needed_blocks, no_expand;
1897 size_t i_size; 1896 size_t i_size;
1898 void *value = NULL; 1897 void *value = NULL;
1899 struct ext4_xattr_ibody_find is = { 1898 struct ext4_xattr_ibody_find is = {
@@ -1910,7 +1909,7 @@ void ext4_inline_data_truncate(struct inode *inode, int *has_inline)
1910 if (IS_ERR(handle)) 1909 if (IS_ERR(handle))
1911 return; 1910 return;
1912 1911
1913 down_write(&EXT4_I(inode)->xattr_sem); 1912 ext4_write_lock_xattr(inode, &no_expand);
1914 if (!ext4_has_inline_data(inode)) { 1913 if (!ext4_has_inline_data(inode)) {
1915 *has_inline = 0; 1914 *has_inline = 0;
1916 ext4_journal_stop(handle); 1915 ext4_journal_stop(handle);
@@ -1968,7 +1967,7 @@ out_error:
1968 up_write(&EXT4_I(inode)->i_data_sem); 1967 up_write(&EXT4_I(inode)->i_data_sem);
1969out: 1968out:
1970 brelse(is.iloc.bh); 1969 brelse(is.iloc.bh);
1971 up_write(&EXT4_I(inode)->xattr_sem); 1970 ext4_write_unlock_xattr(inode, &no_expand);
1972 kfree(value); 1971 kfree(value);
1973 if (inode->i_nlink) 1972 if (inode->i_nlink)
1974 ext4_orphan_del(handle, inode); 1973 ext4_orphan_del(handle, inode);
@@ -1984,7 +1983,7 @@ out:
1984 1983
1985int ext4_convert_inline_data(struct inode *inode) 1984int ext4_convert_inline_data(struct inode *inode)
1986{ 1985{
1987 int error, needed_blocks; 1986 int error, needed_blocks, no_expand;
1988 handle_t *handle; 1987 handle_t *handle;
1989 struct ext4_iloc iloc; 1988 struct ext4_iloc iloc;
1990 1989
@@ -2006,15 +2005,10 @@ int ext4_convert_inline_data(struct inode *inode)
2006 goto out_free; 2005 goto out_free;
2007 } 2006 }
2008 2007
2009 down_write(&EXT4_I(inode)->xattr_sem); 2008 ext4_write_lock_xattr(inode, &no_expand);
2010 if (!ext4_has_inline_data(inode)) { 2009 if (ext4_has_inline_data(inode))
2011 up_write(&EXT4_I(inode)->xattr_sem); 2010 error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
2012 goto out; 2011 ext4_write_unlock_xattr(inode, &no_expand);
2013 }
2014
2015 error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
2016 up_write(&EXT4_I(inode)->xattr_sem);
2017out:
2018 ext4_journal_stop(handle); 2012 ext4_journal_stop(handle);
2019out_free: 2013out_free:
2020 brelse(iloc.bh); 2014 brelse(iloc.bh);
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index d77be9e9f535..4448ed37181b 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1174,16 +1174,14 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
1174 struct ext4_xattr_block_find bs = { 1174 struct ext4_xattr_block_find bs = {
1175 .s = { .not_found = -ENODATA, }, 1175 .s = { .not_found = -ENODATA, },
1176 }; 1176 };
1177 unsigned long no_expand; 1177 int no_expand;
1178 int error; 1178 int error;
1179 1179
1180 if (!name) 1180 if (!name)
1181 return -EINVAL; 1181 return -EINVAL;
1182 if (strlen(name) > 255) 1182 if (strlen(name) > 255)
1183 return -ERANGE; 1183 return -ERANGE;
1184 down_write(&EXT4_I(inode)->xattr_sem); 1184 ext4_write_lock_xattr(inode, &no_expand);
1185 no_expand = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
1186 ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
1187 1185
1188 error = ext4_reserve_inode_write(handle, inode, &is.iloc); 1186 error = ext4_reserve_inode_write(handle, inode, &is.iloc);
1189 if (error) 1187 if (error)
@@ -1251,7 +1249,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
1251 ext4_xattr_update_super_block(handle, inode->i_sb); 1249 ext4_xattr_update_super_block(handle, inode->i_sb);
1252 inode->i_ctime = ext4_current_time(inode); 1250 inode->i_ctime = ext4_current_time(inode);
1253 if (!value) 1251 if (!value)
1254 ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); 1252 no_expand = 0;
1255 error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); 1253 error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
1256 /* 1254 /*
1257 * The bh is consumed by ext4_mark_iloc_dirty, even with 1255 * The bh is consumed by ext4_mark_iloc_dirty, even with
@@ -1265,9 +1263,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
1265cleanup: 1263cleanup:
1266 brelse(is.iloc.bh); 1264 brelse(is.iloc.bh);
1267 brelse(bs.bh); 1265 brelse(bs.bh);
1268 if (no_expand == 0) 1266 ext4_write_unlock_xattr(inode, &no_expand);
1269 ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
1270 up_write(&EXT4_I(inode)->xattr_sem);
1271 return error; 1267 return error;
1272} 1268}
1273 1269
@@ -1484,12 +1480,11 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
1484 int error = 0, tried_min_extra_isize = 0; 1480 int error = 0, tried_min_extra_isize = 0;
1485 int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize); 1481 int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
1486 int isize_diff; /* How much do we need to grow i_extra_isize */ 1482 int isize_diff; /* How much do we need to grow i_extra_isize */
1483 int no_expand;
1484
1485 if (ext4_write_trylock_xattr(inode, &no_expand) == 0)
1486 return 0;
1487 1487
1488 down_write(&EXT4_I(inode)->xattr_sem);
1489 /*
1490 * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
1491 */
1492 ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
1493retry: 1488retry:
1494 isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize; 1489 isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
1495 if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) 1490 if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
@@ -1571,17 +1566,16 @@ shift:
1571 EXT4_I(inode)->i_extra_isize = new_extra_isize; 1566 EXT4_I(inode)->i_extra_isize = new_extra_isize;
1572 brelse(bh); 1567 brelse(bh);
1573out: 1568out:
1574 ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); 1569 ext4_write_unlock_xattr(inode, &no_expand);
1575 up_write(&EXT4_I(inode)->xattr_sem);
1576 return 0; 1570 return 0;
1577 1571
1578cleanup: 1572cleanup:
1579 brelse(bh); 1573 brelse(bh);
1580 /* 1574 /*
1581 * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode 1575 * Inode size expansion failed; don't try again
1582 * size expansion failed.
1583 */ 1576 */
1584 up_write(&EXT4_I(inode)->xattr_sem); 1577 no_expand = 1;
1578 ext4_write_unlock_xattr(inode, &no_expand);
1585 return error; 1579 return error;
1586} 1580}
1587 1581
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index a92e783fa057..099c8b670ef5 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -102,6 +102,38 @@ extern const struct xattr_handler ext4_xattr_security_handler;
102 102
103#define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c" 103#define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c"
104 104
105/*
106 * The EXT4_STATE_NO_EXPAND is overloaded and used for two purposes.
107 * The first is to signal that there the inline xattrs and data are
108 * taking up so much space that we might as well not keep trying to
109 * expand it. The second is that xattr_sem is taken for writing, so
110 * we shouldn't try to recurse into the inode expansion. For this
111 * second case, we need to make sure that we take save and restore the
112 * NO_EXPAND state flag appropriately.
113 */
114static inline void ext4_write_lock_xattr(struct inode *inode, int *save)
115{
116 down_write(&EXT4_I(inode)->xattr_sem);
117 *save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
118 ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
119}
120
121static inline int ext4_write_trylock_xattr(struct inode *inode, int *save)
122{
123 if (down_write_trylock(&EXT4_I(inode)->xattr_sem) == 0)
124 return 0;
125 *save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
126 ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
127 return 1;
128}
129
130static inline void ext4_write_unlock_xattr(struct inode *inode, int *save)
131{
132 if (*save == 0)
133 ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
134 up_write(&EXT4_I(inode)->xattr_sem);
135}
136
105extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); 137extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);
106 138
107extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t); 139extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);