extent-tree.c 18.3 KB
Newer Older
1
#include <linux/module.h>
2 3 4
#include "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
5
#include "transaction.h"
6

7 8 9 10 11
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
			    *orig_root, u64 num_blocks, u64 search_start, u64
			    search_end, struct btrfs_key *ins);
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root);
C
Chris Mason 已提交
12 13
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root);
14

C
Chris Mason 已提交
15 16 17
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
				struct btrfs_root *root,
				u64 blocknr, u64 num_blocks)
C
Chris Mason 已提交
18
{
19
	struct btrfs_path *path;
C
Chris Mason 已提交
20
	int ret;
C
Chris Mason 已提交
21
	struct btrfs_key key;
C
Chris Mason 已提交
22 23
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
C
Chris Mason 已提交
24
	struct btrfs_key ins;
C
Chris Mason 已提交
25
	u32 refs;
C
Chris Mason 已提交
26

27 28
	find_free_extent(trans, root->fs_info->extent_root, 0, 0, (u64)-1,
			 &ins);
29 30 31
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
32 33
	key.objectid = blocknr;
	key.flags = 0;
34
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
35
	key.offset = num_blocks;
36
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
37
				0, 1);
38 39
	if (ret != 0) {
printk("can't find block %Lu %Lu\n", blocknr, num_blocks);
40
		BUG();
41
	}
C
Chris Mason 已提交
42
	BUG_ON(ret != 0);
43 44
	l = btrfs_buffer_leaf(path->nodes[0]);
	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
45 46
	refs = btrfs_extent_refs(item);
	btrfs_set_extent_refs(item, refs + 1);
47
	btrfs_mark_buffer_dirty(path->nodes[0]);
48

49 50
	btrfs_release_path(root->fs_info->extent_root, path);
	btrfs_free_path(path);
51
	finish_current_insert(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
52
	del_pending_extents(trans, root->fs_info->extent_root);
C
Chris Mason 已提交
53 54 55
	return 0;
}

C
Chris Mason 已提交
56 57 58
static int lookup_extent_ref(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root, u64 blocknr,
			     u64 num_blocks, u32 *refs)
59
{
60
	struct btrfs_path *path;
61
	int ret;
C
Chris Mason 已提交
62
	struct btrfs_key key;
C
Chris Mason 已提交
63 64
	struct btrfs_leaf *l;
	struct btrfs_extent_item *item;
65 66 67

	path = btrfs_alloc_path();
	btrfs_init_path(path);
68
	key.objectid = blocknr;
69
	key.offset = num_blocks;
70 71
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
72
	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
73
				0, 0);
74 75
	if (ret != 0)
		BUG();
76 77
	l = btrfs_buffer_leaf(path->nodes[0]);
	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
C
Chris Mason 已提交
78
	*refs = btrfs_extent_refs(item);
79 80
	btrfs_release_path(root->fs_info->extent_root, path);
	btrfs_free_path(path);
81 82 83
	return 0;
}

C
Chris Mason 已提交
84 85 86
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root)
{
C
Chris Mason 已提交
87
	return btrfs_inc_extent_ref(trans, root, bh_blocknr(root->node), 1);
C
Chris Mason 已提交
88 89
}

90
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
91
		  struct buffer_head *buf)
C
Chris Mason 已提交
92 93
{
	u64 blocknr;
C
Chris Mason 已提交
94
	struct btrfs_node *buf_node;
95 96 97
	struct btrfs_leaf *buf_leaf;
	struct btrfs_disk_key *key;
	struct btrfs_file_extent_item *fi;
C
Chris Mason 已提交
98
	int i;
99 100
	int leaf;
	int ret;
101

102
	if (!root->ref_cows)
103
		return 0;
C
Chris Mason 已提交
104
	buf_node = btrfs_buffer_node(buf);
105 106
	leaf = btrfs_is_leaf(buf_node);
	buf_leaf = btrfs_buffer_leaf(buf);
C
Chris Mason 已提交
107
	for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
108 109 110 111 112 113
		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);
C
Chris Mason 已提交
114
			ret = btrfs_inc_extent_ref(trans, root,
115 116 117 118 119
				    btrfs_file_extent_disk_blocknr(fi),
				    btrfs_file_extent_disk_num_blocks(fi));
			BUG_ON(ret);
		} else {
			blocknr = btrfs_node_blockptr(buf_node, i);
C
Chris Mason 已提交
120
			ret = btrfs_inc_extent_ref(trans, root, blocknr, 1);
121 122
			BUG_ON(ret);
		}
C
Chris Mason 已提交
123 124 125 126
	}
	return 0;
}

127 128
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
			       btrfs_root *root)
129
{
C
Chris Mason 已提交
130
	unsigned long gang[8];
131
	u64 first = 0;
132 133
	int ret;
	int i;
C
Chris Mason 已提交
134
	struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
135 136

	while(1) {
C
Chris Mason 已提交
137 138
		ret = find_first_radix_bit(pinned_radix, gang,
					   ARRAY_SIZE(gang));
139 140
		if (!ret)
			break;
141
		if (!first)
C
Chris Mason 已提交
142
			first = gang[0];
143
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
144
			clear_radix_bit(pinned_radix, gang[i]);
145
		}
146
	}
147 148
	if (root->fs_info->last_insert.objectid > first)
		root->fs_info->last_insert.objectid = first;
149
	root->fs_info->last_insert.offset = 0;
150 151 152
	return 0;
}

153 154
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
				 btrfs_root *extent_root)
C
Chris Mason 已提交
155
{
C
Chris Mason 已提交
156
	struct btrfs_key ins;
C
Chris Mason 已提交
157
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
158 159
	int i;
	int ret;
160 161
	u64 super_blocks_used;
	struct btrfs_fs_info *info = extent_root->fs_info;
C
Chris Mason 已提交
162

C
Chris Mason 已提交
163
	btrfs_set_extent_refs(&extent_item, 1);
C
Chris Mason 已提交
164 165
	ins.offset = 1;
	ins.flags = 0;
166
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
C
Chris Mason 已提交
167

168 169 170
	for (i = 0; i < extent_root->fs_info->current_insert.flags; i++) {
		ins.objectid = extent_root->fs_info->current_insert.objectid +
				i;
171 172 173
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used + 1);
174 175
		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
					sizeof(extent_item));
C
Chris Mason 已提交
176 177
		BUG_ON(ret);
	}
178
	extent_root->fs_info->current_insert.offset = 0;
C
Chris Mason 已提交
179 180 181
	return 0;
}

C
Chris Mason 已提交
182
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
183 184
{
	int err;
C
Chris Mason 已提交
185
	struct btrfs_header *header;
C
Chris Mason 已提交
186 187
	struct buffer_head *bh;

C
Chris Mason 已提交
188
	if (!pending) {
189
		bh = btrfs_find_tree_block(root, blocknr);
C
Chris Mason 已提交
190 191 192 193 194 195 196 197 198 199
		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 已提交
200
			}
C
Chris Mason 已提交
201
			btrfs_block_release(root, bh);
C
Chris Mason 已提交
202 203
		}
		err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
C
Chris Mason 已提交
204 205 206
	} else {
		err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
	}
C
Chris Mason 已提交
207
	BUG_ON(err);
C
Chris Mason 已提交
208 209 210
	return 0;
}

211
/*
212
 * remove an extent from the root, returns 0 on success
213
 */
214
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
215
			 *root, u64 blocknr, u64 num_blocks, int pin)
216
{
217
	struct btrfs_path *path;
C
Chris Mason 已提交
218
	struct btrfs_key key;
219 220
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
221
	int ret;
C
Chris Mason 已提交
222
	struct btrfs_extent_item *ei;
C
Chris Mason 已提交
223
	struct btrfs_key ins;
C
Chris Mason 已提交
224
	u32 refs;
C
Chris Mason 已提交
225

226 227
	key.objectid = blocknr;
	key.flags = 0;
228
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
229 230
	key.offset = num_blocks;

231
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
232 233 234
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
235

236
	ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1);
237
	if (ret) {
238
		printk("failed to find %Lu\n", key.objectid);
C
Chris Mason 已提交
239
		btrfs_print_tree(extent_root, extent_root->node);
240
		printk("failed to find %Lu\n", key.objectid);
241 242
		BUG();
	}
243
	ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
C
Chris Mason 已提交
244
			    struct btrfs_extent_item);
245
	BUG_ON(ei->refs == 0);
C
Chris Mason 已提交
246 247
	refs = btrfs_extent_refs(ei) - 1;
	btrfs_set_extent_refs(ei, refs);
248
	btrfs_mark_buffer_dirty(path->nodes[0]);
C
Chris Mason 已提交
249
	if (refs == 0) {
250
		u64 super_blocks_used;
C
Chris Mason 已提交
251 252

		if (pin) {
C
Chris Mason 已提交
253
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
254 255 256
			BUG_ON(ret);
		}

257 258 259
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used - num_blocks);
260
		ret = btrfs_del_item(trans, extent_root, path);
261 262 263
		if (ret)
			BUG();
	}
264 265
	btrfs_release_path(extent_root, path);
	btrfs_free_path(path);
266
	finish_current_insert(trans, extent_root);
267 268 269 270 271 272 273
	return ret;
}

/*
 * find all the blocks marked as pending in the radix tree and remove
 * them from the extent map
 */
274 275
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
			       btrfs_root *extent_root)
276 277
{
	int ret;
C
Chris Mason 已提交
278 279
	int wret;
	int err = 0;
C
Chris Mason 已提交
280
	unsigned long gang[4];
281
	int i;
C
Chris Mason 已提交
282 283 284 285 286
	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;
287 288

	while(1) {
C
Chris Mason 已提交
289 290
		ret = find_first_radix_bit(pending_radix, gang,
					   ARRAY_SIZE(gang));
291 292 293
		if (!ret)
			break;
		for (i = 0; i < ret; i++) {
C
Chris Mason 已提交
294 295 296 297
			wret = set_radix_bit(pinned_radix, gang[i]);
			BUG_ON(wret);
			wret = clear_radix_bit(pending_radix, gang[i]);
			BUG_ON(wret);
298
			wret = __free_extent(trans, extent_root,
C
Chris Mason 已提交
299
					     gang[i], 1, 0);
C
Chris Mason 已提交
300 301
			if (wret)
				err = wret;
302 303
		}
	}
C
Chris Mason 已提交
304
	return err;
305 306 307 308 309
}

/*
 * remove an extent from the root, returns 0 on success
 */
310 311
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, u64 blocknr, u64 num_blocks, int pin)
312
{
313
	struct btrfs_root *extent_root = root->fs_info->extent_root;
314 315
	int pending_ret;
	int ret;
316

317
	if (root == extent_root) {
C
Chris Mason 已提交
318
		pin_down_block(root, blocknr, 1);
319 320
		return 0;
	}
C
Chris Mason 已提交
321
	ret = __free_extent(trans, root, blocknr, num_blocks, pin);
C
Chris Mason 已提交
322
	pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
323 324 325 326 327 328 329
	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
330
 * ins->flags = BTRFS_EXTENT_ITEM_KEY
331 332 333
 * ins->offset == number of blocks
 * Any available blocks before search_start are skipped.
 */
334 335 336
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)
337
{
338
	struct btrfs_path *path;
C
Chris Mason 已提交
339
	struct btrfs_key key;
340 341 342
	int ret;
	u64 hole_size = 0;
	int slot = 0;
C
Chris Mason 已提交
343
	u64 last_block = 0;
C
Chris Mason 已提交
344
	u64 test_block;
345
	int start_found;
C
Chris Mason 已提交
346
	struct btrfs_leaf *l;
347
	struct btrfs_root * root = orig_root->fs_info->extent_root;
348
	int total_needed = num_blocks;
C
Chris Mason 已提交
349
	int level;
350

351 352 353 354
	path = btrfs_alloc_path();
	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);

C
Chris Mason 已提交
355 356
	level = btrfs_header_level(btrfs_buffer_header(root->node));
	total_needed += (level + 1) * 3;
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	if (root->fs_info->last_insert.objectid == 0 && search_end == (u64)-1) {
		struct btrfs_disk_key *last_key;
		btrfs_init_path(path);
		ins->objectid = (u64)-1;
		ins->offset = (u64)-1;
		ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
		if (ret < 0)
			goto error;
		BUG_ON(ret == 0);
		if (path->slots[0] > 0)
			path->slots[0]--;
		l = btrfs_buffer_leaf(path->nodes[0]);
		last_key = &l->items[path->slots[0]].key;
		search_start = btrfs_disk_key_objectid(last_key);
	}
372 373
	if (root->fs_info->last_insert.objectid > search_start)
		search_start = root->fs_info->last_insert.objectid;
374

375
check_failed:
376
	btrfs_init_path(path);
377 378 379
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
380
	ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
C
Chris Mason 已提交
381 382
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
383

384 385
	if (path->slots[0] > 0)
		path->slots[0]--;
386

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

/*
 * 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 已提交
460
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
461
			*root, u64 num_blocks, u64 search_start, u64
C
Chris Mason 已提交
462
			search_end, struct btrfs_key *ins)
463 464 465
{
	int ret;
	int pending_ret;
466 467 468
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
469
	struct btrfs_extent_item extent_item;
C
Chris Mason 已提交
470

C
Chris Mason 已提交
471
	btrfs_set_extent_refs(&extent_item, 1);
472

C
Chris Mason 已提交
473
	if (root == extent_root) {
474
		BUG_ON(extent_root->fs_info->current_insert.offset == 0);
C
Chris Mason 已提交
475
		BUG_ON(num_blocks != 1);
476 477
		BUG_ON(extent_root->fs_info->current_insert.flags ==
		       extent_root->fs_info->current_insert.offset);
C
Chris Mason 已提交
478
		ins->offset = 1;
479 480
		ins->objectid = extent_root->fs_info->current_insert.objectid +
				extent_root->fs_info->current_insert.flags++;
481 482
		return 0;
	}
483
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
484 485 486
			       search_end, ins);
	if (ret)
		return ret;
487

488 489 490
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
491 492
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
493

494
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
495
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
496 497 498 499 500
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
501 502 503 504 505 506
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
507
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
508
					    struct btrfs_root *root)
509
{
C
Chris Mason 已提交
510
	struct btrfs_key ins;
511
	int ret;
C
Chris Mason 已提交
512
	struct buffer_head *buf;
513

C
Chris Mason 已提交
514
	ret = btrfs_alloc_extent(trans, root, 1, 0, (unsigned long)-1, &ins);
515 516 517 518
	if (ret) {
		BUG();
		return NULL;
	}
519
	buf = btrfs_find_create_tree_block(root, ins.objectid);
520
	set_buffer_uptodate(buf);
521 522
	return buf;
}
523

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
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 已提交
555 556 557 558
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
559 560
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
561
{
C
Chris Mason 已提交
562 563
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
564 565 566 567
	u64 blocknr;
	int ret;
	u32 refs;

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

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

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

668 669 670
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
671

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

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