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

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

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

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

	pending_radix = &extent_root->fs_info->pending_del_radix;
	pinned_radix = &extent_root->fs_info->pinned_radix;
278 279

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

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

308
	if (root == extent_root) {
C
Chris Mason 已提交
309
		pin_down_block(root, blocknr, 1);
310 311
		return 0;
	}
C
Chris Mason 已提交
312
	ret = __free_extent(trans, root, blocknr, num_blocks, pin);
C
Chris Mason 已提交
313
	pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
314 315 316 317 318 319 320
	return ret ? ret : pending_ret;
}

/*
 * walks the btree of allocated extents and find a hole of a given size.
 * The key ins is changed to record the hole:
 * ins->objectid == block start
321
 * ins->flags = BTRFS_EXTENT_ITEM_KEY
322 323 324
 * ins->offset == number of blocks
 * Any available blocks before search_start are skipped.
 */
325 326 327
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
			    *orig_root, u64 num_blocks, u64 search_start, u64
			    search_end, struct btrfs_key *ins)
328
{
329
	struct btrfs_path *path;
C
Chris Mason 已提交
330
	struct btrfs_key key;
331 332 333
	int ret;
	u64 hole_size = 0;
	int slot = 0;
C
Chris Mason 已提交
334
	u64 last_block = 0;
C
Chris Mason 已提交
335
	u64 test_block;
336
	int start_found;
C
Chris Mason 已提交
337
	struct btrfs_leaf *l;
338
	struct btrfs_root * root = orig_root->fs_info->extent_root;
339
	int total_needed = num_blocks;
C
Chris Mason 已提交
340
	int level;
341

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

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

366
	path = btrfs_alloc_path();
367

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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