extent-tree.c 15.8 KB
Newer Older
1
#include <linux/module.h>
2 3 4
#include "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
5
#include "transaction.h"
6

7 8 9 10 11
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
			    *orig_root, u64 num_blocks, u64 search_start, u64
			    search_end, struct btrfs_key *ins);
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root);
C
Chris Mason 已提交
12 13
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root);
14

15 16
static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
			 *root, u64 blocknr)
C
Chris Mason 已提交
17
{
C
Chris Mason 已提交
18
	struct btrfs_path path;
C
Chris Mason 已提交
19
	int ret;
C
Chris Mason 已提交
20
	struct btrfs_key key;
C
Chris Mason 已提交
21 22
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
C
Chris Mason 已提交
23
	struct btrfs_key ins;
C
Chris Mason 已提交
24
	u32 refs;
C
Chris Mason 已提交
25

26 27
	find_free_extent(trans, root->fs_info->extent_root, 0, 0, (u64)-1,
			 &ins);
C
Chris Mason 已提交
28
	btrfs_init_path(&path);
C
Chris Mason 已提交
29 30
	key.objectid = blocknr;
	key.flags = 0;
31
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
32
	key.offset = 1;
33 34
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path,
				0, 1);
35 36
	if (ret != 0)
		BUG();
C
Chris Mason 已提交
37
	BUG_ON(ret != 0);
C
Chris Mason 已提交
38
	l = btrfs_buffer_leaf(path.nodes[0]);
39
	item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
40 41
	refs = btrfs_extent_refs(item);
	btrfs_set_extent_refs(item, refs + 1);
42
	mark_buffer_dirty(path.nodes[0]);
43

44 45
	btrfs_release_path(root->fs_info->extent_root, &path);
	finish_current_insert(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
46
	del_pending_extents(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
47 48 49
	return 0;
}

50 51
static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
			    *root, u64 blocknr, u32 *refs)
52
{
C
Chris Mason 已提交
53
	struct btrfs_path path;
54
	int ret;
C
Chris Mason 已提交
55
	struct btrfs_key key;
C
Chris Mason 已提交
56 57 58
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
	btrfs_init_path(&path);
59 60
	key.objectid = blocknr;
	key.offset = 1;
61 62
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
63 64
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path,
				0, 0);
65 66
	if (ret != 0)
		BUG();
C
Chris Mason 已提交
67
	l = btrfs_buffer_leaf(path.nodes[0]);
68
	item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
69
	*refs = btrfs_extent_refs(item);
70
	btrfs_release_path(root->fs_info->extent_root, &path);
71 72 73
	return 0;
}

74
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
75
		  struct buffer_head *buf)
C
Chris Mason 已提交
76 77
{
	u64 blocknr;
C
Chris Mason 已提交
78
	struct btrfs_node *buf_node;
C
Chris Mason 已提交
79
	int i;
80

81
	if (!root->ref_cows)
82
		return 0;
C
Chris Mason 已提交
83 84
	buf_node = btrfs_buffer_node(buf);
	if (btrfs_is_leaf(buf_node))
85 86
		return 0;

C
Chris Mason 已提交
87 88
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
		blocknr = btrfs_node_blockptr(buf_node, i);
89
		inc_block_ref(trans, root, blocknr);
C
Chris Mason 已提交
90 91 92 93
	}
	return 0;
}

94 95
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
			       btrfs_root *root)
96
{
C
Chris Mason 已提交
97
	unsigned long gang[8];
98
	u64 first = 0;
99 100
	int ret;
	int i;
C
Chris Mason 已提交
101
	struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
102 103

	while(1) {
C
Chris Mason 已提交
104 105
		ret = find_first_radix_bit(pinned_radix, gang,
					   ARRAY_SIZE(gang));
106 107
		if (!ret)
			break;
108
		if (!first)
C
Chris Mason 已提交
109
			first = gang[0];
110
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
111
			clear_radix_bit(pinned_radix, gang[i]);
112
		}
113
	}
114 115
	if (root->fs_info->last_insert.objectid > first)
		root->fs_info->last_insert.objectid = first;
116
	root->fs_info->last_insert.offset = 0;
117 118 119
	return 0;
}

120 121
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root)
C
Chris Mason 已提交
122
{
C
Chris Mason 已提交
123
	struct btrfs_key ins;
C
Chris Mason 已提交
124
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
125 126
	int i;
	int ret;
127 128
	u64 super_blocks_used;
	struct btrfs_fs_info *info = extent_root->fs_info;
C
Chris Mason 已提交
129

C
Chris Mason 已提交
130 131
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item,
C
Chris Mason 已提交
132
		btrfs_header_parentid(btrfs_buffer_header(extent_root->node)));
C
Chris Mason 已提交
133 134
	ins.offset = 1;
	ins.flags = 0;
135
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
136

137 138 139
	for (i = 0; i < extent_root->fs_info->current_insert.flags; i++) {
		ins.objectid = extent_root->fs_info->current_insert.objectid +
				i;
140 141 142
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used + 1);
143 144
		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
					sizeof(extent_item));
C
Chris Mason 已提交
145 146
		BUG_ON(ret);
	}
147
	extent_root->fs_info->current_insert.offset = 0;
C
Chris Mason 已提交
148 149 150
	return 0;
}

C
Chris Mason 已提交
151
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
152 153
{
	int err;
C
Chris Mason 已提交
154
	struct btrfs_header *header;
C
Chris Mason 已提交
155 156 157 158 159 160 161 162 163 164
	struct buffer_head *bh;

	bh = sb_find_get_block(root->fs_info->sb, blocknr);
	if (bh) {
		header = btrfs_buffer_header(bh);
		if (btrfs_header_generation(header) ==
		    root->fs_info->running_transaction->transid) {
			brelse(bh);
			return 0;
		}
C
Chris Mason 已提交
165
		brelse(bh);
C
Chris Mason 已提交
166 167 168 169 170 171
	}
	if (pending)
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	else
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
	BUG_ON(err);
C
Chris Mason 已提交
172 173 174
	return 0;
}

175
/*
176
 * remove an extent from the root, returns 0 on success
177
 */
178
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
179
			 *root, u64 blocknr, u64 num_blocks, int pin)
180
{
C
Chris Mason 已提交
181
	struct btrfs_path path;
C
Chris Mason 已提交
182
	struct btrfs_key key;
183 184
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
185
	int ret;
C
Chris Mason 已提交
186
	struct btrfs_extent_item *ei;
C
Chris Mason 已提交
187
	struct btrfs_key ins;
C
Chris Mason 已提交
188
	u32 refs;
C
Chris Mason 已提交
189

190 191
	key.objectid = blocknr;
	key.flags = 0;
192
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
193 194
	key.offset = num_blocks;

195
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
C
Chris Mason 已提交
196
	btrfs_init_path(&path);
197
	ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
198
	if (ret) {
199
		printk("failed to find %Lu\n", key.objectid);
C
Chris Mason 已提交
200
		btrfs_print_tree(extent_root, extent_root->node);
201
		printk("failed to find %Lu\n", key.objectid);
202 203
		BUG();
	}
C
Chris Mason 已提交
204
	ei = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0],
C
Chris Mason 已提交
205
			    struct btrfs_extent_item);
206
	BUG_ON(ei->refs == 0);
C
Chris Mason 已提交
207 208 209
	refs = btrfs_extent_refs(ei) - 1;
	btrfs_set_extent_refs(ei, refs);
	if (refs == 0) {
210
		u64 super_blocks_used;
C
Chris Mason 已提交
211 212

		if (pin) {
C
Chris Mason 已提交
213
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
214 215 216
			BUG_ON(ret);
		}

217 218 219
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used - num_blocks);
220
		ret = btrfs_del_item(trans, extent_root, &path);
C
Chris Mason 已提交
221
		if (extent_root->fs_info->last_insert.objectid > blocknr)
222
			extent_root->fs_info->last_insert.objectid = blocknr;
223 224 225
		if (ret)
			BUG();
	}
226
	mark_buffer_dirty(path.nodes[0]);
C
Chris Mason 已提交
227
	btrfs_release_path(extent_root, &path);
228
	finish_current_insert(trans, extent_root);
229 230 231 232 233 234 235
	return ret;
}

/*
 * find all the blocks marked as pending in the radix tree and remove
 * them from the extent map
 */
236 237
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root)
238 239
{
	int ret;
C
Chris Mason 已提交
240 241
	int wret;
	int err = 0;
C
Chris Mason 已提交
242
	unsigned long gang[4];
243
	int i;
C
Chris Mason 已提交
244 245 246 247 248
	struct radix_tree_root *pending_radix;
	struct radix_tree_root *pinned_radix;

	pending_radix = &extent_root->fs_info->pending_del_radix;
	pinned_radix = &extent_root->fs_info->pinned_radix;
249 250

	while(1) {
C
Chris Mason 已提交
251 252
		ret = find_first_radix_bit(pending_radix, gang,
					   ARRAY_SIZE(gang));
253 254 255
		if (!ret)
			break;
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
256 257 258 259
			wret = set_radix_bit(pinned_radix, gang[i]);
			BUG_ON(wret);
			wret = clear_radix_bit(pending_radix, gang[i]);
			BUG_ON(wret);
260
			wret = __free_extent(trans, extent_root,
C
Chris Mason 已提交
261
					     gang[i], 1, 0);
C
Chris Mason 已提交
262 263
			if (wret)
				err = wret;
264 265
		}
	}
C
Chris Mason 已提交
266
	return err;
267 268 269 270 271
}

/*
 * remove an extent from the root, returns 0 on success
 */
272 273
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
274
{
275
	struct btrfs_root *extent_root = root->fs_info->extent_root;
C
Chris Mason 已提交
276
	struct buffer_head *t;
277 278
	int pending_ret;
	int ret;
279

280
	if (root == extent_root) {
281
		t = find_tree_block(root, blocknr);
C
Chris Mason 已提交
282
		pin_down_block(root, blocknr, 1);
283 284
		return 0;
	}
C
Chris Mason 已提交
285
	ret = __free_extent(trans, root, blocknr, num_blocks, pin);
C
Chris Mason 已提交
286
	pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
287 288 289 290 291 292 293
	return ret ? ret : pending_ret;
}

/*
 * walks the btree of allocated extents and find a hole of a given size.
 * The key ins is changed to record the hole:
 * ins->objectid == block start
294
 * ins->flags = BTRFS_EXTENT_ITEM_KEY
295 296 297
 * ins->offset == number of blocks
 * Any available blocks before search_start are skipped.
 */
298 299 300
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
			    *orig_root, u64 num_blocks, u64 search_start, u64
			    search_end, struct btrfs_key *ins)
301
{
C
Chris Mason 已提交
302
	struct btrfs_path path;
C
Chris Mason 已提交
303
	struct btrfs_key key;
304 305 306
	int ret;
	u64 hole_size = 0;
	int slot = 0;
C
Chris Mason 已提交
307
	u64 last_block = 0;
C
Chris Mason 已提交
308
	u64 test_block;
309
	int start_found;
C
Chris Mason 已提交
310
	struct btrfs_leaf *l;
311
	struct btrfs_root * root = orig_root->fs_info->extent_root;
312
	int total_needed = num_blocks;
C
Chris Mason 已提交
313
	int level;
314

C
Chris Mason 已提交
315 316
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
317 318
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
319 320 321 322

	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);

323
check_failed:
C
Chris Mason 已提交
324
	btrfs_init_path(&path);
325 326 327
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
328
	ret = btrfs_search_slot(trans, root, ins, &path, 0, 0);
C
Chris Mason 已提交
329 330
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
331

332 333 334
	if (path.slots[0] > 0)
		path.slots[0]--;

335
	while (1) {
C
Chris Mason 已提交
336
		l = btrfs_buffer_leaf(path.nodes[0]);
337
		slot = path.slots[0];
338
		if (slot >= btrfs_header_nritems(&l->header)) {
C
Chris Mason 已提交
339
			ret = btrfs_next_leaf(root, &path);
340 341
			if (ret == 0)
				continue;
C
Chris Mason 已提交
342 343
			if (ret < 0)
				goto error;
344 345
			if (!start_found) {
				ins->objectid = search_start;
C
Chris Mason 已提交
346
				ins->offset = (u64)-1;
347 348 349 350 351
				start_found = 1;
				goto check_pending;
			}
			ins->objectid = last_block > search_start ?
					last_block : search_start;
C
Chris Mason 已提交
352
			ins->offset = (u64)-1;
353 354
			goto check_pending;
		}
C
Chris Mason 已提交
355 356
		btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
		if (key.objectid >= search_start) {
357
			if (start_found) {
358 359
				if (last_block < search_start)
					last_block = search_start;
C
Chris Mason 已提交
360
				hole_size = key.objectid - last_block;
C
Chris Mason 已提交
361
				if (hole_size > total_needed) {
362
					ins->objectid = last_block;
C
Chris Mason 已提交
363
					ins->offset = hole_size;
364 365
					goto check_pending;
				}
366
			}
367
		}
368
		start_found = 1;
C
Chris Mason 已提交
369
		last_block = key.objectid + key.offset;
370 371 372 373 374 375 376
		path.slots[0]++;
	}
	// FIXME -ENOSPC
check_pending:
	/* we have to make sure we didn't find an extent that has already
	 * been allocated by the map tree or the original allocation
	 */
C
Chris Mason 已提交
377
	btrfs_release_path(root, &path);
378
	BUG_ON(ins->objectid < search_start);
C
Chris Mason 已提交
379 380
	for (test_block = ins->objectid;
	     test_block < ins->objectid + total_needed; test_block++) {
C
Chris Mason 已提交
381
		if (test_radix_bit(&root->fs_info->pinned_radix,
382
				      test_block)) {
C
Chris Mason 已提交
383
			search_start = test_block + 1;
384 385 386
			goto check_failed;
		}
	}
387 388 389 390 391
	BUG_ON(root->fs_info->current_insert.offset);
	root->fs_info->current_insert.offset = total_needed - num_blocks;
	root->fs_info->current_insert.objectid = ins->objectid + num_blocks;
	root->fs_info->current_insert.flags = 0;
	root->fs_info->last_insert.objectid = ins->objectid;
C
Chris Mason 已提交
392
	ins->offset = num_blocks;
393
	return 0;
C
Chris Mason 已提交
394
error:
C
Chris Mason 已提交
395
	btrfs_release_path(root, &path);
C
Chris Mason 已提交
396
	return ret;
397 398 399 400 401 402 403 404 405
}

/*
 * finds a free extent and does all the dirty work required for allocation
 * returns the key for the extent through ins, and a tree buffer for
 * the first block of the extent through buf.
 *
 * returns 0 if everything worked, non-zero otherwise.
 */
C
Chris Mason 已提交
406
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
407 408
			*root, u64 num_blocks, u64 search_start, u64
			search_end, u64 owner, struct btrfs_key *ins)
409 410 411
{
	int ret;
	int pending_ret;
412 413 414
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
415
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
416

C
Chris Mason 已提交
417 418
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item, owner);
419

C
Chris Mason 已提交
420
	if (root == extent_root) {
421
		BUG_ON(extent_root->fs_info->current_insert.offset == 0);
C
Chris Mason 已提交
422
		BUG_ON(num_blocks != 1);
423 424
		BUG_ON(extent_root->fs_info->current_insert.flags ==
		       extent_root->fs_info->current_insert.offset);
C
Chris Mason 已提交
425
		ins->offset = 1;
426 427
		ins->objectid = extent_root->fs_info->current_insert.objectid +
				extent_root->fs_info->current_insert.flags++;
428 429
		return 0;
	}
430
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
431 432 433
			       search_end, ins);
	if (ret)
		return ret;
434

435 436 437
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
438 439
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
440

441
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
442
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
443 444 445 446 447
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
448 449 450 451 452 453
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
454
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
455
					    struct btrfs_root *root)
456
{
C
Chris Mason 已提交
457
	struct btrfs_key ins;
458
	int ret;
C
Chris Mason 已提交
459
	struct buffer_head *buf;
460

C
Chris Mason 已提交
461
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1,
C
Chris Mason 已提交
462
		btrfs_header_parentid(btrfs_buffer_header(root->node)), &ins);
463 464 465 466
	if (ret) {
		BUG();
		return NULL;
	}
C
Chris Mason 已提交
467
	buf = find_tree_block(root, ins.objectid);
468
	set_buffer_uptodate(buf);
469 470
	return buf;
}
471

C
Chris Mason 已提交
472 473 474 475
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
476 477
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
478
{
C
Chris Mason 已提交
479 480
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
481 482 483 484
	u64 blocknr;
	int ret;
	u32 refs;

C
Chris Mason 已提交
485
	ret = lookup_block_ref(trans, root, path->nodes[*level]->b_blocknr,
486
			       &refs);
C
Chris Mason 已提交
487 488 489
	BUG_ON(ret);
	if (refs > 1)
		goto out;
C
Chris Mason 已提交
490 491 492
	/*
	 * walk down to the last node level and free all the leaves
	 */
C
Chris Mason 已提交
493 494
	while(*level > 0) {
		cur = path->nodes[*level];
495
		if (path->slots[*level] >=
C
Chris Mason 已提交
496
		    btrfs_header_nritems(btrfs_buffer_header(cur)))
C
Chris Mason 已提交
497
			break;
C
Chris Mason 已提交
498 499
		blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
					      path->slots[*level]);
500
		ret = lookup_block_ref(trans, root, blocknr, &refs);
C
Chris Mason 已提交
501 502
		if (refs != 1 || *level == 1) {
			path->slots[*level]++;
503
			ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
C
Chris Mason 已提交
504 505 506 507 508
			BUG_ON(ret);
			continue;
		}
		BUG_ON(ret);
		next = read_tree_block(root, blocknr);
C
Chris Mason 已提交
509
		if (path->nodes[*level-1])
C
Chris Mason 已提交
510
			btrfs_block_release(root, path->nodes[*level-1]);
C
Chris Mason 已提交
511
		path->nodes[*level-1] = next;
C
Chris Mason 已提交
512
		*level = btrfs_header_level(btrfs_buffer_header(next));
C
Chris Mason 已提交
513 514 515
		path->slots[*level] = 0;
	}
out:
C
Chris Mason 已提交
516 517
	ret = btrfs_free_extent(trans, root, path->nodes[*level]->b_blocknr,
				1, 1);
C
Chris Mason 已提交
518
	btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
519 520 521 522 523 524
	path->nodes[*level] = NULL;
	*level += 1;
	BUG_ON(ret);
	return 0;
}

C
Chris Mason 已提交
525 526 527 528 529
/*
 * helper for dropping snapshots.  This walks back up the tree in the path
 * to find the first node higher up where we haven't yet gone through
 * all the slots
 */
530 531
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			*root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
532 533 534 535
{
	int i;
	int slot;
	int ret;
C
Chris Mason 已提交
536
	for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
C
Chris Mason 已提交
537
		slot = path->slots[i];
C
Chris Mason 已提交
538 539
		if (slot < btrfs_header_nritems(
		    btrfs_buffer_header(path->nodes[i])) - 1) {
C
Chris Mason 已提交
540 541 542 543
			path->slots[i]++;
			*level = i;
			return 0;
		} else {
544
			ret = btrfs_free_extent(trans, root,
C
Chris Mason 已提交
545
						path->nodes[*level]->b_blocknr,
546
						1, 1);
C
Chris Mason 已提交
547
			btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
548
			path->nodes[*level] = NULL;
C
Chris Mason 已提交
549 550 551 552 553 554 555
			*level = i + 1;
			BUG_ON(ret);
		}
	}
	return 1;
}

C
Chris Mason 已提交
556 557 558 559 560
/*
 * drop the reference count on the tree rooted at 'snap'.  This traverses
 * the tree freeing any blocks that have a ref count of zero after being
 * decremented.
 */
561
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
562
			*root, struct buffer_head *snap)
C
Chris Mason 已提交
563
{
564
	int ret = 0;
C
Chris Mason 已提交
565
	int wret;
C
Chris Mason 已提交
566
	int level;
C
Chris Mason 已提交
567
	struct btrfs_path path;
C
Chris Mason 已提交
568 569 570
	int i;
	int orig_level;

C
Chris Mason 已提交
571
	btrfs_init_path(&path);
C
Chris Mason 已提交
572

C
Chris Mason 已提交
573
	level = btrfs_header_level(btrfs_buffer_header(snap));
C
Chris Mason 已提交
574 575 576 577
	orig_level = level;
	path.nodes[level] = snap;
	path.slots[level] = 0;
	while(1) {
578
		wret = walk_down_tree(trans, root, &path, &level);
C
Chris Mason 已提交
579
		if (wret > 0)
C
Chris Mason 已提交
580
			break;
C
Chris Mason 已提交
581 582 583
		if (wret < 0)
			ret = wret;

584
		wret = walk_up_tree(trans, root, &path, &level);
C
Chris Mason 已提交
585
		if (wret > 0)
C
Chris Mason 已提交
586
			break;
C
Chris Mason 已提交
587 588
		if (wret < 0)
			ret = wret;
C
Chris Mason 已提交
589
	}
C
Chris Mason 已提交
590 591
	for (i = 0; i <= orig_level; i++) {
		if (path.nodes[i]) {
C
Chris Mason 已提交
592
			btrfs_block_release(root, path.nodes[i]);
C
Chris Mason 已提交
593
		}
C
Chris Mason 已提交
594
	}
C
Chris Mason 已提交
595
	return ret;
C
Chris Mason 已提交
596
}