/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BCACHEFS_SB_COUNTERS_H
#define _BCACHEFS_SB_COUNTERS_H

#include "bcachefs.h"
#include "sb/io.h"

int bch2_sb_counters_to_cpu(struct bch_fs *);
int bch2_sb_counters_from_cpu(struct bch_fs *);

void bch2_fs_counters_exit(struct bch_fs *);
void bch2_fs_counters_init_early(struct bch_fs *);
int bch2_fs_counters_init(struct bch_fs *);
int bch2_fs_counters_init_late(struct bch_fs *);

void bch2_counter_reset(struct bch_fs *, unsigned);

extern const char * const bch2_counter_names[];
extern const enum bch_counters_flags bch2_counter_flags_map[];
extern const u16 bch2_counter_stable_map[];
extern const struct bch_sb_field_ops bch_sb_field_ops_counters;

long bch2_ioctl_query_counters(struct bch_fs *,
			struct bch_ioctl_query_counters __user *);

void bch2_sb_recent_counters_to_text(struct printbuf *out, struct bch_fs_counters *c);

#define counter_typecheck(_name, _type)					\
	BUILD_BUG_ON(bch2_counter_flags[BCH_COUNTER_##_name] != _type)

#define event_inc(_c, _name)						\
do {									\
	counter_typecheck(_name, TYPE_COUNTER);				\
	this_cpu_inc((_c)->counters.now[BCH_COUNTER_##_name]);		\
} while (0)

#define event_add(_c, _name, _nr)					\
do {									\
	counter_typecheck(_name, TYPE_SECTORS);				\
	this_cpu_add((_c)->counters.now[BCH_COUNTER_##_name], _nr);	\
} while (0)

#define __event_trace(_c, _name, _buf, ...)				\
do {									\
	CLASS(printbuf, _buf)();					\
	printbuf_indent_add_nextline(&_buf, 2);				\
	__VA_ARGS__;							\
	trace_##_name(_c, _buf.buf);					\
} while (0)

#define event_trace(_c, _name, _buf, ...)				\
do {									\
	if (trace_##_name##_enabled()) {				\
		__event_trace(_c, _name, _buf, __VA_ARGS__);		\
	}								\
} while (0)

#define event_add_trace(_c, _name, _nr, ...)				\
do {									\
	event_trace(_c, _name, __VA_ARGS__);				\
	event_add(_c, _name, _nr);					\
} while (0)

#define event_inc_trace(_c, _name, ...)					\
do {									\
	event_trace(_c, _name, __VA_ARGS__);				\
	event_inc(_c, _name);						\
} while (0)

/*
 * _fn variants: the tracepoint body is supplied as a function-call expression,
 * so the call site's if-block contains nothing but the call instruction. The
 * called function (typically __cold __noinline) does the printbuf+trace work
 * out-of-line, keeping the hot path tight in icache regardless of whether
 * the compiler's cold-partition pass honors the asm-goto-derived probability.
 */
#define event_trace_fn(_c, _name, _fn_call)				\
do {									\
	if (trace_##_name##_enabled())					\
		_fn_call;						\
} while (0)

#define event_add_trace_fn(_c, _name, _nr, _fn_call)			\
do {									\
	event_trace_fn(_c, _name, _fn_call);				\
	event_add(_c, _name, _nr);					\
} while (0)

#define event_inc_trace_fn(_c, _name, _fn_call)				\
do {									\
	event_trace_fn(_c, _name, _fn_call);				\
	event_inc(_c, _name);						\
} while (0)

#endif // _BCACHEFS_SB_COUNTERS_H
