extent-tree.c 17.4 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
		bh = btrfs_find_tree_block(root, blocknr);
C
Chris Mason 已提交
176 177 178 179 180 181 182 183 184 185
		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 已提交
186
			}
C
Chris Mason 已提交
187
			btrfs_block_release(root, bh);
C
Chris Mason 已提交
188 189
		}
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
C
Chris Mason 已提交
190 191 192
	} else {
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	}
C
Chris Mason 已提交
193
	BUG_ON(err);
C
Chris Mason 已提交
194 195 196
	return 0;
}

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

212 213
	key.objectid = blocknr;
	key.flags = 0;
214
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
215 216
	key.offset = num_blocks;

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

		if (pin) {
C
Chris Mason 已提交
236
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
237 238 239
			BUG_ON(ret);
		}

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

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

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

/*
 * remove an extent from the root, returns 0 on success
 */
294 295
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
296
{
297
	struct btrfs_root *extent_root = root->fs_info->extent_root;
298 299
	int pending_ret;
	int ret;
300

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

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

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

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

352 353 354
	if (path.slots[0] > 0)
		path.slots[0]--;

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

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

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

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

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

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

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

C
Chris Mason 已提交
481
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1,
C
Chris Mason 已提交
482
		btrfs_header_parentid(btrfs_buffer_header(root->node)), &ins);
483 484 485 486
	if (ret) {
		BUG();
		return NULL;
	}
487
	buf = btrfs_find_create_tree_block(root, ins.objectid);
488
	set_buffer_uptodate(buf);
489 490
	return buf;
}
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 521 522
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 已提交
523 524 525 526
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
527 528
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
529
{
C
Chris Mason 已提交
530 531
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
532 533 534 535
	u64 blocknr;
	int ret;
	u32 refs;

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

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

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

C
Chris Mason 已提交
629
	btrfs_init_path(&path);
C
Chris Mason 已提交
630

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

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