From: Greg Kroah-Hartman Date: Tue, 24 Apr 2018 12:29:33 +0000 (+0200) Subject: 3.18-stable patches X-Git-Tag: v4.16.5~23 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b1a0ce701e89bb0e09a0b93f1d7b05dcc18f023e;p=thirdparty%2Fkernel%2Fstable-queue.git 3.18-stable patches added patches: ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch --- diff --git a/queue-3.18/ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch b/queue-3.18/ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch new file mode 100644 index 00000000000..95e54a5d99c --- /dev/null +++ b/queue-3.18/ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch @@ -0,0 +1,72 @@ +From 4e800c0359d9a53e6bf0ab216954971b2515247f Mon Sep 17 00:00:00 2001 +From: wangguang +Date: Thu, 15 Sep 2016 11:32:46 -0400 +Subject: ext4: bugfix for mmaped pages in mpage_release_unused_pages() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: wangguang + +commit 4e800c0359d9a53e6bf0ab216954971b2515247f upstream. + +Pages clear buffers after ext4 delayed block allocation failed, +However, it does not clean its pte_dirty flag. +if the pages unmap ,in cording to the pte_dirty , +unmap_page_range may try to call __set_page_dirty, + +which may lead to the bugon at +mpage_prepare_extent_to_map:head = page_buffers(page);. + +This patch just call clear_page_dirty_for_io to clean pte_dirty +at mpage_release_unused_pages for pages mmaped. + +Steps to reproduce the bug: + +(1) mmap a file in ext4 + addr = (char *)mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, + fd, 0); + memset(addr, 'i', 4096); + +(2) return EIO at + + ext4_writepages->mpage_map_and_submit_extent->mpage_map_one_extent + +which causes this log message to be print: + + ext4_msg(sb, KERN_CRIT, + "Delayed block allocation failed for " + "inode %lu at logical offset %llu with" + " max blocks %u with error %d", + inode->i_ino, + (unsigned long long)map->m_lblk, + (unsigned)map->m_len, -err); + +(3)Unmap the addr cause warning at + + __set_page_dirty:WARN_ON_ONCE(warn && !PageUptodate(page)); + +(4) wait for a minute,then bugon happen. + +Cc: stable@vger.kernel.org +Signed-off-by: wangguang +Signed-off-by: Theodore Ts'o +[@nathanchance: Resolved conflict from lack of 09cbfeaf1a5a6] +Signed-off-by: Nathan Chancellor +Signed-off-by: Harsh Shandilya +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/inode.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -1338,6 +1338,8 @@ static void mpage_release_unused_pages(s + BUG_ON(!PageLocked(page)); + BUG_ON(PageWriteback(page)); + if (invalidate) { ++ if (page_mapped(page)) ++ clear_page_dirty_for_io(page); + block_invalidatepage(page, 0, PAGE_CACHE_SIZE); + ClearPageUptodate(page); + } diff --git a/queue-3.18/ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch b/queue-3.18/ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch new file mode 100644 index 00000000000..eaef9a5e973 --- /dev/null +++ b/queue-3.18/ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch @@ -0,0 +1,106 @@ +From 044e6e3d74a3d7103a0c8a9305dfd94d64000660 Mon Sep 17 00:00:00 2001 +From: Theodore Ts'o +Date: Mon, 19 Feb 2018 14:16:47 -0500 +Subject: ext4: don't update checksum of new initialized bitmaps + +From: Theodore Ts'o + +commit 044e6e3d74a3d7103a0c8a9305dfd94d64000660 upstream. + +When reading the inode or block allocation bitmap, if the bitmap needs +to be initialized, do not update the checksum in the block group +descriptor. That's because we're not set up to journal those changes. +Instead, just set the verified bit on the bitmap block, so that it's +not necessary to validate the checksum. + +When a block or inode allocation actually happens, at that point the +checksum will be calculated, and update of the bg descriptor block +will be properly journalled. + +Signed-off-by: Theodore Ts'o +Cc: stable@vger.kernel.org +Signed-off-by: Harsh Shandilya +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/balloc.c | 3 +-- + fs/ext4/ialloc.c | 43 +++---------------------------------------- + 2 files changed, 4 insertions(+), 42 deletions(-) + +--- a/fs/ext4/balloc.c ++++ b/fs/ext4/balloc.c +@@ -243,8 +243,6 @@ static int ext4_init_block_bitmap(struct + */ + ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group), + sb->s_blocksize * 8, bh->b_data); +- ext4_block_bitmap_csum_set(sb, block_group, gdp, bh); +- ext4_group_desc_csum_set(sb, block_group, gdp); + return 0; + } + +@@ -446,6 +444,7 @@ ext4_read_block_bitmap_nowait(struct sup + err = ext4_init_block_bitmap(sb, bh, block_group, desc); + set_bitmap_uptodate(bh); + set_buffer_uptodate(bh); ++ set_buffer_verified(bh); + ext4_unlock_group(sb, block_group); + unlock_buffer(bh); + if (err) +--- a/fs/ext4/ialloc.c ++++ b/fs/ext4/ialloc.c +@@ -64,45 +64,6 @@ void ext4_mark_bitmap_end(int start_bit, + memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); + } + +-/* Initializes an uninitialized inode bitmap */ +-static unsigned ext4_init_inode_bitmap(struct super_block *sb, +- struct buffer_head *bh, +- ext4_group_t block_group, +- struct ext4_group_desc *gdp) +-{ +- struct ext4_group_info *grp; +- struct ext4_sb_info *sbi = EXT4_SB(sb); +- J_ASSERT_BH(bh, buffer_locked(bh)); +- +- /* If checksum is bad mark all blocks and inodes use to prevent +- * allocation, essentially implementing a per-group read-only flag. */ +- if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) { +- ext4_error(sb, "Checksum bad for group %u", block_group); +- grp = ext4_get_group_info(sb, block_group); +- if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) +- percpu_counter_sub(&sbi->s_freeclusters_counter, +- grp->bb_free); +- set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state); +- if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) { +- int count; +- count = ext4_free_inodes_count(sb, gdp); +- percpu_counter_sub(&sbi->s_freeinodes_counter, +- count); +- } +- set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state); +- return 0; +- } +- +- memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); +- ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8, +- bh->b_data); +- ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh, +- EXT4_INODES_PER_GROUP(sb) / 8); +- ext4_group_desc_csum_set(sb, block_group, gdp); +- +- return EXT4_INODES_PER_GROUP(sb); +-} +- + void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate) + { + if (uptodate) { +@@ -151,7 +112,9 @@ ext4_read_inode_bitmap(struct super_bloc + + ext4_lock_group(sb, block_group); + if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { +- ext4_init_inode_bitmap(sb, bh, block_group, desc); ++ memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); ++ ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), ++ sb->s_blocksize * 8, bh->b_data); + set_bitmap_uptodate(bh); + set_buffer_uptodate(bh); + set_buffer_verified(bh); diff --git a/queue-3.18/ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch b/queue-3.18/ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch new file mode 100644 index 00000000000..b2a8463947f --- /dev/null +++ b/queue-3.18/ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch @@ -0,0 +1,475 @@ +From c755e251357a0cee0679081f08c3f4ba797a8009 Mon Sep 17 00:00:00 2001 +From: Theodore Ts'o +Date: Wed, 11 Jan 2017 21:50:46 -0500 +Subject: ext4: fix deadlock between inline_data and ext4_expand_extra_isize_ea() + +From: Theodore Ts'o + +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: [] ext4_expand_extra_isize_ea+0x3d/0x4cd +[ 11.161960] +[ 11.161960] but task is already holding lock: +[ 11.161960] (&ei->xattr_sem){++++..}, at: [] 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: [] mnt_want_write+0x1e/0x3e +[ 11.161960] #1: (&type->i_mutex_dir_key){++++++}, at: [] path_openat+0x338/0x67a +[ 11.161960] #2: (jbd2_handle){++++..}, at: [] start_this_handle+0x582/0x622 +[ 11.161960] #3: (&ei->xattr_sem){++++..}, at: [] 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 + +Cc: stable@vger.kernel.org # 3.10 (requires 2e81a4eeedca as a prereq) +Reported-by: George Spelvin +Signed-off-by: Theodore Ts'o +Signed-off-by: Harsh Shandilya +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/inline.c | 66 +++++++++++++++++++++++++------------------------------ + fs/ext4/xattr.c | 30 ++++++++++--------------- + fs/ext4/xattr.h | 32 ++++++++++++++++++++++++++ + 3 files changed, 74 insertions(+), 54 deletions(-) + +--- a/fs/ext4/inline.c ++++ b/fs/ext4/inline.c +@@ -374,7 +374,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)) +@@ -384,15 +384,14 @@ static int ext4_prepare_inline_data(hand + 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; + } + +@@ -522,7 +521,7 @@ static int ext4_convert_inline_data_to_e + 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; +@@ -562,7 +561,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)) { +@@ -598,7 +597,7 @@ retry: + page_cache_release(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; +@@ -624,7 +623,7 @@ out: + page_cache_release(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); +@@ -717,7 +716,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; + +@@ -735,7 +734,7 @@ int ext4_write_inline_data_end(struct in + 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); +@@ -745,7 +744,7 @@ int ext4_write_inline_data_end(struct in + /* 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; +@@ -756,7 +755,7 @@ ext4_journalled_write_inline_data(struct + unsigned len, + struct page *page) + { +- int ret; ++ int ret, no_expand; + void *kaddr; + struct ext4_iloc iloc; + +@@ -766,11 +765,11 @@ ext4_journalled_write_inline_data(struct + 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; + } +@@ -1245,7 +1244,7 @@ out: + int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, + struct inode *inode) + { +- int ret, inline_size; ++ int ret, inline_size, no_expand; + void *inline_start; + struct ext4_iloc iloc; + struct inode *dir = dentry->d_parent->d_inode; +@@ -1254,7 +1253,7 @@ int ext4_try_add_inline_entry(handle_t * + 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; + +@@ -1299,7 +1298,7 @@ int ext4_try_add_inline_entry(handle_t * + + 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; + } +@@ -1655,7 +1654,7 @@ int ext4_delete_inline_entry(handle_t *h + 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; + +@@ -1663,7 +1662,7 @@ int ext4_delete_inline_entry(handle_t *h + 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; +@@ -1698,7 +1697,7 @@ int ext4_delete_inline_entry(handle_t *h + + 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); +@@ -1797,11 +1796,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; + } +@@ -1879,7 +1878,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 = { +@@ -1896,7 +1895,7 @@ void ext4_inline_data_truncate(struct in + 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); +@@ -1954,7 +1953,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); +@@ -1970,7 +1969,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; + +@@ -1992,15 +1991,10 @@ int ext4_convert_inline_data(struct inod + 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); +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1120,16 +1120,14 @@ ext4_xattr_set_handle(handle_t *handle, + struct ext4_xattr_block_find bs = { + .s = { .not_found = -ENODATA, }, + }; +- unsigned long no_expand; ++ int no_expand; + int error; + + if (!name) + return -EINVAL; + if (strlen(name) > 255) + return -ERANGE; +- down_write(&EXT4_I(inode)->xattr_sem); +- no_expand = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND); +- ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ ext4_write_lock_xattr(inode, &no_expand); + + error = ext4_reserve_inode_write(handle, inode, &is.iloc); + if (error) +@@ -1190,7 +1188,7 @@ ext4_xattr_set_handle(handle_t *handle, + ext4_xattr_update_super_block(handle, inode->i_sb); + inode->i_ctime = ext4_current_time(inode); + if (!value) +- ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ no_expand = 0; + error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); + /* + * The bh is consumed by ext4_mark_iloc_dirty, even with +@@ -1204,9 +1202,7 @@ ext4_xattr_set_handle(handle_t *handle, + cleanup: + brelse(is.iloc.bh); + brelse(bs.bh); +- if (no_expand == 0) +- ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); +- up_write(&EXT4_I(inode)->xattr_sem); ++ ext4_write_unlock_xattr(inode, &no_expand); + return error; + } + +@@ -1289,12 +1285,11 @@ int ext4_expand_extra_isize_ea(struct in + void *base, *start, *end; + int extra_isize = 0, error = 0, tried_min_extra_isize = 0; + int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize); ++ int no_expand; ++ ++ if (ext4_write_trylock_xattr(inode, &no_expand) == 0) ++ return 0; + +- down_write(&EXT4_I(inode)->xattr_sem); +- /* +- * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty +- */ +- ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); + retry: + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) + goto out; +@@ -1487,8 +1482,7 @@ retry: + } + brelse(bh); + out: +- ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); +- up_write(&EXT4_I(inode)->xattr_sem); ++ ext4_write_unlock_xattr(inode, &no_expand); + return 0; + + cleanup: +@@ -1500,10 +1494,10 @@ cleanup: + kfree(bs); + brelse(bh); + /* +- * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode +- * size expansion failed. ++ * Inode size expansion failed; don't try again + */ +- up_write(&EXT4_I(inode)->xattr_sem); ++ no_expand = 1; ++ ext4_write_unlock_xattr(inode, &no_expand); + return error; + } + +--- a/fs/ext4/xattr.h ++++ b/fs/ext4/xattr.h +@@ -98,6 +98,38 @@ extern const struct xattr_handler ext4_x + extern const struct xattr_handler ext4_xattr_trusted_handler; + extern const struct xattr_handler ext4_xattr_security_handler; + ++/* ++ * The EXT4_STATE_NO_EXPAND is overloaded and used for two purposes. ++ * The first is to signal that there the inline xattrs and data are ++ * taking up so much space that we might as well not keep trying to ++ * expand it. The second is that xattr_sem is taken for writing, so ++ * we shouldn't try to recurse into the inode expansion. For this ++ * second case, we need to make sure that we take save and restore the ++ * NO_EXPAND state flag appropriately. ++ */ ++static inline void ext4_write_lock_xattr(struct inode *inode, int *save) ++{ ++ down_write(&EXT4_I(inode)->xattr_sem); ++ *save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); ++} ++ ++static inline int ext4_write_trylock_xattr(struct inode *inode, int *save) ++{ ++ if (down_write_trylock(&EXT4_I(inode)->xattr_sem) == 0) ++ return 0; ++ *save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ return 1; ++} ++ ++static inline void ext4_write_unlock_xattr(struct inode *inode, int *save) ++{ ++ if (*save == 0) ++ ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); ++ up_write(&EXT4_I(inode)->xattr_sem); ++} ++ + extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); + + extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t); diff --git a/queue-3.18/series b/queue-3.18/series index 39435454351..4105cee923c 100644 --- a/queue-3.18/series +++ b/queue-3.18/series @@ -1,2 +1,5 @@ cifs-do-not-allow-creating-sockets-except-with-smb1-posix-exensions.patch x86-tsc-prevent-32bit-truncation-in-calc_hpet_ref.patch +ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch +ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch +ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch