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/ext4 | |
| 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/ext4')
| -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); |
