/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: stats_disk.h 1.6 90/07/29 $"

/*
 * stats_disk.h
 *	Disk performance statistics
 */

/* $Log:	stats_disk.h,v $
 */

#ifndef _SYS_STATS_DISK_H_
/*
 * Guidelines for adding the hooks to gather the statistics
 *
 *  -  struct disk_stats *
 *     ds_alloc_stats(name, size, unit, flags)
 *	char	*name;
 *	ulong	size;
 *	int	unit;
 *	ushort	flags;
 *
 *	Allocates a disk_stats structure and returns a pointer to it.  Returns
 *	NULL if the allocation fails.  This is typically done during
 *	initialization and the pointer stored in a device-specific structure.
 *
 *	'name' is the prefix for the device (usually the same as what would
 *		appear in the device name).  A real name for this particular
 *		device is constructed from 'name' and 'unit'.
 *	'size' is the "size" of the device, either in cylinders if this info
 *		is available or in DEV_BSIZE blocks.  If it is in blocks,
 *		then the DS_BLK_HIST flag must be set (see flags).
 *	'unit' is the unit number for this particular device.  It is used
 *		along with 'name' in constructing the name for this device.
 *	'flags' - flag bits.  Currently only DS_BLK_HIST or DS_CYL_HIST may
 *		be specified.  DS_CYL_HIST is specified if it is possible to
 *		to determine which cylinder a request will access.  In this
 *		case 'size' is set to the number of cylinders.
 *		Otherwise, DS_BLK_HIST is specified and 'size' is the size
 *		of the device in DEV_BSIZE blocks.
 *
 *  -  DS_QUEUED_TIME(bp)
 *	struct buf *bp;
 *
 *	At each point when a request has been added to the driver queue but
 *	before it has been queued to the device, add a call to
 *	DS_QUEUED_TIME().
 *
 *	'bp' is a pointer to the buf structure for the request being processed.
 *
 *  -  DS_Q(dsp)
 *	struct disk_stats *dsp;
 *
 *	DS_Q() updates queue length statistics.
 *	At each point when a request has been added to the driver queue
 *	a call to DS_Q() should be added.  If an attempt to queue the request
 *	to the device is done just after the request has been queued to
 *	the driver, then the call to DS_Q() may be done immediately after
 *	the request has been queued to the device as long as it is done
 *	behind whatever mutex is guarding access to the device.
 *
 *  -  DS_IOSTART_TIME(dsp)
 *	struct disk_stats *dsp;
 *
 *	At the point the device will be making a transition from idle to active,
 *	a call to DS_IOSTART_TIME() must be inserted.  Typically this would
 *	be done in the routine that is queueing the request to the device.
 *	This records the time that this request was started.  Start time for
 *	requests while the device is busy are handled elsewhere.
 *
 *  -  ds_access_stats(dsp,  cyl)
 *	struct disk_stats *dsp;
 *	ulong cyl;
 *
 *	At the point that the request is queued to the device, a call to
 *	ds_access_stats() should be made.
 *
 *	'cyl' is the cylinder at which the request being processed starts
 *		(if DS_CYL_HIST was specified) or the physical block number
 *		the request starts at (if DS_BLK_HIST was specified).
 *
 *  -  DS_IODONE_TIME(dsp)
 *	struct disk_stats *dsp;
 *
 *	DS_IODONE_TIME() be called at the time a request finishes
 *	and before the next one is started.  It records the time the
 *	request finished for later processing by ds_completion_stats().
 *	This also marks the start time for the next request.  While not
 *	completely accurate as a start time for the next request (consider
 *	multiple requests queued), this provides accurate measures of drive
 *	active time and thus average service time for each request.
 *
 *  -  ds_completion_stats(dsp, bp)
 *	struct tape_stats *dsp;
 *	struct buf *bp;
 *
 *	ds_completion_stats() should be called at the time a request finishes
 *	and may be done after the next request is started.
 *
 *	'bp' is a pointer to the buf structure for the request being processed.
 */

#define	DS_SEEK_N_SMALL	64		/* # of small buckets */
#define DS_SEEK_SMALL	1		/* bucket size for small buckets */
					/* 1 cyl (dists 0-63) */
#define DS_SEEK_LARGE	16		/* bucket size for rest of distances */

/*
 * DS_SEEK_HISTSZ() determines the number of buckets needed for the
 * seek distance histogram given the number of cylinders
 */
#define DS_SEEK_HISTSZ(cyls) \
			(((cyls) / DS_SEEK_SMALL) <= DS_SEEK_N_SMALL ? \
					((cyls) / DS_SEEK_SMALL) : \
					DS_SEEK_N_SMALL + ((cyls) - \
					DS_SEEK_SMALL * DS_SEEK_N_SMALL + \
					(DS_SEEK_LARGE-1))/DS_SEEK_LARGE)

#define	DS_Q_N_SMALL	32		/* # of small Q buckets */
#define DS_Q_SMALL	1		/* bucket size for small buckets */
					/* bound for items in small buckets */
#define DS_Q_SML_UPPER	(DS_Q_SMALL * DS_Q_N_SMALL)
#define DS_Q_LARGE	8		/* bucket size for rest of Q lens */
#define DS_Q_N_LARGE	4		/* # of big Q buckets */
					/* bound for items in big buckets */
#define DS_Q_LRG_UPPER	(DS_Q_LARGE * DS_Q_N_LARGE + DS_Q_SML_UPPER)

#define DS_QHISTSZ	(DS_Q_N_SMALL + DS_Q_N_LARGE + 1)

#define DS_RT_N_SMALL	100		/* # of small buckets */
#define DS_RT_SMALL	1		/* bucket size for small buckets */
					/* bound for items in small buckets */
#define DS_RT_SML_UPPER	(DS_RT_SMALL * DS_RT_N_SMALL)
#define DS_RT_LARGE	10		/* bucket size for big buckets  */
#define DS_RT_N_LARGE	40		/* # of big buckets */
					/* bound for items in big buckets */
#define DS_RT_LRG_UPPER (DS_RT_LARGE * DS_RT_N_LARGE + DS_RT_SML_UPPER)

#define DS_RTHISTSZ	(DS_RT_N_SMALL + DS_RT_N_LARGE + 1)


/*
 * The disk statistics structure
 */
struct disk_stats {
	char	ds_name[12];		/* name for this drive */
	ulong	ds_cyls;		/* number of cylinders in this drive */
	ushort	ds_flags;		/* See flag defs below */
	ushort	ds_qlen;		/* Current queue length */
	ulong	ds_start;		/* time I/O was queued to controller */
	ulong	ds_savedstart;		/* saved start time (used for service */
					/* time calculation)		      */
	ulong	ds_cyl;			/* cylinder drive is currently on */
	ulong	ds_resp[2];		/* cumulative response time */
	ulong	ds_active[2];		/* cumulative active time */
	ulong	ds_cqlen;		/* cumulative Q lengths */
	ulong	ds_misc;		/* other operations (ioctls) */
	ulong	ds_miscblks;		/* sectors accessed by misc ops */
	ulong	ds_reads[B_TYPES];	/* number of reads (by xfer type) */
	ulong	ds_writes[B_TYPES];	/* number of writes (by xfer type) */
	ulong	ds_rdblks[B_TYPES];	/* sectors read (by xfer type) */
	ulong	ds_wtblks[B_TYPES];	/* sectors written (by xfer type) */
	ulong	ds_qhist[DS_QHISTSZ];	/* queue length histogram */
	ulong	ds_resphist[DS_RTHISTSZ]; /* response time histogram */
	ulong	*ds_cylhist;		/* histogram of cylinder accesses */
	ulong	*ds_seekhist;		/* histogram of seek distances */
};

/*
 * List of arrays of disk statistics structures
 * (To facilitate dynamic allocation.)
 * The number of structures in each array is controlled by ds_arraysize in
 * stats_space.c
 */

struct disk_stats_list {
	struct disk_stats_list *dl_next; /* Next in chain */
	short	dl_num;			/* no. of stat structs in this chunk */
	short	dl_used;		/* no. of structs left in this chunk */
	struct disk_stats *dl_stats;	/* (really larger) array of stats */
};

/*
 * Disk stats flags
 */

#define DS_CYL_HIST	0x0		/* true cylinder access histogram */
#define DS_BLK_HIST	0x1		/* histogram access to buckets of */
					/* blocks */

#ifdef	INKERNEL

/*
 * Queued (at the driver) and I/O start (queued to controller) timestamps
 */
#define DS_QUEUED_TIME(bp)		((bp)->b_queued = *k_usclk)

#define DS_IOSTART_TIME(dsp) { \
	if ((dsp) != NULL) \
		(dsp)->ds_start = *k_usclk; \
}

#define DS_IODONE_TIME(dsp) { \
	if ((dsp) != NULL) { \
		(dsp)->ds_savedstart = (dsp)->ds_start; \
		DS_IOSTART_TIME(dsp); \
	} \
}

/*
 * Update Q length statistics.
 * argument is a pointer to a disk_stats structure
 *
 * DS_QLEN()   is the general case
 */

#define	DS_Q(dsp) { \
	if (dsp != NULL) { \
	    (dsp)->ds_qlen++; \
	    (dsp)->ds_cqlen += (dsp)->ds_qlen; \
	    (dsp)->ds_qhist[((dsp)->ds_qlen < DS_Q_SML_UPPER) ? \
	        (dsp)->ds_qlen / DS_Q_SMALL : \
		(((dsp)->ds_qlen) < DS_Q_LRG_UPPER) ? \
		DS_Q_N_SMALL + (((dsp)->ds_qlen - DS_Q_SML_UPPER) / \
		DS_Q_LARGE) : DS_Q_N_SMALL + DS_Q_N_LARGE]++; \
	} \
}

extern ulong volatile *k_usclk;	/* usclk ptr for kernel use */

extern struct disk_stats *ds_alloc_stats();
extern void ds_access_stats();
extern void ds_completion_stats();

#endif	/* INKERNEL */

#define _SYS_STATS_DISK_H_
#endif /* _SYS_STATS_DISK_H_ */
