#include <sys/atomic.h>
#include <sys/zfs_events.h>

krwlock_t rz_zev_rwlock;
volatile boolean_t rz_zev_is_active = B_FALSE;

void
rz_zev_init()
{
	rw_init(&rz_zev_rwlock, NULL, RW_DRIVER, NULL);
}

void
rz_zev_fini()
{
	rw_destroy(&rz_zev_rwlock);
}

boolean_t
rz_zev_active(void)
{
	return rz_zev_is_active;
}

void
rz_zev_set_active(boolean_t active)
{
	rz_zev_is_active = active;
}

struct rz_zev_ops_counters {
	/* zfsvfs ops */
	uint64_t	rz_zev_zfs_mount;
	uint64_t	rz_zev_zfs_umount;
	/* zvol ops */
	uint64_t	rz_zev_zvol_write;
	uint64_t	rz_zev_zvol_truncate;
	/* znode ops */
	uint64_t	rz_zev_znode_close_after_update;
	uint64_t	rz_zev_znode_create;
	uint64_t	rz_zev_znode_remove;
	uint64_t	rz_zev_znode_link;
	uint64_t	rz_zev_znode_symlink;
	uint64_t	rz_zev_znode_rename;
	uint64_t	rz_zev_znode_write;
	uint64_t	rz_zev_znode_truncate;
	uint64_t	rz_zev_znode_setattr;
	uint64_t	rz_zev_znode_acl;
	/* general statistics */
	uint64_t	rz_zev_total;
} rz_zev_ops_counters = {
		.rz_zev_zfs_mount                = 0,
		.rz_zev_zfs_umount               = 0,
		.rz_zev_zvol_write               = 0,
		.rz_zev_zvol_truncate            = 0,
		.rz_zev_znode_close_after_update = 0,
		.rz_zev_znode_create             = 0,
		.rz_zev_znode_remove             = 0,
		.rz_zev_znode_link               = 0,
		.rz_zev_znode_symlink            = 0,
		.rz_zev_znode_rename             = 0,
		.rz_zev_znode_write              = 0,
		.rz_zev_znode_truncate           = 0,
		.rz_zev_znode_setattr            = 0,
		.rz_zev_znode_acl                = 0,
		.rz_zev_total                    = 0,
	};

static void
rz_zev_inc_total_ops_count(void)
{
	atomic_inc_64(&rz_zev_ops_counters.rz_zev_total);
	DTRACE_PROBE1(rz_zev_ops_counter_total, unit64_t,
		rz_zev_ops_counters.rz_zev_total); /* non-atomic access */
}

#define RZ_COUNTER_CB(opname, ...)					\
	void								\
	rz_zev_counter_##opname(__VA_ARGS__)				\
	{								\
		atomic_inc_64(&rz_zev_ops_counters.rz_zev_##opname);	\
		DTRACE_PROBE1(rz_zev_ops_counter_##opname, uint64_t,	\
			rz_zev_ops_counters.rz_zev_##opname);		\
		rz_zev_inc_total_ops_count();				\
	}

/* zfsvfs */
RZ_COUNTER_CB(zfs_mount, vfs_t *vfs, vnode_t *mpt, char *dataset,
    boolean_t remount)
RZ_COUNTER_CB(zfs_umount, vfs_t *vfs)
/* zvol */
RZ_COUNTER_CB(zvol_truncate, char *dataset, objset_t *os, dmu_tx_t *tx,
    uint64_t off, uint64_t len)
RZ_COUNTER_CB(zvol_write, char *dataset, objset_t *os, dmu_tx_t *tx,
    uint64_t off, uint64_t len)
/* znode */
RZ_COUNTER_CB(znode_close_after_update, znode_t *zp)
RZ_COUNTER_CB(znode_create, znode_t *dzp, znode_t *zp,
    dmu_tx_t *tx, char *name, uint64_t txmode) 
RZ_COUNTER_CB(znode_remove, znode_t *dzp, znode_t *zp,
    dmu_tx_t *tx, char *name, uint64_t txmode)
RZ_COUNTER_CB(znode_link, znode_t *dzp, znode_t *zp, dmu_tx_t *tx, char *name)
RZ_COUNTER_CB(znode_symlink, znode_t *dzp, znode_t *zp,
    dmu_tx_t *tx, char *name, char *link)
RZ_COUNTER_CB(znode_rename, znode_t *sdzp,
    char *sname, znode_t *tdzp, char *tname, znode_t *szp,
    znode_t *tzp, dmu_tx_t *tx)
RZ_COUNTER_CB(znode_write, znode_t *zp, dmu_tx_t *tx,
    uint64_t off, uint64_t len)
RZ_COUNTER_CB(znode_truncate, znode_t *zp, dmu_tx_t *tx,
    uint64_t off, uint64_t len)
RZ_COUNTER_CB(znode_setattr, znode_t *zp, dmu_tx_t *tx)
RZ_COUNTER_CB(znode_acl, znode_t *zp, dmu_tx_t *tx)

rz_zev_callbacks_t rz_zev_counter_callbacks = {
	/* zfsvfs */
	rz_zev_counter_zfs_mount,			/* mount */
	rz_zev_counter_zfs_umount,			/* umount */
	/* zvol */
	rz_zev_counter_zvol_truncate,			/* zvol truncate */
	rz_zev_counter_zvol_write,			/* zvol write */
	/* znode */
	rz_zev_counter_znode_close_after_update,	/* close */
	rz_zev_counter_znode_create,			/* create */
	rz_zev_counter_znode_remove,			/* remove */
	rz_zev_counter_znode_link,			/* link */
	rz_zev_counter_znode_symlink,			/* symlink */
	rz_zev_counter_znode_rename,			/* rename */
	rz_zev_counter_znode_write,			/* write */
	rz_zev_counter_znode_truncate,			/* truncate */
	rz_zev_counter_znode_setattr,			/* setattr */
	rz_zev_counter_znode_acl,			/* acl */
};

/*
 * Hooks for write operation event callbacks.  Will be changed by
 * a loadable module if event callbacks are desired.
 */

rz_zev_callbacks_t *rz_zev_default_callbacks = &rz_zev_counter_callbacks;
rz_zev_callbacks_t *rz_zev_callbacks = &rz_zev_counter_callbacks;