]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Apr 2018 12:29:33 +0000 (14:29 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Apr 2018 12:29:33 +0000 (14:29 +0200)
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

queue-3.18/ext4-bugfix-for-mmaped-pages-in-mpage_release_unused_pages.patch [new file with mode: 0644]
queue-3.18/ext4-don-t-update-checksum-of-new-initialized-bitmaps.patch [new file with mode: 0644]
queue-3.18/ext4-fix-deadlock-between-inline_data-and-ext4_expand_extra_isize_ea.patch [new file with mode: 0644]
queue-3.18/series

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 (file)
index 0000000..95e54a5
--- /dev/null
@@ -0,0 +1,72 @@
+From 4e800c0359d9a53e6bf0ab216954971b2515247f Mon Sep 17 00:00:00 2001
+From: wangguang <wang.guang55@zte.com.cn>
+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 <wang.guang55@zte.com.cn>
+
+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 <wangguang03@zte.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+[@nathanchance: Resolved conflict from lack of 09cbfeaf1a5a6]
+Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
+Signed-off-by: Harsh Shandilya <harsh@prjkt.io>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..eaef9a5
--- /dev/null
@@ -0,0 +1,106 @@
+From 044e6e3d74a3d7103a0c8a9305dfd94d64000660 Mon Sep 17 00:00:00 2001
+From: Theodore Ts'o <tytso@mit.edu>
+Date: Mon, 19 Feb 2018 14:16:47 -0500
+Subject: ext4: don't update checksum of new initialized bitmaps
+
+From: Theodore Ts'o <tytso@mit.edu>
+
+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 <tytso@mit.edu>
+Cc: stable@vger.kernel.org
+Signed-off-by: Harsh Shandilya <harsh@prjkt.io>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..b2a8463
--- /dev/null
@@ -0,0 +1,475 @@
+From c755e251357a0cee0679081f08c3f4ba797a8009 Mon Sep 17 00:00:00 2001
+From: Theodore Ts'o <tytso@mit.edu>
+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 <tytso@mit.edu>
+
+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
+
+Cc: stable@vger.kernel.org # 3.10 (requires 2e81a4eeedca as a prereq)
+Reported-by: George Spelvin <linux@sciencehorizons.net>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Harsh Shandilya <harsh@prjkt.io>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
index 394354543515cf211e03563ce1518232c17e2946..4105cee923c38ab37fd81151829340c5c0c8c42b 100644 (file)
@@ -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