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 <linux/writeback.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 struct btrfs_block_group_cache *
__btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache *hint,
u64 search_start, int data, int owner);
void maybe_lock_mutex(struct btrfs_root *root)
{
if (root != root->fs_info->extent_root &&
root != root->fs_info->chunk_root &&
root != root->fs_info->dev_root) {
mutex_lock(&root->fs_info->alloc_mutex);
}
}
void maybe_unlock_mutex(struct btrfs_root *root)
{
if (root != root->fs_info->extent_root &&
root != root->fs_info->chunk_root &&
root != root->fs_info->dev_root) {
mutex_unlock(&root->fs_info->alloc_mutex);
}
}
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;

Chris Mason
committed
/*
* we get into deadlocks with paths held by callers of this function.
* since the alloc_mutex is protecting things right now, just
* skip the locking here
*/
path->skip_locking = 1;
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;
}
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct
btrfs_fs_info *info,
u64 bytenr)
{
struct extent_io_tree *block_group_cache;
struct btrfs_block_group_cache *block_group = NULL;
u64 ptr;
u64 start;
u64 end;
int ret;
bytenr = max_t(u64, bytenr,
BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE);
block_group_cache = &info->block_group_cache;
ret = find_first_extent_bit(block_group_cache,
bytenr, &start, &end,
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
BLOCK_GROUP_SYSTEM);
if (ret) {
return NULL;
}
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
return NULL;
block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr;
return block_group;
}
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;
bytenr = max_t(u64, bytenr,
BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE);
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,
{
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;
WARN_ON(!mutex_is_locked(&root->fs_info->alloc_mutex));
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);
goto out;

Chris Mason
committed

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

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) {
do {
state = extent_state_next(state);
} while(state && !(state->state & EXTENT_DIRTY));

Chris Mason
committed
}
spin_unlock_irq(&free_space_cache->lock);
goto new_group;
if (start + num > cache->key.objectid + cache->key.offset)
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:
cache = btrfs_lookup_first_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_first_block_group(root->fs_info, last);

Chris Mason
committed
}
cache = __btrfs_find_block_group(root, cache, last, data, 0);
if (!cache)
goto no_cache;
*cache_ret = cache;
goto again;
}

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;
}
static struct btrfs_block_group_cache *
__btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache *hint,
u64 search_start, int data, int owner)
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 start;
u64 end;
u64 free_check;
u64 ptr;
int bit;

Chris Mason
committed
int factor = 10;
block_group_cache = &info->block_group_cache;
if (data & BTRFS_BLOCK_GROUP_METADATA)
factor = 9;
bit = block_group_state_bits(data);
shint = btrfs_lookup_first_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)) {
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;
last = max(hint->key.objectid, 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) {
last = end + 1;
continue;
}
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->ro && block_group_bits(cache, data)) {
free_check = div_factor(cache->key.offset, factor);
if (used + cache->pinned < free_check) {
found_group = cache;
goto found;
}
if (!wrapped) {
last = search_start;
wrapped = 1;
goto again;
}
if (!full_search && factor < 10) {
struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache
*hint, u64 search_start,
int data, int owner)
{
struct btrfs_block_group_cache *ret;
mutex_lock(&root->fs_info->alloc_mutex);
ret = __btrfs_find_block_group(root, hint, search_start, data, owner);
mutex_unlock(&root->fs_info->alloc_mutex);
return ret;
}
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;
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
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
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/*
* 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;
static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
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_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)
{
int ret;
mutex_lock(&root->fs_info->alloc_mutex);
ret = __btrfs_inc_extent_ref(trans, root, bytenr, num_bytes,
root_objectid, ref_generation,
owner, owner_offset);
mutex_unlock(&root->fs_info->alloc_mutex);
return ret;
}
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 expected_owner,
u64 first_extent)
{
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_path *path;
u64 bytenr;
u64 found_objectid;
u64 found_owner;
u64 root_objectid = root->root_key.objectid;
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;
/* FIXME, needs locking */
BUG();
mutex_lock(&root->fs_info->alloc_mutex);
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
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);
extent_refs = btrfs_extent_refs(l, item);
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;
if (level == -1) {
found_owner = btrfs_ref_objectid(l, ref_item);
if (found_owner != expected_owner) {
total_count = 2;
goto out;
}
/*
* nasty. we don't count a reference held by
* the running transaction. This allows nodatacow
* to avoid cow most of the time
*/
if (found_owner >= BTRFS_FIRST_FREE_OBJECTID &&
btrfs_ref_generation(l, ref_item) ==
root->fs_info->generation) {
extent_refs--;
}
}
total_count = 1;
/*
* if there is more than one reference against a data extent,
* we have to assume the other ref is another snapshot
*/
if (level == -1 && extent_refs > 1) {
total_count = 2;
goto out;
}
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);
mutex_unlock(&root->fs_info->alloc_mutex);
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++) {
cond_resched();
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)