1998-09-15 08:16:17 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1997, 1998 Kenneth D. Merry.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
|
|
*
|
1998-12-04 22:54:57 +00:00
|
|
|
* $Id: subr_devstat.c,v 1.6 1998/11/15 23:57:22 ken Exp $
|
1998-09-15 08:16:17 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
|
|
|
#include <sys/devicestat.h>
|
|
|
|
|
|
|
|
static int devstat_num_devs;
|
1998-09-20 00:11:23 +00:00
|
|
|
static long devstat_generation;
|
1998-09-15 08:16:17 +00:00
|
|
|
static int devstat_version = DEVSTAT_VERSION;
|
|
|
|
static int devstat_current_devnumber;
|
|
|
|
|
|
|
|
STAILQ_HEAD(devstatlist, devstat) device_statq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take a malloced and zeroed devstat structure given to us, fill it in
|
|
|
|
* and add it to the queue of devices.
|
|
|
|
*/
|
|
|
|
void
|
1998-11-14 21:58:51 +00:00
|
|
|
devstat_add_entry(struct devstat *ds, const char *dev_name,
|
1998-09-15 08:16:17 +00:00
|
|
|
int unit_number, u_int32_t block_size,
|
|
|
|
devstat_support_flags flags,
|
|
|
|
devstat_type_flags device_type)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct devstatlist *devstat_head;
|
|
|
|
|
|
|
|
if (ds == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (devstat_num_devs == 0)
|
|
|
|
STAILQ_INIT(&device_statq);
|
|
|
|
|
|
|
|
devstat_generation++;
|
|
|
|
devstat_num_devs++;
|
|
|
|
|
|
|
|
devstat_head = &device_statq;
|
|
|
|
|
|
|
|
STAILQ_INSERT_TAIL(devstat_head, ds, dev_links);
|
|
|
|
|
|
|
|
ds->device_number = devstat_current_devnumber++;
|
|
|
|
ds->unit_number = unit_number;
|
|
|
|
strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN);
|
1998-12-04 22:54:57 +00:00
|
|
|
ds->device_name[DEVSTAT_NAME_LEN - 1] = 0;
|
1998-09-15 08:16:17 +00:00
|
|
|
ds->block_size = block_size;
|
|
|
|
ds->flags = flags;
|
|
|
|
ds->device_type = device_type;
|
|
|
|
|
|
|
|
s = splclock();
|
|
|
|
getmicrotime(&ds->dev_creation_time);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a devstat structure from the list of devices.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
devstat_remove_entry(struct devstat *ds)
|
|
|
|
{
|
|
|
|
struct devstatlist *devstat_head;
|
|
|
|
|
|
|
|
if (ds == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
devstat_generation++;
|
|
|
|
devstat_num_devs--;
|
|
|
|
|
|
|
|
devstat_head = &device_statq;
|
|
|
|
|
|
|
|
/* Remove this entry from the devstat queue */
|
|
|
|
STAILQ_REMOVE(devstat_head, ds, devstat, dev_links);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record a transaction start.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
devstat_start_transaction(struct devstat *ds)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ds == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only want to set the start time when we are going from idle
|
|
|
|
* to busy. The start time is really the start of the latest busy
|
|
|
|
* period.
|
|
|
|
*/
|
|
|
|
if (ds->busy_count == 0) {
|
|
|
|
s = splclock();
|
|
|
|
getmicrouptime(&ds->start_time);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
ds->busy_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record the ending of a transaction, and incrment the various counters.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
devstat_end_transaction(struct devstat *ds, u_int32_t bytes,
|
|
|
|
devstat_tag_type tag_type, devstat_trans_flags flags)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct timeval busy_time;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ds == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
s = splclock();
|
|
|
|
getmicrouptime(&ds->last_comp_time);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
ds->busy_count--;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There might be some transactions (DEVSTAT_NO_DATA) that don't
|
|
|
|
* transfer any data.
|
|
|
|
*/
|
|
|
|
if (flags == DEVSTAT_READ) {
|
|
|
|
ds->bytes_read += bytes;
|
|
|
|
ds->num_reads++;
|
|
|
|
} else if (flags == DEVSTAT_WRITE) {
|
|
|
|
ds->bytes_written += bytes;
|
|
|
|
ds->num_writes++;
|
|
|
|
} else
|
|
|
|
ds->num_other++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep a count of the various tag types sent.
|
|
|
|
*/
|
|
|
|
if (tag_type != DEVSTAT_TAG_NONE)
|
|
|
|
ds->tag_types[tag_type]++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only update the busy time when we go idle. Otherwise, this
|
|
|
|
* calculation would require many more clock cycles.
|
|
|
|
*/
|
|
|
|
if (ds->busy_count == 0) {
|
|
|
|
/* Calculate how long we were busy */
|
|
|
|
busy_time = ds->last_comp_time;
|
|
|
|
timevalsub(&busy_time, &ds->start_time);
|
|
|
|
|
|
|
|
/* Add our busy time to the total busy time. */
|
|
|
|
timevaladd(&ds->busy_time, &busy_time);
|
1998-11-15 23:57:22 +00:00
|
|
|
} else if (ds->busy_count < 0)
|
1998-10-06 04:16:07 +00:00
|
|
|
printf("devstat_end_transaction: HELP!! busy_count "
|
|
|
|
"for %s%d is < 0 (%d)!\n", ds->device_name,
|
|
|
|
ds->unit_number, ds->busy_count);
|
1998-09-15 08:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the sysctl handler for the devstat package. The data pushed out
|
|
|
|
* on the kern.devstat.all sysctl variable consists of the current devstat
|
|
|
|
* generation number, and then an array of devstat structures, one for each
|
|
|
|
* device in the system.
|
|
|
|
*
|
|
|
|
* I'm really not too fond of this method of doing things, but there really
|
|
|
|
* aren't that many alternatives. We must have some method of making sure
|
|
|
|
* that the generation number the user gets corresponds with the data the
|
|
|
|
* user gets. If the user makes a separate sysctl call to get the
|
|
|
|
* generation, and then a sysctl call to get the device statistics, the
|
|
|
|
* device list could have changed in that brief period of time. By
|
|
|
|
* supplying the generation number along with the statistics output, we can
|
|
|
|
* guarantee that the generation number and the statistics match up.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_devstat SYSCTL_HANDLER_ARGS
|
|
|
|
{
|
|
|
|
int error, i;
|
|
|
|
struct devstat *nds;
|
|
|
|
struct devstatlist *devstat_head;
|
|
|
|
|
|
|
|
if (devstat_num_devs == 0)
|
|
|
|
return(EINVAL);
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
devstat_head = &device_statq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First push out the generation number.
|
|
|
|
*/
|
1998-09-20 00:11:23 +00:00
|
|
|
error = SYSCTL_OUT(req, &devstat_generation, sizeof(long));
|
1998-09-15 08:16:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now push out all the devices.
|
|
|
|
*/
|
|
|
|
for (i = 0, nds = devstat_head->stqh_first;
|
|
|
|
(nds != NULL) && (i < devstat_num_devs) && (error == 0);
|
|
|
|
nds = nds->dev_links.stqe_next, i++)
|
|
|
|
error = SYSCTL_OUT(req, nds, sizeof(struct devstat));
|
|
|
|
|
|
|
|
return(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sysctl entries for devstat. The first one is a node that all the rest
|
|
|
|
* hang off of.
|
|
|
|
*/
|
|
|
|
SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics");
|
|
|
|
|
|
|
|
SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE,
|
|
|
|
0, 0, sysctl_devstat, "S,devstat", "All Devices");
|
|
|
|
/*
|
|
|
|
* Export the number of devices in the system so that userland utilities
|
|
|
|
* can determine how much memory to allocate to hold all the devices.
|
|
|
|
*/
|
|
|
|
SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, &devstat_num_devs,
|
|
|
|
0, "Number of devices in the devstat list");
|
1998-09-20 00:11:23 +00:00
|
|
|
SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD,
|
|
|
|
&devstat_generation, 0, "Devstat list generation");
|
1998-09-15 08:16:17 +00:00
|
|
|
SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, &devstat_version,
|
|
|
|
0, "Devstat list version number");
|