extent-tree.c 17.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
static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
16
			 *root, u64 blocknr, u64 num_blocks)
C
Chris Mason 已提交
17
{
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);
28 29 30
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
31 32
	key.objectid = blocknr;
	key.flags = 0;
33
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
34
	key.offset = num_blocks;
35
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
36
				0, 1);
37 38
	if (ret != 0)
		BUG();
C
Chris Mason 已提交
39
	BUG_ON(ret != 0);
40 41
	l = btrfs_buffer_leaf(path->nodes[0]);
	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
42 43
	refs = btrfs_extent_refs(item);
	btrfs_set_extent_refs(item, refs + 1);
44
	btrfs_mark_buffer_dirty(path->nodes[0]);
45

46 47
	btrfs_release_path(root->fs_info->extent_root, path);
	btrfs_free_path(path);
48
	finish_current_insert(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
49
	del_pending_extents(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
50 51 52
	return 0;
}

53
static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
54
			    *root, u64 blocknr, u64 num_blocks, u32 *refs)
55
{
56
	struct btrfs_path *path;
57
	int ret;
C
Chris Mason 已提交
58
	struct btrfs_key key;
C
Chris Mason 已提交
59 60
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
61 62 63

	path = btrfs_alloc_path();
	btrfs_init_path(path);
64
	key.objectid = blocknr;
65
	key.offset = num_blocks;
66 67
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
68
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
69
				0, 0);
70 71
	if (ret != 0)
		BUG();
72 73
	l = btrfs_buffer_leaf(path->nodes[0]);
	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
74
	*refs = btrfs_extent_refs(item);
75 76
	btrfs_release_path(root->fs_info->extent_root, path);
	btrfs_free_path(path);
77 78 79
	return 0;
}

80
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
81
		  struct buffer_head *buf)
C
Chris Mason 已提交
82 83
{
	u64 blocknr;
C
Chris Mason 已提交
84
	struct btrfs_node *buf_node;
85 86 87
	struct btrfs_leaf *buf_leaf;
	struct btrfs_disk_key *key;
	struct btrfs_file_extent_item *fi;
C
Chris Mason 已提交
88
	int i;
89 90
	int leaf;
	int ret;
91

92
	if (!root->ref_cows)
93
		return 0;
C
Chris Mason 已提交
94
	buf_node = btrfs_buffer_node(buf);
95 96
	leaf = btrfs_is_leaf(buf_node);
	buf_leaf = btrfs_buffer_leaf(buf);
C
Chris Mason 已提交
97
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
		if (leaf) {
			key = &buf_leaf->items[i].key;
			if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY)
				continue;
			fi = btrfs_item_ptr(buf_leaf, i,
					    struct btrfs_file_extent_item);
			ret = inc_block_ref(trans, root,
				    btrfs_file_extent_disk_blocknr(fi),
				    btrfs_file_extent_disk_num_blocks(fi));
			BUG_ON(ret);
		} else {
			blocknr = btrfs_node_blockptr(buf_node, i);
			ret = inc_block_ref(trans, root, blocknr, 1);
			BUG_ON(ret);
		}
C
Chris Mason 已提交
113 114 115 116
	}
	return 0;
}

117 118
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
			       btrfs_root *root)
119
{
C
Chris Mason 已提交
120
	unsigned long gang[8];
121
	u64 first = 0;
122 123
	int ret;
	int i;
C
Chris Mason 已提交
124
	struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
125 126

	while(1) {
C
Chris Mason 已提交
127 128
		ret = find_first_radix_bit(pinned_radix, gang,
					   ARRAY_SIZE(gang));
129 130
		if (!ret)
			break;
131
		if (!first)
C
Chris Mason 已提交
132
			first = gang[0];
133
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
134
			clear_radix_bit(pinned_radix, gang[i]);
135
		}
136
	}
137 138
	if (root->fs_info->last_insert.objectid > first)
		root->fs_info->last_insert.objectid = first;
139
	root->fs_info->last_insert.offset = 0;
140 141 142
	return 0;
}

143 144
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root)
C
Chris Mason 已提交
145
{
C
Chris Mason 已提交
146
	struct btrfs_key ins;
C
Chris Mason 已提交
147
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
148 149
	int i;
	int ret;
150 151
	u64 super_blocks_used;
	struct btrfs_fs_info *info = extent_root->fs_info;
C
Chris Mason 已提交
152

C
Chris Mason 已提交
153 154
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item,
C
Chris Mason 已提交
155
		btrfs_header_parentid(btrfs_buffer_header(extent_root->node)));
C
Chris Mason 已提交
156 157
	ins.offset = 1;
	ins.flags = 0;
158
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
159

160 161 162
	for (i = 0; i < extent_root->fs_info->current_insert.flags; i++) {
		ins.objectid = extent_root->fs_info->current_insert.objectid +
				i;
163 164 165
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used + 1);
166 167
		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
					sizeof(extent_item));
C
Chris Mason 已提交
168 169
		BUG_ON(ret);
	}
170
	extent_root->fs_info->current_insert.offset = 0;
C
Chris Mason 已提交
171 172 173
	return 0;
}

C
Chris Mason 已提交
174
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
175 176
{
	int err;
C
Chris Mason 已提交
177
	struct btrfs_header *header;
C
Chris Mason 已提交
178 179
	struct buffer_head *bh;

C
Chris Mason 已提交
180
	if (!pending) {
181
		bh = btrfs_find_tree_block(root, blocknr);
C
Chris Mason 已提交
182 183 184 185 186 187 188 189 190 191
		if (bh) {
			if (buffer_uptodate(bh)) {
				u64 transid =
				    root->fs_info->running_transaction->transid;
				header = btrfs_buffer_header(bh);
				if (btrfs_header_generation(header) ==
				    transid) {
					btrfs_block_release(root, bh);
					return 0;
				}
C
Chris Mason 已提交
192
			}
C
Chris Mason 已提交
193
			btrfs_block_release(root, bh);
C
Chris Mason 已提交
194 195
		}
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
C
Chris Mason 已提交
196 197 198
	} else {
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	}
C
Chris Mason 已提交
199
	BUG_ON(err);
C
Chris Mason 已提交
200 201 202
	return 0;
}

203
/*
204
 * remove an extent from the root, returns 0 on success
205
 */
206
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
207
			 *root, u64 blocknr, u64 num_blocks, int pin)
208
{
209
	struct btrfs_path *path;
C
Chris Mason 已提交
210
	struct btrfs_key key;
211 212
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
213
	int ret;
C
Chris Mason 已提交
214
	struct btrfs_extent_item *ei;
C
Chris Mason 已提交
215
	struct btrfs_key ins;
C
Chris Mason 已提交
216
	u32 refs;
C
Chris Mason 已提交
217

218 219
	key.objectid = blocknr;
	key.flags = 0;
220
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
221 222
	key.offset = num_blocks;

223
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
224 225 226 227
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
	ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1);
228
	if (ret) {
229
		printk("failed to find %Lu\n", key.objectid);
C
Chris Mason 已提交
230
		btrfs_print_tree(extent_root, extent_root->node);
231
		printk("failed to find %Lu\n", key.objectid);
232 233
		BUG();
	}
234
	ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
C
Chris Mason 已提交
235
			    struct btrfs_extent_item);
236
	BUG_ON(ei->refs == 0);
C
Chris Mason 已提交
237 238
	refs = btrfs_extent_refs(ei) - 1;
	btrfs_set_extent_refs(ei, refs);
239
	btrfs_mark_buffer_dirty(path->nodes[0]);
C
Chris Mason 已提交
240
	if (refs == 0) {
241
		u64 super_blocks_used;
C
Chris Mason 已提交
242 243

		if (pin) {
C
Chris Mason 已提交
244
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
245 246 247
			BUG_ON(ret);
		}

248 249 250
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used - num_blocks);
251
		ret = btrfs_del_item(trans, extent_root, path);
252 253 254
		if (ret)
			BUG();
	}
255 256
	btrfs_release_path(extent_root, path);
	btrfs_free_path(path);
257
	finish_current_insert(trans, extent_root);
258 259 260 261 262 263 264
	return ret;
}

/*
 * find all the blocks marked as pending in the radix tree and remove
 * them from the extent map
 */
265 266
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root)
267 268
{
	int ret;
C
Chris Mason 已提交
269 270
	int wret;
	int err = 0;
C
Chris Mason 已提交
271
	unsigned long gang[4];
272
	int i;
C
Chris Mason 已提交
273 274 275 276 277
	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;
278 279

	while(1) {
C
Chris Mason 已提交
280 281
		ret = find_first_radix_bit(pending_radix, gang,
					   ARRAY_SIZE(gang));
282 283 284
		if (!ret)
			break;
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
285 286 287 288
			wret = set_radix_bit(pinned_radix, gang[i]);
			BUG_ON(wret);
			wret = clear_radix_bit(pending_radix, gang[i]);
			BUG_ON(wret);
289
			wret = __free_extent(trans, extent_root,
C
Chris Mason 已提交
290
					     gang[i], 1, 0);
C
Chris Mason 已提交
291 292
			if (wret)
				err = wret;
293 294
		}
	}
C
Chris Mason 已提交
295
	return err;
296 297 298 299 300
}

/*
 * remove an extent from the root, returns 0 on success
 */
301 302
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
303
{
304
	struct btrfs_root *extent_root = root->fs_info->extent_root;
305 306
	int pending_ret;
	int ret;
307

308
	if (root == extent_root) {
C
Chris Mason 已提交
309
		pin_down_block(root, blocknr, 1);
310 311
		return 0;
	}
C
Chris Mason 已提交
312
	ret = __free_extent(trans, root, blocknr, num_blocks, pin);
C
Chris Mason 已提交
313
	pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
314 315 316 317 318 319 320
	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
321
 * ins->flags = BTRFS_EXTENT_ITEM_KEY
322 323 324
 * ins->offset == number of blocks
 * Any available blocks before search_start are skipped.
 */
325 326 327
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)
328
{
329
	struct btrfs_path *path;
C
Chris Mason 已提交
330
	struct btrfs_key key;
331 332 333
	int ret;
	u64 hole_size = 0;
	int slot = 0;
C
Chris Mason 已提交
334
	u64 last_block = 0;
C
Chris Mason 已提交
335
	u64 test_block;
336
	int start_found;
C
Chris Mason 已提交
337
	struct btrfs_leaf *l;
338
	struct btrfs_root * root = orig_root->fs_info->extent_root;
339
	int total_needed = num_blocks;
C
Chris Mason 已提交
340
	int level;
341

C
Chris Mason 已提交
342 343
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
344 345
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
346 347 348

	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
349
	path = btrfs_alloc_path();
350

351
check_failed:
352
	btrfs_init_path(path);
353 354 355
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
356
	ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
C
Chris Mason 已提交
357 358
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
359

360 361
	if (path->slots[0] > 0)
		path->slots[0]--;
362

363
	while (1) {
364 365
		l = btrfs_buffer_leaf(path->nodes[0]);
		slot = path->slots[0];
366
		if (slot >= btrfs_header_nritems(&l->header)) {
367
			ret = btrfs_next_leaf(root, path);
368 369
			if (ret == 0)
				continue;
C
Chris Mason 已提交
370 371
			if (ret < 0)
				goto error;
372 373
			if (!start_found) {
				ins->objectid = search_start;
C
Chris Mason 已提交
374
				ins->offset = (u64)-1;
375 376 377 378 379
				start_found = 1;
				goto check_pending;
			}
			ins->objectid = last_block > search_start ?
					last_block : search_start;
C
Chris Mason 已提交
380
			ins->offset = (u64)-1;
381 382
			goto check_pending;
		}
C
Chris Mason 已提交
383 384
		btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
		if (key.objectid >= search_start) {
385
			if (start_found) {
386 387
				if (last_block < search_start)
					last_block = search_start;
C
Chris Mason 已提交
388
				hole_size = key.objectid - last_block;
C
Chris Mason 已提交
389
				if (hole_size > total_needed) {
390
					ins->objectid = last_block;
C
Chris Mason 已提交
391
					ins->offset = hole_size;
392 393
					goto check_pending;
				}
394
			}
395
		}
396
		start_found = 1;
C
Chris Mason 已提交
397
		last_block = key.objectid + key.offset;
398
		path->slots[0]++;
399 400 401 402 403 404
	}
	// 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
	 */
405
	btrfs_release_path(root, path);
406
	BUG_ON(ins->objectid < search_start);
C
Chris Mason 已提交
407 408
	for (test_block = ins->objectid;
	     test_block < ins->objectid + total_needed; test_block++) {
C
Chris Mason 已提交
409
		if (test_radix_bit(&root->fs_info->pinned_radix,
410
				      test_block)) {
C
Chris Mason 已提交
411
			search_start = test_block + 1;
412 413 414
			goto check_failed;
		}
	}
415 416 417 418 419
	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 已提交
420
	ins->offset = num_blocks;
421
	btrfs_free_path(path);
422
	return 0;
C
Chris Mason 已提交
423
error:
424 425
	btrfs_release_path(root, path);
	btrfs_free_path(path);
C
Chris Mason 已提交
426
	return ret;
427 428 429 430 431 432 433 434 435
}

/*
 * 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 已提交
436
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
437 438
			*root, u64 num_blocks, u64 search_start, u64
			search_end, u64 owner, struct btrfs_key *ins)
439 440 441
{
	int ret;
	int pending_ret;
442 443 444
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
445
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
446

C
Chris Mason 已提交
447 448
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item, owner);
449

C
Chris Mason 已提交
450
	if (root == extent_root) {
451
		BUG_ON(extent_root->fs_info->current_insert.offset == 0);
C
Chris Mason 已提交
452
		BUG_ON(num_blocks != 1);
453 454
		BUG_ON(extent_root->fs_info->current_insert.flags ==
		       extent_root->fs_info->current_insert.offset);
C
Chris Mason 已提交
455
		ins->offset = 1;
456 457
		ins->objectid = extent_root->fs_info->current_insert.objectid +
				extent_root->fs_info->current_insert.flags++;
458 459
		return 0;
	}
460
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
461 462 463
			       search_end, ins);
	if (ret)
		return ret;
464

465 466 467
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
468 469
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
470

471
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
472
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
473 474 475 476 477
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
478 479 480 481 482 483
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
484
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
485
					    struct btrfs_root *root)
486
{
C
Chris Mason 已提交
487
	struct btrfs_key ins;
488
	int ret;
C
Chris Mason 已提交
489
	struct buffer_head *buf;
490

C
Chris Mason 已提交
491
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1,
C
Chris Mason 已提交
492
		btrfs_header_parentid(btrfs_buffer_header(root->node)), &ins);
493 494 495 496
	if (ret) {
		BUG();
		return NULL;
	}
497
	buf = btrfs_find_create_tree_block(root, ins.objectid);
498
	set_buffer_uptodate(buf);
499 500
	return buf;
}
501

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
static int drop_leaf_ref(struct btrfs_trans_handle *trans,
			 struct btrfs_root *root, struct buffer_head *cur)
{
	struct btrfs_disk_key *key;
	struct btrfs_leaf *leaf;
	struct btrfs_file_extent_item *fi;
	int i;
	int nritems;
	int ret;

	BUG_ON(!btrfs_is_leaf(btrfs_buffer_node(cur)));
	leaf = btrfs_buffer_leaf(cur);
	nritems = btrfs_header_nritems(&leaf->header);
	for (i = 0; i < nritems; i++) {
		key = &leaf->items[i].key;
		if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY)
			continue;
		fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
		/*
		 * FIXME make sure to insert a trans record that
		 * repeats the snapshot del on crash
		 */
		ret = btrfs_free_extent(trans, root,
					btrfs_file_extent_disk_blocknr(fi),
					btrfs_file_extent_disk_num_blocks(fi),
					0);
		BUG_ON(ret);
	}
	return 0;
}

C
Chris Mason 已提交
533 534 535 536
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
537 538
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
539
{
C
Chris Mason 已提交
540 541
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
542 543 544 545
	u64 blocknr;
	int ret;
	u32 refs;

546 547
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
C
Chris Mason 已提交
548
	ret = lookup_block_ref(trans, root, path->nodes[*level]->b_blocknr,
549
			       1, &refs);
C
Chris Mason 已提交
550 551 552
	BUG_ON(ret);
	if (refs > 1)
		goto out;
C
Chris Mason 已提交
553 554 555
	/*
	 * walk down to the last node level and free all the leaves
	 */
556
	while(*level >= 0) {
557 558
		WARN_ON(*level < 0);
		WARN_ON(*level >= BTRFS_MAX_LEVEL);
C
Chris Mason 已提交
559
		cur = path->nodes[*level];
C
Chris Mason 已提交
560 561
		if (btrfs_header_level(btrfs_buffer_header(cur)) != *level)
			WARN_ON(1);
562
		if (path->slots[*level] >=
C
Chris Mason 已提交
563
		    btrfs_header_nritems(btrfs_buffer_header(cur)))
C
Chris Mason 已提交
564
			break;
565 566 567 568 569
		if (*level == 0) {
			ret = drop_leaf_ref(trans, root, cur);
			BUG_ON(ret);
			break;
		}
C
Chris Mason 已提交
570 571
		blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
					      path->slots[*level]);
572 573 574
		ret = lookup_block_ref(trans, root, blocknr, 1, &refs);
		BUG_ON(ret);
		if (refs != 1) {
C
Chris Mason 已提交
575
			path->slots[*level]++;
576
			ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
C
Chris Mason 已提交
577 578 579 580
			BUG_ON(ret);
			continue;
		}
		next = read_tree_block(root, blocknr);
581
		WARN_ON(*level <= 0);
C
Chris Mason 已提交
582
		if (path->nodes[*level-1])
C
Chris Mason 已提交
583
			btrfs_block_release(root, path->nodes[*level-1]);
C
Chris Mason 已提交
584
		path->nodes[*level-1] = next;
C
Chris Mason 已提交
585
		*level = btrfs_header_level(btrfs_buffer_header(next));
C
Chris Mason 已提交
586 587 588
		path->slots[*level] = 0;
	}
out:
589 590
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
591 592
	ret = btrfs_free_extent(trans, root,
				path->nodes[*level]->b_blocknr, 1, 1);
C
Chris Mason 已提交
593
	btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
594 595 596 597 598 599
	path->nodes[*level] = NULL;
	*level += 1;
	BUG_ON(ret);
	return 0;
}

C
Chris Mason 已提交
600 601 602 603 604
/*
 * 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
 */
605 606
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			*root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
607 608 609 610
{
	int i;
	int slot;
	int ret;
C
Chris Mason 已提交
611
	for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
C
Chris Mason 已提交
612
		slot = path->slots[i];
C
Chris Mason 已提交
613 614
		if (slot < btrfs_header_nritems(
		    btrfs_buffer_header(path->nodes[i])) - 1) {
C
Chris Mason 已提交
615 616 617 618
			path->slots[i]++;
			*level = i;
			return 0;
		} else {
619
			ret = btrfs_free_extent(trans, root,
C
Chris Mason 已提交
620
						path->nodes[*level]->b_blocknr,
621
						1, 1);
622
			BUG_ON(ret);
C
Chris Mason 已提交
623
			btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
624
			path->nodes[*level] = NULL;
C
Chris Mason 已提交
625 626 627 628 629 630
			*level = i + 1;
		}
	}
	return 1;
}

C
Chris Mason 已提交
631 632 633 634 635
/*
 * 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.
 */
636
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
637
			*root, struct buffer_head *snap)
C
Chris Mason 已提交
638
{
639
	int ret = 0;
C
Chris Mason 已提交
640
	int wret;
C
Chris Mason 已提交
641
	int level;
642
	struct btrfs_path *path;
C
Chris Mason 已提交
643 644 645
	int i;
	int orig_level;

646 647 648
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
649

C
Chris Mason 已提交
650
	level = btrfs_header_level(btrfs_buffer_header(snap));
C
Chris Mason 已提交
651
	orig_level = level;
652 653
	path->nodes[level] = snap;
	path->slots[level] = 0;
C
Chris Mason 已提交
654
	while(1) {
655
		wret = walk_down_tree(trans, root, path, &level);
C
Chris Mason 已提交
656
		if (wret > 0)
C
Chris Mason 已提交
657
			break;
C
Chris Mason 已提交
658 659 660
		if (wret < 0)
			ret = wret;

661
		wret = walk_up_tree(trans, root, path, &level);
C
Chris Mason 已提交
662
		if (wret > 0)
C
Chris Mason 已提交
663
			break;
C
Chris Mason 已提交
664 665
		if (wret < 0)
			ret = wret;
C
Chris Mason 已提交
666
	}
C
Chris Mason 已提交
667
	for (i = 0; i <= orig_level; i++) {
668 669
		if (path->nodes[i]) {
			btrfs_block_release(root, path->nodes[i]);
C
Chris Mason 已提交
670
		}
C
Chris Mason 已提交
671
	}
672
	btrfs_free_path(path);
C
Chris Mason 已提交
673
	return ret;
C
Chris Mason 已提交
674
}