extent-tree.c 18.4 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 83 84 85
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root)
{
	return inc_block_ref(trans, root, root->node->b_blocknr, 1);
}

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 160
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item,
C
Chris Mason 已提交
161
		btrfs_header_parentid(btrfs_buffer_header(extent_root->node)));
C
Chris Mason 已提交
162 163
	ins.offset = 1;
	ins.flags = 0;
164
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

C
Chris Mason 已提交
353 354
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
	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);
	}
370 371
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
372

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

382 383
	if (path->slots[0] > 0)
		path->slots[0]--;
384

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

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

C
Chris Mason 已提交
469 470
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item, owner);
471

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

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

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

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

C
Chris Mason 已提交
513
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1,
C
Chris Mason 已提交
514
		btrfs_header_parentid(btrfs_buffer_header(root->node)), &ins);
515 516 517 518
	if (ret) {
		BUG();
		return NULL;
	}
519
	buf = btrfs_find_create_tree_block(root, ins.objectid);
520
	set_buffer_uptodate(buf);
521 522
	return buf;
}
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 551 552 553 554
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 已提交
555 556 557 558
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
559 560
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
561
{
C
Chris Mason 已提交
562 563
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
564 565 566 567
	u64 blocknr;
	int ret;
	u32 refs;

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

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

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

668 669 670
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
671

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

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