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