Merge tag 'for-5.3-rc8-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Sep 2019 08:48:47 +0000 (09:48 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Sep 2019 08:48:47 +0000 (09:48 +0100)
Pull btrfs fixes from David Sterba:
 "Here are two fixes, one of them urgent fixing a bug introduced in 5.2
  and reported by many users. It took time to identify the root cause,
  catching the 5.3 release is higly desired also to push the fix to 5.2
  stable tree.

  The bug is a mess up of return values after adding proper error
  handling and honestly the kind of bug that can cause sleeping
  disorders until it's caught. My appologies to everybody who was
  affected.

  Summary of what could happen:

  1) either a hang when committing a transaction, if this happens
     there's no risk of corruption, still the hang is very inconvenient
     and can't be resolved without a reboot

  2) writeback for some btree nodes may never be started and we end up
     committing a transaction without noticing that, this is really
     serious and that will lead to the "parent transid verify failed"
     messages"

* tag 'for-5.3-rc8-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  Btrfs: fix unwritten extent buffers and hangs on future writeback attempts
  Btrfs: fix assertion failure during fsync and use of stale transaction

fs/btrfs/extent_io.c
fs/btrfs/tree-log.c

index 1ff438fd5bc2906499a0eb075e8c38740e64be7b..eeb75281894ed6a3ad41dfe53d9f1fa7df2d84dc 100644 (file)
@@ -3628,6 +3628,13 @@ void wait_on_extent_buffer_writeback(struct extent_buffer *eb)
                       TASK_UNINTERRUPTIBLE);
 }
 
+static void end_extent_buffer_writeback(struct extent_buffer *eb)
+{
+       clear_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
+       smp_mb__after_atomic();
+       wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK);
+}
+
 /*
  * Lock eb pages and flush the bio if we can't the locks
  *
@@ -3699,8 +3706,11 @@ static noinline_for_stack int lock_extent_buffer_for_io(struct extent_buffer *eb
 
                if (!trylock_page(p)) {
                        if (!flush) {
-                               ret = flush_write_bio(epd);
-                               if (ret < 0) {
+                               int err;
+
+                               err = flush_write_bio(epd);
+                               if (err < 0) {
+                                       ret = err;
                                        failed_page_nr = i;
                                        goto err_unlock;
                                }
@@ -3715,16 +3725,23 @@ err_unlock:
        /* Unlock already locked pages */
        for (i = 0; i < failed_page_nr; i++)
                unlock_page(eb->pages[i]);
+       /*
+        * Clear EXTENT_BUFFER_WRITEBACK and wake up anyone waiting on it.
+        * Also set back EXTENT_BUFFER_DIRTY so future attempts to this eb can
+        * be made and undo everything done before.
+        */
+       btrfs_tree_lock(eb);
+       spin_lock(&eb->refs_lock);
+       set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
+       end_extent_buffer_writeback(eb);
+       spin_unlock(&eb->refs_lock);
+       percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, eb->len,
+                                fs_info->dirty_metadata_batch);
+       btrfs_clear_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
+       btrfs_tree_unlock(eb);
        return ret;
 }
 
-static void end_extent_buffer_writeback(struct extent_buffer *eb)
-{
-       clear_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
-       smp_mb__after_atomic();
-       wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK);
-}
-
 static void set_btree_ioerr(struct page *page)
 {
        struct extent_buffer *eb = (struct extent_buffer *)page->private;
index 6c8297bcfeb7caa63bc2b39bdd06b5a1f7708489..1bfd7e34f31e33709f0e937f83c6d978d74564c6 100644 (file)
@@ -4985,7 +4985,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
                                                      BTRFS_I(inode),
                                                      LOG_OTHER_INODE_ALL,
                                                      0, LLONG_MAX, ctx);
-                                       iput(inode);
+                                       btrfs_add_delayed_iput(inode);
                                }
                        }
                        continue;
@@ -5000,7 +5000,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
                ret = btrfs_log_inode(trans, root, BTRFS_I(inode),
                                      LOG_OTHER_INODE, 0, LLONG_MAX, ctx);
                if (ret) {
-                       iput(inode);
+                       btrfs_add_delayed_iput(inode);
                        continue;
                }
 
@@ -5009,7 +5009,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
                key.offset = 0;
                ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
                if (ret < 0) {
-                       iput(inode);
+                       btrfs_add_delayed_iput(inode);
                        continue;
                }
 
@@ -5056,7 +5056,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
                        }
                        path->slots[0]++;
                }
-               iput(inode);
+               btrfs_add_delayed_iput(inode);
        }
 
        return ret;
@@ -5689,7 +5689,7 @@ process_leaf:
                        }
 
                        if (btrfs_inode_in_log(BTRFS_I(di_inode), trans->transid)) {
-                               iput(di_inode);
+                               btrfs_add_delayed_iput(di_inode);
                                break;
                        }
 
@@ -5701,7 +5701,7 @@ process_leaf:
                        if (!ret &&
                            btrfs_must_commit_transaction(trans, BTRFS_I(di_inode)))
                                ret = 1;
-                       iput(di_inode);
+                       btrfs_add_delayed_iput(di_inode);
                        if (ret)
                                goto next_dir_inode;
                        if (ctx->log_new_dentries) {
@@ -5848,7 +5848,7 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans,
                        if (!ret && ctx && ctx->log_new_dentries)
                                ret = log_new_dir_dentries(trans, root,
                                                   BTRFS_I(dir_inode), ctx);
-                       iput(dir_inode);
+                       btrfs_add_delayed_iput(dir_inode);
                        if (ret)
                                goto out;
                }
@@ -5891,7 +5891,7 @@ static int log_new_ancestors(struct btrfs_trans_handle *trans,
                        ret = btrfs_log_inode(trans, root, BTRFS_I(inode),
                                              LOG_INODE_EXISTS,
                                              0, LLONG_MAX, ctx);
-               iput(inode);
+               btrfs_add_delayed_iput(inode);
                if (ret)
                        return ret;