summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_devstat.c
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1998-09-15 08:16:17 +0000
committergibbs <gibbs@FreeBSD.org>1998-09-15 08:16:17 +0000
commit78f0dc839b3d746460e6fd2f0ffbb260bf45504c (patch)
tree6dc8d67b9845c94159f542a3a07d6f831bec247c /sys/kern/subr_devstat.c
parent1bb65e0d8a28cf476487e517d8b682254d70de95 (diff)
downloadFreeBSD-src-78f0dc839b3d746460e6fd2f0ffbb260bf45504c.zip
FreeBSD-src-78f0dc839b3d746460e6fd2f0ffbb260bf45504c.tar.gz
New Kernel device statistics code.
Submitted by: "Kenneth D. Merry" <ken@plutotech.com>
Diffstat (limited to 'sys/kern/subr_devstat.c')
-rw-r--r--sys/kern/subr_devstat.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/sys/kern/subr_devstat.c b/sys/kern/subr_devstat.c
new file mode 100644
index 0000000..fe1f832
--- /dev/null
+++ b/sys/kern/subr_devstat.c
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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;
+static int devstat_generation;
+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
+devstat_add_entry(struct devstat *ds, char *dev_name,
+ 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);
+ 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);
+ } else if (ds->busy_count < 0)
+ printf("devstat_end_transaction: HELP!! busy_count is < 0!\n");
+}
+
+/*
+ * 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.
+ */
+ error = SYSCTL_OUT(req, &devstat_generation, sizeof(int));
+
+ /*
+ * 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");
+SYSCTL_INT(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, &devstat_generation,
+ 0, "Devstat list generation");
+SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, &devstat_version,
+ 0, "Devstat list version number");
OpenPOWER on IntegriCloud