Commit c7f04fbc authored by Boris Burkov's avatar Boris Burkov Committed by David Sterba
Browse files

btrfs: sysfs: track current commit duration in commit_stats



When debugging/detecting outlier commit stalls, having an indicator that
we are currently in a long commit critical section can be very useful.
Extend the commit_stats sysfs file to also include the current commit
critical section duration.

Since this requires storing the last commit start time, use that rather
than a separate stack variable for storing the finished commit durations
as well.

This also requires slightly moving up the timing of the stats updating
to *inside* the critical section to avoid the transaction T+1 setting
the critical_section_start_time to 0 before transaction T can update its
stats, which would trigger the new ASSERT. This is an improvement in and
of itself, as it makes the stats more accurately represent the true
critical section time. It may be yet better to pull the stats up to where
start_transaction gets unblocked, rather than the next commit, but this
seems like a good enough place as well.

Signed-off-by: default avatarBoris Burkov <boris@bur.io>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 46d54992
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -420,6 +420,8 @@ struct btrfs_commit_stats {
	u64 last_commit_dur;
	/* The total commit duration in ns */
	u64 total_commit_dur;
	/* Start of the last critical section in ns. */
	u64 critical_section_start_time;
};

struct btrfs_fs_info {
+8 −0
Original line number Diff line number Diff line
@@ -1138,13 +1138,21 @@ static ssize_t btrfs_commit_stats_show(struct kobject *kobj,
				       struct kobj_attribute *a, char *buf)
{
	struct btrfs_fs_info *fs_info = to_fs_info(kobj);
	u64 now = ktime_get_ns();
	u64 start_time = fs_info->commit_stats.critical_section_start_time;
	u64 pending = 0;

	if (start_time)
		pending = now - start_time;

	return sysfs_emit(buf,
		"commits %llu\n"
		"cur_commit_ms %llu\n"
		"last_commit_ms %llu\n"
		"max_commit_ms %llu\n"
		"total_commit_ms %llu\n",
		fs_info->commit_stats.commit_count,
		div_u64(pending, NSEC_PER_MSEC),
		div_u64(fs_info->commit_stats.last_commit_dur, NSEC_PER_MSEC),
		div_u64(fs_info->commit_stats.max_commit_dur, NSEC_PER_MSEC),
		div_u64(fs_info->commit_stats.total_commit_dur, NSEC_PER_MSEC));
+9 −9
Original line number Diff line number Diff line
@@ -2163,13 +2163,19 @@ static void add_pending_snapshot(struct btrfs_trans_handle *trans)
	list_add(&trans->pending_snapshot->list, &cur_trans->pending_snapshots);
}

static void update_commit_stats(struct btrfs_fs_info *fs_info, ktime_t interval)
static void update_commit_stats(struct btrfs_fs_info *fs_info)
{
	ktime_t now = ktime_get_ns();
	ktime_t interval = now - fs_info->commit_stats.critical_section_start_time;

	ASSERT(fs_info->commit_stats.critical_section_start_time);

	fs_info->commit_stats.commit_count++;
	fs_info->commit_stats.last_commit_dur = interval;
	fs_info->commit_stats.max_commit_dur =
			max_t(u64, fs_info->commit_stats.max_commit_dur, interval);
	fs_info->commit_stats.total_commit_dur += interval;
	fs_info->commit_stats.critical_section_start_time = 0;
}

int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
@@ -2178,8 +2184,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
	struct btrfs_transaction *cur_trans = trans->transaction;
	struct btrfs_transaction *prev_trans = NULL;
	int ret;
	ktime_t start_time;
	ktime_t interval;

	ASSERT(refcount_read(&trans->use_count) == 1);
	btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP);
@@ -2312,8 +2316,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
	 * Get the time spent on the work done by the commit thread and not
	 * the time spent waiting on a previous commit
	 */
	start_time = ktime_get_ns();

	fs_info->commit_stats.critical_section_start_time = ktime_get_ns();
	extwriter_counter_dec(cur_trans, trans->type);

	ret = btrfs_start_delalloc_flush(fs_info);
@@ -2545,6 +2548,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
	if (ret)
		goto scrub_continue;

	update_commit_stats(fs_info);
	/*
	 * We needn't acquire the lock here because there is no other task
	 * which can change it.
@@ -2581,8 +2585,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)

	trace_btrfs_transaction_commit(fs_info);

	interval = ktime_get_ns() - start_time;

	btrfs_scrub_continue(fs_info);

	if (current->journal_info == trans)
@@ -2590,8 +2592,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)

	kmem_cache_free(btrfs_trans_handle_cachep, trans);

	update_commit_stats(fs_info, interval);

	return ret;

unlock_reloc: