extent-tree.c 18.3 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

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

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

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

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

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

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

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

100
	if (!root->ref_cows)
101
		return 0;
C
Chris Mason 已提交
102
	buf_node = btrfs_buffer_node(buf);
103 104
	leaf = btrfs_is_leaf(buf_node);
	buf_leaf = btrfs_buffer_leaf(buf);
C
Chris Mason 已提交
105
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
106 107 108 109 110 111
		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);
C
Chris Mason 已提交
112
			ret = btrfs_inc_extent_ref(trans, root,
113 114 115 116 117
				    btrfs_file_extent_disk_blocknr(fi),
				    btrfs_file_extent_disk_num_blocks(fi));
			BUG_ON(ret);
		} else {
			blocknr = btrfs_node_blockptr(buf_node, i);
C
Chris Mason 已提交
118
			ret = btrfs_inc_extent_ref(trans, root, blocknr, 1);
119 120
			BUG_ON(ret);
		}
C
Chris Mason 已提交
121 122 123 124
	}
	return 0;
}

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

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

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

C
Chris Mason 已提交
161
	btrfs_set_extent_refs(&extent_item, 1);
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
			*root, u64 num_blocks, u64 search_start, u64
C
Chris Mason 已提交
460
			search_end, 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
	btrfs_set_extent_refs(&extent_item, 1);
470

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

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

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

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

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

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

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

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

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

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

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