at v2.6.30-rc4 231 lines 5.6 kB view raw
1/* 2 * Copyright (C) 2008 Oracle. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public 6 * License v2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public 14 * License along with this program; if not, write to the 15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 * Boston, MA 021110-1307, USA. 17 */ 18 19#include <linux/sched.h> 20#include <linux/sort.h> 21#include "ctree.h" 22#include "ref-cache.h" 23#include "transaction.h" 24 25/* 26 * leaf refs are used to cache the information about which extents 27 * a given leaf has references on. This allows us to process that leaf 28 * in btrfs_drop_snapshot without needing to read it back from disk. 29 */ 30 31/* 32 * kmalloc a leaf reference struct and update the counters for the 33 * total ref cache size 34 */ 35struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root, 36 int nr_extents) 37{ 38 struct btrfs_leaf_ref *ref; 39 size_t size = btrfs_leaf_ref_size(nr_extents); 40 41 ref = kmalloc(size, GFP_NOFS); 42 if (ref) { 43 spin_lock(&root->fs_info->ref_cache_lock); 44 root->fs_info->total_ref_cache_size += size; 45 spin_unlock(&root->fs_info->ref_cache_lock); 46 47 memset(ref, 0, sizeof(*ref)); 48 atomic_set(&ref->usage, 1); 49 INIT_LIST_HEAD(&ref->list); 50 } 51 return ref; 52} 53 54/* 55 * free a leaf reference struct and update the counters for the 56 * total ref cache size 57 */ 58void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) 59{ 60 if (!ref) 61 return; 62 WARN_ON(atomic_read(&ref->usage) == 0); 63 if (atomic_dec_and_test(&ref->usage)) { 64 size_t size = btrfs_leaf_ref_size(ref->nritems); 65 66 BUG_ON(ref->in_tree); 67 kfree(ref); 68 69 spin_lock(&root->fs_info->ref_cache_lock); 70 root->fs_info->total_ref_cache_size -= size; 71 spin_unlock(&root->fs_info->ref_cache_lock); 72 } 73} 74 75static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr, 76 struct rb_node *node) 77{ 78 struct rb_node **p = &root->rb_node; 79 struct rb_node *parent = NULL; 80 struct btrfs_leaf_ref *entry; 81 82 while (*p) { 83 parent = *p; 84 entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node); 85 86 if (bytenr < entry->bytenr) 87 p = &(*p)->rb_left; 88 else if (bytenr > entry->bytenr) 89 p = &(*p)->rb_right; 90 else 91 return parent; 92 } 93 94 entry = rb_entry(node, struct btrfs_leaf_ref, rb_node); 95 rb_link_node(node, parent, p); 96 rb_insert_color(node, root); 97 return NULL; 98} 99 100static struct rb_node *tree_search(struct rb_root *root, u64 bytenr) 101{ 102 struct rb_node *n = root->rb_node; 103 struct btrfs_leaf_ref *entry; 104 105 while (n) { 106 entry = rb_entry(n, struct btrfs_leaf_ref, rb_node); 107 WARN_ON(!entry->in_tree); 108 109 if (bytenr < entry->bytenr) 110 n = n->rb_left; 111 else if (bytenr > entry->bytenr) 112 n = n->rb_right; 113 else 114 return n; 115 } 116 return NULL; 117} 118 119int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen, 120 int shared) 121{ 122 struct btrfs_leaf_ref *ref = NULL; 123 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 124 125 if (shared) 126 tree = &root->fs_info->shared_ref_tree; 127 if (!tree) 128 return 0; 129 130 spin_lock(&tree->lock); 131 while (!list_empty(&tree->list)) { 132 ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list); 133 BUG_ON(ref->tree != tree); 134 if (ref->root_gen > max_root_gen) 135 break; 136 if (!xchg(&ref->in_tree, 0)) { 137 cond_resched_lock(&tree->lock); 138 continue; 139 } 140 141 rb_erase(&ref->rb_node, &tree->root); 142 list_del_init(&ref->list); 143 144 spin_unlock(&tree->lock); 145 btrfs_free_leaf_ref(root, ref); 146 cond_resched(); 147 spin_lock(&tree->lock); 148 } 149 spin_unlock(&tree->lock); 150 return 0; 151} 152 153/* 154 * find the leaf ref for a given extent. This returns the ref struct with 155 * a usage reference incremented 156 */ 157struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root, 158 u64 bytenr) 159{ 160 struct rb_node *rb; 161 struct btrfs_leaf_ref *ref = NULL; 162 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 163again: 164 if (tree) { 165 spin_lock(&tree->lock); 166 rb = tree_search(&tree->root, bytenr); 167 if (rb) 168 ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); 169 if (ref) 170 atomic_inc(&ref->usage); 171 spin_unlock(&tree->lock); 172 if (ref) 173 return ref; 174 } 175 if (tree != &root->fs_info->shared_ref_tree) { 176 tree = &root->fs_info->shared_ref_tree; 177 goto again; 178 } 179 return NULL; 180} 181 182/* 183 * add a fully filled in leaf ref struct 184 * remove all the refs older than a given root generation 185 */ 186int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref, 187 int shared) 188{ 189 int ret = 0; 190 struct rb_node *rb; 191 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 192 193 if (shared) 194 tree = &root->fs_info->shared_ref_tree; 195 196 spin_lock(&tree->lock); 197 rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node); 198 if (rb) { 199 ret = -EEXIST; 200 } else { 201 atomic_inc(&ref->usage); 202 ref->tree = tree; 203 ref->in_tree = 1; 204 list_add_tail(&ref->list, &tree->list); 205 } 206 spin_unlock(&tree->lock); 207 return ret; 208} 209 210/* 211 * remove a single leaf ref from the tree. This drops the ref held by the tree 212 * only 213 */ 214int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) 215{ 216 struct btrfs_leaf_ref_tree *tree; 217 218 if (!xchg(&ref->in_tree, 0)) 219 return 0; 220 221 tree = ref->tree; 222 spin_lock(&tree->lock); 223 224 rb_erase(&ref->rb_node, &tree->root); 225 list_del_init(&ref->list); 226 227 spin_unlock(&tree->lock); 228 229 btrfs_free_leaf_ref(root, ref); 230 return 0; 231}