extent-tree.c 17.2 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 174 175 176 177 178 179 180 181
	struct buffer_head *bh;

	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 已提交
182
		brelse(bh);
C
Chris Mason 已提交
183 184 185 186 187 188
	}
	if (pending)
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	else
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
	BUG_ON(err);
C
Chris Mason 已提交
189 190 191
	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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