summaryrefslogtreecommitdiff
path: root/check/mode-lowmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'check/mode-lowmem.c')
-rw-r--r--check/mode-lowmem.c650
1 files changed, 519 insertions, 131 deletions
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index bfe45aba..66da4531 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -28,6 +28,8 @@
#include "check/mode-common.h"
#include "check/mode-lowmem.h"
+static u64 last_allocated_chunk;
+
static int calc_extent_flag(struct btrfs_root *root, struct extent_buffer *eb,
u64 *flags_ret)
{
@@ -184,8 +186,8 @@ static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
if (bytenr != (u64)-1) {
/* the return value of this function seems a mistake */
- ret = btrfs_lookup_extent_info(NULL, root, bytenr,
- level, 1, &refs, &flags);
+ ret = btrfs_lookup_extent_info(NULL, root->fs_info, bytenr,
+ level, 1, &refs, &flags);
/* temporary fix */
if (ret < 0 && !check_all)
return ret;
@@ -234,17 +236,343 @@ static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
}
/*
+ * Mark all extents unfree in the block group. And set @block_group->cached
+ * according to @cache.
+ */
+static int modify_block_group_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_block_group_cache *block_group, int cache)
+{
+ struct extent_io_tree *free_space_cache = &fs_info->free_space_cache;
+ u64 start = block_group->key.objectid;
+ u64 end = start + block_group->key.offset;
+
+ if (cache && !block_group->cached) {
+ block_group->cached = 1;
+ clear_extent_dirty(free_space_cache, start, end - 1);
+ }
+
+ if (!cache && block_group->cached) {
+ block_group->cached = 0;
+ clear_extent_dirty(free_space_cache, start, end - 1);
+ }
+ return 0;
+}
+
+/*
+ * Modify block groups which have @flags unfree in free space cache.
+ *
+ * @cache: if 0, clear block groups cache state;
+ * not 0, mark blocks groups cached.
+ */
+static int modify_block_groups_cache(struct btrfs_fs_info *fs_info, u64 flags,
+ int cache)
+{
+ struct btrfs_root *root = fs_info->extent_root;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_block_group_cache *bg_cache;
+ struct btrfs_block_group_item *bi;
+ struct btrfs_block_group_item bg_item;
+ struct extent_buffer *eb;
+ int slot;
+ int ret;
+
+ key.objectid = 0;
+ key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+ key.offset = 0;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0) {
+ error("fail to search block groups due to %s", strerror(-ret));
+ goto out;
+ }
+
+ while (1) {
+ eb = path.nodes[0];
+ slot = path.slots[0];
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ bg_cache = btrfs_lookup_block_group(fs_info, key.objectid);
+ if (!bg_cache) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
+ read_extent_buffer(eb, &bg_item, (unsigned long)bi,
+ sizeof(bg_item));
+ if (btrfs_block_group_flags(&bg_item) & flags)
+ modify_block_group_cache(fs_info, bg_cache, cache);
+
+ ret = btrfs_next_item(root, &path);
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static int mark_block_groups_full(struct btrfs_fs_info *fs_info, u64 flags)
+{
+ return modify_block_groups_cache(fs_info, flags, 1);
+}
+
+static int clear_block_groups_full(struct btrfs_fs_info *fs_info, u64 flags)
+{
+ return modify_block_groups_cache(fs_info, flags, 0);
+}
+
+static int create_chunk_and_block_group(struct btrfs_fs_info *fs_info,
+ u64 flags, u64 *start, u64 *nbytes)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *root = fs_info->extent_root;
+ int ret;
+
+ if ((flags & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0)
+ return -EINVAL;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("error starting transaction %s", strerror(-ret));
+ return ret;
+ }
+ ret = btrfs_alloc_chunk(trans, fs_info, start, nbytes, flags);
+ if (ret) {
+ error("fail to allocate new chunk %s", strerror(-ret));
+ goto out;
+ }
+ ret = btrfs_make_block_group(trans, fs_info, 0, flags, *start,
+ *nbytes);
+ if (ret) {
+ error("fail to make block group for chunk %llu %llu %s",
+ *start, *nbytes, strerror(-ret));
+ goto out;
+ }
+out:
+ btrfs_commit_transaction(trans, root);
+ return ret;
+}
+
+static int force_cow_in_new_chunk(struct btrfs_fs_info *fs_info,
+ u64 *start_ret)
+{
+ struct btrfs_block_group_cache *bg;
+ u64 start;
+ u64 nbytes;
+ u64 alloc_profile;
+ u64 flags;
+ int ret;
+
+ alloc_profile = (fs_info->avail_metadata_alloc_bits &
+ fs_info->metadata_alloc_profile);
+ flags = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
+ if (btrfs_fs_incompat(fs_info, MIXED_GROUPS))
+ flags |= BTRFS_BLOCK_GROUP_DATA;
+
+ ret = create_chunk_and_block_group(fs_info, flags, &start, &nbytes);
+ if (ret)
+ goto err;
+ printf("Created new chunk [%llu %llu]\n", start, nbytes);
+
+ flags = BTRFS_BLOCK_GROUP_METADATA;
+ /* Mark all metadata block groups cached and full in free space*/
+ ret = mark_block_groups_full(fs_info, flags);
+ if (ret)
+ goto clear_bgs_full;
+
+ bg = btrfs_lookup_block_group(fs_info, start);
+ if (!bg) {
+ ret = -ENOENT;
+ error("fail to look up block group %llu %llu", start, nbytes);
+ goto clear_bgs_full;
+ }
+
+ /* Clear block group cache just allocated */
+ ret = modify_block_group_cache(fs_info, bg, 0);
+ if (ret)
+ goto clear_bgs_full;
+ if (start_ret)
+ *start_ret = start;
+ return 0;
+
+clear_bgs_full:
+ clear_block_groups_full(fs_info, flags);
+err:
+ return ret;
+}
+
+/*
+ * Returns 0 means not almost full.
+ * Returns >0 means almost full.
+ * Returns <0 means fatal error.
+ */
+static int is_chunk_almost_full(struct btrfs_fs_info *fs_info, u64 start)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_root *root = fs_info->extent_root;
+ struct btrfs_block_group_item *bi;
+ struct btrfs_block_group_item bg_item;
+ struct extent_buffer *eb;
+ u64 used;
+ u64 total;
+ u64 min_free;
+ int ret;
+ int slot;
+
+ key.objectid = start;
+ key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ goto out;
+ ret = btrfs_previous_item(root, &path, start,
+ BTRFS_BLOCK_GROUP_ITEM_KEY);
+ if (ret) {
+ error("failed to find block group %llu", start);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ eb = path.nodes[0];
+ slot = path.slots[0];
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ if (key.objectid != start) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ total = key.offset;
+ bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
+ read_extent_buffer(eb, &bg_item, (unsigned long)bi, sizeof(bg_item));
+ used = btrfs_block_group_used(&bg_item);
+
+ /*
+ * if the free space in the chunk is less than %10 of total,
+ * or not not enough for CoW once, we think the chunk is almost full.
+ */
+ min_free = max_t(u64, (BTRFS_MAX_LEVEL + 1) * fs_info->nodesize,
+ div_factor(total, 1));
+
+ if ((total - used) > min_free)
+ ret = 0;
+ else
+ ret = 1;
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+/*
+ * Returns <0 for error.
+ * Returns 0 for success.
+ */
+static int try_to_force_cow_in_new_chunk(struct btrfs_fs_info *fs_info,
+ u64 old_start, u64 *new_start)
+{
+ int ret;
+
+ if (old_start) {
+ ret = is_chunk_almost_full(fs_info, old_start);
+ if (ret <= 0)
+ return ret;
+ }
+ ret = force_cow_in_new_chunk(fs_info, new_start);
+ return ret;
+}
+
+static int avoid_extents_overwrite(struct btrfs_fs_info *fs_info)
+{
+ int ret;
+ int mixed = btrfs_fs_incompat(fs_info, MIXED_GROUPS);
+
+ if (fs_info->excluded_extents)
+ return 0;
+
+ if (last_allocated_chunk != (u64)-1) {
+ ret = try_to_force_cow_in_new_chunk(fs_info,
+ last_allocated_chunk, &last_allocated_chunk);
+ if (!ret)
+ goto out;
+ /*
+ * If failed, do not try to allocate chunk again in
+ * next call.
+ * If there is no space left to allocate, try to exclude all
+ * metadata blocks. Mixed filesystem is unsupported.
+ */
+ last_allocated_chunk = (u64)-1;
+ if (ret != -ENOSPC || mixed)
+ goto out;
+ }
+
+ printf(
+ "Try to exclude all metadata blcoks and extents, it may be slow\n");
+ ret = exclude_metadata_blocks(fs_info);
+out:
+ if (ret)
+ error("failed to avoid extents overwrite %s", strerror(-ret));
+ return ret;
+}
+
+static int end_avoid_extents_overwrite(struct btrfs_fs_info *fs_info)
+{
+ int ret = 0;
+
+ cleanup_excluded_extents(fs_info);
+ if (last_allocated_chunk)
+ ret = clear_block_groups_full(fs_info,
+ BTRFS_BLOCK_GROUP_METADATA);
+ return ret;
+}
+
+/*
+ * Wrapper function for btrfs_fix_block_accounting().
+ *
+ * Returns 0 on success.
+ * Returns != 0 on error.
+ */
+static int repair_block_accounting(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_trans_handle *trans = NULL;
+ struct btrfs_root *root = fs_info->extent_root;
+ int ret;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("fail to start transaction %s", strerror(-ret));
+ return ret;
+ }
+
+ ret = btrfs_fix_block_accounting(trans);
+ btrfs_commit_transaction(trans, root);
+ return ret;
+}
+
+/*
* This function only handles BACKREF_MISSING,
* If corresponding extent item exists, increase the ref, else insert an extent
* item and backref.
*
* Returns error bits after repair.
*/
-static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int repair_tree_block_ref(struct btrfs_root *root,
struct extent_buffer *node,
struct node_refs *nrefs, int level, int err)
{
+ struct btrfs_trans_handle *trans = NULL;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *extent_root = fs_info->extent_root;
struct btrfs_path path;
@@ -294,6 +622,16 @@ static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
if (nrefs->full_backref[level] != 0)
flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+ ret = avoid_extents_overwrite(root->fs_info);
+ if (ret)
+ goto out;
+ trans = btrfs_start_transaction(extent_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ trans = NULL;
+ error("fail to start transaction %s", strerror(-ret));
+ goto out;
+ }
/* insert an extent item */
if (insert_extent) {
struct btrfs_disk_key copy_key;
@@ -359,6 +697,8 @@ static int repair_tree_block_ref(struct btrfs_trans_handle *trans,
nrefs->refs[level]++;
out:
+ if (trans)
+ btrfs_commit_transaction(trans, extent_root);
btrfs_release_path(&path);
if (ret) {
error(
@@ -390,7 +730,7 @@ static void account_bytes(struct btrfs_root *root, struct btrfs_path *path,
total_extent_tree_bytes += eb->len;
if (level == 0) {
- btree_space_waste += btrfs_leaf_free_space(root->fs_info, eb);
+ btree_space_waste += btrfs_leaf_free_space(eb);
} else {
free_nrs = (BTRFS_NODEPTRS_PER_BLOCK(root->fs_info) -
btrfs_header_nritems(eb));
@@ -635,6 +975,10 @@ int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
goto out;
}
if (stage == 1) {
+ ret = btrfs_unlink(trans, root, ino, dir_ino, index, name,
+ name_len, 0);
+ if (ret)
+ goto out;
ret = btrfs_add_link(trans, root, ino, dir_ino, name, name_len,
filetype, &index, 1, 1);
goto out;
@@ -921,14 +1265,13 @@ next:
* @namelen: the length of name in the INODE_REF/INODE_EXTREF
* @index_ret: the index in the INODE_REF/INODE_EXTREF,
* value (64)-1 means do not check index
- * @ext_ref: the EXTENDED_IREF feature
*
* Return 0 if no error occurred.
* Return >0 for error bitmap
*/
static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
- char *name, int namelen, u64 *index_ret,
- unsigned int ext_ref)
+ char *name, int namelen, u64 *index_ret)
+
{
struct btrfs_path path;
struct btrfs_inode_ref *ref;
@@ -1001,8 +1344,9 @@ next_ref:
}
extref:
+
/* Skip if not support EXTENDED_IREF feature */
- if (!ext_ref)
+ if (!btrfs_fs_incompat(root->fs_info, EXTENDED_IREF))
goto out;
btrfs_release_path(&path);
@@ -1188,14 +1532,12 @@ static void print_dir_item_err(struct btrfs_root *root, struct btrfs_key *key,
* @key: the key of the INODE_REF/INODE_EXTREF
* @path: the path
* @size: the st_size of the INODE_ITEM
- * @ext_ref: the EXTENDED_IREF feature
*
* Return 0 if no error occurred.
* Return DIR_COUNT_AGAIN if the isize of the inode should be recalculated.
*/
static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
- struct btrfs_path *path, u64 *size,
- unsigned int ext_ref)
+ struct btrfs_path *path, u64 *size)
{
struct btrfs_dir_item *di;
struct btrfs_inode_item *ii;
@@ -1217,14 +1559,6 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
int tmp_err;
int need_research = 0;
- /*
- * For DIR_ITEM set index to (u64)-1, so that find_inode_ref
- * ignore index check.
- */
- if (di_key->type == BTRFS_DIR_INDEX_KEY)
- index = di_key->offset;
- else
- index = (u64)-1;
begin:
err = 0;
cur = 0;
@@ -1254,6 +1588,15 @@ begin:
memset(namebuf, 0, sizeof(namebuf) / sizeof(*namebuf));
while (cur < total) {
+ /*
+ * For DIR_ITEM set index to (u64)-1, so that find_inode_ref
+ * ignore index check.
+ */
+ if (di_key->type == BTRFS_DIR_INDEX_KEY)
+ index = di_key->offset;
+ else
+ index = (u64)-1;
+
data_len = btrfs_dir_data_len(node, di);
tmp_err = 0;
if (data_len)
@@ -1311,8 +1654,7 @@ begin:
key.objectid = location.objectid;
key.type = BTRFS_INODE_REF_KEY;
key.offset = di_key->objectid;
- tmp_err |= find_inode_ref(root, &key, namebuf, len,
- &index, ext_ref);
+ tmp_err |= find_inode_ref(root, &key, namebuf, len, &index);
/* check relative INDEX/ITEM */
key.objectid = di_key->objectid;
@@ -1921,13 +2263,10 @@ static bool has_orphan_item(struct btrfs_root *root, u64 ino)
* 2. check inode ref/extref
* 3. check dir item/index
*
- * @ext_ref: the EXTENDED_IREF feature
- *
* Return 0 if no error occurred.
* Return >0 for error or hit the traversal is done(by error bitmap)
*/
-static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
- unsigned int ext_ref)
+static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
{
struct extent_buffer *node;
struct btrfs_inode_item *ii;
@@ -1935,6 +2274,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key last_key;
u64 inode_id;
u32 mode;
+ u64 flags;
u64 nlink;
u64 nbytes;
u64 isize;
@@ -1968,10 +2308,19 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
isize = btrfs_inode_size(node, ii);
nbytes = btrfs_inode_nbytes(node, ii);
mode = btrfs_inode_mode(node, ii);
+ flags = btrfs_inode_flags(node, ii);
dir = imode_to_type(mode) == BTRFS_FT_DIR;
nlink = btrfs_inode_nlink(node, ii);
nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM;
+ if (S_ISLNK(mode) &&
+ flags & (BTRFS_INODE_IMMUTABLE | BTRFS_INODE_APPEND)) {
+ err |= INODE_FLAGS_ERROR;
+ error(
+"symlinks must never have immutable/append flags set, root %llu inode item %llu flags %llu may be corrupted",
+ root->objectid, inode_id, flags);
+ }
+
while (1) {
btrfs_item_key_to_cpu(path->nodes[0], &last_key, path->slots[0]);
ret = btrfs_next_item(root, path);
@@ -1996,6 +2345,9 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
err |= ret;
break;
case BTRFS_INODE_EXTREF_KEY:
+ {
+ bool ext_ref = btrfs_fs_incompat(root->fs_info,
+ EXTENDED_IREF);
if (key.type == BTRFS_INODE_EXTREF_KEY && !ext_ref)
warning("root %llu EXTREF[%llu %llu] isn't supported",
root->objectid, key.objectid,
@@ -2004,6 +2356,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
mode);
err |= ret;
break;
+ }
case BTRFS_DIR_ITEM_KEY:
case BTRFS_DIR_INDEX_KEY:
if (!dir) {
@@ -2012,7 +2365,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
imode_to_type(mode), key.objectid,
key.offset);
}
- ret = check_dir_item(root, &key, path, &size, ext_ref);
+ ret = check_dir_item(root, &key, path, &size);
err |= ret;
break;
case BTRFS_EXTENT_DATA_KEY:
@@ -2143,7 +2496,7 @@ out:
* Returns 0 No errors found
*/
static int process_one_leaf(struct btrfs_root *root, struct btrfs_path *path,
- struct node_refs *nrefs, int *level, int ext_ref)
+ struct node_refs *nrefs, int *level)
{
struct extent_buffer *cur = path->nodes[0];
struct btrfs_key key;
@@ -2174,7 +2527,7 @@ static int process_one_leaf(struct btrfs_root *root, struct btrfs_path *path,
path->slots[0] = i;
again:
- err |= check_inode_item(root, path, ext_ref);
+ err |= check_inode_item(root, path);
/* modify cur since check_inode_item may change path */
cur = path->nodes[0];
@@ -2491,12 +2844,12 @@ out:
*
* Returns error bits after reapir.
*/
-static int repair_extent_data_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int repair_extent_data_item(struct btrfs_root *root,
struct btrfs_path *pathp,
struct node_refs *nrefs,
int err)
{
+ struct btrfs_trans_handle *trans = NULL;
struct btrfs_file_extent_item *fi;
struct btrfs_key fi_key;
struct btrfs_key key;
@@ -2513,6 +2866,7 @@ static int repair_extent_data_item(struct btrfs_trans_handle *trans,
u64 file_offset;
int generation;
int slot;
+ int need_insert = 0;
int ret = 0;
eb = pathp->nodes[0];
@@ -2531,6 +2885,11 @@ static int repair_extent_data_item(struct btrfs_trans_handle *trans,
extent_offset = btrfs_file_extent_offset(eb, fi);
offset = file_offset - extent_offset;
+ if (nrefs->full_backref[0])
+ parent = btrfs_header_bytenr(eb);
+ else
+ parent = 0;
+
/* now repair only adds backref */
if ((err & BACKREF_MISSING) == 0)
return err;
@@ -2546,9 +2905,20 @@ static int repair_extent_data_item(struct btrfs_trans_handle *trans,
ret = -EIO;
goto out;
}
+ need_insert = ret;
+ ret = avoid_extents_overwrite(root->fs_info);
+ if (ret)
+ goto out;
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ trans = NULL;
+ error("fail to start transaction %s", strerror(-ret));
+ goto out;
+ }
/* insert an extent item */
- if (ret > 0) {
+ if (need_insert) {
key.objectid = disk_bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes;
@@ -2572,11 +2942,6 @@ static int repair_extent_data_item(struct btrfs_trans_handle *trans,
btrfs_release_path(&path);
}
- if (nrefs->full_backref[0])
- parent = btrfs_header_bytenr(eb);
- else
- parent = 0;
-
ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, parent,
root->objectid,
parent ? BTRFS_FIRST_FREE_OBJECTID : fi_key.objectid,
@@ -2593,6 +2958,9 @@ static int repair_extent_data_item(struct btrfs_trans_handle *trans,
err &= ~BACKREF_MISSING;
out:
+ if (trans)
+ btrfs_commit_transaction(trans, root);
+ btrfs_release_path(&path);
if (ret)
error("can't repair root %llu extent data item[%llu %llu]",
root->objectid, disk_bytenr, num_bytes);
@@ -3326,40 +3694,55 @@ out:
* means error after repair
* Returns 0 nothing happened
*/
-static int repair_extent_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
+static int repair_extent_item(struct btrfs_root *root, struct btrfs_path *path,
u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
u64 owner, u64 offset, int err)
{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_key old_key;
int freed = 0;
int ret;
btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]);
- if (err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) {
- /* delete the backref */
- ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr,
- num_bytes, parent, root_objectid, owner, offset);
- if (!ret) {
- freed = 1;
- err &= ~REFERENCER_MISSING;
- printf("Delete backref in extent [%llu %llu]\n",
- bytenr, num_bytes);
- } else {
- error("fail to delete backref in extent [%llu %llu]",
- bytenr, num_bytes);
- }
+ if ((err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) == 0)
+ return err;
+
+ ret = avoid_extents_overwrite(root->fs_info);
+ if (ret)
+ return err;
+
+ trans = btrfs_start_transaction(extent_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("fail to start transaction %s", strerror(-ret));
+ /* nothing happened */
+ ret = 0;
+ goto out;
+ }
+ /* delete the backref */
+ ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr,
+ num_bytes, parent, root_objectid, owner, offset);
+ if (!ret) {
+ freed = 1;
+ err &= ~REFERENCER_MISSING;
+ printf("Delete backref in extent [%llu %llu]\n",
+ bytenr, num_bytes);
+ } else {
+ error("fail to delete backref in extent [%llu %llu]",
+ bytenr, num_bytes);
}
+ btrfs_commit_transaction(trans, extent_root);
/* btrfs_free_extent may delete the extent */
btrfs_release_path(path);
ret = btrfs_search_slot(NULL, root, &old_key, path, 0, 0);
-
if (ret)
ret = -ENOENT;
else if (freed)
ret = err;
+out:
return ret;
}
@@ -3369,8 +3752,7 @@ static int repair_extent_item(struct btrfs_trans_handle *trans,
*
* Since we don't use extent_record anymore, introduce new error bit
*/
-static int check_extent_item(struct btrfs_trans_handle *trans,
- struct btrfs_fs_info *fs_info,
+static int check_extent_item(struct btrfs_fs_info *fs_info,
struct btrfs_path *path)
{
struct btrfs_extent_item *ei;
@@ -3501,7 +3883,7 @@ next:
}
if (err && repair) {
- ret = repair_extent_item(trans, fs_info->extent_root, path,
+ ret = repair_extent_item(fs_info->extent_root, path,
key.objectid, num_bytes, parent, root_objectid,
owner, owner_offset, ret);
if (ret < 0)
@@ -3764,13 +4146,14 @@ out:
*
* Returns error after repair.
*/
-static int repair_chunk_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *chunk_root,
+static int repair_chunk_item(struct btrfs_root *chunk_root,
struct btrfs_path *path, int err)
{
struct btrfs_chunk *chunk;
struct btrfs_key chunk_key;
struct extent_buffer *eb = path->nodes[0];
+ struct btrfs_root *extent_root = chunk_root->fs_info->extent_root;
+ struct btrfs_trans_handle *trans;
u64 length;
int slot = path->slots[0];
u64 type;
@@ -3783,31 +4166,55 @@ static int repair_chunk_item(struct btrfs_trans_handle *trans,
type = btrfs_chunk_type(path->nodes[0], chunk);
length = btrfs_chunk_length(eb, chunk);
- if (err & REFERENCER_MISSING) {
- ret = btrfs_make_block_group(trans, chunk_root->fs_info, 0,
- type, chunk_key.offset, length);
- if (ret) {
- error("fail to add block group item[%llu %llu]",
- chunk_key.offset, length);
- goto out;
- } else {
- err &= ~REFERENCER_MISSING;
- printf("Added block group item[%llu %llu]\n",
- chunk_key.offset, length);
- }
+ /* now repair only adds block group */
+ if ((err & REFERENCER_MISSING) == 0)
+ return err;
+
+ ret = avoid_extents_overwrite(chunk_root->fs_info);
+ if (ret)
+ return ret;
+
+ trans = btrfs_start_transaction(extent_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("fail to start transaction %s", strerror(-ret));
+ return ret;
}
-out:
+ ret = btrfs_make_block_group(trans, chunk_root->fs_info, 0, type,
+ chunk_key.offset, length);
+ if (ret) {
+ error("fail to add block group item [%llu %llu]",
+ chunk_key.offset, length);
+ } else {
+ err &= ~REFERENCER_MISSING;
+ printf("Added block group item[%llu %llu]\n", chunk_key.offset,
+ length);
+ }
+
+ btrfs_commit_transaction(trans, extent_root);
+ if (ret)
+ error("fail to repair item(s) related to chunk item [%llu %llu]",
+ chunk_key.objectid, chunk_key.offset);
return err;
}
-static int delete_extent_tree_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int delete_extent_tree_item(struct btrfs_root *root,
struct btrfs_path *path)
{
struct btrfs_key key;
+ struct btrfs_trans_handle *trans;
int ret = 0;
+ ret = avoid_extents_overwrite(root->fs_info);
+ if (ret)
+ return ret;
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("fail to start transaction %s", strerror(-ret));
+ goto out;
+ }
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
btrfs_release_path(path);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
@@ -3825,6 +4232,7 @@ static int delete_extent_tree_item(struct btrfs_trans_handle *trans,
else
path->slots[0]--;
out:
+ btrfs_commit_transaction(trans, root);
if (ret)
error("failed to delete root %llu item[%llu, %u, %llu]",
root->objectid, key.objectid, key.type, key.offset);
@@ -3837,8 +4245,7 @@ out:
/*
* Main entry function to check known items and update related accounting info
*/
-static int check_leaf_items(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
+static int check_leaf_items(struct btrfs_root *root, struct btrfs_path *path,
struct node_refs *nrefs, int account_bytes)
{
struct btrfs_fs_info *fs_info = root->fs_info;
@@ -3869,15 +4276,14 @@ again:
case BTRFS_EXTENT_DATA_KEY:
ret = check_extent_data_item(root, path, nrefs, account_bytes);
if (repair && ret)
- ret = repair_extent_data_item(trans, root, path, nrefs,
- ret);
+ ret = repair_extent_data_item(root, path, nrefs, ret);
err |= ret;
break;
case BTRFS_BLOCK_GROUP_ITEM_KEY:
ret = check_block_group_item(fs_info, eb, slot);
if (repair &&
ret & REFERENCER_MISSING)
- ret = delete_extent_tree_item(trans, root, path);
+ ret = delete_extent_tree_item(root, path);
err |= ret;
break;
case BTRFS_DEV_ITEM_KEY:
@@ -3887,7 +4293,7 @@ again:
case BTRFS_CHUNK_ITEM_KEY:
ret = check_chunk_item(fs_info, eb, slot);
if (repair && ret)
- ret = repair_chunk_item(trans, root, path, ret);
+ ret = repair_chunk_item(root, path, ret);
err |= ret;
break;
case BTRFS_DEV_EXTENT_KEY:
@@ -3896,7 +4302,7 @@ again:
break;
case BTRFS_EXTENT_ITEM_KEY:
case BTRFS_METADATA_ITEM_KEY:
- ret = check_extent_item(trans, fs_info, path);
+ ret = check_extent_item(fs_info, path);
err |= ret;
break;
case BTRFS_EXTENT_CSUM_KEY:
@@ -3908,7 +4314,7 @@ again:
key.objectid, -1);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
+ ret = delete_extent_tree_item(root, path);
err |= ret;
break;
case BTRFS_EXTENT_DATA_REF_KEY:
@@ -3921,7 +4327,7 @@ again:
btrfs_extent_data_ref_count(eb, dref));
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
+ ret = delete_extent_tree_item(root, path);
err |= ret;
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
@@ -3929,7 +4335,7 @@ again:
key.objectid, -1);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
+ ret = delete_extent_tree_item(root, path);
err |= ret;
break;
case BTRFS_SHARED_DATA_REF_KEY:
@@ -3937,7 +4343,7 @@ again:
key.objectid);
if (repair &&
ret & (REFERENCER_MISMATCH | REFERENCER_MISSING))
- ret = delete_extent_tree_item(trans, root, path);
+ ret = delete_extent_tree_item(root, path);
err |= ret;
break;
default:
@@ -3959,10 +4365,8 @@ out:
* Returns <0 Fatal error, must exit the whole check
* Returns 0 No errors found
*/
-static int walk_down_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
- int *level, struct node_refs *nrefs, int ext_ref,
- int check_all)
+static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
+ int *level, struct node_refs *nrefs, int check_all)
{
enum btrfs_tree_block_status status;
u64 bytenr;
@@ -4004,7 +4408,7 @@ static int walk_down_tree(struct btrfs_trans_handle *trans,
btrfs_header_owner(cur), nrefs);
if (repair && ret)
- ret = repair_tree_block_ref(trans, root,
+ ret = repair_tree_block_ref(root,
path->nodes[*level], nrefs, *level, ret);
err |= ret;
@@ -4032,10 +4436,9 @@ static int walk_down_tree(struct btrfs_trans_handle *trans,
ret = 0;
if (!check_all)
- ret = process_one_leaf(root, path, nrefs,
- level, ext_ref);
+ ret = process_one_leaf(root, path, nrefs, level);
else
- ret = check_leaf_items(trans, root, path,
+ ret = check_leaf_items(root, path,
nrefs, account_file_data);
err |= ret;
break;
@@ -4200,7 +4603,7 @@ out:
* returns >0 means error
* returns <0 means fatal error
*/
-static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
+static int check_fs_first_inode(struct btrfs_root *root)
{
struct btrfs_path path;
struct btrfs_key key;
@@ -4241,7 +4644,7 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
/* special index value */
index = 0;
- ret = find_inode_ref(root, &key, "..", strlen(".."), &index, ext_ref);
+ ret = find_inode_ref(root, &key, "..", strlen(".."), &index);
if (ret < 0)
goto out;
err |= ret;
@@ -4266,16 +4669,13 @@ out:
* blocks and integrity of fs tree items.
*
* @root: the root of the tree to be checked.
- * @ext_ref feature EXTENDED_IREF is enable or not.
* @account if NOT 0 means check the tree (including tree)'s treeblocks.
* otherwise means check fs tree(s) items relationship and
* @root MUST be a fs tree root.
* Returns 0 represents OK.
- * Returns not 0 represents error.
+ * Returns >0 represents error bits.
*/
-static int check_btrfs_root(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, unsigned int ext_ref,
- int check_all)
+static int check_btrfs_root(struct btrfs_root *root, int check_all)
{
struct btrfs_path path;
struct node_refs nrefs;
@@ -4292,9 +4692,9 @@ static int check_btrfs_root(struct btrfs_trans_handle *trans,
* the first inode item in the leaf, if inode item (256) is
* missing we will skip it forever.
*/
- ret = check_fs_first_inode(root, ext_ref);
+ ret = check_fs_first_inode(root);
if (ret < 0)
- return ret;
+ return FATAL_ERROR;
}
@@ -4319,14 +4719,13 @@ static int check_btrfs_root(struct btrfs_trans_handle *trans,
}
while (1) {
- ret = walk_down_tree(trans, root, &path, &level, &nrefs,
- ext_ref, check_all);
-
- err |= !!ret;
+ ret = walk_down_tree(root, &path, &level, &nrefs, check_all);
+ if (ret > 0)
+ err |= ret;
/* if ret is negative, walk shall stop */
if (ret < 0) {
- ret = err;
+ ret = err | FATAL_ERROR;
break;
}
@@ -4347,15 +4746,14 @@ out:
* Iterate all items in the tree and call check_inode_item() to check.
*
* @root: the root of the tree to be checked.
- * @ext_ref: the EXTENDED_IREF feature
*
* Return 0 if no error found.
* Return <0 for error.
*/
-static int check_fs_root(struct btrfs_root *root, unsigned int ext_ref)
+static int check_fs_root(struct btrfs_root *root)
{
reset_cached_block_groups(root->fs_info);
- return check_btrfs_root(NULL, root, ext_ref, 0);
+ return check_btrfs_root(root, 0);
}
/*
@@ -4464,13 +4862,10 @@ int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info)
struct btrfs_path path;
struct btrfs_key key;
struct extent_buffer *node;
- unsigned int ext_ref;
int slot;
int ret;
int err = 0;
- ext_ref = btrfs_fs_incompat(fs_info, EXTENDED_IREF);
-
btrfs_init_path(&path);
key.objectid = BTRFS_FS_TREE_OBJECTID;
key.offset = 0;
@@ -4508,7 +4903,7 @@ int check_fs_roots_lowmem(struct btrfs_fs_info *fs_info)
goto next;
}
- ret = check_fs_root(cur_root, ext_ref);
+ ret = check_fs_root(cur_root);
err |= ret;
if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
@@ -4538,7 +4933,6 @@ out:
*/
int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info)
{
- struct btrfs_trans_handle *trans = NULL;
struct btrfs_path path;
struct btrfs_key old_key;
struct btrfs_key key;
@@ -4550,20 +4944,12 @@ int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info)
root = fs_info->fs_root;
- if (repair) {
- trans = btrfs_start_transaction(fs_info->extent_root, 1);
- if (IS_ERR(trans)) {
- error("failed to start transaction before check");
- return PTR_ERR(trans);
- }
- }
-
root1 = root->fs_info->chunk_root;
- ret = check_btrfs_root(trans, root1, 0, 1);
+ ret = check_btrfs_root(root1, 1);
err |= ret;
root1 = root->fs_info->tree_root;
- ret = check_btrfs_root(trans, root1, 0, 1);
+ ret = check_btrfs_root(root1, 1);
err |= ret;
btrfs_init_path(&path);
@@ -4594,7 +4980,7 @@ int check_chunks_and_extents_lowmem(struct btrfs_fs_info *fs_info)
goto next;
}
- ret = check_btrfs_root(trans, cur_root, 0, 1);
+ ret = check_btrfs_root(cur_root, 1);
err |= ret;
if (key.objectid == BTRFS_TREE_RELOC_OBJECTID)
@@ -4612,19 +4998,21 @@ next:
}
out:
- /* if repair, update block accounting */
if (repair) {
- ret = btrfs_fix_block_accounting(trans, root);
+ ret = end_avoid_extents_overwrite(fs_info);
+ if (ret < 0)
+ ret = FATAL_ERROR;
+ err |= ret;
+
+ reset_cached_block_groups(fs_info);
+ /* update block accounting */
+ ret = repair_block_accounting(fs_info);
if (ret)
err |= ret;
else
err &= ~BG_ACCOUNTING_ERROR;
}
- if (trans)
- btrfs_commit_transaction(trans, root->fs_info->extent_root);
-
btrfs_release_path(&path);
-
return err;
}