extent-tree.c 18.2 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;
}

C
Chris Mason 已提交
80 81 82
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root)
{
83
	return inc_block_ref(trans, root, bh_blocknr(root->node), 1);
C
Chris Mason 已提交
84 85
}

86
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
87
		  struct buffer_head *buf)
C
Chris Mason 已提交
88 89
{
	u64 blocknr;
C
Chris Mason 已提交
90
	struct btrfs_node *buf_node;
91 92 93
	struct btrfs_leaf *buf_leaf;
	struct btrfs_disk_key *key;
	struct btrfs_file_extent_item *fi;
C
Chris Mason 已提交
94
	int i;
95 96
	int leaf;
	int ret;
97

98
	if (!root->ref_cows)
99
		return 0;
C
Chris Mason 已提交
100
	buf_node = btrfs_buffer_node(buf);
101 102
	leaf = btrfs_is_leaf(buf_node);
	buf_leaf = btrfs_buffer_leaf(buf);
C
Chris Mason 已提交
103
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
		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 已提交
119 120 121 122
	}
	return 0;
}

123 124
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
			       btrfs_root *root)
125
{
C
Chris Mason 已提交
126
	unsigned long gang[8];
127
	u64 first = 0;
128 129
	int ret;
	int i;
C
Chris Mason 已提交
130
	struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
131 132

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

149 150
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root)
C
Chris Mason 已提交
151
{
C
Chris Mason 已提交
152
	struct btrfs_key ins;
C
Chris Mason 已提交
153
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
154 155
	int i;
	int ret;
156 157
	u64 super_blocks_used;
	struct btrfs_fs_info *info = extent_root->fs_info;
C
Chris Mason 已提交
158

C
Chris Mason 已提交
159
	btrfs_set_extent_refs(&extent_item, 1);
C
Chris Mason 已提交
160 161
	ins.offset = 1;
	ins.flags = 0;
162
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
163

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

C
Chris Mason 已提交
178
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
179 180
{
	int err;
C
Chris Mason 已提交
181
	struct btrfs_header *header;
C
Chris Mason 已提交
182 183
	struct buffer_head *bh;

C
Chris Mason 已提交
184
	if (!pending) {
185
		bh = btrfs_find_tree_block(root, blocknr);
C
Chris Mason 已提交
186 187 188 189 190 191 192 193 194 195
		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 已提交
196
			}
C
Chris Mason 已提交
197
			btrfs_block_release(root, bh);
C
Chris Mason 已提交
198 199
		}
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
C
Chris Mason 已提交
200 201 202
	} else {
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	}
C
Chris Mason 已提交
203
	BUG_ON(err);
C
Chris Mason 已提交
204 205 206
	return 0;
}

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

222 223
	key.objectid = blocknr;
	key.flags = 0;
224
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
225 226
	key.offset = num_blocks;

227
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
228 229 230
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
231

232
	ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1);
233
	if (ret) {
234
		printk("failed to find %Lu\n", key.objectid);
C
Chris Mason 已提交
235
		btrfs_print_tree(extent_root, extent_root->node);
236
		printk("failed to find %Lu\n", key.objectid);
237 238
		BUG();
	}
239
	ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
C
Chris Mason 已提交
240
			    struct btrfs_extent_item);
241
	BUG_ON(ei->refs == 0);
C
Chris Mason 已提交
242 243
	refs = btrfs_extent_refs(ei) - 1;
	btrfs_set_extent_refs(ei, refs);
244
	btrfs_mark_buffer_dirty(path->nodes[0]);
C
Chris Mason 已提交
245
	if (refs == 0) {
246
		u64 super_blocks_used;
C
Chris Mason 已提交
247 248

		if (pin) {
C
Chris Mason 已提交
249
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
250 251 252
			BUG_ON(ret);
		}

253 254 255
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used - num_blocks);
256
		ret = btrfs_del_item(trans, extent_root, path);
257 258 259
		if (ret)
			BUG();
	}
260 261
	btrfs_release_path(extent_root, path);
	btrfs_free_path(path);
262
	finish_current_insert(trans, extent_root);
263 264 265 266 267 268 269
	return ret;
}

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

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

/*
 * remove an extent from the root, returns 0 on success
 */
306 307
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
308
{
309
	struct btrfs_root *extent_root = root->fs_info->extent_root;
310 311
	int pending_ret;
	int ret;
312

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

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

C
Chris Mason 已提交
351 352
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	if (root->fs_info->last_insert.objectid == 0 && search_end == (u64)-1) {
		struct btrfs_disk_key *last_key;
		btrfs_init_path(path);
		ins->objectid = (u64)-1;
		ins->offset = (u64)-1;
		ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
		if (ret < 0)
			goto error;
		BUG_ON(ret == 0);
		if (path->slots[0] > 0)
			path->slots[0]--;
		l = btrfs_buffer_leaf(path->nodes[0]);
		last_key = &l->items[path->slots[0]].key;
		search_start = btrfs_disk_key_objectid(last_key);
	}
368 369
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
370

371
check_failed:
372
	btrfs_init_path(path);
373 374 375
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
376
	ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
C
Chris Mason 已提交
377 378
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
379

380 381
	if (path->slots[0] > 0)
		path->slots[0]--;
382

383
	while (1) {
384 385
		l = btrfs_buffer_leaf(path->nodes[0]);
		slot = path->slots[0];
386
		if (slot >= btrfs_header_nritems(&l->header)) {
387
			ret = btrfs_next_leaf(root, path);
388 389
			if (ret == 0)
				continue;
C
Chris Mason 已提交
390 391
			if (ret < 0)
				goto error;
392 393
			if (!start_found) {
				ins->objectid = search_start;
C
Chris Mason 已提交
394
				ins->offset = (u64)-1;
395 396 397 398 399
				start_found = 1;
				goto check_pending;
			}
			ins->objectid = last_block > search_start ?
					last_block : search_start;
C
Chris Mason 已提交
400
			ins->offset = (u64)-1;
401 402
			goto check_pending;
		}
C
Chris Mason 已提交
403 404
		btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
		if (key.objectid >= search_start) {
405
			if (start_found) {
406 407
				if (last_block < search_start)
					last_block = search_start;
C
Chris Mason 已提交
408
				hole_size = key.objectid - last_block;
C
Chris Mason 已提交
409
				if (hole_size > total_needed) {
410
					ins->objectid = last_block;
C
Chris Mason 已提交
411
					ins->offset = hole_size;
412 413
					goto check_pending;
				}
414
			}
415
		}
416
		start_found = 1;
C
Chris Mason 已提交
417
		last_block = key.objectid + key.offset;
418
		path->slots[0]++;
419 420 421 422 423 424
	}
	// 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
	 */
425
	btrfs_release_path(root, path);
426
	BUG_ON(ins->objectid < search_start);
C
Chris Mason 已提交
427 428
	for (test_block = ins->objectid;
	     test_block < ins->objectid + total_needed; test_block++) {
C
Chris Mason 已提交
429
		if (test_radix_bit(&root->fs_info->pinned_radix,
430
				      test_block)) {
C
Chris Mason 已提交
431
			search_start = test_block + 1;
432 433 434
			goto check_failed;
		}
	}
435 436 437 438 439
	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 已提交
440
	ins->offset = num_blocks;
441
	btrfs_free_path(path);
442
	return 0;
C
Chris Mason 已提交
443
error:
444 445
	btrfs_release_path(root, path);
	btrfs_free_path(path);
C
Chris Mason 已提交
446
	return ret;
447 448 449 450 451 452 453 454 455
}

/*
 * 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 已提交
456
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
457
			*root, u64 num_blocks, u64 search_start, u64
C
Chris Mason 已提交
458
			search_end, struct btrfs_key *ins)
459 460 461
{
	int ret;
	int pending_ret;
462 463 464
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
465
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
466

C
Chris Mason 已提交
467
	btrfs_set_extent_refs(&extent_item, 1);
468

C
Chris Mason 已提交
469
	if (root == extent_root) {
470
		BUG_ON(extent_root->fs_info->current_insert.offset == 0);
C
Chris Mason 已提交
471
		BUG_ON(num_blocks != 1);
472 473
		BUG_ON(extent_root->fs_info->current_insert.flags ==
		       extent_root->fs_info->current_insert.offset);
C
Chris Mason 已提交
474
		ins->offset = 1;
475 476
		ins->objectid = extent_root->fs_info->current_insert.objectid +
				extent_root->fs_info->current_insert.flags++;
477 478
		return 0;
	}
479
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
480 481 482
			       search_end, ins);
	if (ret)
		return ret;
483

484 485 486
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
487 488
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
489

490
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
491
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
492 493 494 495 496
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
497 498 499 500 501 502
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
503
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
504
					    struct btrfs_root *root)
505
{
C
Chris Mason 已提交
506
	struct btrfs_key ins;
507
	int ret;
C
Chris Mason 已提交
508
	struct buffer_head *buf;
509

C
Chris Mason 已提交
510
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1, &ins);
511 512 513 514
	if (ret) {
		BUG();
		return NULL;
	}
515
	buf = btrfs_find_create_tree_block(root, ins.objectid);
516
	set_buffer_uptodate(buf);
517 518
	return buf;
}
519

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
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 已提交
551 552 553 554
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
555 556
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
557
{
C
Chris Mason 已提交
558 559
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
560 561 562 563
	u64 blocknr;
	int ret;
	u32 refs;

564 565
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
566
	ret = lookup_block_ref(trans, root, bh_blocknr(path->nodes[*level]),
567
			       1, &refs);
C
Chris Mason 已提交
568 569 570
	BUG_ON(ret);
	if (refs > 1)
		goto out;
C
Chris Mason 已提交
571 572 573
	/*
	 * walk down to the last node level and free all the leaves
	 */
574
	while(*level >= 0) {
575 576
		WARN_ON(*level < 0);
		WARN_ON(*level >= BTRFS_MAX_LEVEL);
C
Chris Mason 已提交
577
		cur = path->nodes[*level];
C
Chris Mason 已提交
578 579
		if (btrfs_header_level(btrfs_buffer_header(cur)) != *level)
			WARN_ON(1);
580
		if (path->slots[*level] >=
C
Chris Mason 已提交
581
		    btrfs_header_nritems(btrfs_buffer_header(cur)))
C
Chris Mason 已提交
582
			break;
583 584 585 586 587
		if (*level == 0) {
			ret = drop_leaf_ref(trans, root, cur);
			BUG_ON(ret);
			break;
		}
C
Chris Mason 已提交
588 589
		blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
					      path->slots[*level]);
590 591 592
		ret = lookup_block_ref(trans, root, blocknr, 1, &refs);
		BUG_ON(ret);
		if (refs != 1) {
C
Chris Mason 已提交
593
			path->slots[*level]++;
594
			ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
C
Chris Mason 已提交
595 596 597 598
			BUG_ON(ret);
			continue;
		}
		next = read_tree_block(root, blocknr);
599
		WARN_ON(*level <= 0);
C
Chris Mason 已提交
600
		if (path->nodes[*level-1])
C
Chris Mason 已提交
601
			btrfs_block_release(root, path->nodes[*level-1]);
C
Chris Mason 已提交
602
		path->nodes[*level-1] = next;
C
Chris Mason 已提交
603
		*level = btrfs_header_level(btrfs_buffer_header(next));
C
Chris Mason 已提交
604 605 606
		path->slots[*level] = 0;
	}
out:
607 608
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
609
	ret = btrfs_free_extent(trans, root,
610
				bh_blocknr(path->nodes[*level]), 1, 1);
C
Chris Mason 已提交
611
	btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
612 613 614 615 616 617
	path->nodes[*level] = NULL;
	*level += 1;
	BUG_ON(ret);
	return 0;
}

C
Chris Mason 已提交
618 619 620 621 622
/*
 * 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
 */
623 624
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			*root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
625 626 627 628
{
	int i;
	int slot;
	int ret;
C
Chris Mason 已提交
629
	for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
C
Chris Mason 已提交
630
		slot = path->slots[i];
C
Chris Mason 已提交
631 632
		if (slot < btrfs_header_nritems(
		    btrfs_buffer_header(path->nodes[i])) - 1) {
C
Chris Mason 已提交
633 634 635 636
			path->slots[i]++;
			*level = i;
			return 0;
		} else {
637
			ret = btrfs_free_extent(trans, root,
638
						bh_blocknr(path->nodes[*level]),
639
						1, 1);
640
			BUG_ON(ret);
C
Chris Mason 已提交
641
			btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
642
			path->nodes[*level] = NULL;
C
Chris Mason 已提交
643 644 645 646 647 648
			*level = i + 1;
		}
	}
	return 1;
}

C
Chris Mason 已提交
649 650 651 652 653
/*
 * 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.
 */
654
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
655
			*root, struct buffer_head *snap)
C
Chris Mason 已提交
656
{
657
	int ret = 0;
C
Chris Mason 已提交
658
	int wret;
C
Chris Mason 已提交
659
	int level;
660
	struct btrfs_path *path;
C
Chris Mason 已提交
661 662 663
	int i;
	int orig_level;

664 665 666
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
667

C
Chris Mason 已提交
668
	level = btrfs_header_level(btrfs_buffer_header(snap));
C
Chris Mason 已提交
669
	orig_level = level;
670 671
	path->nodes[level] = snap;
	path->slots[level] = 0;
C
Chris Mason 已提交
672
	while(1) {
673
		wret = walk_down_tree(trans, root, path, &level);
C
Chris Mason 已提交
674
		if (wret > 0)
C
Chris Mason 已提交
675
			break;
C
Chris Mason 已提交
676 677 678
		if (wret < 0)
			ret = wret;

679
		wret = walk_up_tree(trans, root, path, &level);
C
Chris Mason 已提交
680
		if (wret > 0)
C
Chris Mason 已提交
681
			break;
C
Chris Mason 已提交
682 683
		if (wret < 0)
			ret = wret;
C
Chris Mason 已提交
684
	}
C
Chris Mason 已提交
685
	for (i = 0; i <= orig_level; i++) {
686 687
		if (path->nodes[i]) {
			btrfs_block_release(root, path->nodes[i]);
C
Chris Mason 已提交
688
		}
C
Chris Mason 已提交
689
	}
690
	btrfs_free_path(path);
C
Chris Mason 已提交
691
	return ret;
C
Chris Mason 已提交
692
}