Newer
Older
/*
* Copyright (C) 2007 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#include <linux/pagemap.h>
#include "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
#include "volumes.h"
#define BLOCK_GROUP_DATA EXTENT_WRITEBACK
#define BLOCK_GROUP_METADATA EXTENT_UPTODATE
#define BLOCK_GROUP_SYSTEM EXTENT_NEW
#define BLOCK_GROUP_DIRTY EXTENT_DIRTY
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root);
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root);
static int cache_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache *block_group)
{
struct btrfs_path *path;
int ret;
struct btrfs_key key;
struct extent_buffer *leaf;
struct extent_io_tree *free_space_cache;
int slot;
u64 last = 0;
u64 hole_size;
u64 first_free;
if (!block_group)
return 0;
root = root->fs_info->extent_root;
free_space_cache = &root->fs_info->free_space_cache;
if (block_group->cached)
return 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
first_free = block_group->key.objectid;
key.objectid = block_group->key.objectid;
key.offset = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
return ret;
ret = btrfs_previous_item(root, path, 0, BTRFS_EXTENT_ITEM_KEY);
if (ret < 0)
return ret;
if (ret == 0) {
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid + key.offset > first_free)
first_free = key.objectid + key.offset;
}
leaf = path->nodes[0];
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path);
if (ret < 0)
goto err;
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid < block_group->key.objectid) {
goto next;
}
if (key.objectid >= block_group->key.objectid +
block_group->key.offset) {
break;
}
if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) {
if (!found) {
last = first_free;
if (key.objectid > last) {
hole_size = key.objectid - last;
set_extent_dirty(free_space_cache, last,
last + hole_size - 1,
GFP_NOFS);
}
last = key.objectid + key.offset;
if (!found)
last = first_free;
if (block_group->key.objectid +
block_group->key.offset > last) {
hole_size = block_group->key.objectid +
block_group->key.offset - last;
set_extent_dirty(free_space_cache, last,
last + hole_size - 1, GFP_NOFS);
err:
btrfs_free_path(path);
return 0;
}
struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
btrfs_fs_info *info,
struct extent_io_tree *block_group_cache;
struct btrfs_block_group_cache *block_group = NULL;
u64 ptr;
u64 start;
u64 end;
block_group_cache = &info->block_group_cache;
ret = find_first_extent_bit(block_group_cache,
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
BLOCK_GROUP_SYSTEM);
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
return NULL;
block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr;
if (block_group->key.objectid <= bytenr && bytenr <
block_group->key.objectid + block_group->key.offset)
return block_group;
static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
{
return (cache->flags & bits) == bits;
}
static int noinline find_search_start(struct btrfs_root *root,
struct btrfs_block_group_cache **cache_ret,
u64 *start_ret, int num, int data)
{
int ret;
struct btrfs_block_group_cache *cache = *cache_ret;
struct extent_state *state;

Chris Mason
committed
u64 last;

Chris Mason
committed
u64 cache_miss = 0;
u64 search_start = *start_ret;

Chris Mason
committed
int wrapped = 0;
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
free_space_cache = &root->fs_info->free_space_cache;
ret = cache_block_group(root, cache);
if (ret)
goto out;

Chris Mason
committed

Chris Mason
committed
last = max(search_start, cache->key.objectid);
if (!block_group_bits(cache, data) || cache->ro) {
goto new_group;
}

Chris Mason
committed
spin_lock_irq(&free_space_cache->lock);
state = find_first_extent_bit_state(free_space_cache, last, EXTENT_DIRTY);

Chris Mason
committed
if (!cache_miss)
cache_miss = last;
spin_unlock_irq(&free_space_cache->lock);

Chris Mason
committed
goto new_group;
}
start = max(last, state->start);
last = state->end + 1;

Chris Mason
committed
if (last - start < num) {
if (last == cache->key.objectid + cache->key.offset)
cache_miss = start;
do {
state = extent_state_next(state);
} while(state && !(state->state & EXTENT_DIRTY));

Chris Mason
committed
}
spin_unlock_irq(&free_space_cache->lock);
if (cache->ro)
goto new_group;
if (start + num > cache->key.objectid + cache->key.offset)
if (start + num > total_fs_bytes)
goto new_group;
if (!block_group_bits(cache, data)) {
printk("block group bits don't match %Lu %d\n", cache->flags, data);
*start_ret = start;
return 0;
cache = btrfs_lookup_block_group(root->fs_info, search_start);
if (!cache) {
printk("Unable to find block group for %Lu\n", search_start);
WARN_ON(1);
}

Chris Mason
committed
last = cache->key.objectid + cache->key.offset;

Chris Mason
committed
wrapped:

Chris Mason
committed
cache = btrfs_lookup_block_group(root->fs_info, last);
if (!cache || cache->key.objectid >= total_fs_bytes) {

Chris Mason
committed
if (!wrapped) {
wrapped = 1;
last = search_start;
goto wrapped;
}

Chris Mason
committed
if (cache_miss && !cache->cached) {
cache_block_group(root, cache);
last = cache_miss;
cache = btrfs_lookup_block_group(root->fs_info, last);
}
cache = btrfs_find_block_group(root, cache, last, data, 0);
if (!cache)
goto no_cache;

Chris Mason
committed
cache_miss = 0;

Chris Mason
committed
if (factor == 10)
return num;
num *= factor;
do_div(num, 10);
return num;
}
static int block_group_state_bits(u64 flags)
{
int bits = 0;
if (flags & BTRFS_BLOCK_GROUP_DATA)
bits |= BLOCK_GROUP_DATA;
if (flags & BTRFS_BLOCK_GROUP_METADATA)
bits |= BLOCK_GROUP_METADATA;
if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
bits |= BLOCK_GROUP_SYSTEM;
return bits;
}
struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache
struct btrfs_block_group_cache *cache;
struct extent_io_tree *block_group_cache;
struct btrfs_block_group_cache *found_group = NULL;
struct btrfs_fs_info *info = root->fs_info;
u64 used;
u64 last = 0;
u64 hint_last;
u64 start;
u64 end;
u64 free_check;
u64 ptr;

Chris Mason
committed
int factor = 10;
block_group_cache = &info->block_group_cache;
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);

Chris Mason
committed
factor = 10;
bit = block_group_state_bits(data);
if (search_start && search_start < total_fs_bytes) {
shint = btrfs_lookup_block_group(info, search_start);
if (shint && block_group_bits(shint, data) && !shint->ro) {
used = btrfs_block_group_used(&shint->item);
if (used + shint->pinned <
div_factor(shint->key.offset, factor)) {
if (hint && !hint->ro && block_group_bits(hint, data) &&
hint->key.objectid < total_fs_bytes) {
used = btrfs_block_group_used(&hint->item);
if (used + hint->pinned <
div_factor(hint->key.offset, factor)) {

Chris Mason
committed
last = hint->key.objectid + hint->key.offset;
hint_last = last;
} else {
if (hint)
hint_last = max(hint->key.objectid, search_start);
else
hint_last = search_start;
if (hint_last >= total_fs_bytes)
hint_last = search_start;
ret = find_first_extent_bit(block_group_cache, last,
&start, &end, bit);
if (ret)
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
break;
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
last = cache->key.objectid + cache->key.offset;
used = btrfs_block_group_used(&cache->item);
if (cache->key.objectid > total_fs_bytes)
break;
if (!cache->ro && block_group_bits(cache, data)) {
if (full_search)
free_check = cache->key.offset;
else
free_check = div_factor(cache->key.offset,
factor);
if (used + cache->pinned < free_check) {
found_group = cache;
goto found;
}
full_search = 1;
goto again;
}
static u64 hash_extent_ref(u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset)
{
u32 high_crc = ~(u32)0;
u32 low_crc = ~(u32)0;
__le64 lenum;
lenum = cpu_to_le64(root_objectid);
high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum));
lenum = cpu_to_le64(ref_generation);
low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
if (owner >= BTRFS_FIRST_FREE_OBJECTID) {
lenum = cpu_to_le64(owner);
low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
lenum = cpu_to_le64(owner_offset);
low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
return ((u64)high_crc << 32) | (u64)low_crc;
}
static int match_extent_ref(struct extent_buffer *leaf,
struct btrfs_extent_ref *disk_ref,
struct btrfs_extent_ref *cpu_ref)
{
int ret;
int len;
if (cpu_ref->objectid)
len = sizeof(*cpu_ref);
else
len = 2 * sizeof(u64);
ret = memcmp_extent_buffer(leaf, cpu_ref, (unsigned long)disk_ref,
len);
return ret == 0;
}
static int noinline lookup_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 bytenr,
u64 root_objectid,
u64 ref_generation, u64 owner,
u64 owner_offset, int del)
{
u64 hash;
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_extent_ref ref;
439
440
441
442
443
444
445
446
447
448
449
450
451
452
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
492
493
struct extent_buffer *leaf;
struct btrfs_extent_ref *disk_ref;
int ret;
int ret2;
btrfs_set_stack_ref_root(&ref, root_objectid);
btrfs_set_stack_ref_generation(&ref, ref_generation);
btrfs_set_stack_ref_objectid(&ref, owner);
btrfs_set_stack_ref_offset(&ref, owner_offset);
hash = hash_extent_ref(root_objectid, ref_generation, owner,
owner_offset);
key.offset = hash;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_REF_KEY;
while (1) {
ret = btrfs_search_slot(trans, root, &key, path,
del ? -1 : 0, del);
if (ret < 0)
goto out;
leaf = path->nodes[0];
if (ret != 0) {
u32 nritems = btrfs_header_nritems(leaf);
if (path->slots[0] >= nritems) {
ret2 = btrfs_next_leaf(root, path);
if (ret2)
goto out;
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
if (found_key.objectid != bytenr ||
found_key.type != BTRFS_EXTENT_REF_KEY)
goto out;
key.offset = found_key.offset;
if (del) {
btrfs_release_path(root, path);
continue;
}
}
disk_ref = btrfs_item_ptr(path->nodes[0],
path->slots[0],
struct btrfs_extent_ref);
if (match_extent_ref(path->nodes[0], disk_ref, &ref)) {
ret = 0;
goto out;
}
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
key.offset = found_key.offset + 1;
btrfs_release_path(root, path);
}
out:
return ret;
}

Chris Mason
committed
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
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
555
556
/*
* Back reference rules. Back refs have three main goals:
*
* 1) differentiate between all holders of references to an extent so that
* when a reference is dropped we can make sure it was a valid reference
* before freeing the extent.
*
* 2) Provide enough information to quickly find the holders of an extent
* if we notice a given block is corrupted or bad.
*
* 3) Make it easy to migrate blocks for FS shrinking or storage pool
* maintenance. This is actually the same as #2, but with a slightly
* different use case.
*
* File extents can be referenced by:
*
* - multiple snapshots, subvolumes, or different generations in one subvol
* - different files inside a single subvolume (in theory, not implemented yet)
* - different offsets inside a file (bookend extents in file.c)
*
* The extent ref structure has fields for:
*
* - Objectid of the subvolume root
* - Generation number of the tree holding the reference
* - objectid of the file holding the reference
* - offset in the file corresponding to the key holding the reference
*
* When a file extent is allocated the fields are filled in:
* (root_key.objectid, trans->transid, inode objectid, offset in file)
*
* When a leaf is cow'd new references are added for every file extent found
* in the leaf. It looks the same as the create case, but trans->transid
* will be different when the block is cow'd.
*
* (root_key.objectid, trans->transid, inode objectid, offset in file)
*
* When a file extent is removed either during snapshot deletion or file
* truncation, the corresponding back reference is found
* by searching for:
*
* (btrfs_header_owner(leaf), btrfs_header_generation(leaf),
* inode objectid, offset in file)
*
* Btree extents can be referenced by:
*
* - Different subvolumes
* - Different generations of the same subvolume
*
* Storing sufficient information for a full reverse mapping of a btree
* block would require storing the lowest key of the block in the backref,
* and it would require updating that lowest key either before write out or
* every time it changed. Instead, the objectid of the lowest key is stored
* along with the level of the tree block. This provides a hint
* about where in the btree the block can be found. Searches through the
* btree only need to look for a pointer to that block, so they stop one
* level higher than the level recorded in the backref.
*
* Some btrees do not do reference counting on their extents. These
* include the extent tree and the tree of tree roots. Backrefs for these
* trees always have a generation of zero.
*
* When a tree block is created, back references are inserted:
*
* (root->root_key.objectid, trans->transid or zero, level, lowest_key_objectid)

Chris Mason
committed
*
* When a tree block is cow'd in a reference counted root,
* new back references are added for all the blocks it points to.
* These are of the form (trans->transid will have increased since creation):
*
* (root->root_key.objectid, trans->transid, level, lowest_key_objectid)

Chris Mason
committed
*
* Because the lowest_key_objectid and the level are just hints
* they are not used when backrefs are deleted. When a backref is deleted:
*
* if backref was for a tree root:
* root_objectid = root->root_key.objectid
* else
* root_objectid = btrfs_header_owner(parent)
*
* (root_objectid, btrfs_header_generation(parent) or zero, 0, 0)
*
* Back Reference Key hashing:
*
* Back references have four fields, each 64 bits long. Unfortunately,
* This is hashed into a single 64 bit number and placed into the key offset.
* The key objectid corresponds to the first byte in the extent, and the
* key type is set to BTRFS_EXTENT_REF_KEY
*/
int btrfs_insert_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 bytenr,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset)
{
u64 hash;
struct btrfs_key key;
struct btrfs_extent_ref ref;
struct btrfs_extent_ref *disk_ref;
int ret;
btrfs_set_stack_ref_root(&ref, root_objectid);
btrfs_set_stack_ref_generation(&ref, ref_generation);
btrfs_set_stack_ref_objectid(&ref, owner);
btrfs_set_stack_ref_offset(&ref, owner_offset);
hash = hash_extent_ref(root_objectid, ref_generation, owner,
owner_offset);
key.offset = hash;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_REF_KEY;
ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(ref));
while (ret == -EEXIST) {
disk_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_ref);
if (match_extent_ref(path->nodes[0], disk_ref, &ref))
goto out;
key.offset++;
btrfs_release_path(root, path);
ret = btrfs_insert_empty_item(trans, root, path, &key,
sizeof(ref));
if (ret)
goto out;
disk_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_ref);
write_extent_buffer(path->nodes[0], &ref, (unsigned long)disk_ref,
sizeof(ref));
btrfs_mark_buffer_dirty(path->nodes[0]);
out:
btrfs_release_path(root, path);
return ret;
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset)
struct extent_buffer *l;
WARN_ON(num_bytes < root->sectorsize);
if (!path)
return -ENOMEM;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
key.offset = num_bytes;
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
if (ret < 0)
return ret;
l = path->nodes[0];
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
refs = btrfs_extent_refs(l, item);
btrfs_set_extent_refs(l, item, refs + 1);
btrfs_mark_buffer_dirty(path->nodes[0]);
btrfs_release_path(root->fs_info->extent_root, path);
ret = btrfs_insert_extent_backref(trans, root->fs_info->extent_root,
path, bytenr, root_objectid,
ref_generation, owner, owner_offset);
BUG_ON(ret);
finish_current_insert(trans, root->fs_info->extent_root);
del_pending_extents(trans, root->fs_info->extent_root);
btrfs_free_path(path);
int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
finish_current_insert(trans, root->fs_info->extent_root);
del_pending_extents(trans, root->fs_info->extent_root);
return 0;
}
static int lookup_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u32 *refs)
struct extent_buffer *l;
WARN_ON(num_bytes < root->sectorsize);
key.objectid = bytenr;
key.offset = num_bytes;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
if (ret < 0)
goto out;
if (ret != 0) {
btrfs_print_leaf(root, path->nodes[0]);
printk("failed to find block number %Lu\n", bytenr);
}
l = path->nodes[0];
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
*refs = btrfs_extent_refs(l, item);
out:
u32 btrfs_count_snapshots_in_path(struct btrfs_root *root,
struct btrfs_path *count_path,
u64 first_extent)
{
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_path *path;
u64 bytenr;
u64 found_objectid;
u64 root_objectid = root->root_key.objectid;
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
u32 total_count = 0;
u32 cur_count;
u32 nritems;
int ret;
struct btrfs_key key;
struct btrfs_key found_key;
struct extent_buffer *l;
struct btrfs_extent_item *item;
struct btrfs_extent_ref *ref_item;
int level = -1;
path = btrfs_alloc_path();
again:
if (level == -1)
bytenr = first_extent;
else
bytenr = count_path->nodes[level]->start;
cur_count = 0;
key.objectid = bytenr;
key.offset = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0)
goto out;
BUG_ON(ret == 0);
l = path->nodes[0];
btrfs_item_key_to_cpu(l, &found_key, path->slots[0]);
if (found_key.objectid != bytenr ||
found_key.type != BTRFS_EXTENT_ITEM_KEY) {
goto out;
}
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
while (1) {
l = path->nodes[0];
nritems = btrfs_header_nritems(l);
if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(extent_root, path);
if (ret == 0)
continue;
break;
}
btrfs_item_key_to_cpu(l, &found_key, path->slots[0]);
if (found_key.objectid != bytenr)
break;
if (found_key.type != BTRFS_EXTENT_REF_KEY) {
path->slots[0]++;
continue;
}
cur_count++;
ref_item = btrfs_item_ptr(l, path->slots[0],
struct btrfs_extent_ref);
found_objectid = btrfs_ref_root(l, ref_item);
if (found_objectid != root_objectid) {
total_count = 2;
}
total_count = 1;
path->slots[0]++;
}
if (cur_count == 0) {
total_count = 0;
goto out;
}
if (level >= 0 && root->node == count_path->nodes[level])
goto out;
level++;
btrfs_release_path(root, path);
goto again;
out:
btrfs_free_path(path);
return total_count;
}
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 owner_objectid)
u64 generation;
u64 key_objectid;
u64 level;
u32 nritems;
struct btrfs_disk_key disk_key;
level = btrfs_header_level(root->node);
generation = trans->transid;
nritems = btrfs_header_nritems(root->node);
if (nritems > 0) {
if (level == 0)
btrfs_item_key(root->node, &disk_key, 0);
else
btrfs_node_key(root->node, &disk_key, 0);
key_objectid = btrfs_disk_key_objectid(&disk_key);
} else {
key_objectid = 0;
}
return btrfs_inc_extent_ref(trans, root, root->node->start,
root->node->len, owner_objectid,
generation, level, key_objectid);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf)
u32 nritems;
struct btrfs_key key;
struct btrfs_file_extent_item *fi;
int faili;

Chris Mason
committed
if (!root->ref_cows)
level = btrfs_header_level(buf);
nritems = btrfs_header_nritems(buf);
for (i = 0; i < nritems; i++) {
if (level == 0) {
u64 disk_bytenr;
btrfs_item_key_to_cpu(buf, &key, i);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
fi = btrfs_item_ptr(buf, i,
if (btrfs_file_extent_type(buf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (disk_bytenr == 0)
ret = btrfs_inc_extent_ref(trans, root, disk_bytenr,
btrfs_file_extent_disk_num_bytes(buf, fi),
root->root_key.objectid, trans->transid,
key.objectid, key.offset);
if (ret) {
faili = i;
goto fail;
}
bytenr = btrfs_node_blockptr(buf, i);
btrfs_node_key_to_cpu(buf, &key, i);
ret = btrfs_inc_extent_ref(trans, root, bytenr,
btrfs_level_size(root, level - 1),
root->root_key.objectid,
trans->transid,
level - 1, key.objectid);
if (ret) {
faili = i;
goto fail;
}
fail:
#if 0
for (i =0; i < faili; i++) {
if (level == 0) {
u64 disk_bytenr;
btrfs_item_key_to_cpu(buf, &key, i);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(buf, i,
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(buf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (disk_bytenr == 0)
continue;
err = btrfs_free_extent(trans, root, disk_bytenr,
btrfs_file_extent_disk_num_bytes(buf,
BUG_ON(err);
} else {
bytenr = btrfs_node_blockptr(buf, i);
err = btrfs_free_extent(trans, root, bytenr,
btrfs_level_size(root, level - 1), 0);
BUG_ON(err);
}
}
#endif
return ret;
static int write_one_cache_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_block_group_cache *cache)
{
int ret;
int pending_ret;
struct btrfs_root *extent_root = root->fs_info->extent_root;
unsigned long bi;
struct extent_buffer *leaf;
ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1);
if (ret < 0)
goto fail;
leaf = path->nodes[0];
bi = btrfs_item_ptr_offset(leaf, path->slots[0]);
write_extent_buffer(leaf, &cache->item, bi, sizeof(cache->item));
btrfs_mark_buffer_dirty(leaf);
fail:
finish_current_insert(trans, extent_root);
pending_ret = del_pending_extents(trans, extent_root);
if (ret)
return ret;
if (pending_ret)
return pending_ret;
return 0;
}
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
struct extent_io_tree *block_group_cache;
struct btrfs_block_group_cache *cache;
int ret;
int err = 0;
int werr = 0;
struct btrfs_path *path;
u64 last = 0;
u64 start;
u64 end;
u64 ptr;
block_group_cache = &root->fs_info->block_group_cache;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
while(1) {
ret = find_first_extent_bit(block_group_cache, last,
&start, &end, BLOCK_GROUP_DIRTY);
if (ret)
last = end + 1;
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
break;
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
err = write_one_cache_group(trans, root,
path, cache);
/*
* if we fail to write the cache group, we want
* to keep it marked dirty in hopes that a later
* write will work
*/
if (err) {
werr = err;
continue;
clear_extent_bits(block_group_cache, start, end,
BLOCK_GROUP_DIRTY, GFP_NOFS);
}
btrfs_free_path(path);
return werr;
}