Switch open_exec() and sys_uselib() to do_open_filp()
[safe/jmp/linux-2.6] / fs / btrfs / ref-cache.c
index 95a9fae..d0cc62b 100644 (file)
  */
 
 #include <linux/sched.h>
+#include <linux/sort.h>
 #include "ctree.h"
 #include "ref-cache.h"
 #include "transaction.h"
 
-struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(int nr_extents)
+/*
+ * leaf refs are used to cache the information about which extents
+ * a given leaf has references on.  This allows us to process that leaf
+ * in btrfs_drop_snapshot without needing to read it back from disk.
+ */
+
+/*
+ * kmalloc a leaf reference struct and update the counters for the
+ * total ref cache size
+ */
+struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,
+                                           int nr_extents)
 {
        struct btrfs_leaf_ref *ref;
+       size_t size = btrfs_leaf_ref_size(nr_extents);
 
-       ref = kmalloc(btrfs_leaf_ref_size(nr_extents), GFP_NOFS);
+       ref = kmalloc(size, GFP_NOFS);
        if (ref) {
+               spin_lock(&root->fs_info->ref_cache_lock);
+               root->fs_info->total_ref_cache_size += size;
+               spin_unlock(&root->fs_info->ref_cache_lock);
+
                memset(ref, 0, sizeof(*ref));
                atomic_set(&ref->usage, 1);
+               INIT_LIST_HEAD(&ref->list);
        }
        return ref;
 }
 
-void btrfs_free_leaf_ref(struct btrfs_leaf_ref *ref)
+/*
+ * free a leaf reference struct and update the counters for the
+ * total ref cache size
+ */
+void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
 {
        if (!ref)
                return;
        WARN_ON(atomic_read(&ref->usage) == 0);
        if (atomic_dec_and_test(&ref->usage)) {
+               size_t size = btrfs_leaf_ref_size(ref->nritems);
+
                BUG_ON(ref->in_tree);
                kfree(ref);
-       }
-}
 
-static int comp_keys(struct btrfs_key *k1, struct btrfs_key *k2)
-{
-       if (k1->objectid > k2->objectid)
-               return 1;
-       if (k1->objectid < k2->objectid)
-               return -1;
-       if (k1->type > k2->type)
-               return 1;
-       if (k1->type < k2->type)
-               return -1;
-       if (k1->offset > k2->offset)
-               return 1;
-       if (k1->offset < k2->offset)
-               return -1;
-       return 0;
+               spin_lock(&root->fs_info->ref_cache_lock);
+               root->fs_info->total_ref_cache_size -= size;
+               spin_unlock(&root->fs_info->ref_cache_lock);
+       }
 }
 
-static struct rb_node *tree_insert(struct rb_root *root, struct btrfs_key *key,
+static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
                                   struct rb_node *node)
 {
-       struct rb_node ** p = &root->rb_node;
-       struct rb_node * parent = NULL;
+       struct rb_node **p = &root->rb_node;
+       struct rb_node *parent = NULL;
        struct btrfs_leaf_ref *entry;
-       int ret;
 
-       while(*p) {
+       while (*p) {
                parent = *p;
                entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node);
-               WARN_ON(!entry->in_tree);
 
-               ret = comp_keys(key, &entry->key);
-               if (ret < 0)
+               if (bytenr < entry->bytenr)
                        p = &(*p)->rb_left;
-               else if (ret > 0)
+               else if (bytenr > entry->bytenr)
                        p = &(*p)->rb_right;
                else
                        return parent;
        }
-       
+
        entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
-       entry->in_tree = 1;
        rb_link_node(node, parent, p);
        rb_insert_color(node, root);
        return NULL;
 }
 
-static struct rb_node *tree_search(struct rb_root *root, struct btrfs_key *key)
+static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
 {
-       struct rb_node * n = root->rb_node;
+       struct rb_node *n = root->rb_node;
        struct btrfs_leaf_ref *entry;
-       int ret;
 
-       while(n) {
+       while (n) {
                entry = rb_entry(n, struct btrfs_leaf_ref, rb_node);
                WARN_ON(!entry->in_tree);
 
-               ret = comp_keys(key, &entry->key);
-               if (ret < 0)
+               if (bytenr < entry->bytenr)
                        n = n->rb_left;
-               else if (ret > 0)
+               else if (bytenr > entry->bytenr)
                        n = n->rb_right;
                else
                        return n;
@@ -111,27 +116,33 @@ static struct rb_node *tree_search(struct rb_root *root, struct btrfs_key *key)
        return NULL;
 }
 
-int btrfs_remove_leaf_refs(struct btrfs_root *root)
+int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen,
+                          int shared)
 {
-       struct rb_node *rb;
        struct btrfs_leaf_ref *ref = NULL;
        struct btrfs_leaf_ref_tree *tree = root->ref_tree;
 
+       if (shared)
+               tree = &root->fs_info->shared_ref_tree;
        if (!tree)
                return 0;
 
        spin_lock(&tree->lock);
-       while(!btrfs_leaf_ref_tree_empty(tree)) {
-               tree->last = NULL;
-               rb = rb_first(&tree->root);
-               ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node);
+       while (!list_empty(&tree->list)) {
+               ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list);
+               BUG_ON(ref->tree != tree);
+               if (ref->root_gen > max_root_gen)
+                       break;
+               if (!xchg(&ref->in_tree, 0)) {
+                       cond_resched_lock(&tree->lock);
+                       continue;
+               }
+
                rb_erase(&ref->rb_node, &tree->root);
-               ref->in_tree = 0;
+               list_del_init(&ref->list);
 
                spin_unlock(&tree->lock);
-
-               btrfs_free_leaf_ref(ref);
-
+               btrfs_free_leaf_ref(root, ref);
                cond_resched();
                spin_lock(&tree->lock);
        }
@@ -139,88 +150,82 @@ int btrfs_remove_leaf_refs(struct btrfs_root *root)
        return 0;
 }
 
+/*
+ * find the leaf ref for a given extent.  This returns the ref struct with
+ * a usage reference incremented
+ */
 struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,
-                                            struct btrfs_key *key)
+                                            u64 bytenr)
 {
        struct rb_node *rb;
        struct btrfs_leaf_ref *ref = NULL;
        struct btrfs_leaf_ref_tree *tree = root->ref_tree;
-
-       if (!tree)
-               return NULL;
-
-       spin_lock(&tree->lock);
-       if (tree->last && comp_keys(key, &tree->last->key) == 0) {
-               ref = tree->last;
-       } else {
-               rb = tree_search(&tree->root, key);
-               if (rb) {
+again:
+       if (tree) {
+               spin_lock(&tree->lock);
+               rb = tree_search(&tree->root, bytenr);
+               if (rb)
                        ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node);
-                       tree->last = ref;
-               }
+               if (ref)
+                       atomic_inc(&ref->usage);
+               spin_unlock(&tree->lock);
+               if (ref)
+                       return ref;
        }
-       if (ref)
-               atomic_inc(&ref->usage);
-       spin_unlock(&tree->lock);
-       return ref;
+       if (tree != &root->fs_info->shared_ref_tree) {
+               tree = &root->fs_info->shared_ref_tree;
+               goto again;
+       }
+       return NULL;
 }
 
-int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
+/*
+ * add a fully filled in leaf ref struct
+ * remove all the refs older than a given root generation
+ */
+int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref,
+                      int shared)
 {
        int ret = 0;
        struct rb_node *rb;
-       size_t size = btrfs_leaf_ref_size(ref->nritems);
        struct btrfs_leaf_ref_tree *tree = root->ref_tree;
-       struct btrfs_transaction *trans = root->fs_info->running_transaction;
+
+       if (shared)
+               tree = &root->fs_info->shared_ref_tree;
 
        spin_lock(&tree->lock);
-       rb = tree_insert(&tree->root, &ref->key, &ref->rb_node);
+       rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node);
        if (rb) {
                ret = -EEXIST;
        } else {
-               spin_lock(&root->fs_info->ref_cache_lock);
-               root->fs_info->total_ref_cache_size += size;
-               if (trans && tree->generation == trans->transid)
-                       root->fs_info->running_ref_cache_size += size;
-               spin_unlock(&root->fs_info->ref_cache_lock);
-
-               tree->last = ref;
                atomic_inc(&ref->usage);
+               ref->tree = tree;
+               ref->in_tree = 1;
+               list_add_tail(&ref->list, &tree->list);
        }
        spin_unlock(&tree->lock);
        return ret;
 }
 
+/*
+ * remove a single leaf ref from the tree.  This drops the ref held by the tree
+ * only
+ */
 int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
 {
-       size_t size = btrfs_leaf_ref_size(ref->nritems);
-       struct btrfs_leaf_ref_tree *tree = root->ref_tree;
-       struct btrfs_transaction *trans = root->fs_info->running_transaction;
+       struct btrfs_leaf_ref_tree *tree;
+
+       if (!xchg(&ref->in_tree, 0))
+               return 0;
 
-       BUG_ON(!ref->in_tree);
+       tree = ref->tree;
        spin_lock(&tree->lock);
-       
-       spin_lock(&root->fs_info->ref_cache_lock);
-       root->fs_info->total_ref_cache_size -= size;
-       if (trans && tree->generation == trans->transid)
-               root->fs_info->running_ref_cache_size -= size;
-       spin_unlock(&root->fs_info->ref_cache_lock);
-
-       if (tree->last == ref) {
-               struct rb_node *next = rb_next(&ref->rb_node);
-               if (next) {
-                       tree->last = rb_entry(next, struct btrfs_leaf_ref,
-                                             rb_node);
-               } else
-                       tree->last = NULL;
-       }
 
        rb_erase(&ref->rb_node, &tree->root);
-       ref->in_tree = 0;
+       list_del_init(&ref->list);
 
        spin_unlock(&tree->lock);
 
-       btrfs_free_leaf_ref(ref);
+       btrfs_free_leaf_ref(root, ref);
        return 0;
 }
-