subr_devstat.c revision 39229
139229Sgibbs/*
239229Sgibbs * Copyright (c) 1997, 1998 Kenneth D. Merry.
339229Sgibbs * All rights reserved.
439229Sgibbs *
539229Sgibbs * Redistribution and use in source and binary forms, with or without
639229Sgibbs * modification, are permitted provided that the following conditions
739229Sgibbs * are met:
839229Sgibbs * 1. Redistributions of source code must retain the above copyright
939229Sgibbs *    notice, this list of conditions and the following disclaimer.
1039229Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1139229Sgibbs *    notice, this list of conditions and the following disclaimer in the
1239229Sgibbs *    documentation and/or other materials provided with the distribution.
1339229Sgibbs * 3. The name of the author may not be used to endorse or promote products
1439229Sgibbs *    derived from this software without specific prior written permission.
1539229Sgibbs *
1639229Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1739229Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1839229Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1939229Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2039229Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2139229Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2239229Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2339229Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2439229Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2539229Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2639229Sgibbs * SUCH DAMAGE.
2739229Sgibbs *
2839229Sgibbs *	$Id$
2939229Sgibbs */
3039229Sgibbs
3139229Sgibbs#include <sys/param.h>
3239229Sgibbs#include <sys/kernel.h>
3339229Sgibbs#include <sys/systm.h>
3439229Sgibbs#include <sys/time.h>
3539229Sgibbs#include <sys/types.h>
3639229Sgibbs#include <sys/sysctl.h>
3739229Sgibbs
3839229Sgibbs#include <sys/devicestat.h>
3939229Sgibbs
4039229Sgibbsstatic int devstat_num_devs;
4139229Sgibbsstatic int devstat_generation;
4239229Sgibbsstatic int devstat_version = DEVSTAT_VERSION;
4339229Sgibbsstatic int devstat_current_devnumber;
4439229Sgibbs
4539229SgibbsSTAILQ_HEAD(devstatlist, devstat) device_statq;
4639229Sgibbs
4739229Sgibbs/*
4839229Sgibbs * Take a malloced and zeroed devstat structure given to us, fill it in
4939229Sgibbs * and add it to the queue of devices.
5039229Sgibbs */
5139229Sgibbsvoid
5239229Sgibbsdevstat_add_entry(struct devstat *ds, char *dev_name,
5339229Sgibbs		  int unit_number, u_int32_t block_size,
5439229Sgibbs		  devstat_support_flags flags,
5539229Sgibbs		  devstat_type_flags device_type)
5639229Sgibbs{
5739229Sgibbs	int s;
5839229Sgibbs	struct devstatlist *devstat_head;
5939229Sgibbs
6039229Sgibbs	if (ds == NULL)
6139229Sgibbs		return;
6239229Sgibbs
6339229Sgibbs	if (devstat_num_devs == 0)
6439229Sgibbs		STAILQ_INIT(&device_statq);
6539229Sgibbs
6639229Sgibbs	devstat_generation++;
6739229Sgibbs	devstat_num_devs++;
6839229Sgibbs
6939229Sgibbs	devstat_head = &device_statq;
7039229Sgibbs
7139229Sgibbs	STAILQ_INSERT_TAIL(devstat_head, ds, dev_links);
7239229Sgibbs
7339229Sgibbs	ds->device_number = devstat_current_devnumber++;
7439229Sgibbs	ds->unit_number = unit_number;
7539229Sgibbs	strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN);
7639229Sgibbs	ds->block_size = block_size;
7739229Sgibbs	ds->flags = flags;
7839229Sgibbs	ds->device_type = device_type;
7939229Sgibbs
8039229Sgibbs	s = splclock();
8139229Sgibbs	getmicrotime(&ds->dev_creation_time);
8239229Sgibbs	splx(s);
8339229Sgibbs}
8439229Sgibbs
8539229Sgibbs/*
8639229Sgibbs * Remove a devstat structure from the list of devices.
8739229Sgibbs */
8839229Sgibbsvoid
8939229Sgibbsdevstat_remove_entry(struct devstat *ds)
9039229Sgibbs{
9139229Sgibbs	struct devstatlist *devstat_head;
9239229Sgibbs
9339229Sgibbs	if (ds == NULL)
9439229Sgibbs		return;
9539229Sgibbs
9639229Sgibbs	devstat_generation++;
9739229Sgibbs	devstat_num_devs--;
9839229Sgibbs
9939229Sgibbs	devstat_head = &device_statq;
10039229Sgibbs
10139229Sgibbs	/* Remove this entry from the devstat queue */
10239229Sgibbs	STAILQ_REMOVE(devstat_head, ds, devstat, dev_links);
10339229Sgibbs}
10439229Sgibbs
10539229Sgibbs/*
10639229Sgibbs * Record a transaction start.
10739229Sgibbs */
10839229Sgibbsvoid
10939229Sgibbsdevstat_start_transaction(struct devstat *ds)
11039229Sgibbs{
11139229Sgibbs	int s;
11239229Sgibbs
11339229Sgibbs	/* sanity check */
11439229Sgibbs	if (ds == NULL)
11539229Sgibbs		return;
11639229Sgibbs
11739229Sgibbs	/*
11839229Sgibbs	 * We only want to set the start time when we are going from idle
11939229Sgibbs	 * to busy.  The start time is really the start of the latest busy
12039229Sgibbs	 * period.
12139229Sgibbs	 */
12239229Sgibbs	if (ds->busy_count == 0) {
12339229Sgibbs		s = splclock();
12439229Sgibbs		getmicrouptime(&ds->start_time);
12539229Sgibbs		splx(s);
12639229Sgibbs	}
12739229Sgibbs	ds->busy_count++;
12839229Sgibbs}
12939229Sgibbs
13039229Sgibbs/*
13139229Sgibbs * Record the ending of a transaction, and incrment the various counters.
13239229Sgibbs */
13339229Sgibbsvoid
13439229Sgibbsdevstat_end_transaction(struct devstat *ds, u_int32_t bytes,
13539229Sgibbs			devstat_tag_type tag_type, devstat_trans_flags flags)
13639229Sgibbs{
13739229Sgibbs	int s;
13839229Sgibbs	struct timeval busy_time;
13939229Sgibbs
14039229Sgibbs	/* sanity check */
14139229Sgibbs	if (ds == NULL)
14239229Sgibbs		return;
14339229Sgibbs
14439229Sgibbs	s = splclock();
14539229Sgibbs	getmicrouptime(&ds->last_comp_time);
14639229Sgibbs	splx(s);
14739229Sgibbs
14839229Sgibbs	ds->busy_count--;
14939229Sgibbs
15039229Sgibbs	/*
15139229Sgibbs	 * There might be some transactions (DEVSTAT_NO_DATA) that don't
15239229Sgibbs	 * transfer any data.
15339229Sgibbs	 */
15439229Sgibbs	if (flags == DEVSTAT_READ) {
15539229Sgibbs		ds->bytes_read += bytes;
15639229Sgibbs		ds->num_reads++;
15739229Sgibbs	} else if (flags == DEVSTAT_WRITE) {
15839229Sgibbs		ds->bytes_written += bytes;
15939229Sgibbs		ds->num_writes++;
16039229Sgibbs	} else
16139229Sgibbs		ds->num_other++;
16239229Sgibbs
16339229Sgibbs	/*
16439229Sgibbs	 * Keep a count of the various tag types sent.
16539229Sgibbs	 */
16639229Sgibbs	if (tag_type != DEVSTAT_TAG_NONE)
16739229Sgibbs		ds->tag_types[tag_type]++;
16839229Sgibbs
16939229Sgibbs	/*
17039229Sgibbs	 * We only update the busy time when we go idle.  Otherwise, this
17139229Sgibbs	 * calculation would require many more clock cycles.
17239229Sgibbs	 */
17339229Sgibbs	if (ds->busy_count == 0) {
17439229Sgibbs		/* Calculate how long we were busy */
17539229Sgibbs		busy_time = ds->last_comp_time;
17639229Sgibbs		timevalsub(&busy_time, &ds->start_time);
17739229Sgibbs
17839229Sgibbs		/* Add our busy time to the total busy time. */
17939229Sgibbs		timevaladd(&ds->busy_time, &busy_time);
18039229Sgibbs	} else if (ds->busy_count < 0)
18139229Sgibbs		printf("devstat_end_transaction: HELP!! busy_count is < 0!\n");
18239229Sgibbs}
18339229Sgibbs
18439229Sgibbs/*
18539229Sgibbs * This is the sysctl handler for the devstat package.  The data pushed out
18639229Sgibbs * on the kern.devstat.all sysctl variable consists of the current devstat
18739229Sgibbs * generation number, and then an array of devstat structures, one for each
18839229Sgibbs * device in the system.
18939229Sgibbs *
19039229Sgibbs * I'm really not too fond of this method of doing things, but there really
19139229Sgibbs * aren't that many alternatives.  We must have some method of making sure
19239229Sgibbs * that the generation number the user gets corresponds with the data the
19339229Sgibbs * user gets.  If the user makes a separate sysctl call to get the
19439229Sgibbs * generation, and then a sysctl call to get the device statistics, the
19539229Sgibbs * device list could have changed in that brief period of time.  By
19639229Sgibbs * supplying the generation number along with the statistics output, we can
19739229Sgibbs * guarantee that the generation number and the statistics match up.
19839229Sgibbs */
19939229Sgibbsstatic int
20039229Sgibbssysctl_devstat SYSCTL_HANDLER_ARGS
20139229Sgibbs{
20239229Sgibbs	int error, i;
20339229Sgibbs	struct devstat *nds;
20439229Sgibbs	struct devstatlist *devstat_head;
20539229Sgibbs
20639229Sgibbs	if (devstat_num_devs == 0)
20739229Sgibbs		return(EINVAL);
20839229Sgibbs
20939229Sgibbs	error = 0;
21039229Sgibbs	devstat_head = &device_statq;
21139229Sgibbs
21239229Sgibbs	/*
21339229Sgibbs	 * First push out the generation number.
21439229Sgibbs	 */
21539229Sgibbs	error = SYSCTL_OUT(req, &devstat_generation, sizeof(int));
21639229Sgibbs
21739229Sgibbs	/*
21839229Sgibbs	 * Now push out all the devices.
21939229Sgibbs	 */
22039229Sgibbs	for (i = 0, nds = devstat_head->stqh_first;
22139229Sgibbs	    (nds != NULL) && (i < devstat_num_devs) && (error == 0);
22239229Sgibbs	     nds = nds->dev_links.stqe_next, i++)
22339229Sgibbs		error = SYSCTL_OUT(req, nds, sizeof(struct devstat));
22439229Sgibbs
22539229Sgibbs	return(error);
22639229Sgibbs}
22739229Sgibbs
22839229Sgibbs/*
22939229Sgibbs * Sysctl entries for devstat.  The first one is a node that all the rest
23039229Sgibbs * hang off of.
23139229Sgibbs */
23239229SgibbsSYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics");
23339229Sgibbs
23439229SgibbsSYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE,
23539229Sgibbs	    0, 0, sysctl_devstat, "S,devstat", "All Devices");
23639229Sgibbs/*
23739229Sgibbs * Export the number of devices in the system so that userland utilities
23839229Sgibbs * can determine how much memory to allocate to hold all the devices.
23939229Sgibbs */
24039229SgibbsSYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, &devstat_num_devs,
24139229Sgibbs	  0, "Number of devices in the devstat list");
24239229SgibbsSYSCTL_INT(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, &devstat_generation,
24339229Sgibbs	  0, "Devstat list generation");
24439229SgibbsSYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, &devstat_version,
24539229Sgibbs	  0, "Devstat list version number");
246