return true;
}
+/* No open or draining run covers @bytenr any more. */
+static bool stripe_log_range_settled(struct btrfs_block_group *bg, u64 bytenr)
+{
+ struct btrfs_open_stripe_run *run;
+ unsigned long flags;
+ bool settled = true;
+
+ spin_lock_irqsave(&bg->stripe_run_lock, flags);
+ list_for_each_entry(run, &bg->open_stripe_runs, list) {
+ if (bytenr >= run->start && bytenr < run->offset) {
+ settled = false;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
+ return settled;
+}
+
+/*
+ * A log commit is about to make the extent at [bytenr, bytenr + num_bytes)
+ * durable. Once the log super lands, no later write may extend or RMW the
+ * extent's stripes -- the log-window analogue of invariant I2, which the
+ * commit-time retirement provides for full commits. Close the open run
+ * covering the extent so nothing further allocates into its stripes, kick
+ * any parked partial writes for it, and wait for all data IO into it to
+ * finish. Extents in full or already-settled stripes cost one list walk.
+ *
+ * The drain is bounded: run inflight is counted from allocation, which
+ * happens during writeback with submission following in the same pass, so
+ * the wait is bio flight time plus the parked-write deadline that the flush
+ * below short-circuits. It is a pure data-IO wait (no transaction joins),
+ * safe under the inode log mutex and a running transaction handle.
+ */
+void btrfs_log_settle_stripes(struct btrfs_fs_info *fs_info, u64 bytenr,
+ u64 num_bytes)
+{
+ struct btrfs_block_group *bg;
+ struct btrfs_open_stripe_run *run;
+ unsigned long flags;
+ u64 tail_start = 0;
+ u64 tail_len = 0;
+ u64 flush_start = 0;
+ u64 flush_len = 0;
+
+ if (list_empty_careful(&fs_info->open_stripe_bgs))
+ return;
+ bg = btrfs_lookup_block_group(fs_info, bytenr);
+ if (!bg)
+ return;
+
+ spin_lock_irqsave(&bg->stripe_run_lock, flags);
+ list_for_each_entry(run, &bg->open_stripe_runs, list) {
+ if (bytenr < run->start || bytenr >= run->offset)
+ continue;
+ flush_start = run->start;
+ flush_len = run->end - run->start;
+ if (run->open) {
+ int class;
+ int band;
+
+ for (class = 0; class < BTRFS_STRIPE_RUN_NR_CLASSES; class++)
+ for (band = 0; band < BTRFS_STRIPE_RUN_NR_BANDS; band++)
+ if (bg->open_stripe[class][band] == run)
+ bg->open_stripe[class][band] = NULL;
+ tail_len = close_open_stripe_run(bg, run, &tail_start);
+ }
+ break;
+ }
+ spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
+
+ if (tail_len)
+ btrfs_add_free_space(bg, tail_start, tail_len);
+ if (flush_len) {
+ btrfs_flush_parked_rbios(fs_info, flush_start, flush_len);
+ wait_var_event(&bg->open_stripe_runs,
+ stripe_log_range_settled(bg, bytenr));
+ }
+ btrfs_put_block_group(bg);
+}
+
/*
* Can stripe-exclusive allocation be enabled on this filesystem? Shared by
* the mount option validation and the "stripe_alloc" filesystem property.
inode->last_reflink_trans < trans->transid)
continue;
+ /*
+ * New data extent made durable by this log commit: settle its
+ * stripes. This must not hide behind the csum skips below --
+ * a nodatasum inode's extents need their stripes settled all
+ * the same. Inline extents and holes have no stripes.
+ */
+ if (!is_old_extent &&
+ btrfs_file_extent_type(src, extent) == BTRFS_FILE_EXTENT_REG) {
+ disk_bytenr = btrfs_file_extent_disk_bytenr(src, extent);
+ disk_num_bytes =
+ btrfs_file_extent_disk_num_bytes(src, extent);
+ if (disk_bytenr)
+ btrfs_log_settle_stripes(trans->fs_info,
+ disk_bytenr,
+ disk_num_bytes);
+ }
+
if (skip_csum)
goto add_to_batch;
block_len = em->disk_num_bytes;
compress_type = btrfs_extent_map_compression(em);
+
+ /*
+ * This log commit makes the extent durable: settle its stripes so no
+ * later write in this transaction can tear them (see
+ * btrfs_log_settle_stripes()). Preallocated extents carry no data
+ * and holes have no stripes; both are skipped.
+ */
+ if (!(em->flags & EXTENT_FLAG_PREALLOC) &&
+ em->disk_bytenr < EXTENT_MAP_LAST_BYTE)
+ btrfs_log_settle_stripes(inode->root->fs_info, em->disk_bytenr,
+ em->disk_num_bytes);
+
if (compress_type != BTRFS_COMPRESS_NONE) {
btrfs_set_stack_file_extent_disk_bytenr(&fi, block_start);
btrfs_set_stack_file_extent_disk_num_bytes(&fi, block_len);