ref-cache.c 5.4 KB
Newer Older
Y
Yan Zheng 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (C) 2008 Oracle.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License v2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 */

#include <linux/sched.h>
#include "ctree.h"
#include "ref-cache.h"
#include "transaction.h"

C
Chris Mason 已提交
24 25 26 27 28 29 30 31 32 33
/*
 * 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
 */
34 35
struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,
					    int nr_extents)
Y
Yan Zheng 已提交
36 37
{
	struct btrfs_leaf_ref *ref;
38
	size_t size = btrfs_leaf_ref_size(nr_extents);
Y
Yan Zheng 已提交
39

40
	ref = kmalloc(size, GFP_NOFS);
Y
Yan Zheng 已提交
41
	if (ref) {
42 43 44 45
		spin_lock(&root->fs_info->ref_cache_lock);
		root->fs_info->total_ref_cache_size += size;
		spin_unlock(&root->fs_info->ref_cache_lock);

Y
Yan Zheng 已提交
46 47
		memset(ref, 0, sizeof(*ref));
		atomic_set(&ref->usage, 1);
C
Chris Mason 已提交
48
		INIT_LIST_HEAD(&ref->list);
Y
Yan Zheng 已提交
49 50 51 52
	}
	return ref;
}

C
Chris Mason 已提交
53 54 55 56
/*
 * free a leaf reference struct and update the counters for the
 * total ref cache size
 */
57
void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
Y
Yan Zheng 已提交
58 59 60 61 62
{
	if (!ref)
		return;
	WARN_ON(atomic_read(&ref->usage) == 0);
	if (atomic_dec_and_test(&ref->usage)) {
63 64
		size_t size = btrfs_leaf_ref_size(ref->nritems);

Y
Yan Zheng 已提交
65 66
		BUG_ON(ref->in_tree);
		kfree(ref);
67 68 69 70

		spin_lock(&root->fs_info->ref_cache_lock);
		root->fs_info->total_ref_cache_size -= size;
		spin_unlock(&root->fs_info->ref_cache_lock);
Y
Yan Zheng 已提交
71 72 73
	}
}

C
Chris Mason 已提交
74
static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
Y
Yan Zheng 已提交
75 76
				   struct rb_node *node)
{
C
Chris Mason 已提交
77 78
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
Y
Yan Zheng 已提交
79 80
	struct btrfs_leaf_ref *entry;

C
Chris Mason 已提交
81
	while (*p) {
Y
Yan Zheng 已提交
82 83 84
		parent = *p;
		entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node);

C
Chris Mason 已提交
85
		if (bytenr < entry->bytenr)
Y
Yan Zheng 已提交
86
			p = &(*p)->rb_left;
C
Chris Mason 已提交
87
		else if (bytenr > entry->bytenr)
Y
Yan Zheng 已提交
88 89 90 91
			p = &(*p)->rb_right;
		else
			return parent;
	}
92

Y
Yan Zheng 已提交
93 94 95 96 97 98
	entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
	rb_link_node(node, parent, p);
	rb_insert_color(node, root);
	return NULL;
}

C
Chris Mason 已提交
99
static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
Y
Yan Zheng 已提交
100
{
C
Chris Mason 已提交
101
	struct rb_node *n = root->rb_node;
Y
Yan Zheng 已提交
102 103
	struct btrfs_leaf_ref *entry;

C
Chris Mason 已提交
104
	while (n) {
Y
Yan Zheng 已提交
105 106 107
		entry = rb_entry(n, struct btrfs_leaf_ref, rb_node);
		WARN_ON(!entry->in_tree);

C
Chris Mason 已提交
108
		if (bytenr < entry->bytenr)
Y
Yan Zheng 已提交
109
			n = n->rb_left;
C
Chris Mason 已提交
110
		else if (bytenr > entry->bytenr)
Y
Yan Zheng 已提交
111 112 113 114 115 116 117
			n = n->rb_right;
		else
			return n;
	}
	return NULL;
}

Z
Zheng Yan 已提交
118 119
int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen,
			   int shared)
Y
Yan Zheng 已提交
120 121 122 123
{
	struct btrfs_leaf_ref *ref = NULL;
	struct btrfs_leaf_ref_tree *tree = root->ref_tree;

Z
Zheng Yan 已提交
124 125
	if (shared)
		tree = &root->fs_info->shared_ref_tree;
Y
Yan Zheng 已提交
126 127 128 129
	if (!tree)
		return 0;

	spin_lock(&tree->lock);
C
Chris Mason 已提交
130
	while (!list_empty(&tree->list)) {
131
		ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list);
Z
Zheng Yan 已提交
132
		BUG_ON(ref->tree != tree);
133 134
		if (ref->root_gen > max_root_gen)
			break;
Z
Zheng Yan 已提交
135 136 137 138
		if (!xchg(&ref->in_tree, 0)) {
			cond_resched_lock(&tree->lock);
			continue;
		}
139

Y
Yan Zheng 已提交
140
		rb_erase(&ref->rb_node, &tree->root);
C
Chris Mason 已提交
141
		list_del_init(&ref->list);
Y
Yan Zheng 已提交
142 143

		spin_unlock(&tree->lock);
144
		btrfs_free_leaf_ref(root, ref);
Y
Yan Zheng 已提交
145 146 147 148 149 150 151
		cond_resched();
		spin_lock(&tree->lock);
	}
	spin_unlock(&tree->lock);
	return 0;
}

C
Chris Mason 已提交
152 153 154 155
/*
 * find the leaf ref for a given extent.  This returns the ref struct with
 * a usage reference incremented
 */
Y
Yan Zheng 已提交
156
struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,
C
Chris Mason 已提交
157
					     u64 bytenr)
Y
Yan Zheng 已提交
158 159 160 161
{
	struct rb_node *rb;
	struct btrfs_leaf_ref *ref = NULL;
	struct btrfs_leaf_ref_tree *tree = root->ref_tree;
Z
Zheng Yan 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
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);
		if (ref)
			atomic_inc(&ref->usage);
		spin_unlock(&tree->lock);
		if (ref)
			return ref;
	}
	if (tree != &root->fs_info->shared_ref_tree) {
		tree = &root->fs_info->shared_ref_tree;
		goto again;
	}
	return NULL;
Y
Yan Zheng 已提交
179 180
}

C
Chris Mason 已提交
181 182 183 184
/*
 * add a fully filled in leaf ref struct
 * remove all the refs older than a given root generation
 */
Z
Zheng Yan 已提交
185 186
int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref,
		       int shared)
Y
Yan Zheng 已提交
187 188 189 190 191
{
	int ret = 0;
	struct rb_node *rb;
	struct btrfs_leaf_ref_tree *tree = root->ref_tree;

Z
Zheng Yan 已提交
192 193 194
	if (shared)
		tree = &root->fs_info->shared_ref_tree;

Y
Yan Zheng 已提交
195
	spin_lock(&tree->lock);
C
Chris Mason 已提交
196
	rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node);
Y
Yan Zheng 已提交
197 198 199 200
	if (rb) {
		ret = -EEXIST;
	} else {
		atomic_inc(&ref->usage);
Z
Zheng Yan 已提交
201 202
		ref->tree = tree;
		ref->in_tree = 1;
C
Chris Mason 已提交
203
		list_add_tail(&ref->list, &tree->list);
Y
Yan Zheng 已提交
204 205 206 207 208
	}
	spin_unlock(&tree->lock);
	return ret;
}

C
Chris Mason 已提交
209 210 211 212
/*
 * remove a single leaf ref from the tree.  This drops the ref held by the tree
 * only
 */
Y
Yan Zheng 已提交
213 214
int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
{
Z
Zheng Yan 已提交
215 216 217 218
	struct btrfs_leaf_ref_tree *tree;

	if (!xchg(&ref->in_tree, 0))
		return 0;
Y
Yan Zheng 已提交
219

Z
Zheng Yan 已提交
220
	tree = ref->tree;
Y
Yan Zheng 已提交
221 222 223
	spin_lock(&tree->lock);

	rb_erase(&ref->rb_node, &tree->root);
C
Chris Mason 已提交
224
	list_del_init(&ref->list);
Y
Yan Zheng 已提交
225 226 227

	spin_unlock(&tree->lock);

228
	btrfs_free_leaf_ref(root, ref);
Y
Yan Zheng 已提交
229 230
	return 0;
}