subr_devstat.c revision 62622
139229Sgibbs/* 243819Sken * Copyright (c) 1997, 1998, 1999 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 * 2850477Speter * $FreeBSD: head/sys/kern/subr_devstat.c 62622 2000-07-05 07:46:41Z jhb $ 2939229Sgibbs */ 3039229Sgibbs 3139229Sgibbs#include <sys/param.h> 3239229Sgibbs#include <sys/kernel.h> 3339229Sgibbs#include <sys/systm.h> 3460041Sphk#include <sys/bio.h> 3539229Sgibbs#include <sys/sysctl.h> 3639229Sgibbs 3739229Sgibbs#include <sys/devicestat.h> 3839229Sgibbs 3939229Sgibbsstatic int devstat_num_devs; 4039498Skenstatic long devstat_generation; 4139229Sgibbsstatic int devstat_version = DEVSTAT_VERSION; 4239229Sgibbsstatic int devstat_current_devnumber; 4339229Sgibbs 4460938Sjakestatic STAILQ_HEAD(devstatlist, devstat) device_statq; 4539229Sgibbs 4639229Sgibbs/* 4739229Sgibbs * Take a malloced and zeroed devstat structure given to us, fill it in 4839229Sgibbs * and add it to the queue of devices. 4939229Sgibbs */ 5039229Sgibbsvoid 5141153Swollmandevstat_add_entry(struct devstat *ds, const char *dev_name, 5239229Sgibbs int unit_number, u_int32_t block_size, 5339229Sgibbs devstat_support_flags flags, 5443819Sken devstat_type_flags device_type, 5543819Sken devstat_priority priority) 5639229Sgibbs{ 5739229Sgibbs struct devstatlist *devstat_head; 5843819Sken struct devstat *ds_tmp; 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 7143819Sken /* 7243819Sken * Priority sort. Each driver passes in its priority when it adds 7343819Sken * its devstat entry. Drivers are sorted first by priority, and 7443819Sken * then by probe order. 7543819Sken * 7643819Sken * For the first device, we just insert it, since the priority 7743819Sken * doesn't really matter yet. Subsequent devices are inserted into 7843819Sken * the list using the order outlined above. 7943819Sken */ 8043819Sken if (devstat_num_devs == 1) 8143819Sken STAILQ_INSERT_TAIL(devstat_head, ds, dev_links); 8243819Sken else { 8343819Sken for (ds_tmp = STAILQ_FIRST(devstat_head); ds_tmp != NULL; 8443819Sken ds_tmp = STAILQ_NEXT(ds_tmp, dev_links)) { 8543819Sken struct devstat *ds_next; 8639229Sgibbs 8743819Sken ds_next = STAILQ_NEXT(ds_tmp, dev_links); 8843819Sken 8943819Sken /* 9043819Sken * If we find a break between higher and lower 9143819Sken * priority items, and if this item fits in the 9243819Sken * break, insert it. This also applies if the 9343819Sken * "lower priority item" is the end of the list. 9443819Sken */ 9543819Sken if ((priority <= ds_tmp->priority) 9643819Sken && ((ds_next == NULL) 9743819Sken || (priority > ds_next->priority))) { 9843819Sken STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 9943819Sken dev_links); 10043819Sken break; 10143819Sken } else if (priority > ds_tmp->priority) { 10243819Sken /* 10343819Sken * If this is the case, we should be able 10443819Sken * to insert ourselves at the head of the 10543819Sken * list. If we can't, something is wrong. 10643819Sken */ 10743819Sken if (ds_tmp == STAILQ_FIRST(devstat_head)) { 10843819Sken STAILQ_INSERT_HEAD(devstat_head, 10943819Sken ds, dev_links); 11043819Sken break; 11143819Sken } else { 11243819Sken STAILQ_INSERT_TAIL(devstat_head, 11343819Sken ds, dev_links); 11443819Sken printf("devstat_add_entry: HELP! " 11543819Sken "sorting problem detected " 11643819Sken "for %s%d\n", dev_name, 11743819Sken unit_number); 11843819Sken break; 11943819Sken } 12043819Sken } 12143819Sken } 12243819Sken } 12343819Sken 12439229Sgibbs ds->device_number = devstat_current_devnumber++; 12539229Sgibbs ds->unit_number = unit_number; 12639229Sgibbs strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 12743819Sken ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 12839229Sgibbs ds->block_size = block_size; 12939229Sgibbs ds->flags = flags; 13039229Sgibbs ds->device_type = device_type; 13143819Sken ds->priority = priority; 13239229Sgibbs getmicrotime(&ds->dev_creation_time); 13339229Sgibbs} 13439229Sgibbs 13539229Sgibbs/* 13639229Sgibbs * Remove a devstat structure from the list of devices. 13739229Sgibbs */ 13839229Sgibbsvoid 13939229Sgibbsdevstat_remove_entry(struct devstat *ds) 14039229Sgibbs{ 14139229Sgibbs struct devstatlist *devstat_head; 14239229Sgibbs 14339229Sgibbs if (ds == NULL) 14439229Sgibbs return; 14539229Sgibbs 14639229Sgibbs devstat_generation++; 14739229Sgibbs devstat_num_devs--; 14839229Sgibbs 14939229Sgibbs devstat_head = &device_statq; 15039229Sgibbs 15139229Sgibbs /* Remove this entry from the devstat queue */ 15260938Sjake STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 15339229Sgibbs} 15439229Sgibbs 15539229Sgibbs/* 15639229Sgibbs * Record a transaction start. 15739229Sgibbs */ 15839229Sgibbsvoid 15939229Sgibbsdevstat_start_transaction(struct devstat *ds) 16039229Sgibbs{ 16139229Sgibbs /* sanity check */ 16239229Sgibbs if (ds == NULL) 16339229Sgibbs return; 16439229Sgibbs 16539229Sgibbs /* 16639229Sgibbs * We only want to set the start time when we are going from idle 16739229Sgibbs * to busy. The start time is really the start of the latest busy 16839229Sgibbs * period. 16939229Sgibbs */ 17051856Sbde if (ds->busy_count == 0) 17139229Sgibbs getmicrouptime(&ds->start_time); 17239229Sgibbs ds->busy_count++; 17339229Sgibbs} 17439229Sgibbs 17539229Sgibbs/* 17639229Sgibbs * Record the ending of a transaction, and incrment the various counters. 17739229Sgibbs */ 17839229Sgibbsvoid 17939229Sgibbsdevstat_end_transaction(struct devstat *ds, u_int32_t bytes, 18039229Sgibbs devstat_tag_type tag_type, devstat_trans_flags flags) 18139229Sgibbs{ 18239229Sgibbs struct timeval busy_time; 18339229Sgibbs 18439229Sgibbs /* sanity check */ 18539229Sgibbs if (ds == NULL) 18639229Sgibbs return; 18739229Sgibbs 18839229Sgibbs getmicrouptime(&ds->last_comp_time); 18939229Sgibbs ds->busy_count--; 19039229Sgibbs 19139229Sgibbs /* 19239229Sgibbs * There might be some transactions (DEVSTAT_NO_DATA) that don't 19339229Sgibbs * transfer any data. 19439229Sgibbs */ 19539229Sgibbs if (flags == DEVSTAT_READ) { 19639229Sgibbs ds->bytes_read += bytes; 19739229Sgibbs ds->num_reads++; 19839229Sgibbs } else if (flags == DEVSTAT_WRITE) { 19939229Sgibbs ds->bytes_written += bytes; 20039229Sgibbs ds->num_writes++; 20151375Sphk } else if (flags == DEVSTAT_FREE) { 20251375Sphk ds->bytes_freed += bytes; 20351375Sphk ds->num_frees++; 20439229Sgibbs } else 20539229Sgibbs ds->num_other++; 20639229Sgibbs 20739229Sgibbs /* 20839229Sgibbs * Keep a count of the various tag types sent. 20939229Sgibbs */ 21051397Sphk if ((ds->flags & DEVSTAT_NO_ORDERED_TAGS) == 0 && 21151375Sphk tag_type != DEVSTAT_TAG_NONE) 21239229Sgibbs ds->tag_types[tag_type]++; 21339229Sgibbs 21439229Sgibbs /* 21539229Sgibbs * We only update the busy time when we go idle. Otherwise, this 21639229Sgibbs * calculation would require many more clock cycles. 21739229Sgibbs */ 21839229Sgibbs if (ds->busy_count == 0) { 21939229Sgibbs /* Calculate how long we were busy */ 22039229Sgibbs busy_time = ds->last_comp_time; 22139229Sgibbs timevalsub(&busy_time, &ds->start_time); 22239229Sgibbs 22339229Sgibbs /* Add our busy time to the total busy time. */ 22439229Sgibbs timevaladd(&ds->busy_time, &busy_time); 22541193Sken } else if (ds->busy_count < 0) 22639985Sken printf("devstat_end_transaction: HELP!! busy_count " 22739985Sken "for %s%d is < 0 (%d)!\n", ds->device_name, 22839985Sken ds->unit_number, ds->busy_count); 22939229Sgibbs} 23039229Sgibbs 23151375Sphkvoid 23258942Sphkdevstat_end_transaction_bio(struct devstat *ds, struct bio *bp) 23358942Sphk{ 23458942Sphk devstat_trans_flags flg; 23558942Sphk 23658942Sphk if (bp->bio_cmd == BIO_DELETE) 23758942Sphk flg = DEVSTAT_FREE; 23858942Sphk else if (bp->bio_cmd == BIO_READ) 23958942Sphk flg = DEVSTAT_READ; 24058942Sphk else 24158942Sphk flg = DEVSTAT_WRITE; 24258942Sphk 24358942Sphk devstat_end_transaction(ds, bp->bio_bcount - bp->bio_resid, 24458942Sphk (bp->bio_flags & BIO_ORDERED) ? 24558942Sphk DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 24658942Sphk} 24758942Sphk 24839229Sgibbs/* 24939229Sgibbs * This is the sysctl handler for the devstat package. The data pushed out 25039229Sgibbs * on the kern.devstat.all sysctl variable consists of the current devstat 25139229Sgibbs * generation number, and then an array of devstat structures, one for each 25239229Sgibbs * device in the system. 25339229Sgibbs * 25439229Sgibbs * I'm really not too fond of this method of doing things, but there really 25539229Sgibbs * aren't that many alternatives. We must have some method of making sure 25639229Sgibbs * that the generation number the user gets corresponds with the data the 25739229Sgibbs * user gets. If the user makes a separate sysctl call to get the 25839229Sgibbs * generation, and then a sysctl call to get the device statistics, the 25939229Sgibbs * device list could have changed in that brief period of time. By 26039229Sgibbs * supplying the generation number along with the statistics output, we can 26139229Sgibbs * guarantee that the generation number and the statistics match up. 26239229Sgibbs */ 26339229Sgibbsstatic int 26462573Sphksysctl_devstat(SYSCTL_HANDLER_ARGS) 26539229Sgibbs{ 26639229Sgibbs int error, i; 26739229Sgibbs struct devstat *nds; 26839229Sgibbs struct devstatlist *devstat_head; 26939229Sgibbs 27039229Sgibbs if (devstat_num_devs == 0) 27139229Sgibbs return(EINVAL); 27239229Sgibbs 27339229Sgibbs error = 0; 27439229Sgibbs devstat_head = &device_statq; 27539229Sgibbs 27639229Sgibbs /* 27739229Sgibbs * First push out the generation number. 27839229Sgibbs */ 27939498Sken error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 28039229Sgibbs 28139229Sgibbs /* 28239229Sgibbs * Now push out all the devices. 28339229Sgibbs */ 28453212Sphk for (i = 0, nds = STAILQ_FIRST(devstat_head); 28539229Sgibbs (nds != NULL) && (i < devstat_num_devs) && (error == 0); 28653212Sphk nds = STAILQ_NEXT(nds, dev_links), i++) 28739229Sgibbs error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 28839229Sgibbs 28939229Sgibbs return(error); 29039229Sgibbs} 29139229Sgibbs 29239229Sgibbs/* 29339229Sgibbs * Sysctl entries for devstat. The first one is a node that all the rest 29439229Sgibbs * hang off of. 29539229Sgibbs */ 29639229SgibbsSYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 29739229Sgibbs 29839229SgibbsSYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 29946381Sbillf 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 30039229Sgibbs/* 30139229Sgibbs * Export the number of devices in the system so that userland utilities 30239229Sgibbs * can determine how much memory to allocate to hold all the devices. 30339229Sgibbs */ 30446381SbillfSYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 30546381Sbillf &devstat_num_devs, 0, "Number of devices in the devstat list"); 30639498SkenSYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 30762622Sjhb &devstat_generation, 0, "Devstat list generation"); 30846381SbillfSYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 30946381Sbillf &devstat_version, 0, "Devstat list version number"); 310