Btrfs: avoid potential super block corruption
[safe/jmp/linux-2.6] / fs / btrfs / tree-defrag.c
index 1677e4e..a6a3956 100644 (file)
 #include "transaction.h"
 #include "locking.h"
 
+/* defrag all the leaves in a given btree.  If cache_only == 1, don't read things
+ * from disk, otherwise read all the leaves and try to get key order to
+ * better reflect disk order
+ */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                        struct btrfs_root *root, int cache_only)
 {
@@ -32,14 +36,20 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
        int wret;
        int level;
        int orig_level;
-       int i;
        int is_extent = 0;
        int next_key_ret = 0;
        u64 last_ret = 0;
+       u64 min_trans = 0;
+
+       if (cache_only)
+               goto out;
 
        if (root->fs_info->extent_root == root) {
-               mutex_lock(&root->fs_info->alloc_mutex);
-               is_extent = 1;
+               /*
+                * there's recursion here right now in the tree locking,
+                * we can't defrag the extent root without deadlock
+                */
+               goto out;
        }
 
        if (root->ref_cows == 0 && !is_extent)
@@ -75,8 +85,19 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                memcpy(&key, &root->defrag_progress, sizeof(key));
        }
 
-       path->lowest_level = 1;
        path->keep_locks = 1;
+       if (cache_only)
+               min_trans = root->defrag_trans_start;
+
+       ret = btrfs_search_forward(root, &key, NULL, path,
+                                  cache_only, min_trans);
+       if (ret < 0)
+               goto out;
+       if (ret > 0) {
+               ret = 0;
+               goto out;
+       }
+       btrfs_release_path(root, path);
        wret = btrfs_search_slot(trans, root, &key, path, 0, 1);
 
        if (wret < 0) {
@@ -88,7 +109,8 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                goto out;
        }
        path->slots[1] = btrfs_header_nritems(path->nodes[1]);
-       next_key_ret = btrfs_find_next_key(root, path, &key, 1);
+       next_key_ret = btrfs_find_next_key(root, path, &key, 1, cache_only,
+                                          min_trans);
        ret = btrfs_realloc_node(trans, root,
                                 path->nodes[1], 0,
                                 cache_only, &last_ret,
@@ -99,23 +121,10 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                ret = -EAGAIN;
        }
 
-       for (i = 1; i < BTRFS_MAX_LEVEL; i++) {
-               if (path->locks[i]) {
-                       btrfs_tree_unlock(path->nodes[i]);
-                       path->locks[i] = 0;
-               }
-               if (path->nodes[i]) {
-                       free_extent_buffer(path->nodes[i]);
-                       path->nodes[i] = NULL;
-               }
-       }
+       btrfs_release_path(root, path);
        if (is_extent)
                btrfs_extent_post_op(trans, root);
-
 out:
-       if (is_extent)
-               mutex_unlock(&root->fs_info->alloc_mutex);
-
        if (path)
                btrfs_free_path(path);
        if (ret == -EAGAIN) {
@@ -131,6 +140,7 @@ done:
        if (ret != -EAGAIN) {
                memset(&root->defrag_progress, 0,
                       sizeof(root->defrag_progress));
+               root->defrag_trans_start = trans->transid;
        }
        return ret;
 }