]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/btrfs/tree-log.c
Merge branch 'for-5.4/ish' into for-linus
[linux.git] / fs / btrfs / tree-log.c
index 3fc8d854d7fbc2247797bac88838053eb56cd2dd..6c8297bcfeb7caa63bc2b39bdd06b5a1f7708489 100644 (file)
@@ -3322,6 +3322,30 @@ int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+/*
+ * Check if an inode was logged in the current transaction. We can't always rely
+ * on an inode's logged_trans value, because it's an in-memory only field and
+ * therefore not persisted. This means that its value is lost if the inode gets
+ * evicted and loaded again from disk (in which case it has a value of 0, and
+ * certainly it is smaller then any possible transaction ID), when that happens
+ * the full_sync flag is set in the inode's runtime flags, so on that case we
+ * assume eviction happened and ignore the logged_trans value, assuming the
+ * worst case, that the inode was logged before in the current transaction.
+ */
+static bool inode_logged(struct btrfs_trans_handle *trans,
+                        struct btrfs_inode *inode)
+{
+       if (inode->logged_trans == trans->transid)
+               return true;
+
+       if (inode->last_trans == trans->transid &&
+           test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags) &&
+           !test_bit(BTRFS_FS_LOG_RECOVERING, &trans->fs_info->flags))
+               return true;
+
+       return false;
+}
+
 /*
  * If both a file and directory are logged, and unlinks or renames are
  * mixed in, we have a few interesting corners:
@@ -3356,7 +3380,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
        int bytes_del = 0;
        u64 dir_ino = btrfs_ino(dir);
 
-       if (dir->logged_trans < trans->transid)
+       if (!inode_logged(trans, dir))
                return 0;
 
        ret = join_running_log_trans(root);
@@ -3460,7 +3484,7 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        u64 index;
        int ret;
 
-       if (inode->logged_trans < trans->transid)
+       if (!inode_logged(trans, inode))
                return 0;
 
        ret = join_running_log_trans(root);
@@ -5420,9 +5444,19 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                }
        }
 
+       /*
+        * Don't update last_log_commit if we logged that an inode exists after
+        * it was loaded to memory (full_sync bit set).
+        * This is to prevent data loss when we do a write to the inode, then
+        * the inode gets evicted after all delalloc was flushed, then we log
+        * it exists (due to a rename for example) and then fsync it. This last
+        * fsync would do nothing (not logging the extents previously written).
+        */
        spin_lock(&inode->lock);
        inode->logged_trans = trans->transid;
-       inode->last_log_commit = inode->last_sub_trans;
+       if (inode_only != LOG_INODE_EXISTS ||
+           !test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags))
+               inode->last_log_commit = inode->last_sub_trans;
        spin_unlock(&inode->lock);
 out_unlock:
        mutex_unlock(&inode->log_mutex);