/*
 * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Matthew Dillon <dillon@backplane.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.h,v 1.23 2008/11/13 02:18:43 dillon Exp $
 */
/*
 * HAMMER ioctl's.  This file can be #included from userland
 */

#ifndef VFS_HAMMER_IOCTL_H_
#define VFS_HAMMER_IOCTL_H_

#include <sys/param.h>
#include <sys/ioccom.h>

#include "hammer_disk.h"

/*
 * Common HAMMER ioctl header
 *
 * Global flags are stored in the upper 16 bits.
 */
struct hammer_ioc_head {
	int32_t		flags;
	int32_t		error;
	int32_t		reserved01[4];
};

#define HAMMER_IOC_HEAD_ERROR	0x00008000
#define HAMMER_IOC_HEAD_INTR	0x00010000
#define HAMMER_IOC_DO_BTREE	0x00020000	/* reblocker */
#define HAMMER_IOC_DO_INODES	0x00040000	/* reblocker */
#define HAMMER_IOC_DO_DATA	0x00080000	/* reblocker */
#define HAMMER_IOC_DO_DIRS	0x00100000	/* reblocker */

#define HAMMER_IOC_DO_FLAGS	(HAMMER_IOC_DO_BTREE |	\
				 HAMMER_IOC_DO_INODES |	\
				 HAMMER_IOC_DO_DATA |	\
				 HAMMER_IOC_DO_DIRS)

/*
 * HAMMERIOC_PRUNE
 *
 * beg/end TID ranges in the element array must be sorted in descending
 * order, with the most recent (highest) range at elms[0].
 */
struct hammer_ioc_prune_elm {
	hammer_tid_t	beg_tid;	/* starting tid */
	hammer_tid_t	end_tid;	/* ending tid (non inclusive) */
	hammer_tid_t	mod_tid;	/* modulo */
};

#define HAMMER_MAX_PRUNE_ELMS	(1024*1024/24)

struct hammer_ioc_prune {
	struct hammer_ioc_head head;
	int		nelms;
	int		reserved01;

	struct hammer_base_elm key_beg;	/* stop forward scan (reverse scan) */
	struct hammer_base_elm key_end;	/* start forward scan (reverse scan) */
	struct hammer_base_elm key_cur;	/* scan interruption point */

	int64_t		stat_scanrecords;/* number of records scanned */
	int64_t		stat_rawrecords; /* number of raw records pruned */
	int64_t		stat_dirrecords; /* number of dir records pruned */
	int64_t		stat_bytes;	 /* number of data bytes pruned */
	int64_t		stat_realignments; /* number of raw records realigned */
	hammer_tid_t	stat_oldest_tid; /* oldest create_tid encountered */
	int64_t		reserved02[6];
	struct hammer_ioc_prune_elm *elms; /* user supplied array */
};

#define HAMMER_IOC_PRUNE_ALL	0x0001

/*
 * HAMMERIOC_REPACK
 *
 * Forward scan leaf-up B-Tree packing.  The saturation point is typically
 * set to HAMMER_BTREE_LEAF_ELMS * 2 / 3 for 2/3rds fill.  Referenced nodes
 * have to be skipped, we can't track cursors through pack ops.
 */
struct hammer_ioc_rebalance {
	struct hammer_ioc_head head;
	int		saturation;	/* saturation pt elements/node */
	int		reserved01;

	struct hammer_base_elm key_beg;	/* start forward scan */
	struct hammer_base_elm key_end; /* stop forward scan (inclusive) */
	struct hammer_base_elm key_cur; /* current scan index */

	int64_t		stat_ncount;	/* number of nodes scanned */
	int64_t		stat_deletions; /* number of nodes deleted */
	int64_t		stat_collisions;/* number of collision retries */
	int64_t		stat_nrebal;	/* number of btree-nodes rebalanced */
	int32_t		allpfs;		/* rebalance all PFS if set */
	int32_t		reserved02;
};

/*
 * HAMMERIOC_GETHISTORY
 *
 * Retrieve an array of ordered transaction ids >= beg and < end indicating
 * all changes made to the specified object's inode up to the
 * maximum.
 *
 * If ATKEY is set the key field indicates a particular key within the
 * inode to retrieve the history for.
 *
 * On return count is set to the number of elements returned, nxt_tid is
 * set to the tid the caller should store in beg_tid to continue the
 * iteration, and nxt_key is set to the nearest key boundary > key
 * indicating the range key - nxt_key (nxt_key non-inclusive) the tid
 * array represents.  Also obj_id is set to the object's inode number.
 *
 * nxt_key can be used to iterate the contents of a single file but should
 * not be stored in key until all modifications at key have been retrieved.
 * To work properly nxt_key should be initialized to HAMMER_MAX_KEY.
 * Successive ioctl() calls will reduce nxt_key as appropriate so at the
 * end of your iterating for 'key', key to nxt_key will represent the
 * shortest range of keys that all returned TIDs apply to.
 */

#define HAMMER_MAX_HISTORY_ELMS	64

struct hammer_ioc_hist_entry {
	hammer_tid_t	tid;
	uint32_t	time32;
	uint32_t	reserved01;
};

struct hammer_ioc_history {
	struct hammer_ioc_head head;
	int64_t		obj_id;
	hammer_tid_t	beg_tid;
	hammer_tid_t	nxt_tid;
	hammer_tid_t	end_tid;
	int64_t		key;
	int64_t		nxt_key;
	int		count;
	int		reserve01;
	struct hammer_ioc_hist_entry hist_ary[HAMMER_MAX_HISTORY_ELMS];
};

#define HAMMER_IOC_HISTORY_ATKEY	0x0001
#define HAMMER_IOC_HISTORY_NEXT_TID	0x0002	/* iterate via nxt_tid */
#define HAMMER_IOC_HISTORY_NEXT_KEY	0x0004	/* iterate via nxt_key */
#define HAMMER_IOC_HISTORY_EOF		0x0008	/* no more keys */
#define HAMMER_IOC_HISTORY_UNSYNCED	0x0010	/* unsynced info in inode */

/*
 * Reblock request
 */
struct hammer_ioc_reblock {
	struct hammer_ioc_head head;
	int32_t		free_level;		/* 0 for maximum compaction */
	uint32_t	reserved01;

	struct hammer_base_elm key_beg;		/* start forward scan */
	struct hammer_base_elm key_end;		/* stop forward scan */
	struct hammer_base_elm key_cur;		/* scan interruption point */

	int64_t		btree_count;		/* B-Tree nodes checked */
	int64_t		reserved02;
	int64_t		data_count;		/* Data segments checked */
	int64_t		data_byte_count;	/* Data bytes checked */

	int64_t		btree_moves;		/* B-Tree nodes moved */
	int64_t		reserved03;
	int64_t		data_moves;		/* Data segments moved */
	int64_t		data_byte_moves;	/* Data bytes moved */

	int16_t		allpfs;			/* Reblock all PFS if set */
	int16_t		vol_no;			/* Volume to reblock if set */
	int32_t		reserved04;
};

/*
 * HAMMERIOC_SYNCTID
 */
enum hammer_synctid_op {
	HAMMER_SYNCTID_NONE,	/* no sync (TID will not be accurate) */
	HAMMER_SYNCTID_ASYNC,	/* async (TID will not be accurate) */
	HAMMER_SYNCTID_SYNC1,	/* single sync - might undo after crash */
	HAMMER_SYNCTID_SYNC2	/* double sync - guarantee no undo */
};

struct hammer_ioc_synctid {
	struct hammer_ioc_head	head;
	enum hammer_synctid_op	op;
	hammer_tid_t		tid;
};

/*
 * HAMMERIOC_GET_INFO
 */
struct hammer_ioc_info {
	struct hammer_ioc_head		head;

	char		vol_label[64];
	hammer_uuid_t	vol_fsid;
	hammer_uuid_t	vol_fstype;

	int		version;
	int		nvolumes;
	int		rootvol;
	int		reserved01;

	int64_t		bigblocks;
	int64_t		freebigblocks;
	int64_t		rsvbigblocks;
	int64_t		inodes;

	int64_t		reservedext[16];
};

/*
 * HAMMERIOC_GET_PSEUDOFS
 * HAMMERIOC_SET_PSEUDOFS
 * HAMMERIOC_SCAN_PSEUDOFS
 */
struct hammer_ioc_pseudofs_rw {
	struct hammer_ioc_head	head;
	int			pfs_id;
	uint32_t		bytes;
	uint32_t		version;
	uint32_t		reserved01;
	hammer_pseudofs_data_t	ondisk;
};

#define HAMMER_IOC_PSEUDOFS_VERSION	1

/*
 * HAMMERIOC_MIRROR_READ/WRITE
 */
struct hammer_ioc_mirror_rw {
	struct hammer_ioc_head	head;
	struct hammer_base_elm	key_beg;	/* start forward scan */
	struct hammer_base_elm	key_end;	/* stop forward scan */
	struct hammer_base_elm	key_cur;	/* interruption point */
	hammer_tid_t		tid_beg;	/* filter modification range */
	hammer_tid_t		tid_end;	/* filter modification range */
	void			*ubuf;		/* user buffer */
	int			count;		/* current size */
	int			size;		/* max size */
	int			pfs_id;		/* PFS id being read/written */
	int			reserved01;
	hammer_uuid_t		shared_uuid;	/* validator for safety */
};

#define HAMMER_IOC_MIRROR_NODATA	0x0001	/* do not include bulk data */

/*
 * NOTE: crc is for the data block starting at rec_size, not including the
 * data[] array.
 */
struct hammer_ioc_mrecord_head {
	uint32_t		signature;	/* signature for byte order */
	hammer_crc_t		rec_crc;
	uint32_t		rec_size;
	uint32_t		type;
	/* extended */
};

struct hammer_ioc_mrecord_rec {
	struct hammer_ioc_mrecord_head	head;
	struct hammer_btree_leaf_elm	leaf;
	/* extended by data */
};

struct hammer_ioc_mrecord_skip {
	struct hammer_ioc_mrecord_head	head;
	struct hammer_base_elm		skip_beg;
	struct hammer_base_elm		skip_end;
};

struct hammer_ioc_mrecord_update {
	struct hammer_ioc_mrecord_head	head;
	hammer_tid_t			tid;
};

struct hammer_ioc_mrecord_sync {
	struct hammer_ioc_mrecord_head	head;
};

struct hammer_ioc_mrecord_pfs {
	struct hammer_ioc_mrecord_head	head;
	uint32_t			version;
	uint32_t			reserved01;
	struct hammer_pseudofs_data	pfsd;
};

struct hammer_ioc_version {
	struct hammer_ioc_head head;
	uint32_t		cur_version;
	uint32_t		min_version;
	uint32_t		wip_version;
	uint32_t		max_version;
	char			description[64];
};

struct hammer_ioc_volume {
	struct hammer_ioc_head head;
	char			device_name[MAXPATHLEN];
	int32_t			vol_no;
	uint8_t			flag;
	uint8_t			reserved[3];
	int64_t			vol_size;
	int64_t			boot_area_size;
	int64_t			memory_log_size;
};

#define HAMMER_IOC_VOLUME_REBLOCK	1

struct hammer_ioc_volume_list {
	struct hammer_ioc_volume *vols;
	int nvols;
};

typedef union hammer_ioc_mrecord_any {
	struct hammer_ioc_mrecord_head	head;
	struct hammer_ioc_mrecord_rec	rec;
	struct hammer_ioc_mrecord_skip	skip;
	struct hammer_ioc_mrecord_update update;
	struct hammer_ioc_mrecord_update sync;
	struct hammer_ioc_mrecord_pfs	pfs;
	struct hammer_ioc_version	version;
} *hammer_ioc_mrecord_any_t;

/*
 * MREC types.  Flags are in the upper 16 bits but some are also included
 * in the type mask to force them into any switch() on the type.
 *
 * NOTE: Any record whos data is CRC-errored will have HAMMER_MRECF_CRC set,
 *	 and the bit is also part of the type mask.
 */
#define HAMMER_MREC_TYPE_RESERVED	0
#define HAMMER_MREC_TYPE_REC		1	/* record w/ data */
#define HAMMER_MREC_TYPE_PFSD		2	/* (userland only) */
#define HAMMER_MREC_TYPE_UPDATE		3	/* (userland only) */
#define HAMMER_MREC_TYPE_SYNC		4	/* (userland only) */
#define HAMMER_MREC_TYPE_SKIP		5	/* skip-range */
#define HAMMER_MREC_TYPE_PASS		6	/* record for cmp only (pass) */
#define HAMMER_MREC_TYPE_TERM		7	/* (userland only) */
#define HAMMER_MREC_TYPE_IDLE		8	/* (userland only) */

#define HAMMER_MREC_TYPE_REC_BADCRC	(HAMMER_MREC_TYPE_REC | \
					 HAMMER_MRECF_CRC_ERROR)
#define HAMMER_MREC_TYPE_REC_NODATA	(HAMMER_MREC_TYPE_REC | \
					 HAMMER_MRECF_NODATA)

#define HAMMER_MRECF_TYPE_LOMASK	0x000000FF
#define HAMMER_MRECF_TYPE_MASK		0x800000FF
#define HAMMER_MRECF_CRC_ERROR		0x80000000

#define HAMMER_MRECF_DATA_CRC_BAD	0x40000000
#define HAMMER_MRECF_RECD_CRC_BAD	0x20000000
#define HAMMER_MRECF_NODATA		0x10000000

#define HAMMER_MREC_CRCOFF	(offsetof(struct hammer_ioc_mrecord_head, rec_size))
#define HAMMER_MREC_HEADSIZE	sizeof(struct hammer_ioc_mrecord_head)

#define HAMMER_IOC_MIRROR_SIGNATURE	0x4dd97272U
#define HAMMER_IOC_MIRROR_SIGNATURE_REV	0x7272d94dU

/*
 * HAMMERIOC_ADD_SNAPSHOT - Add snapshot tid(s).
 * HAMMERIOC_DEL_SNAPSHOT - Delete snapshot tids.
 * HAMMERIOC_GET_SNAPSHOT - Get/continue retrieving snapshot tids.
 *			    (finds restart point based on last snaps[] entry)
 *
 * These are per-PFS operations.
 *
 * NOTE: There is no limit on the number of snapshots, but there is a limit
 *	 on how many can be set or returned in each ioctl.
 *
 * NOTE: ADD and DEL start at snap->index.  If an error occurs the index will
 *	 point at the errored record.  snap->index must be set to 0 for GET.
 */
#define HAMMER_SNAPS_PER_IOCTL		16

#define HAMMER_IOC_SNAPSHOT_EOF		0x0008	/* no more keys */

struct hammer_ioc_snapshot {
	struct hammer_ioc_head	head;
	int			reserved01;
	uint32_t		index;
	uint32_t		count;
	struct hammer_snapshot_data snaps[HAMMER_SNAPS_PER_IOCTL];
};

/*
 * HAMMERIOC_GET_CONFIG
 * HAMMERIOC_SET_CONFIG
 *
 * The configuration space is a freeform nul-terminated string, typically
 * a text file.  It is per-PFS and used by the 'hammer cleanup' utility.
 *
 * The configuration space is NOT mirrored.  mirror-write will ignore
 * configuration space records.
 */
struct hammer_ioc_config {
	struct hammer_ioc_head	head;
	uint32_t		reserved01;
	uint32_t		reserved02;
	uint64_t		reserved03[4];
	struct hammer_config_data config;
};

/*
 * HAMMERIOC_DEDUP
 */
struct hammer_ioc_dedup {
	struct hammer_ioc_head	head;
	struct hammer_base_elm	elm1;
	struct hammer_base_elm	elm2; /* candidate for dedup */
};

#define HAMMER_IOC_DEDUP_CMP_FAILURE	0x0001 /* verification failed */
#define HAMMER_IOC_DEDUP_UNDERFLOW	0x0002 /* big-block underflow */
#define HAMMER_IOC_DEDUP_INVALID_ZONE	0x0004 /* we can't dedup all zones */

/*
 * HAMMERIOC_GET_DATA
 */
struct hammer_ioc_data {
	struct hammer_ioc_head		head;
	struct hammer_base_elm		elm;	/* btree key to lookup */
	struct hammer_btree_leaf_elm	leaf;
	void				*ubuf;	/* user buffer */
	int				size;	/* max size */
};

/*
 * Ioctl cmd ids
 */
#define HAMMERIOC_PRUNE		_IOWR('h',1,struct hammer_ioc_prune)
#define HAMMERIOC_GETHISTORY	_IOWR('h',2,struct hammer_ioc_history)
#define HAMMERIOC_REBLOCK	_IOWR('h',3,struct hammer_ioc_reblock)
#define HAMMERIOC_SYNCTID	_IOWR('h',4,struct hammer_ioc_synctid)
#define HAMMERIOC_SET_PSEUDOFS	_IOWR('h',5,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_GET_PSEUDOFS	_IOWR('h',6,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_MIRROR_READ	_IOWR('h',7,struct hammer_ioc_mirror_rw)
#define HAMMERIOC_MIRROR_WRITE	_IOWR('h',8,struct hammer_ioc_mirror_rw)
#define HAMMERIOC_UPG_PSEUDOFS	_IOWR('h',9,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_DGD_PSEUDOFS	_IOWR('h',10,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_RMR_PSEUDOFS	_IOWR('h',11,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_WAI_PSEUDOFS	_IOWR('h',12,struct hammer_ioc_pseudofs_rw)
#define HAMMERIOC_GET_VERSION	_IOWR('h',13,struct hammer_ioc_version)
#define HAMMERIOC_SET_VERSION	_IOWR('h',14,struct hammer_ioc_version)
#define HAMMERIOC_REBALANCE	_IOWR('h',15,struct hammer_ioc_rebalance)
#define HAMMERIOC_GET_INFO	_IOR('h',16,struct hammer_ioc_info)
#define HAMMERIOC_ADD_VOLUME	_IOWR('h',17,struct hammer_ioc_volume)
#define HAMMERIOC_ADD_SNAPSHOT	_IOWR('h',18,struct hammer_ioc_snapshot)
#define HAMMERIOC_DEL_SNAPSHOT	_IOWR('h',19,struct hammer_ioc_snapshot)
#define HAMMERIOC_GET_SNAPSHOT	_IOWR('h',20,struct hammer_ioc_snapshot)
#define HAMMERIOC_GET_CONFIG	_IOWR('h',21,struct hammer_ioc_config)
#define HAMMERIOC_SET_CONFIG	_IOWR('h',22,struct hammer_ioc_config)
#define HAMMERIOC_DEL_VOLUME	_IOWR('h',24,struct hammer_ioc_volume)
#define HAMMERIOC_DEDUP		_IOWR('h',25,struct hammer_ioc_dedup)
#define HAMMERIOC_GET_DATA	_IOWR('h',26,struct hammer_ioc_data)
#define HAMMERIOC_LIST_VOLUMES	_IOWR('h',27,struct hammer_ioc_volume_list)
#define HAMMERIOC_SCAN_PSEUDOFS	_IOWR('h',28,struct hammer_ioc_pseudofs_rw)

#endif /* !VFS_HAMMER_IOCTL_H_ */

