extent-tree.c 17.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
{
C
Chris Mason 已提交
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);
C
Chris Mason 已提交
28
	btrfs_init_path(&path);
C
Chris Mason 已提交
29 30
	key.objectid = blocknr;
	key.flags = 0;
31
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
32
	key.offset = num_blocks;
33 34
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path,
				0, 1);
35 36
	if (ret != 0)
		BUG();
C
Chris Mason 已提交
37
	BUG_ON(ret != 0);
C
Chris Mason 已提交
38
	l = btrfs_buffer_leaf(path.nodes[0]);
39
	item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
40 41
	refs = btrfs_extent_refs(item);
	btrfs_set_extent_refs(item, refs + 1);
42
	mark_buffer_dirty(path.nodes[0]);
43

44 45
	btrfs_release_path(root->fs_info->extent_root, &path);
	finish_current_insert(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
46
	del_pending_extents(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
47 48 49
	return 0;
}

50
static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
51
			    *root, u64 blocknr, u64 num_blocks, u32 *refs)
52
{
C
Chris Mason 已提交
53
	struct btrfs_path path;
54
	int ret;
C
Chris Mason 已提交
55
	struct btrfs_key key;
C
Chris Mason 已提交
56 57 58
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
	btrfs_init_path(&path);
59
	key.objectid = blocknr;
60
	key.offset = num_blocks;
61 62
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
63 64
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path,
				0, 0);
65 66
	if (ret != 0)
		BUG();
C
Chris Mason 已提交
67
	l = btrfs_buffer_leaf(path.nodes[0]);
68
	item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
69
	*refs = btrfs_extent_refs(item);
70
	btrfs_release_path(root->fs_info->extent_root, &path);
71 72 73
	return 0;
}

74
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
75
		  struct buffer_head *buf)
C
Chris Mason 已提交
76 77
{
	u64 blocknr;
C
Chris Mason 已提交
78
	struct btrfs_node *buf_node;
79 80 81
	struct btrfs_leaf *buf_leaf;
	struct btrfs_disk_key *key;
	struct btrfs_file_extent_item *fi;
C
Chris Mason 已提交
82
	int i;
83 84
	int leaf;
	int ret;
85

86
	if (!root->ref_cows)
87
		return 0;
C
Chris Mason 已提交
88
	buf_node = btrfs_buffer_node(buf);
89 90
	leaf = btrfs_is_leaf(buf_node);
	buf_leaf = btrfs_buffer_leaf(buf);
C
Chris Mason 已提交
91
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
		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 已提交
107 108 109 110
	}
	return 0;
}

111 112
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
			       btrfs_root *root)
113
{
C
Chris Mason 已提交
114
	unsigned long gang[8];
115
	u64 first = 0;
116 117
	int ret;
	int i;
C
Chris Mason 已提交
118
	struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
119 120

	while(1) {
C
Chris Mason 已提交
121 122
		ret = find_first_radix_bit(pinned_radix, gang,
					   ARRAY_SIZE(gang));
123 124
		if (!ret)
			break;
125
		if (!first)
C
Chris Mason 已提交
126
			first = gang[0];
127
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
128
			clear_radix_bit(pinned_radix, gang[i]);
129
		}
130
	}
131 132
	if (root->fs_info->last_insert.objectid > first)
		root->fs_info->last_insert.objectid = first;
133
	root->fs_info->last_insert.offset = 0;
134 135 136
	return 0;
}

137 138
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root)
C
Chris Mason 已提交
139
{
C
Chris Mason 已提交
140
	struct btrfs_key ins;
C
Chris Mason 已提交
141
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
142 143
	int i;
	int ret;
144 145
	u64 super_blocks_used;
	struct btrfs_fs_info *info = extent_root->fs_info;
C
Chris Mason 已提交
146

C
Chris Mason 已提交
147 148
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item,
C
Chris Mason 已提交
149
		btrfs_header_parentid(btrfs_buffer_header(extent_root->node)));
C
Chris Mason 已提交
150 151
	ins.offset = 1;
	ins.flags = 0;
152
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
153

154 155 156
	for (i = 0; i < extent_root->fs_info->current_insert.flags; i++) {
		ins.objectid = extent_root->fs_info->current_insert.objectid +
				i;
157 158 159
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used + 1);
160 161
		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
					sizeof(extent_item));
C
Chris Mason 已提交
162 163
		BUG_ON(ret);
	}
164
	extent_root->fs_info->current_insert.offset = 0;
C
Chris Mason 已提交
165 166 167
	return 0;
}

C
Chris Mason 已提交
168
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
169 170
{
	int err;
C
Chris Mason 已提交
171
	struct btrfs_header *header;
C
Chris Mason 已提交
172 173
	struct buffer_head *bh;

C
Chris Mason 已提交
174 175 176 177 178 179 180 181 182
	if (!pending) {
		bh = sb_find_get_block(root->fs_info->sb, blocknr);
		if (bh) {
			header = btrfs_buffer_header(bh);
			if (btrfs_header_generation(header) ==
			    root->fs_info->running_transaction->transid) {
				brelse(bh);
				return 0;
			}
C
Chris Mason 已提交
183 184 185
			brelse(bh);
		}
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
C
Chris Mason 已提交
186 187 188
	} else {
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	}
C
Chris Mason 已提交
189
	BUG_ON(err);
C
Chris Mason 已提交
190 191 192
	return 0;
}

193
/*
194
 * remove an extent from the root, returns 0 on success
195
 */
196
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
197
			 *root, u64 blocknr, u64 num_blocks, int pin)
198
{
C
Chris Mason 已提交
199
	struct btrfs_path path;
C
Chris Mason 已提交
200
	struct btrfs_key key;
201 202
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
203
	int ret;
C
Chris Mason 已提交
204
	struct btrfs_extent_item *ei;
C
Chris Mason 已提交
205
	struct btrfs_key ins;
C
Chris Mason 已提交
206
	u32 refs;
C
Chris Mason 已提交
207

208 209
	key.objectid = blocknr;
	key.flags = 0;
210
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
211 212
	key.offset = num_blocks;

213
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
C
Chris Mason 已提交
214
	btrfs_init_path(&path);
215
	ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
216
	if (ret) {
217
		printk("failed to find %Lu\n", key.objectid);
C
Chris Mason 已提交
218
		btrfs_print_tree(extent_root, extent_root->node);
219
		printk("failed to find %Lu\n", key.objectid);
220 221
		BUG();
	}
C
Chris Mason 已提交
222
	ei = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0],
C
Chris Mason 已提交
223
			    struct btrfs_extent_item);
224
	BUG_ON(ei->refs == 0);
C
Chris Mason 已提交
225 226
	refs = btrfs_extent_refs(ei) - 1;
	btrfs_set_extent_refs(ei, refs);
C
Chris Mason 已提交
227
	mark_buffer_dirty(path.nodes[0]);
C
Chris Mason 已提交
228
	if (refs == 0) {
229
		u64 super_blocks_used;
C
Chris Mason 已提交
230 231

		if (pin) {
C
Chris Mason 已提交
232
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
233 234 235
			BUG_ON(ret);
		}

236 237 238
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used - num_blocks);
239
		ret = btrfs_del_item(trans, extent_root, &path);
C
Chris Mason 已提交
240
		if (extent_root->fs_info->last_insert.objectid > blocknr)
241
			extent_root->fs_info->last_insert.objectid = blocknr;
242 243 244
		if (ret)
			BUG();
	}
C
Chris Mason 已提交
245
	btrfs_release_path(extent_root, &path);
246
	finish_current_insert(trans, extent_root);
247 248 249 250 251 252 253
	return ret;
}

/*
 * find all the blocks marked as pending in the radix tree and remove
 * them from the extent map
 */
254 255
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root)
256 257
{
	int ret;
C
Chris Mason 已提交
258 259
	int wret;
	int err = 0;
C
Chris Mason 已提交
260
	unsigned long gang[4];
261
	int i;
C
Chris Mason 已提交
262 263 264 265 266
	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;
267 268

	while(1) {
C
Chris Mason 已提交
269 270
		ret = find_first_radix_bit(pending_radix, gang,
					   ARRAY_SIZE(gang));
271 272 273
		if (!ret)
			break;
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
274 275 276 277
			wret = set_radix_bit(pinned_radix, gang[i]);
			BUG_ON(wret);
			wret = clear_radix_bit(pending_radix, gang[i]);
			BUG_ON(wret);
278
			wret = __free_extent(trans, extent_root,
C
Chris Mason 已提交
279
					     gang[i], 1, 0);
C
Chris Mason 已提交
280 281
			if (wret)
				err = wret;
282 283
		}
	}
C
Chris Mason 已提交
284
	return err;
285 286 287 288 289
}

/*
 * remove an extent from the root, returns 0 on success
 */
290 291
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
292
{
293
	struct btrfs_root *extent_root = root->fs_info->extent_root;
C
Chris Mason 已提交
294
	struct buffer_head *t;
295 296
	int pending_ret;
	int ret;
297

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

C
Chris Mason 已提交
333 334
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
335 336
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
337 338 339 340

	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);

341
check_failed:
C
Chris Mason 已提交
342
	btrfs_init_path(&path);
343 344 345
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
346
	ret = btrfs_search_slot(trans, root, ins, &path, 0, 0);
C
Chris Mason 已提交
347 348
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
349

350 351 352
	if (path.slots[0] > 0)
		path.slots[0]--;

353
	while (1) {
C
Chris Mason 已提交
354
		l = btrfs_buffer_leaf(path.nodes[0]);
355
		slot = path.slots[0];
356
		if (slot >= btrfs_header_nritems(&l->header)) {
C
Chris Mason 已提交
357
			ret = btrfs_next_leaf(root, &path);
358 359
			if (ret == 0)
				continue;
C
Chris Mason 已提交
360 361
			if (ret < 0)
				goto error;
362 363
			if (!start_found) {
				ins->objectid = search_start;
C
Chris Mason 已提交
364
				ins->offset = (u64)-1;
365 366 367 368 369
				start_found = 1;
				goto check_pending;
			}
			ins->objectid = last_block > search_start ?
					last_block : search_start;
C
Chris Mason 已提交
370
			ins->offset = (u64)-1;
371 372
			goto check_pending;
		}
C
Chris Mason 已提交
373 374
		btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
		if (key.objectid >= search_start) {
375
			if (start_found) {
376 377
				if (last_block < search_start)
					last_block = search_start;
C
Chris Mason 已提交
378
				hole_size = key.objectid - last_block;
C
Chris Mason 已提交
379
				if (hole_size > total_needed) {
380
					ins->objectid = last_block;
C
Chris Mason 已提交
381
					ins->offset = hole_size;
382 383
					goto check_pending;
				}
384
			}
385
		}
386
		start_found = 1;
C
Chris Mason 已提交
387
		last_block = key.objectid + key.offset;
388 389 390 391 392 393 394
		path.slots[0]++;
	}
	// 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
	 */
C
Chris Mason 已提交
395
	btrfs_release_path(root, &path);
396
	BUG_ON(ins->objectid < search_start);
C
Chris Mason 已提交
397 398
	for (test_block = ins->objectid;
	     test_block < ins->objectid + total_needed; test_block++) {
C
Chris Mason 已提交
399
		if (test_radix_bit(&root->fs_info->pinned_radix,
400
				      test_block)) {
C
Chris Mason 已提交
401
			search_start = test_block + 1;
402 403 404
			goto check_failed;
		}
	}
405 406 407 408 409
	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 已提交
410
	ins->offset = num_blocks;
411
	return 0;
C
Chris Mason 已提交
412
error:
C
Chris Mason 已提交
413
	btrfs_release_path(root, &path);
C
Chris Mason 已提交
414
	return ret;
415 416 417 418 419 420 421 422 423
}

/*
 * 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 已提交
424
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
425 426
			*root, u64 num_blocks, u64 search_start, u64
			search_end, u64 owner, struct btrfs_key *ins)
427 428 429
{
	int ret;
	int pending_ret;
430 431 432
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
433
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
434

C
Chris Mason 已提交
435 436
	btrfs_set_extent_refs(&extent_item, 1);
	btrfs_set_extent_owner(&extent_item, owner);
437

C
Chris Mason 已提交
438
	if (root == extent_root) {
439
		BUG_ON(extent_root->fs_info->current_insert.offset == 0);
C
Chris Mason 已提交
440
		BUG_ON(num_blocks != 1);
441 442
		BUG_ON(extent_root->fs_info->current_insert.flags ==
		       extent_root->fs_info->current_insert.offset);
C
Chris Mason 已提交
443
		ins->offset = 1;
444 445
		ins->objectid = extent_root->fs_info->current_insert.objectid +
				extent_root->fs_info->current_insert.flags++;
446 447
		return 0;
	}
448
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
449 450 451
			       search_end, ins);
	if (ret)
		return ret;
452

453 454 455
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
456 457
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
458

459
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
460
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
461 462 463 464 465
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
466 467 468 469 470 471
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
472
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
473
					    struct btrfs_root *root)
474
{
C
Chris Mason 已提交
475
	struct btrfs_key ins;
476
	int ret;
C
Chris Mason 已提交
477
	struct buffer_head *buf;
478

C
Chris Mason 已提交
479
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1,
C
Chris Mason 已提交
480
		btrfs_header_parentid(btrfs_buffer_header(root->node)), &ins);
481 482 483 484
	if (ret) {
		BUG();
		return NULL;
	}
C
Chris Mason 已提交
485
	buf = find_tree_block(root, ins.objectid);
486
	set_buffer_uptodate(buf);
487 488
	return buf;
}
489

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
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 已提交
521 522 523 524
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
525 526
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
527
{
C
Chris Mason 已提交
528 529
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
530 531 532 533
	u64 blocknr;
	int ret;
	u32 refs;

C
Chris Mason 已提交
534
	ret = lookup_block_ref(trans, root, path->nodes[*level]->b_blocknr,
535
			       1, &refs);
C
Chris Mason 已提交
536 537 538
	BUG_ON(ret);
	if (refs > 1)
		goto out;
C
Chris Mason 已提交
539 540 541
	/*
	 * walk down to the last node level and free all the leaves
	 */
542
	while(*level >= 0) {
C
Chris Mason 已提交
543
		cur = path->nodes[*level];
544
		if (path->slots[*level] >=
C
Chris Mason 已提交
545
		    btrfs_header_nritems(btrfs_buffer_header(cur)))
C
Chris Mason 已提交
546
			break;
547 548 549 550 551
		if (*level == 0) {
			ret = drop_leaf_ref(trans, root, cur);
			BUG_ON(ret);
			break;
		}
C
Chris Mason 已提交
552 553
		blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
					      path->slots[*level]);
554 555 556
		ret = lookup_block_ref(trans, root, blocknr, 1, &refs);
		BUG_ON(ret);
		if (refs != 1) {
C
Chris Mason 已提交
557
			path->slots[*level]++;
558
			ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
C
Chris Mason 已提交
559 560 561 562
			BUG_ON(ret);
			continue;
		}
		next = read_tree_block(root, blocknr);
C
Chris Mason 已提交
563
		if (path->nodes[*level-1])
C
Chris Mason 已提交
564
			btrfs_block_release(root, path->nodes[*level-1]);
C
Chris Mason 已提交
565
		path->nodes[*level-1] = next;
C
Chris Mason 已提交
566
		*level = btrfs_header_level(btrfs_buffer_header(next));
C
Chris Mason 已提交
567 568 569
		path->slots[*level] = 0;
	}
out:
570 571
	ret = btrfs_free_extent(trans, root,
				path->nodes[*level]->b_blocknr, 1, 1);
C
Chris Mason 已提交
572
	btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
573 574 575 576 577 578
	path->nodes[*level] = NULL;
	*level += 1;
	BUG_ON(ret);
	return 0;
}

C
Chris Mason 已提交
579 580 581 582 583
/*
 * 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
 */
584 585
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			*root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
586 587 588 589
{
	int i;
	int slot;
	int ret;
C
Chris Mason 已提交
590
	for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
C
Chris Mason 已提交
591
		slot = path->slots[i];
C
Chris Mason 已提交
592 593
		if (slot < btrfs_header_nritems(
		    btrfs_buffer_header(path->nodes[i])) - 1) {
C
Chris Mason 已提交
594 595 596 597
			path->slots[i]++;
			*level = i;
			return 0;
		} else {
598
			ret = btrfs_free_extent(trans, root,
C
Chris Mason 已提交
599
						path->nodes[*level]->b_blocknr,
600
						1, 1);
601
			BUG_ON(ret);
C
Chris Mason 已提交
602
			btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
603
			path->nodes[*level] = NULL;
C
Chris Mason 已提交
604 605 606 607 608 609
			*level = i + 1;
		}
	}
	return 1;
}

C
Chris Mason 已提交
610 611 612 613 614
/*
 * 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.
 */
615
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
616
			*root, struct buffer_head *snap)
C
Chris Mason 已提交
617
{
618
	int ret = 0;
C
Chris Mason 已提交
619
	int wret;
C
Chris Mason 已提交
620
	int level;
C
Chris Mason 已提交
621
	struct btrfs_path path;
C
Chris Mason 已提交
622 623 624
	int i;
	int orig_level;

C
Chris Mason 已提交
625
	btrfs_init_path(&path);
C
Chris Mason 已提交
626

C
Chris Mason 已提交
627
	level = btrfs_header_level(btrfs_buffer_header(snap));
C
Chris Mason 已提交
628 629 630 631
	orig_level = level;
	path.nodes[level] = snap;
	path.slots[level] = 0;
	while(1) {
632
		wret = walk_down_tree(trans, root, &path, &level);
C
Chris Mason 已提交
633
		if (wret > 0)
C
Chris Mason 已提交
634
			break;
C
Chris Mason 已提交
635 636 637
		if (wret < 0)
			ret = wret;

638
		wret = walk_up_tree(trans, root, &path, &level);
C
Chris Mason 已提交
639
		if (wret > 0)
C
Chris Mason 已提交
640
			break;
C
Chris Mason 已提交
641 642
		if (wret < 0)
			ret = wret;
C
Chris Mason 已提交
643
	}
C
Chris Mason 已提交
644 645
	for (i = 0; i <= orig_level; i++) {
		if (path.nodes[i]) {
C
Chris Mason 已提交
646
			btrfs_block_release(root, path.nodes[i]);
C
Chris Mason 已提交
647
		}
C
Chris Mason 已提交
648
	}
C
Chris Mason 已提交
649
	return ret;
C
Chris Mason 已提交
650
}