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

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
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
227

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

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

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

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

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

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

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

343 344 345 346
	path = btrfs_alloc_path();
	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);

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

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

376 377
	if (path->slots[0] > 0)
		path->slots[0]--;
378

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

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

C
Chris Mason 已提交
463 464
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item, owner);
465

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

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

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

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

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

518 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
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 已提交
549 550 551 552
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
553 554
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
555
{
C
Chris Mason 已提交
556 557
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
558 559 560 561
	u64 blocknr;
	int ret;
	u32 refs;

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

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

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

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

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

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