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.c294
1 files changed, 206 insertions, 88 deletions
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 66da4531..6fb397ab 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -284,7 +284,8 @@ static int modify_block_groups_cache(struct btrfs_fs_info *fs_info, u64 flags,
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));
+ errno = -ret;
+ error("fail to search block groups due to %m");
goto out;
}
@@ -341,19 +342,22 @@ static int create_chunk_and_block_group(struct btrfs_fs_info *fs_info,
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- error("error starting transaction %s", strerror(-ret));
+ errno = -ret;
+ error("error starting transaction %m");
return ret;
}
ret = btrfs_alloc_chunk(trans, fs_info, start, nbytes, flags);
if (ret) {
- error("fail to allocate new chunk %s", strerror(-ret));
+ errno = -ret;
+ error("fail to allocate new chunk %m");
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));
+ errno = -ret;
+ error("fail to make block group for chunk %llu %llu %m",
+ *start, *nbytes);
goto out;
}
out:
@@ -521,8 +525,10 @@ static int avoid_extents_overwrite(struct btrfs_fs_info *fs_info)
"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));
+ if (ret) {
+ errno = -ret;
+ error("failed to avoid extents overwrite %m");
+ }
return ret;
}
@@ -552,7 +558,8 @@ static int repair_block_accounting(struct btrfs_fs_info *fs_info)
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
return ret;
}
@@ -629,7 +636,8 @@ static int repair_tree_block_ref(struct btrfs_root *root,
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
trans = NULL;
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
goto out;
}
/* insert an extent item */
@@ -701,9 +709,10 @@ out:
btrfs_commit_transaction(trans, extent_root);
btrfs_release_path(&path);
if (ret) {
+ errno = -ret;
error(
- "failed to repair tree block ref start %llu root %llu due to %s",
- bytenr, root->objectid, strerror(-ret));
+ "failed to repair tree block ref start %llu root %llu due to %m",
+ bytenr, root->objectid);
} else {
printf("Added one tree block ref start %llu %s %llu\n",
bytenr, parent ? "parent" : "root",
@@ -944,7 +953,7 @@ out:
* returns 0 means success.
* returns not 0 means on error;
*/
-int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
+static int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
u64 index, char *name, int name_len, u8 filetype,
int err)
{
@@ -1735,23 +1744,163 @@ static int punch_extent_hole(struct btrfs_root *root, u64 ino, u64 start,
return ret;
}
+static int repair_inline_ram_bytes(struct btrfs_root *root,
+ struct btrfs_path *path, u64 *ram_bytes_ret)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_key key;
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_item *item;
+ u32 on_disk_data_len;
+ int ret;
+ int recover_ret;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ return ret;
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_release_path(path);
+ ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ /* Not really possible */
+ if (ret > 0) {
+ ret = -ENOENT;
+ btrfs_release_path(path);
+ goto recover;
+ }
+ if (ret < 0)
+ goto recover;
+
+ item = btrfs_item_nr(path->slots[0]);
+ on_disk_data_len = btrfs_file_extent_inline_item_len(path->nodes[0],
+ item);
+
+ fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(path->nodes[0], fi) !=
+ BTRFS_FILE_EXTENT_INLINE ||
+ btrfs_file_extent_compression(path->nodes[0], fi) !=
+ BTRFS_COMPRESS_NONE)
+ return -EINVAL;
+ btrfs_set_file_extent_ram_bytes(path->nodes[0], fi, on_disk_data_len);
+ btrfs_mark_buffer_dirty(path->nodes[0]);
+
+ ret = btrfs_commit_transaction(trans, root);
+ if (!ret) {
+ printf(
+ "Successfully repaired inline ram_bytes for root %llu ino %llu\n",
+ root->objectid, key.objectid);
+ *ram_bytes_ret = on_disk_data_len;
+ }
+ return ret;
+
+recover:
+ /*
+ * COW search failed, mostly due to the extra COW work (extent
+ * allocation, etc). Since we have a good path from before, readonly
+ * search should still work, or later checks will fail due to empty
+ * path.
+ */
+ recover_ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+
+ /* This really shouldn't happen, or we have a big problem */
+ ASSERT(recover_ret == 0);
+ return ret;
+}
+
+static int check_file_extent_inline(struct btrfs_root *root,
+ struct btrfs_path *path, u64 *size,
+ u64 *end)
+{
+ u32 max_inline_extent_size = min_t(u32, root->fs_info->sectorsize - 1,
+ BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info));
+ struct extent_buffer *node = path->nodes[0];
+ struct btrfs_item *e = btrfs_item_nr(path->slots[0]);
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_key fkey;
+ u64 extent_num_bytes;
+ u32 item_inline_len;
+ int ret;
+ int compressed = 0;
+ int err = 0;
+
+ fi = btrfs_item_ptr(node, path->slots[0], struct btrfs_file_extent_item);
+ item_inline_len = btrfs_file_extent_inline_item_len(node, e);
+ extent_num_bytes = btrfs_file_extent_ram_bytes(node, fi);
+ compressed = btrfs_file_extent_compression(node, fi);
+ btrfs_item_key_to_cpu(node, &fkey, path->slots[0]);
+
+ if (extent_num_bytes == 0) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
+ root->objectid, fkey.objectid, fkey.offset);
+ err |= FILE_EXTENT_ERROR;
+ }
+
+ if (compressed) {
+ if (extent_num_bytes > root->fs_info->sectorsize) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] too large inline extent ram size, have %llu, max: %u",
+ root->objectid, fkey.objectid, fkey.offset,
+ extent_num_bytes, root->fs_info->sectorsize - 1);
+ err |= FILE_EXTENT_ERROR;
+ }
+
+ if (item_inline_len > max_inline_extent_size) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] too large inline extent on-disk size, have %u, max: %u",
+ root->objectid, fkey.objectid, fkey.offset,
+ item_inline_len, max_inline_extent_size);
+ err |= FILE_EXTENT_ERROR;
+ }
+ } else {
+ if (extent_num_bytes > max_inline_extent_size) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] too large inline extent size, have %llu, max: %u",
+ root->objectid, fkey.objectid, fkey.offset,
+ extent_num_bytes, max_inline_extent_size);
+ err |= FILE_EXTENT_ERROR;
+ }
+
+ if (extent_num_bytes != item_inline_len) {
+ error(
+"root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
+ root->objectid, fkey.objectid, fkey.offset,
+ extent_num_bytes, item_inline_len);
+ if (repair) {
+ ret = repair_inline_ram_bytes(root, path,
+ &extent_num_bytes);
+ if (ret)
+ err |= FILE_EXTENT_ERROR;
+ } else {
+ err |= FILE_EXTENT_ERROR;
+ }
+ }
+ }
+ *end += extent_num_bytes;
+ *size += extent_num_bytes;
+
+ return err;
+}
+
/*
* Check file extent datasum/hole, update the size of the file extents,
* check and update the last offset of the file extent.
*
* @root: the root of fs/file tree.
- * @fkey: the key of the file extent.
* @nodatasum: INODE_NODATASUM feature.
* @size: the sum of all EXTENT_DATA items size for this inode.
* @end: the offset of the last extent.
*
* Return 0 if no error occurred.
*/
-static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
- struct extent_buffer *node, int slot,
+static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path,
unsigned int nodatasum, u64 *size, u64 *end)
{
struct btrfs_file_extent_item *fi;
+ struct btrfs_key fkey;
+ struct extent_buffer *node = path->nodes[0];
u64 disk_bytenr;
u64 disk_num_bytes;
u64 extent_num_bytes;
@@ -1759,78 +1908,31 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
u64 csum_found; /* In byte size, sectorsize aligned */
u64 search_start; /* Logical range start we search for csum */
u64 search_len; /* Logical range len we search for csum */
- u32 max_inline_extent_size = min_t(u32, root->fs_info->sectorsize - 1,
- BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info));
unsigned int extent_type;
unsigned int is_hole;
+ int slot = path->slots[0];
int compressed = 0;
int ret;
int err = 0;
+ btrfs_item_key_to_cpu(node, &fkey, slot);
fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
-
- /* Check inline extent */
extent_type = btrfs_file_extent_type(node, fi);
- if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
- struct btrfs_item *e = btrfs_item_nr(slot);
- u32 item_inline_len;
-
- item_inline_len = btrfs_file_extent_inline_item_len(node, e);
- extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi);
- compressed = btrfs_file_extent_compression(node, fi);
- if (extent_num_bytes == 0) {
- error(
- "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
- root->objectid, fkey->objectid, fkey->offset);
- err |= FILE_EXTENT_ERROR;
- }
- if (compressed) {
- if (extent_num_bytes > root->fs_info->sectorsize) {
- error(
-"root %llu EXTENT_DATA[%llu %llu] too large inline extent ram size, have %llu, max: %u",
- root->objectid, fkey->objectid,
- fkey->offset, extent_num_bytes,
- root->fs_info->sectorsize - 1);
- err |= FILE_EXTENT_ERROR;
- }
- if (item_inline_len > max_inline_extent_size) {
- error(
-"root %llu EXTENT_DATA[%llu %llu] too large inline extent on-disk size, have %u, max: %u",
- root->objectid, fkey->objectid,
- fkey->offset, item_inline_len,
- max_inline_extent_size);
- err |= FILE_EXTENT_ERROR;
- }
- } else {
- if (extent_num_bytes > max_inline_extent_size) {
- error(
- "root %llu EXTENT_DATA[%llu %llu] too large inline extent size, have %llu, max: %u",
- root->objectid, fkey->objectid, fkey->offset,
- extent_num_bytes, max_inline_extent_size);
- err |= FILE_EXTENT_ERROR;
- }
- }
- if (!compressed && extent_num_bytes != item_inline_len) {
- error(
- "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
- root->objectid, fkey->objectid, fkey->offset,
- extent_num_bytes, item_inline_len);
- err |= FILE_EXTENT_ERROR;
- }
- *end += extent_num_bytes;
- *size += extent_num_bytes;
- return err;
- }
/* Check extent type */
if (extent_type != BTRFS_FILE_EXTENT_REG &&
- extent_type != BTRFS_FILE_EXTENT_PREALLOC) {
+ extent_type != BTRFS_FILE_EXTENT_PREALLOC &&
+ extent_type != BTRFS_FILE_EXTENT_INLINE) {
err |= FILE_EXTENT_ERROR;
error("root %llu EXTENT_DATA[%llu %llu] type bad",
- root->objectid, fkey->objectid, fkey->offset);
+ root->objectid, fkey.objectid, fkey.offset);
return err;
}
+ /* Check inline extent */
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE)
+ return check_file_extent_inline(root, path, size, end);
+
/* Check REG_EXTENT/PREALLOC_EXTENT */
disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
@@ -1864,12 +1966,12 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
if (csum_found > 0 && nodatasum) {
err |= ODD_CSUM_ITEM;
error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
- root->objectid, fkey->objectid, fkey->offset);
+ root->objectid, fkey.objectid, fkey.offset);
} else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
!is_hole && (ret < 0 || csum_found < search_len)) {
err |= CSUM_ITEM_MISSING;
error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
- root->objectid, fkey->objectid, fkey->offset,
+ root->objectid, fkey.objectid, fkey.offset,
csum_found, search_len);
} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC &&
csum_found > 0) {
@@ -1881,22 +1983,22 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
err |= ODD_CSUM_ITEM;
error(
"root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
- root->objectid, fkey->objectid, fkey->offset,
+ root->objectid, fkey.objectid, fkey.offset,
csum_found);
}
}
/* Check EXTENT_DATA hole */
- if (!no_holes && *end != fkey->offset) {
+ if (!no_holes && *end != fkey.offset) {
if (repair)
- ret = punch_extent_hole(root, fkey->objectid,
- *end, fkey->offset - *end);
+ ret = punch_extent_hole(root, fkey.objectid,
+ *end, fkey.offset - *end);
if (!repair || ret) {
err |= FILE_EXTENT_ERROR;
error(
"root %llu EXTENT_DATA[%llu %llu] gap exists, expected: EXTENT_DATA[%llu %llu]",
- root->objectid, fkey->objectid, fkey->offset,
- fkey->objectid, *end);
+ root->objectid, fkey.objectid, fkey.offset,
+ fkey.objectid, *end);
}
}
@@ -2374,9 +2476,8 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
root->objectid, inode_id, key.objectid,
key.offset);
}
- ret = check_file_extent(root, &key, node, slot,
- nodatasum, &extent_size,
- &extent_end);
+ ret = check_file_extent(root, path, nodatasum,
+ &extent_size, &extent_end);
err |= ret;
break;
case BTRFS_XATTR_ITEM_KEY:
@@ -2535,7 +2636,7 @@ again:
if (err & LAST_ITEM)
goto out;
- /* still have inode items in thie leaf */
+ /* still have inode items in this leaf */
if (cur->start == cur_bytenr)
goto again;
@@ -2573,7 +2674,7 @@ out:
/*
* @level if @level == -1 means extent data item
- * else normal treeblocl.
+ * else normal treeblock.
*/
static int should_check_extent_strictly(struct btrfs_root *root,
struct node_refs *nrefs, int level)
@@ -2914,7 +3015,8 @@ static int repair_extent_data_item(struct btrfs_root *root,
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
trans = NULL;
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
goto out;
}
/* insert an extent item */
@@ -3604,6 +3706,18 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
if (slot >= btrfs_header_nritems(leaf) ||
btrfs_header_owner(leaf) != root_id)
goto next;
+ /*
+ * For tree blocks have been relocated, data backref are
+ * shared instead of keyed. Do not account it.
+ */
+ if (btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
+ /*
+ * skip the leaf to speed up.
+ */
+ slot = btrfs_header_nritems(leaf);
+ goto next;
+ }
+
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid != objectid ||
key.type != BTRFS_EXTENT_DATA_KEY)
@@ -3716,7 +3830,8 @@ static int repair_extent_item(struct btrfs_root *root, struct btrfs_path *path,
trans = btrfs_start_transaction(extent_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
/* nothing happened */
ret = 0;
goto out;
@@ -4177,7 +4292,8 @@ static int repair_chunk_item(struct btrfs_root *chunk_root,
trans = btrfs_start_transaction(extent_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
return ret;
}
@@ -4212,7 +4328,8 @@ static int delete_extent_tree_item(struct btrfs_root *root,
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
- error("fail to start transaction %s", strerror(-ret));
+ errno = -ret;
+ error("fail to start transaction: %m");
goto out;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
@@ -4719,6 +4836,7 @@ static int check_btrfs_root(struct btrfs_root *root, int check_all)
}
while (1) {
+ ctx.item_count++;
ret = walk_down_tree(root, &path, &level, &nrefs, check_all);
if (ret > 0)