@@ -620,3 +620,132 @@ invalid_arg:
error("invalid parameter for %s", __func__);
return -EINVAL;
}
+
+/*
+ * Scrub one full data stripe of RAID5/6.
+ * This means it will check any data/metadata extent in the data stripe
+ * spcified by @stripe and @stripe_len
+ *
+ * This function will only *CHECK* if the data stripe has any corruption.
+ * Won't repair at this function.
+ *
+ * Return 0 if the full stripe is OK.
+ * Return <0 if any error is found.
+ * Note: Missing csum is not counted as error (NODATACSUM is valid)
+ */
+static int scrub_one_data_stripe(struct btrfs_fs_info *fs_info,
+ struct btrfs_scrub_progress *scrub_ctx,
+ struct scrub_stripe *stripe, u32 stripe_len)
+{
+ struct btrfs_path *path;
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct btrfs_key key;
+ u64 extent_start;
+ u64 extent_len;
+ u64 orig_csum_discards;
+ int ret;
+
+ if (!is_data_stripe(stripe))
+ return -EINVAL;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = stripe->logical + stripe_len;
+ key.offset = 0;
+ key.type = 0;
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ while (1) {
+ struct btrfs_extent_item *ei;
+ struct extent_buffer *eb;
+ char *data;
+ int slot;
+ int metadata = 0;
+ u64 check_start;
+ u64 check_len;
+
+ ret = btrfs_previous_extent_item(extent_root, path, 0);
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+ eb = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(eb, &key, slot);
+ extent_start = key.objectid;
+ ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
+
+ /* tree block scrub */
+ if (key.type == BTRFS_METADATA_ITEM_KEY ||
+ btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+ extent_len = extent_root->fs_info->nodesize;
+ metadata = 1;
+ } else {
+ extent_len = key.offset;
+ metadata = 0;
+ }
+
+ /* Current extent is out of our range, loop comes to end */
+ if (extent_start + extent_len <= stripe->logical)
+ break;
+
+ if (metadata) {
+ /*
+ * Check crossing stripe first, which can't be scrubbed
+ */
+ if (check_crossing_stripes(fs_info, extent_start,
+ extent_root->fs_info->nodesize)) {
+ error("tree block at %llu is crossing stripe boundary, unable to scrub",
+ extent_start);
+ ret = -EIO;
+ goto out;
+ }
+ data = stripe->data + extent_start - stripe->logical;
+ ret = check_tree_mirror(fs_info, scrub_ctx,
+ data, extent_start, 0);
+ /* Any csum/verify error means the stripe is screwed */
+ if (ret < 0) {
+ stripe->csum_mismatch = 1;
+ ret = -EIO;
+ goto out;
+ }
+ ret = 0;
+ continue;
+ }
+ /* Restrict the extent range to fit stripe range */
+ check_start = max(extent_start, stripe->logical);
+ check_len = min(extent_start + extent_len, stripe->logical +
+ stripe_len) - check_start;
+
+ /* Record original csum_discards to detect missing csum case */
+ orig_csum_discards = scrub_ctx->csum_discards;
+
+ data = stripe->data + check_start - stripe->logical;
+ ret = check_data_mirror(fs_info, scrub_ctx, data, check_start,
+ check_len, 0, NULL);
+ /* Csum mismatch, no need to continue anyway*/
+ if (ret < 0) {
+ stripe->csum_mismatch = 1;
+ goto out;
+ }
+ /* Check if there is any missing csum for data */
+ if (scrub_ctx->csum_discards != orig_csum_discards)
+ stripe->csum_missing = 1;
+ /*
+ * Only increase data_extents_scrubbed if we are scrubbing the
+ * tailing part of the data extent
+ */
+ if (extent_start + extent_len <= stripe->logical + stripe_len)
+ scrub_ctx->data_extents_scrubbed++;
+ ret = 0;
+ }
+out:
+ btrfs_free_path(path);
+ return ret;
+}