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);
C
Chris Mason 已提交
42
	btrfs_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
	if (!pending) {
175 176
		bh = btrfs_find_tree_block(root, blocknr);
		if (bh && buffer_uptodate(bh)) {
C
Chris Mason 已提交
177 178 179
			header = btrfs_buffer_header(bh);
			if (btrfs_header_generation(header) ==
			    root->fs_info->running_transaction->transid) {
C
Chris Mason 已提交
180
				btrfs_block_release(root, bh);
C
Chris Mason 已提交
181 182
				return 0;
			}
C
Chris Mason 已提交
183
			btrfs_block_release(root, bh);
C
Chris Mason 已提交
184 185
		}
		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
	btrfs_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;
294 295
	int pending_ret;
	int ret;
296

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

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

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

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

348 349 350
	if (path.slots[0] > 0)
		path.slots[0]--;

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

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

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

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

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

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

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

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

488 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
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 已提交
519 520 521 522
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
523 524
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
525
{
C
Chris Mason 已提交
526 527
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
528 529 530 531
	u64 blocknr;
	int ret;
	u32 refs;

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

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

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

C
Chris Mason 已提交
623
	btrfs_init_path(&path);
C
Chris Mason 已提交
624

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

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