atomic64_t meta_rmw_cur;
atomic64_t meta_rmw_old;
atomic64_t meta_rmw_free;
+ atomic64_t congestion_short;
} stripe_park_stats;
/* BTRFS_STRIPE_RMW_* masks; see btrfs_stripe_allow_rmw(). */
u32 stripe_rmw_opt;
*/
u32 stripe_park_timeout_ms;
u32 stripe_park_sync_timeout_ms;
+ /* Parked-rbio backlog above which parks go short; 0 = off. */
+ u32 stripe_park_congestion;
+ /* Live count of currently-parked rbios (the backlog gauge). */
+ atomic_t stripe_parked_now;
/* Private scrub information */
struct mutex scrub_lock;
{
list_del_init(&rbio->parked_node);
clear_bit(RBIO_PARKED_BIT, &rbio->flags);
+ atomic_dec(&rbio->bioc->fs_info->stripe_parked_now);
}
/* Does any bio gathered in @rbio carry a sync hint (a blocked waiter)? */
timeout_ms == sync_timeout_ms)
return false;
+ /*
+ * Park-congestion feedback. stripe_parked_now is the live count of
+ * parked rbios. When it runs ahead of the stripe_park_congestion knob
+ * (0 = off) the park machinery is not clearing its backlog -- merges
+ * are not arriving fast enough, the regime a small-file / partial-write
+ * fill produces -- so collapse this write's deadline to the short sync
+ * value (the sync-park path below flushes at it, skipping the stuck
+ * cap). Negative feedback: the shorter deadline drains the backlog, so
+ * once it falls back under the knob new writes regain the full deadline
+ * and full-stripe batching. No space-state assumption.
+ */
+ {
+ unsigned int congestion =
+ READ_ONCE(fs_info->stripe_park_congestion);
+
+ if (timeout_ms != sync_timeout_ms && sync_timeout_ms != 0 &&
+ congestion &&
+ atomic_read(&fs_info->stripe_parked_now) > (int)congestion) {
+ timeout_ms = sync_timeout_ms;
+ atomic64_inc(&fs_info->stripe_park_stats.congestion_short);
+ }
+ }
+
spin_lock(&table->parked_lock);
set_bit(RBIO_PARKED_BIT, &rbio->flags);
set_bit(RBIO_WAS_PARKED_BIT, &rbio->flags);
rbio->park_stuck_deadline = jiffies +
msecs_to_jiffies(timeout_ms * 10);
list_add_tail(&rbio->parked_node, &table->parked);
+ atomic_inc(&fs_info->stripe_parked_now);
spin_unlock(&table->parked_lock);
atomic64_inc(&fs_info->stripe_park_stats.parked);
queue_delayed_work(system_percpu_wq, &table->parked_work,
"meta_rmw %lld\n"
"meta_rmw_cur %lld\n"
"meta_rmw_old %lld\n"
- "meta_rmw_free %lld\n",
+ "meta_rmw_free %lld\n"
+ "congestion_short %lld\n"
+ "parked_now %d\n",
atomic64_read(&fs_info->stripe_park_stats.parked),
atomic64_read(&fs_info->stripe_park_stats.merged_bytes),
atomic64_read(&fs_info->stripe_park_stats.filled),
atomic64_read(&fs_info->stripe_park_stats.meta_rmw),
atomic64_read(&fs_info->stripe_park_stats.meta_rmw_cur),
atomic64_read(&fs_info->stripe_park_stats.meta_rmw_old),
- atomic64_read(&fs_info->stripe_park_stats.meta_rmw_free));
+ atomic64_read(&fs_info->stripe_park_stats.meta_rmw_free),
+ atomic64_read(&fs_info->stripe_park_stats.congestion_short),
+ atomic_read(&fs_info->stripe_parked_now));
}
BTRFS_ATTR(, stripe_park_stats, btrfs_stripe_park_stats_show);
BTRFS_PARK_TIMEOUT_ATTR(stripe_park_timeout_ms, stripe_park_timeout_ms);
BTRFS_PARK_TIMEOUT_ATTR(stripe_park_sync_timeout_ms, stripe_park_sync_timeout_ms);
+BTRFS_PARK_TIMEOUT_ATTR(stripe_park_congestion, stripe_park_congestion);
BTRFS_ATTR_PTR(, stripe_park_stats),
BTRFS_ATTR_PTR(, stripe_park_timeout_ms),
BTRFS_ATTR_PTR(, stripe_park_sync_timeout_ms),
+ BTRFS_ATTR_PTR(, stripe_park_congestion),
BTRFS_ATTR_PTR(, temp_fsid),
#ifdef CONFIG_BTRFS_EXPERIMENTAL
BTRFS_ATTR_PTR(, offload_csum),