if (return_free_space)
btrfs_add_free_space(cache, start, len);
+ /*
+ * The rbio stripe cache may still remember this range's
+ * pre-free contents; a later sub-stripe write into the
+ * reused stripes would steal those pages and generate
+ * parity from them. Drop the stale entries now that the
+ * extents are gone.
+ */
+ if (cache->flags & BTRFS_BLOCK_GROUP_RAID56_MASK)
+ btrfs_raid56_uncache_range(fs_info, start, len);
+
start += len;
total_unpinned += len;
space_info = cache->space_info;
spin_unlock(&table->cache_lock);
}
+/*
+ * Drop cached rbios whose full stripes overlap [start, start + len).
+ *
+ * Called when extents in that range are freed. A cached rbio holds an
+ * in-memory copy of its stripe's data sectors; once the extents are gone
+ * the space can be reallocated, and a later sub-stripe write to the same
+ * stripe would steal the cached pages (see steal_rbio()) and compute
+ * parity from the cache's memory of the stripe's previous life instead
+ * of reading the disk. If the on-disk content diverged from that memory
+ * in the meantime -- for example a device error corrupted a column that
+ * no longer had live data, so nothing noticed -- the write produces
+ * parity that disagrees with the on-disk columns, and the stripe is
+ * latently unreconstructible: every read of the live data passes csum,
+ * scrub sees nothing wrong, and the loss only surfaces when a device
+ * failure forces reconstruction. Freed extents' cache entries have no
+ * value to keep: drop them.
+ */
+void btrfs_raid56_uncache_range(struct btrfs_fs_info *info, u64 start, u64 len)
+{
+ struct btrfs_stripe_hash_table *table = info->stripe_hash_table;
+ struct btrfs_raid_bio *rbio;
+ struct btrfs_raid_bio *tmp;
+
+ if (!table)
+ return;
+
+ spin_lock(&table->cache_lock);
+ list_for_each_entry_safe(rbio, tmp, &table->stripe_cache, stripe_cache) {
+ const u64 rbio_start = rbio->bioc->full_stripe_logical;
+ const u64 rbio_len = (u64)rbio->nr_data << BTRFS_STRIPE_LEN_SHIFT;
+
+ if (rbio_start < start + len && start < rbio_start + rbio_len)
+ __remove_rbio_from_cache(rbio);
+ }
+ spin_unlock(&table->cache_lock);
+}
+
/*
* remove everything in the cache
*/