diff options
author | Theodore Ts'o <tytso@mit.edu> | 2017-01-11 21:50:46 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-03-12 00:41:41 -0500 |
commit | da1e40237f8f3516581b534c484c236a79ccfd14 (patch) | |
tree | 27da64666bd1563e07dfe9ff3947c5ce6eaa07ed /fs | |
parent | 719f1765b02c6a65eff5db3765c87444a1156cb9 (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.c | 66 | ||||
-rw-r--r-- | fs/ext4/xattr.c | 30 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 32 |
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: | |||
376 | static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, | 376 | static 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: | |||
719 | int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, | 718 | int 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); |
752 | out: | 751 | out: |
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: | |||
1249 | int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, | 1248 | int 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 | ||
1304 | out: | 1303 | out: |
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); |
1707 | out: | 1706 | out: |
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 | ||
1805 | int ext4_destroy_inline_data(handle_t *handle, struct inode *inode) | 1804 | int 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: | |||
1893 | void ext4_inline_data_truncate(struct inode *inode, int *has_inline) | 1892 | void 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); |
1969 | out: | 1968 | out: |
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 | ||
1985 | int ext4_convert_inline_data(struct inode *inode) | 1984 | int 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); | ||
2017 | out: | ||
2018 | ext4_journal_stop(handle); | 2012 | ext4_journal_stop(handle); |
2019 | out_free: | 2013 | out_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, | |||
1265 | cleanup: | 1263 | cleanup: |
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); | ||
1493 | retry: | 1488 | retry: |
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); |
1573 | out: | 1568 | out: |
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 | ||
1578 | cleanup: | 1572 | cleanup: |
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 | */ | ||
114 | static 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 | |||
121 | static 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 | |||
130 | static 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 | |||
105 | extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); | 137 | extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); |
106 | 138 | ||
107 | extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t); | 139 | extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t); |