extent-tree.c 20.0 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);
114 115 116
			if (btrfs_file_extent_type(fi) ==
			    BTRFS_FILE_EXTENT_INLINE)
				continue;
C
Chris Mason 已提交
117
			ret = btrfs_inc_extent_ref(trans, root,
118 119 120 121 122
				    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 已提交
123
			ret = btrfs_inc_extent_ref(trans, root, blocknr, 1);
124 125
			BUG_ON(ret);
		}
C
Chris Mason 已提交
126 127 128 129
	}
	return 0;
}

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

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

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

C
Chris Mason 已提交
166
	btrfs_set_extent_refs(&extent_item, 1);
C
Chris Mason 已提交
167 168
	ins.offset = 1;
	ins.flags = 0;
169
	btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
170
	btrfs_set_extent_owner(&extent_item, extent_root->root_key.objectid);
C
Chris Mason 已提交
171

172 173
	for (i = 0; i < extent_root->fs_info->extent_tree_insert_nr; i++) {
		ins.objectid = extent_root->fs_info->extent_tree_insert[i];
174 175 176
		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
		btrfs_set_super_blocks_used(info->disk_super,
					    super_blocks_used + 1);
177 178
		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
					sizeof(extent_item));
C
Chris Mason 已提交
179 180
		BUG_ON(ret);
	}
181 182
	extent_root->fs_info->extent_tree_insert_nr = 0;
	extent_root->fs_info->extent_tree_prealloc_nr = 0;
C
Chris Mason 已提交
183 184 185
	return 0;
}

C
Chris Mason 已提交
186
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
C
Chris Mason 已提交
187 188
{
	int err;
C
Chris Mason 已提交
189
	struct btrfs_header *header;
C
Chris Mason 已提交
190 191
	struct buffer_head *bh;

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

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

230 231
	key.objectid = blocknr;
	key.flags = 0;
232
	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
233 234
	key.offset = num_blocks;

235
	find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
236 237 238
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
239

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

		if (pin) {
C
Chris Mason 已提交
257
			ret = pin_down_block(root, blocknr, 0);
C
Chris Mason 已提交
258 259 260
			BUG_ON(ret);
		}

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

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

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

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

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

358 359 360 361
	path = btrfs_alloc_path();
	ins->flags = 0;
	btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);

C
Chris Mason 已提交
362
	level = btrfs_header_level(btrfs_buffer_header(root->node));
363 364 365 366 367 368
	if (num_blocks == 0) {
		fill_prealloc = 1;
		num_blocks = 1;
		total_needed = min(level + 2, BTRFS_MAX_LEVEL) * 3;
	}
	if (info->last_insert.objectid == 0 && search_end == (u64)-1) {
369 370 371 372 373 374 375 376 377 378 379 380 381 382
		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);
	}
383 384
	if (info->last_insert.objectid > search_start)
		search_start = info->last_insert.objectid;
385

386
check_failed:
387
	btrfs_init_path(path);
388 389 390
	ins->objectid = search_start;
	ins->offset = 0;
	start_found = 0;
391
	ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
C
Chris Mason 已提交
392 393
	if (ret < 0)
		goto error;
C
Chris Mason 已提交
394

395 396
	if (path->slots[0] > 0)
		path->slots[0]--;
397

398
	while (1) {
399 400
		l = btrfs_buffer_leaf(path->nodes[0]);
		slot = path->slots[0];
401
		if (slot >= btrfs_header_nritems(&l->header)) {
402 403 404 405
			if (fill_prealloc) {
				info->extent_tree_prealloc_nr = 0;
				total_found = 0;
			}
406
			ret = btrfs_next_leaf(root, path);
407 408
			if (ret == 0)
				continue;
C
Chris Mason 已提交
409 410
			if (ret < 0)
				goto error;
411 412
			if (!start_found) {
				ins->objectid = search_start;
413
				ins->offset = (u64)-1 - search_start;
414 415 416 417 418
				start_found = 1;
				goto check_pending;
			}
			ins->objectid = last_block > search_start ?
					last_block : search_start;
419
			ins->offset = (u64)-1 - ins->objectid;
420 421
			goto check_pending;
		}
C
Chris Mason 已提交
422 423
		btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
		if (key.objectid >= search_start) {
424
			if (start_found) {
425 426
				if (last_block < search_start)
					last_block = search_start;
C
Chris Mason 已提交
427
				hole_size = key.objectid - last_block;
428
				if (hole_size > num_blocks) {
429
					ins->objectid = last_block;
C
Chris Mason 已提交
430
					ins->offset = hole_size;
431 432
					goto check_pending;
				}
433
			}
434
		}
435
		start_found = 1;
C
Chris Mason 已提交
436
		last_block = key.objectid + key.offset;
437
		path->slots[0]++;
438 439 440 441 442 443
	}
	// 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
	 */
444
	btrfs_release_path(root, path);
445
	BUG_ON(ins->objectid < search_start);
C
Chris Mason 已提交
446
	for (test_block = ins->objectid;
447 448
	     test_block < ins->objectid + num_blocks; test_block++) {
		if (test_radix_bit(&info->pinned_radix, test_block)) {
C
Chris Mason 已提交
449
			search_start = test_block + 1;
450 451 452
			goto check_failed;
		}
	}
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
	if (!fill_prealloc && info->extent_tree_insert_nr) {
		u64 last =
		  info->extent_tree_insert[info->extent_tree_insert_nr - 1];
		if (ins->objectid + num_blocks >
		    info->extent_tree_insert[0] &&
		    ins->objectid <= last) {
			search_start = last + 1;
			WARN_ON(1);
			goto check_failed;
		}
	}
	if (!fill_prealloc && info->extent_tree_prealloc_nr) {
		u64 first =
		  info->extent_tree_prealloc[info->extent_tree_prealloc_nr - 1];
		if (ins->objectid + num_blocks > first &&
		    ins->objectid <= info->extent_tree_prealloc[0]) {
			search_start = info->extent_tree_prealloc[0] + 1;
			WARN_ON(1);
			goto check_failed;
		}
	}
	if (fill_prealloc) {
		int nr;
		test_block = ins->objectid;
		while(test_block < ins->objectid + ins->offset &&
		      total_found < total_needed) {
			nr = total_needed - total_found - 1;
			BUG_ON(nr < 0);
			root->fs_info->extent_tree_prealloc[nr] =
				test_block;
			total_found++;
			test_block++;
		}
		if (total_found < total_needed) {
			search_start = test_block;
			goto check_failed;
		}
		root->fs_info->extent_tree_prealloc_nr = total_found;
	}
492
	root->fs_info->last_insert.objectid = ins->objectid;
C
Chris Mason 已提交
493
	ins->offset = num_blocks;
494
	btrfs_free_path(path);
495
	return 0;
C
Chris Mason 已提交
496
error:
497 498
	btrfs_release_path(root, path);
	btrfs_free_path(path);
C
Chris Mason 已提交
499
	return ret;
500 501 502 503 504 505 506 507 508
}

/*
 * 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.
 */
509 510
int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, u64 owner,
511
		       u64 num_blocks, u64 search_start,
512
		       u64 search_end, struct btrfs_key *ins)
513 514 515
{
	int ret;
	int pending_ret;
516 517 518
	u64 super_blocks_used;
	struct btrfs_fs_info *info = root->fs_info;
	struct btrfs_root *extent_root = info->extent_root;
C
Chris Mason 已提交
519
	struct btrfs_extent_item extent_item;
520
	struct btrfs_key prealloc_key;
C
Chris Mason 已提交
521

C
Chris Mason 已提交
522
	btrfs_set_extent_refs(&extent_item, 1);
523
	btrfs_set_extent_owner(&extent_item, owner);
524

C
Chris Mason 已提交
525
	if (root == extent_root) {
526 527
		int nr;
		BUG_ON(info->extent_tree_prealloc_nr == 0);
C
Chris Mason 已提交
528 529
		BUG_ON(num_blocks != 1);
		ins->offset = 1;
530 531 532 533 534
		info->extent_tree_prealloc_nr--;
		nr = info->extent_tree_prealloc_nr;
		ins->objectid = info->extent_tree_prealloc[nr];
		info->extent_tree_insert[info->extent_tree_insert_nr++] =
			ins->objectid;
535 536
		return 0;
	}
537
	/* do the real allocation */
538
	ret = find_free_extent(trans, root, num_blocks, search_start,
C
Chris Mason 已提交
539 540 541
			       search_end, ins);
	if (ret)
		return ret;
542

543 544 545 546 547 548
	/* then do prealloc for the extent tree */
	ret = find_free_extent(trans, root, 0, ins->objectid + ins->offset,
			       search_end, &prealloc_key);
	if (ret)
		return ret;

549 550 551
	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
				    num_blocks);
552 553
	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
				sizeof(extent_item));
C
Chris Mason 已提交
554

555
	finish_current_insert(trans, extent_root);
C
Chris Mason 已提交
556
	pending_ret = del_pending_extents(trans, extent_root);
C
Chris Mason 已提交
557 558 559 560 561
	if (ret)
		return ret;
	if (pending_ret)
		return pending_ret;
	return 0;
562 563 564 565 566 567
}

/*
 * helper function to allocate a block for a given tree
 * returns the tree buffer or NULL.
 */
C
Chris Mason 已提交
568
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
569
					   struct btrfs_root *root)
570
{
C
Chris Mason 已提交
571
	struct btrfs_key ins;
572
	int ret;
C
Chris Mason 已提交
573
	struct buffer_head *buf;
574

575 576
	ret = btrfs_alloc_extent(trans, root, root->root_key.objectid,
				 1, 0, (unsigned long)-1, &ins);
577 578 579 580
	if (ret) {
		BUG();
		return NULL;
	}
581
	buf = btrfs_find_create_tree_block(root, ins.objectid);
582
	set_buffer_uptodate(buf);
583 584
	return buf;
}
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
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);
604 605
		if (btrfs_file_extent_type(fi) == BTRFS_FILE_EXTENT_INLINE)
			continue;
606 607 608 609 610 611 612 613 614 615 616 617 618
		/*
		 * 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 已提交
619 620 621 622
/*
 * helper function for drop_snapshot, this walks down the tree dropping ref
 * counts as it goes.
 */
623 624
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
625
{
C
Chris Mason 已提交
626 627
	struct buffer_head *next;
	struct buffer_head *cur;
C
Chris Mason 已提交
628 629 630 631
	u64 blocknr;
	int ret;
	u32 refs;

632 633
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
C
Chris Mason 已提交
634
	ret = lookup_extent_ref(trans, root, bh_blocknr(path->nodes[*level]),
635
			       1, &refs);
C
Chris Mason 已提交
636 637 638
	BUG_ON(ret);
	if (refs > 1)
		goto out;
C
Chris Mason 已提交
639 640 641
	/*
	 * walk down to the last node level and free all the leaves
	 */
642
	while(*level >= 0) {
643 644
		WARN_ON(*level < 0);
		WARN_ON(*level >= BTRFS_MAX_LEVEL);
C
Chris Mason 已提交
645
		cur = path->nodes[*level];
C
Chris Mason 已提交
646 647
		if (btrfs_header_level(btrfs_buffer_header(cur)) != *level)
			WARN_ON(1);
648
		if (path->slots[*level] >=
C
Chris Mason 已提交
649
		    btrfs_header_nritems(btrfs_buffer_header(cur)))
C
Chris Mason 已提交
650
			break;
651 652 653 654 655
		if (*level == 0) {
			ret = drop_leaf_ref(trans, root, cur);
			BUG_ON(ret);
			break;
		}
C
Chris Mason 已提交
656 657
		blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
					      path->slots[*level]);
C
Chris Mason 已提交
658
		ret = lookup_extent_ref(trans, root, blocknr, 1, &refs);
659 660
		BUG_ON(ret);
		if (refs != 1) {
C
Chris Mason 已提交
661
			path->slots[*level]++;
662
			ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
C
Chris Mason 已提交
663 664 665 666
			BUG_ON(ret);
			continue;
		}
		next = read_tree_block(root, blocknr);
667
		WARN_ON(*level <= 0);
C
Chris Mason 已提交
668
		if (path->nodes[*level-1])
C
Chris Mason 已提交
669
			btrfs_block_release(root, path->nodes[*level-1]);
C
Chris Mason 已提交
670
		path->nodes[*level-1] = next;
C
Chris Mason 已提交
671
		*level = btrfs_header_level(btrfs_buffer_header(next));
C
Chris Mason 已提交
672 673 674
		path->slots[*level] = 0;
	}
out:
675 676
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);
677
	ret = btrfs_free_extent(trans, root,
678
				bh_blocknr(path->nodes[*level]), 1, 1);
C
Chris Mason 已提交
679
	btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
680 681 682 683 684 685
	path->nodes[*level] = NULL;
	*level += 1;
	BUG_ON(ret);
	return 0;
}

C
Chris Mason 已提交
686 687 688 689 690
/*
 * 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
 */
691 692
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
			*root, struct btrfs_path *path, int *level)
C
Chris Mason 已提交
693 694 695 696
{
	int i;
	int slot;
	int ret;
C
Chris Mason 已提交
697
	for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
C
Chris Mason 已提交
698
		slot = path->slots[i];
C
Chris Mason 已提交
699 700
		if (slot < btrfs_header_nritems(
		    btrfs_buffer_header(path->nodes[i])) - 1) {
C
Chris Mason 已提交
701 702 703 704
			path->slots[i]++;
			*level = i;
			return 0;
		} else {
705
			ret = btrfs_free_extent(trans, root,
706
						bh_blocknr(path->nodes[*level]),
707
						1, 1);
708
			BUG_ON(ret);
C
Chris Mason 已提交
709
			btrfs_block_release(root, path->nodes[*level]);
C
Chris Mason 已提交
710
			path->nodes[*level] = NULL;
C
Chris Mason 已提交
711 712 713 714 715 716
			*level = i + 1;
		}
	}
	return 1;
}

C
Chris Mason 已提交
717 718 719 720 721
/*
 * 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.
 */
722
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
723
			*root, struct buffer_head *snap)
C
Chris Mason 已提交
724
{
725
	int ret = 0;
C
Chris Mason 已提交
726
	int wret;
C
Chris Mason 已提交
727
	int level;
728
	struct btrfs_path *path;
C
Chris Mason 已提交
729 730 731
	int i;
	int orig_level;

732 733 734
	path = btrfs_alloc_path();
	BUG_ON(!path);
	btrfs_init_path(path);
C
Chris Mason 已提交
735

C
Chris Mason 已提交
736
	level = btrfs_header_level(btrfs_buffer_header(snap));
C
Chris Mason 已提交
737
	orig_level = level;
738 739
	path->nodes[level] = snap;
	path->slots[level] = 0;
C
Chris Mason 已提交
740
	while(1) {
741
		wret = walk_down_tree(trans, root, path, &level);
C
Chris Mason 已提交
742
		if (wret > 0)
C
Chris Mason 已提交
743
			break;
C
Chris Mason 已提交
744 745 746
		if (wret < 0)
			ret = wret;

747
		wret = walk_up_tree(trans, root, path, &level);
C
Chris Mason 已提交
748
		if (wret > 0)
C
Chris Mason 已提交
749
			break;
C
Chris Mason 已提交
750 751
		if (wret < 0)
			ret = wret;
C
Chris Mason 已提交
752
	}
C
Chris Mason 已提交
753
	for (i = 0; i <= orig_level; i++) {
754 755
		if (path->nodes[i]) {
			btrfs_block_release(root, path->nodes[i]);
C
Chris Mason 已提交
756
		}
C
Chris Mason 已提交
757
	}
758
	btrfs_free_path(path);
C
Chris Mason 已提交
759
	return ret;
C
Chris Mason 已提交
760
}