/*-
 * Copyright (c) 1997, 1998
 *	Nan Yang Computer Services Limited.  All rights reserved.
 *
 *  This software is distributed under the so-called ``Berkeley
 *  License'':
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Nan Yang Computer
 *      Services Limited.
 * 4. Neither the name of the Company 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 ``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 company 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.
 */

/* Information needed to set up a transfer */

enum xferinfo {
	XFR_NORMAL_READ = 1,
	XFR_NORMAL_WRITE = 2,		/* write request in normal mode */
	XFR_RECOVERY_READ = 4,
	XFR_DEGRADED_WRITE = 8,
	XFR_PARITYLESS_WRITE = 0x10,
	XFR_NO_PARITY_STRIPE = 0x20,	/* parity stripe is not available */
	XFR_DATA_BLOCK = 0x40,		/* data block in request */
	XFR_PARITY_BLOCK = 0x80,	/* parity block in request */
	XFR_BAD_SUBDISK = 0x100,	/* this subdisk is dead */
	XFR_MALLOCED = 0x200,		/* this buffer is malloced */
#ifdef VINUMDEBUG
	XFR_PHASE2 = 0x800,		/* debug only: 2nd phase write */
#endif
	XFR_REVIVECONFLICT = 0x1000,	/* possible conflict with a revive */
	XFR_BUFLOCKED = 0x2000,		/* BUF_LOCK performed on this buffer */

	/*
	 * operations that need a parity block
	 */
	XFR_PARITYOP = (XFR_NORMAL_WRITE | XFR_RECOVERY_READ |
			XFR_DEGRADED_WRITE),

	/*
	 * operations that use the group parameters
	 */
	XFR_GROUPOP = (XFR_DEGRADED_WRITE | XFR_RECOVERY_READ),

	/*
	 * operations that that use the data parameters
	 */
	XFR_DATAOP = (XFR_NORMAL_READ | XFR_NORMAL_WRITE |
		      XFR_PARITYLESS_WRITE),

	/*
	 * operations requiring read before write
	 */
	XFR_RBW = (XFR_NORMAL_WRITE | XFR_DEGRADED_WRITE),

	/*
	 * operations that need a malloced buffer
	 */
	XFR_NEEDS_MALLOC = (XFR_NORMAL_WRITE | XFR_RECOVERY_READ |
			    XFR_DEGRADED_WRITE)
};

/*
 * Describe one low-level request, part of a
 * high-level request.  This is an extended
 * struct buf buffer, and the first element
 * *must* be a struct buf.  We pass this
 * structure to the I/O routines instead of a
 * struct buf in order to be able to locate the
 * high-level request when it completes.
 *
 * All offsets and lengths are in sectors.
 */
struct rqelement {
	struct buf b;			/* buf structure */
	struct rqgroup *rqg;		/* pointer to our group */

	vinum_off_t sdoffset;		/* offset in subdisk */
	int useroffset;			/* offset of data in user buffer */

	/*
	 * dataoffset and datalen refer to "individual" data
	 * transfers which involve only this drive (normal read,
	 * parityless write) and also degraded write.
	 *
	 * groupoffset and grouplen refer to the other "group"
	 * operations (normal write, recovery read) which involve
	 * more than one drive.  Both the offsets are relative to
	 * the start of the local buffer.
	 */
	int dataoffset;			/* offset of the normal data */
	int groupoffset;		/* offset of group data */
	short datalen;			/* length of normal data (sectors) */
	short grouplen;			/* length of group data (sectors) */
	short buflen;			/* total buffer length to allocate */
	short flags;			/* really enum xferinfo (see above) */

	/*
	 * Ways to find other components
	 */
	short sdno;			/* subdisk number */
	short driveno;			/* drive number */
};

/*
 * A group of requests built to satisfy an I/O
 * transfer on a single plex.
 */
struct rqgroup {
	struct rqgroup *next;		/* pointer to next group */
	struct request *rq;		/* pointer to the request */
	short count;			/* number of requests in this group */
	short active;			/* and number active */
	short plexno;			/* index of plex */
	int badsdno;			/* index of bad subdisk or -1 */
	enum xferinfo flags;		/* description of transfer */
	struct rangelock *lock;		/* lock for this transfer */
	vinum_off_t lockbase;		/* and lock address */
	struct rqelement rqe[0];	/* and the elements of this request */
};

/*
 * Describe one high-level request and the
 * work we have to do to satisfy it.
 */
struct request {
	struct bio *bio;		/* pointer to the high-level request */
	enum xferinfo flags;
	union {
	    int volno;			/* volume index */
	    int plexno;			/* or plex index */
	} volplex;
	int error;			/* current error indication */
	int sdno;			/* reviving subdisk	*/
					/* (XFR_REVIVECONFLICT) */
	short isplex;			/* set if this is a plex request */
	short active;			/* number of subrequests still active */
	struct rqgroup *rqg;		/* ptr to the first group of requests */
	struct rqgroup *lrqg;		/* and to the last group of requests */
	struct request *next;		/* link of waiting requests */
};

/*
 * Extended buffer header for subdisk I/O.  Includes
 * a pointer to the user I/O request.
 */
struct sdbuf {
	struct buf b;			/* our buffer */
	struct bio *bio;		/* and pointer to parent */
	short driveno;			/* drive index */
	short sdno;			/* and subdisk index */
};

/*
 * Values returned by rqe and friends.  Be careful
 * with these: they are in order of increasing
 * seriousness.  Some routines check for
 * > REQUEST_RECOVERED to indicate a failed request. XXX
 */
enum requeststatus {
	REQUEST_OK,		/* request built OK */
	REQUEST_RECOVERED,	/* request OK, but involves RAID5 recovery */
	REQUEST_DEGRADED,	/* parts of request failed */
	REQUEST_EOF,		/* parts of request failed: outside plex */
	REQUEST_DOWN,		/* all of request failed: subdisk(s) down */
	REQUEST_ENOMEM		/* all of request failed: ran out of memory */
};

#ifdef VINUMDEBUG

/*
 * Trace entry for request info (DEBUG_LASTREQS)
 */
enum rqinfo_type {
	loginfo_unused,		/* never been used */
	loginfo_user_bp,	/* this is the bp when strategy is called */
	loginfo_user_bpl,	/* and this is the bp at launch time */
	loginfo_rqe,		/* user RQE */
	loginfo_iodone,		/* iodone */
	loginfo_raid5_data,	/* write RAID-5 data block */
	loginfo_raid5_parity,	/* write RAID-5 parity block */
	loginfo_sdio,		/* subdisk I/O */
	loginfo_sdiol,		/* subdisk I/O launch */
	loginfo_sdiodone,	/* subdisk iodone */
	loginfo_lockwait,	/* wait for range lock */
	loginfo_lock,		/* lock range */
	loginfo_unlock,		/* unlock range */
};

/*
 * Info to pass to logrq
 */
union rqinfou {
	struct bio *bio;
	struct rqelement *rqe;		/* addr of request, for correlation */
	struct rangelock *lockinfo;
};

struct rqinfo {
	enum rqinfo_type type;		/* kind of event */
	struct timeval timestamp;	/* time it happened */
	struct bio *bio;		/* point to user buffer */
	int devmajor;			/* major and minor device info */
	int devminor;
	union {
		struct buf b;		/* yup, the *whole* buffer header */
		struct bio bio;
		struct rqelement rqe;	/* and the whole rqe */
		struct rangelock lockinfo;
	} info;
};

#define RQINFO_SIZE 128		/* number of info slots in buffer */

void logrq(enum rqinfo_type type, union rqinfou info, struct bio *ubio);

#endif

/* Structures for the daemon */

/*
 * Types of request to the daemon
 */
enum daemonrq {
	daemonrq_none,		/* dummy to catch bugs */
	daemonrq_ioerror,	/* error occurred on I/O */
	daemonrq_saveconfig,	/* save configuration */
	daemonrq_return,	/* return to userland */
	daemonrq_ping,		/* show sign of life */
	daemonrq_init,		/* initialize a plex */
	daemonrq_revive,	/* revive a subdisk */
	daemonrq_closedrive,	/* close a drive */
};

/*
 * Info field for daemon requests and the request information
 */
union daemoninfo {
	struct request *rq;	/* for daemonrq_ioerror */
	struct sd *sd;		/* for daemonrq_revive */
	struct plex *plex;	/* for daemonrq_init */
	struct drive *drive;	/* for daemonrq_closedrive */
	int nothing;		/* for passing NULL */
};

struct daemonq {
	struct daemonq *next;	/* pointer to next element in queue */
	enum daemonrq type;	/* type of request */
	int privateinuse;	/* private element, being used */
	union daemoninfo info;	/* and the request information */
};

void queue_daemon_request(enum daemonrq type, union daemoninfo info);

extern int daemon_options;

enum daemon_option {
	daemon_verbose = 1,	/* talk about what we're doing */
	daemon_stopped = 2,
	daemon_noupdate = 4,	/* don't update the disk config, for recovery */
};

void freerq(struct request *rq);
void unlockrange(int plexno, struct rangelock *);
