#ifndef __ZEV_H__
#define __ZEV_H__

#include <sys/types.h>
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/sha1.h>

#ifdef _KERNEL
#include <sys/dmu_objset.h>
#include <sys/dsl_dataset.h>
#include <sys/zfs_vfsops.h>
#include <sys/dsl_dir.h>
#include <sys/spa_impl.h>
#endif

/* increased for incompatible interface and behaviour changes */
#define ZEV_MAJOR_VERSION		2
/* increased for compatible changes, including new ioctls(), etc. */
#define ZEV_MINOR_VERSION		0

#define ZEV_MAX_QUEUE_NAME_LEN		40
#define ZEV_MAX_QUEUES			60
#define ZEV_CONTROL_DEVICE_NAME		"ctrl"
#define ZEV_TMPQUEUE_DEVICE_NAME	"tmpqueue"

/* global limit, no queue may grow larger than this. */
#define ZEV_MAX_QUEUE_LEN		(1 * 1024 * 1024 * 1024)

/* Don't wake up poll()ing processes for every single message. */
#define ZEV_DEFAULT_POLL_WAKEUP_QUEUE_LEN	8192
#define ZEV_MAX_POLL_WAKEUP_QUEUE_LEN		65536

#define ZEVIOC				  ('z' << 8)
#define ZEV_IOC_GET_GLOBAL_STATISTICS	  (ZEVIOC | 1)	/* get global stats */
#define ZEV_IOC_MUTE_POOL		  (ZEVIOC | 2)	/* no events for pool */
#define ZEV_IOC_UNMUTE_POOL		  (ZEVIOC | 3)	/* send pool events */
#define ZEV_IOC_SET_MAX_QUEUE_LEN	  (ZEVIOC | 4)	/* when to block ops */
#define ZEV_IOC_SET_POLL_WAKEUP_QUEUE_LEN (ZEVIOC | 5)	/* poll throttle */
#define ZEV_IOC_MARK			  (ZEVIOC | 6)	/* add mark to queue */
#define ZEV_IOC_GET_GEN			  (ZEVIOC | 7)	/* get generation no. */
#define	ZEV_IOC_ADD_QUEUE		  (ZEVIOC | 8)  /* create new queue */
#define	ZEV_IOC_REMOVE_QUEUE		  (ZEVIOC | 9)  /* delete queue */
#define ZEV_IOC_GET_QUEUE_PROPERTIES	  (ZEVIOC | 10)	/* get properties */
#define ZEV_IOC_SET_QUEUE_PROPERTIES	  (ZEVIOC | 11)	/* set properties */
#define ZEV_IOC_GET_QUEUE_STATISTICS	  (ZEVIOC | 12)	/* get queue stats */
#define ZEV_IOC_GET_DEBUG_INFO		  (ZEVIOC | 13)	/* get internal info */
#define ZEV_IOC_GET_QUEUE_LIST		  (ZEVIOC | 14)	/* get queue list */
#define ZEV_IOC_GET_FILE_SIGNATURES	  (ZEVIOC | 15)	/* get beaver sigs */
#define ZEV_IOC_GET_ZEV_VERSION		  (ZEVIOC | 16)	/* get zev version */

#define ZEV_OP_MIN			 1
#define ZEV_OP_ERROR			 1
#define ZEV_OP_MARK			 2
#define	ZEV_OP_ZFS_MOUNT		 3
#define	ZEV_OP_ZFS_UMOUNT		 4
#define	ZEV_OP_ZVOL_WRITE		 5
#define	ZEV_OP_ZVOL_TRUNCATE		 6
#define	ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE	 7
#define	ZEV_OP_ZNODE_CREATE		 8
#define	ZEV_OP_ZNODE_MKDIR		 9
#define	ZEV_OP_ZNODE_MAKE_XATTR_DIR	10
#define	ZEV_OP_ZNODE_REMOVE		11
#define	ZEV_OP_ZNODE_RMDIR		12
#define	ZEV_OP_ZNODE_LINK		13
#define	ZEV_OP_ZNODE_SYMLINK		14
#define	ZEV_OP_ZNODE_RENAME		15
#define	ZEV_OP_ZNODE_WRITE		16
#define	ZEV_OP_ZNODE_TRUNCATE		17
#define	ZEV_OP_ZNODE_SETATTR		18
#define	ZEV_OP_ZNODE_ACL		19
#define	ZEV_OP_MAX			19

/* zev event flags */
#define	ZEV_FL_XATTR			0x0001

/* zev queue flags */
#define ZEV_FL_BLOCK_WHILE_QUEUE_FULL	0x0001
#define ZEV_FL_PERSISTENT		0x0002
#define ZEV_FL_INITIALLY_EMPTY		0x0004

/* checksum block sizes */
#define ZEV_L0_SIZE	4096
#define ZEV_L1_SIZE	(256 * ZEV_L0_SIZE)

/* zfs event records (as they are represented through the character device) */

#pragma pack(1)
typedef struct zev_inode_info_t {
	uint64_t	ino;
	uint64_t	gen;
	uint64_t	mtime;
	uint64_t	ctime;
	uint64_t	size;
	uint64_t	mode;
	uint64_t	links;
	uint32_t	type;
	uint32_t	flags;
} zev_inode_info_t;

#define ZEV_ERRSTR(rec)		((char *)(rec + 1))
#define ZEV_DATASET(rec)	((char *)(rec + 1))
#define ZEV_MOUNTPOINT(rec)	(((char *)(rec + 1)) + rec->dataset_len + 1)
#define ZEV_NAME(rec)		((char *)(rec + 1))
#define ZEV_SRCNAME(rec)	((char *)(rec + 1))
#define ZEV_DSTNAME(rec)	(((char *)(rec + 1)) + rec->srcname_len + 1)
#define ZEV_LINK(rec)		(((char *)(rec + 1)) + rec->name_len + 1)
#define ZEV_PAYLOAD(rec)	((char *)(rec + 1))
#define ZEV_SIGNATURES(rec)	((char *)(rec + 1))

#define ZEV_COMMON_FIELDS						\
	uint32_t		record_len;				\
	uint32_t		op;					\
	uint64_t		op_time;				\
	uint64_t		guid

typedef struct zev_sig_t {
	uint16_t	level;
	uint16_t	padding1;
	uint32_t	padding2;
	uint64_t	block_offset;
	uint8_t		value[SHA1_DIGEST_LENGTH];  /* 20 bytes -> no padding */
} zev_sig_t;

typedef struct zev_header_t {
	ZEV_COMMON_FIELDS;
} zev_header_t;

typedef struct zev_error_t {
	ZEV_COMMON_FIELDS;
	uint32_t		failed_op;
	uint32_t		errstr_len;
	/* error string follows */
} zev_error_t;

typedef struct zev_mark_t {
	ZEV_COMMON_FIELDS;
	uint64_t		mark_id;
	uint32_t		payload_len;
	uint32_t		padding;
	/* payload follows */
} zev_mark_t;

typedef struct zev_zfs_mount_t {
	ZEV_COMMON_FIELDS;
	zev_inode_info_t	root;
	uint64_t		txg;
	uint32_t		remount;
	uint32_t		dataset_len;
	uint32_t		mountpoint_len;
	uint32_t		padding;
	/* dataset follows */
	/* mountpoint follows */
} zev_zfs_mount_t;

typedef struct zev_zfs_umount_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	covered;
} zev_zfs_umount_t;

typedef struct zev_zvol_truncate_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	uint64_t		offset;
	uint64_t		length;
	uint32_t		dataset_len;
	uint32_t		padding;
	/* dataset follows */
} zev_zvol_truncate_t;

typedef struct zev_zvol_write_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	uint64_t		offset;
	uint64_t		length;
	uint32_t		dataset_len;
	uint32_t		padding;
	/* dataset follows */
} zev_zvol_write_t;

typedef struct zev_znode_close_after_update_t {
	ZEV_COMMON_FIELDS;
	zev_inode_info_t	file;
} zev_znode_close_after_update_t;

typedef struct zev_znode_create_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
	zev_inode_info_t	parent;
	zev_sig_t		signature;
	uint32_t		name_len;
	uint32_t		padding;
	/* name follows */
} zev_znode_create_t;

typedef struct zev_znode_create_t zev_znode_mkdir_t;
typedef struct zev_znode_create_t zev_znode_make_xattr_dir_t;

typedef struct zev_znode_remove_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
	zev_inode_info_t	parent;
	uint32_t		name_len;
	uint32_t		padding;
	/* name follows */
} zev_znode_remove_t;

typedef struct zev_znode_remove_t zev_znode_rmdir_t;

typedef struct zev_znode_link_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	parent;
	zev_inode_info_t	file;
	uint32_t		name_len;
	uint32_t		padding;
	/* new_name follows */
} zev_znode_link_t;

typedef struct zev_znode_symlink_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	parent;
	zev_inode_info_t	file;
	zev_sig_t		signature;
	uint32_t		name_len;
	uint32_t		link_len;
	/* name follows */
	/* link follows */
} zev_znode_symlink_t;

typedef struct zev_znode_rename_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	srcdir;
	zev_inode_info_t	dstdir;
	zev_inode_info_t	file;
	zev_inode_info_t	clobbered_file;
	uint32_t		srcname_len;
	uint32_t		dstname_len;
	/* srcname follows */
	/* dstname follows */
} zev_znode_rename_t;

typedef struct zev_znode_write_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
	uint64_t		offset;
	uint64_t		length;
	uint64_t		signature_cnt;
	/* signatures follow */
} zev_znode_write_t;

typedef struct zev_znode_truncate_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
	uint64_t		offset;
	uint64_t		length;
	uint64_t		signature_cnt;
	/* signatures follow */
} zev_znode_truncate_t;

typedef struct zev_znode_setattr_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
} zev_znode_setattr_t;

typedef struct zev_znode_acl_t {
	ZEV_COMMON_FIELDS;
	uint64_t		txg;
	zev_inode_info_t	file;
} zev_znode_acl_t;

/* convenience helper definition */
typedef union {
	zev_header_t				header;

	zev_error_t				error;
	zev_mark_t				mark;
	union {
		zev_zfs_mount_t			mount;
		zev_zfs_umount_t		umount;
	} zfs;
	union {
		zev_zvol_truncate_t		truncate;
		zev_zvol_write_t		write;
	} zvol;
	union {
		zev_znode_close_after_update_t	close;
		zev_znode_create_t		create;
		zev_znode_mkdir_t		mkdir;
		zev_znode_make_xattr_dir_t	mkxattrdir;
		zev_znode_remove_t		remove;
		zev_znode_rmdir_t		rmdir;
		zev_znode_link_t		link;
		zev_znode_symlink_t		symlink;
		zev_znode_rename_t		rename;
		zev_znode_write_t		write;
		zev_znode_truncate_t		truncate;
		zev_znode_setattr_t		setattr;
		zev_znode_acl_t			acl;
	} znode;
} zev_event_t;



typedef struct zev_statistics_t {
	uint64_t	zev_queue_len;
	uint64_t	zev_bytes_read;
	uint64_t	zev_bytes_discarded;
	/* runtime settings */
	uint64_t	zev_max_queue_len;
	/* counters */
	uint64_t	zev_cnt_total_events;
	uint64_t	zev_cnt_discarded_events;
	uint64_t	zev_cnt_errors;
	uint64_t	zev_cnt_marks;
	/* zfsvfs ops */
	uint64_t	zev_cnt_zfs_mount;
	uint64_t	zev_cnt_zfs_umount;
	/* zvol ops */
	uint64_t	zev_cnt_zvol_write;
	uint64_t	zev_cnt_zvol_truncate;
	/* znode ops */
	uint64_t	zev_cnt_znode_close_after_update;
	uint64_t	zev_cnt_znode_create;
	uint64_t	zev_cnt_znode_remove;
	uint64_t	zev_cnt_znode_link;
	uint64_t	zev_cnt_znode_symlink;
	uint64_t	zev_cnt_znode_rename;
	uint64_t	zev_cnt_znode_write;
	uint64_t	zev_cnt_znode_truncate;
	uint64_t	zev_cnt_znode_setattr;
	uint64_t	zev_cnt_znode_acl;
} zev_statistics_t;

typedef struct zev_queue_name {
	uint64_t		zev_namelen;
	char			zev_name[ZEV_MAX_QUEUE_NAME_LEN];
} zev_queue_name_t;

typedef struct zev_ioctl_poolarg {
	uint64_t	zev_poolname_len;
	char		zev_poolname[MAXPATHLEN];
} zev_ioctl_poolarg_t;

typedef struct zev_ioctl_mark {
	uint64_t	zev_mark_id;
	uint64_t	zev_guid;
	uint32_t	zev_payload_len;
	uint32_t	padding;
	/* payload follows */
} zev_ioctl_mark_t;

typedef struct zev_ioctl_get_gen {
	/* input */
	uint64_t	inode;
	uint32_t	fd;	/* open fd to any object on the same fs */
	/* noput */
	uint32_t	padding;
	/* output */
	uint64_t	generation;
	uint64_t	crtime;
	uint64_t	guid;
	char		dataset[MAXPATHLEN];
} zev_ioctl_get_gen_t;

typedef struct zev_ioctl_set_queue_properties {
	uint64_t		zev_max_queue_len;
	uint64_t		zev_poll_wakeup_threshold;
	uint16_t		zev_flags;
	uint16_t		padding1;
	uint32_t		padding2;
	zev_queue_name_t	zev_queue_name;
} zev_ioctl_set_queue_properties_t;

typedef struct zev_ioctl_set_queue_properties zev_ioctl_get_queue_properties_t;

typedef struct zev_ioctl_add_queue {
	uint64_t	zev_max_queue_len;
	uint32_t	padding;
	uint16_t	zev_flags;
	uint16_t	zev_namelen;
	char		zev_name[ZEV_MAX_QUEUE_NAME_LEN];
} zev_ioctl_add_queue_t;

typedef struct zev_ioctl_remove_queue {
	zev_queue_name_t	zev_queue_name;
} zev_ioctl_remove_queue_t;

typedef struct zev_ioctl_get_queue_statistics {
	zev_queue_name_t	zev_queue_name;
	zev_statistics_t	zev_statistics;
} zev_ioctl_get_queue_statistics_t;

typedef struct zev_ioctl_debug_info {
	uint64_t	zev_memory_allocated;
	uint64_t	zev_chksum_cache_size;
	uint64_t	zev_chksum_cache_hits;
	uint64_t	zev_chksum_cache_misses;
} zev_ioctl_debug_info_t;

typedef struct zev_ioctl_get_queue_list {
	uint64_t		zev_n_queues;
	zev_queue_name_t	zev_queue_name[ZEV_MAX_QUEUES];
} zev_ioctl_get_queue_list_t;

typedef struct zev_ioctl_get_signatures {
	/* in */
	uint64_t	zev_offset;
	uint64_t	zev_len;
	uint32_t	zev_fd;
	uint32_t	zev_bufsize;
	/* out */
	uint64_t	zev_signature_cnt;
	/* up to zev_bufsize bytes of checksums will be written here */
} zev_ioctl_get_signatures_t;

typedef struct zev_ioctl_get_zev_version {
	uint64_t		zev_major_version;
	uint64_t		zev_minor_version;
} zev_ioctl_get_zev_version;

#pragma pack()

#ifdef _KERNEL

extern uint64_t zev_memory_allocated;
extern uint64_t zev_memory_freed;

#define ZEV_MEM_ADD(memsize)						\
	do {								\
		int64_t tmp_delta = (int64_t)(memsize);			\
		atomic_add_64(&zev_memory_allocated, tmp_delta);	\
	} while(0)

#define ZEV_MEM_SUB(memsize)						\
	do {								\
		int64_t tmp_delta = (int64_t)(memsize);			\
		atomic_add_64(&zev_memory_freed, tmp_delta);		\
	} while(0)

void *zev_alloc(ssize_t sz);
void *zev_zalloc(ssize_t sz);
void zev_free(void *ptr, ssize_t sz);

typedef struct zev_msg_t {
	struct zev_msg_t	*next;
	struct zev_msg_t	*prev;
	uint64_t		 seq;
	uint16_t		 size;
	uint16_t		 read;
	/* data follows */
} zev_msg_t;

void zev_queue_error(int op, char *fmt, ...);
void zev_queue_message(int op, zev_msg_t *msg);
void zev_queue_message_nvlist(int op, nvlist_t *nvl);
int zev_skip_pool(objset_t *os);
int zev_skip_fs(zfsvfs_t *fs);

#endif

#endif /* __ZEV_H__ */