]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/btrfs/tree-log.c
Merge branch 'linus' into perf/urgent, to synchronize with upstream
[linux.git] / fs / btrfs / tree-log.c
index 9a6b1303fcabcebed3b19f593e099ddcd9056245..7dd7552f53a42217cf26bf400bdec7e610e89624 100644 (file)
@@ -2674,14 +2674,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
        u32 blocksize;
        int ret = 0;
 
-       WARN_ON(*level < 0);
-       WARN_ON(*level >= BTRFS_MAX_LEVEL);
-
        while (*level > 0) {
                struct btrfs_key first_key;
 
-               WARN_ON(*level < 0);
-               WARN_ON(*level >= BTRFS_MAX_LEVEL);
                cur = path->nodes[*level];
 
                WARN_ON(btrfs_header_level(cur) != *level);
@@ -2748,7 +2743,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                        return ret;
                }
 
-               WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
                        free_extent_buffer(path->nodes[*level-1]);
                path->nodes[*level-1] = next;
@@ -2756,9 +2750,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                path->slots[*level] = 0;
                cond_resched();
        }
-       WARN_ON(*level < 0);
-       WARN_ON(*level >= BTRFS_MAX_LEVEL);
-
        path->slots[*level] = btrfs_header_nritems(path->nodes[*level]);
 
        cond_resched();
@@ -4831,6 +4822,50 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
                        }
                        continue;
                }
+               /*
+                * If the inode was already logged skip it - otherwise we can
+                * hit an infinite loop. Example:
+                *
+                * From the commit root (previous transaction) we have the
+                * following inodes:
+                *
+                * inode 257 a directory
+                * inode 258 with references "zz" and "zz_link" on inode 257
+                * inode 259 with reference "a" on inode 257
+                *
+                * And in the current (uncommitted) transaction we have:
+                *
+                * inode 257 a directory, unchanged
+                * inode 258 with references "a" and "a2" on inode 257
+                * inode 259 with reference "zz_link" on inode 257
+                * inode 261 with reference "zz" on inode 257
+                *
+                * When logging inode 261 the following infinite loop could
+                * happen if we don't skip already logged inodes:
+                *
+                * - we detect inode 258 as a conflicting inode, with inode 261
+                *   on reference "zz", and log it;
+                *
+                * - we detect inode 259 as a conflicting inode, with inode 258
+                *   on reference "a", and log it;
+                *
+                * - we detect inode 258 as a conflicting inode, with inode 259
+                *   on reference "zz_link", and log it - again! After this we
+                *   repeat the above steps forever.
+                */
+               spin_lock(&BTRFS_I(inode)->lock);
+               /*
+                * Check the inode's logged_trans only instead of
+                * btrfs_inode_in_log(). This is because the last_log_commit of
+                * the inode is not updated when we only log that it exists and
+                * and it has the full sync bit set (see btrfs_log_inode()).
+                */
+               if (BTRFS_I(inode)->logged_trans == trans->transid) {
+                       spin_unlock(&BTRFS_I(inode)->lock);
+                       btrfs_add_delayed_iput(inode);
+                       continue;
+               }
+               spin_unlock(&BTRFS_I(inode)->lock);
                /*
                 * We are safe logging the other inode without acquiring its
                 * lock as long as we log with the LOG_INODE_EXISTS mode. We