17a59208dSJustin T. Gibbs /* 22a888f93SKenneth D. Merry * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. 37a59208dSJustin T. Gibbs * All rights reserved. 47a59208dSJustin T. Gibbs * 57a59208dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 67a59208dSJustin T. Gibbs * modification, are permitted provided that the following conditions 77a59208dSJustin T. Gibbs * are met: 87a59208dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 97a59208dSJustin T. Gibbs * notice, this list of conditions and the following disclaimer. 107a59208dSJustin T. Gibbs * 2. Redistributions in binary form must reproduce the above copyright 117a59208dSJustin T. Gibbs * notice, this list of conditions and the following disclaimer in the 127a59208dSJustin T. Gibbs * documentation and/or other materials provided with the distribution. 137a59208dSJustin T. Gibbs * 3. The name of the author may not be used to endorse or promote products 147a59208dSJustin T. Gibbs * derived from this software without specific prior written permission. 157a59208dSJustin T. Gibbs * 167a59208dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 177a59208dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 187a59208dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 197a59208dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 207a59208dSJustin T. Gibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 217a59208dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 227a59208dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 237a59208dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 247a59208dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 257a59208dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 267a59208dSJustin T. Gibbs * SUCH DAMAGE. 277a59208dSJustin T. Gibbs * 28c3aac50fSPeter Wemm * $FreeBSD$ 297a59208dSJustin T. Gibbs */ 307a59208dSJustin T. Gibbs 317a59208dSJustin T. Gibbs #include <sys/param.h> 327a59208dSJustin T. Gibbs #include <sys/kernel.h> 337a59208dSJustin T. Gibbs #include <sys/systm.h> 349626b608SPoul-Henning Kamp #include <sys/bio.h> 35f80d57eeSPoul-Henning Kamp #include <sys/buf.h> 367a59208dSJustin T. Gibbs #include <sys/sysctl.h> 377a59208dSJustin T. Gibbs 387a59208dSJustin T. Gibbs #include <sys/devicestat.h> 397a59208dSJustin T. Gibbs 407a59208dSJustin T. Gibbs static int devstat_num_devs; 41bcc6a3daSKenneth D. Merry static long devstat_generation; 427a59208dSJustin T. Gibbs static int devstat_version = DEVSTAT_VERSION; 437a59208dSJustin T. Gibbs static int devstat_current_devnumber; 447a59208dSJustin T. Gibbs 452a96b3faSEivind Eklund static STAILQ_HEAD(devstatlist, devstat) device_statq; 467a59208dSJustin T. Gibbs 477a59208dSJustin T. Gibbs /* 487a59208dSJustin T. Gibbs * Take a malloced and zeroed devstat structure given to us, fill it in 497a59208dSJustin T. Gibbs * and add it to the queue of devices. 507a59208dSJustin T. Gibbs */ 517a59208dSJustin T. Gibbs void 5214177d72SGarrett Wollman devstat_add_entry(struct devstat *ds, const char *dev_name, 537a59208dSJustin T. Gibbs int unit_number, u_int32_t block_size, 547a59208dSJustin T. Gibbs devstat_support_flags flags, 552a888f93SKenneth D. Merry devstat_type_flags device_type, 562a888f93SKenneth D. Merry devstat_priority priority) 577a59208dSJustin T. Gibbs { 587a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 592a888f93SKenneth D. Merry struct devstat *ds_tmp; 607a59208dSJustin T. Gibbs 617a59208dSJustin T. Gibbs if (ds == NULL) 627a59208dSJustin T. Gibbs return; 637a59208dSJustin T. Gibbs 647a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 657a59208dSJustin T. Gibbs STAILQ_INIT(&device_statq); 667a59208dSJustin T. Gibbs 677a59208dSJustin T. Gibbs devstat_generation++; 687a59208dSJustin T. Gibbs devstat_num_devs++; 697a59208dSJustin T. Gibbs 707a59208dSJustin T. Gibbs devstat_head = &device_statq; 717a59208dSJustin T. Gibbs 722a888f93SKenneth D. Merry /* 732a888f93SKenneth D. Merry * Priority sort. Each driver passes in its priority when it adds 742a888f93SKenneth D. Merry * its devstat entry. Drivers are sorted first by priority, and 752a888f93SKenneth D. Merry * then by probe order. 762a888f93SKenneth D. Merry * 772a888f93SKenneth D. Merry * For the first device, we just insert it, since the priority 782a888f93SKenneth D. Merry * doesn't really matter yet. Subsequent devices are inserted into 792a888f93SKenneth D. Merry * the list using the order outlined above. 802a888f93SKenneth D. Merry */ 812a888f93SKenneth D. Merry if (devstat_num_devs == 1) 827a59208dSJustin T. Gibbs STAILQ_INSERT_TAIL(devstat_head, ds, dev_links); 832a888f93SKenneth D. Merry else { 842a888f93SKenneth D. Merry for (ds_tmp = STAILQ_FIRST(devstat_head); ds_tmp != NULL; 852a888f93SKenneth D. Merry ds_tmp = STAILQ_NEXT(ds_tmp, dev_links)) { 862a888f93SKenneth D. Merry struct devstat *ds_next; 872a888f93SKenneth D. Merry 882a888f93SKenneth D. Merry ds_next = STAILQ_NEXT(ds_tmp, dev_links); 892a888f93SKenneth D. Merry 902a888f93SKenneth D. Merry /* 912a888f93SKenneth D. Merry * If we find a break between higher and lower 922a888f93SKenneth D. Merry * priority items, and if this item fits in the 932a888f93SKenneth D. Merry * break, insert it. This also applies if the 942a888f93SKenneth D. Merry * "lower priority item" is the end of the list. 952a888f93SKenneth D. Merry */ 962a888f93SKenneth D. Merry if ((priority <= ds_tmp->priority) 972a888f93SKenneth D. Merry && ((ds_next == NULL) 982a888f93SKenneth D. Merry || (priority > ds_next->priority))) { 992a888f93SKenneth D. Merry STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 1002a888f93SKenneth D. Merry dev_links); 1012a888f93SKenneth D. Merry break; 1022a888f93SKenneth D. Merry } else if (priority > ds_tmp->priority) { 1032a888f93SKenneth D. Merry /* 1042a888f93SKenneth D. Merry * If this is the case, we should be able 1052a888f93SKenneth D. Merry * to insert ourselves at the head of the 1062a888f93SKenneth D. Merry * list. If we can't, something is wrong. 1072a888f93SKenneth D. Merry */ 1082a888f93SKenneth D. Merry if (ds_tmp == STAILQ_FIRST(devstat_head)) { 1092a888f93SKenneth D. Merry STAILQ_INSERT_HEAD(devstat_head, 1102a888f93SKenneth D. Merry ds, dev_links); 1112a888f93SKenneth D. Merry break; 1122a888f93SKenneth D. Merry } else { 1132a888f93SKenneth D. Merry STAILQ_INSERT_TAIL(devstat_head, 1142a888f93SKenneth D. Merry ds, dev_links); 1152a888f93SKenneth D. Merry printf("devstat_add_entry: HELP! " 1162a888f93SKenneth D. Merry "sorting problem detected " 1172a888f93SKenneth D. Merry "for %s%d\n", dev_name, 1182a888f93SKenneth D. Merry unit_number); 1192a888f93SKenneth D. Merry break; 1202a888f93SKenneth D. Merry } 1212a888f93SKenneth D. Merry } 1222a888f93SKenneth D. Merry } 1232a888f93SKenneth D. Merry } 1247a59208dSJustin T. Gibbs 1257a59208dSJustin T. Gibbs ds->device_number = devstat_current_devnumber++; 1267a59208dSJustin T. Gibbs ds->unit_number = unit_number; 1277a59208dSJustin T. Gibbs strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 1282a888f93SKenneth D. Merry ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 1297a59208dSJustin T. Gibbs ds->block_size = block_size; 1307a59208dSJustin T. Gibbs ds->flags = flags; 1317a59208dSJustin T. Gibbs ds->device_type = device_type; 1322a888f93SKenneth D. Merry ds->priority = priority; 1337a59208dSJustin T. Gibbs getmicrotime(&ds->dev_creation_time); 1347a59208dSJustin T. Gibbs } 1357a59208dSJustin T. Gibbs 1367a59208dSJustin T. Gibbs /* 1377a59208dSJustin T. Gibbs * Remove a devstat structure from the list of devices. 1387a59208dSJustin T. Gibbs */ 1397a59208dSJustin T. Gibbs void 1407a59208dSJustin T. Gibbs devstat_remove_entry(struct devstat *ds) 1417a59208dSJustin T. Gibbs { 1427a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 1437a59208dSJustin T. Gibbs 1447a59208dSJustin T. Gibbs if (ds == NULL) 1457a59208dSJustin T. Gibbs return; 1467a59208dSJustin T. Gibbs 1477a59208dSJustin T. Gibbs devstat_generation++; 1487a59208dSJustin T. Gibbs devstat_num_devs--; 1497a59208dSJustin T. Gibbs 1507a59208dSJustin T. Gibbs devstat_head = &device_statq; 1517a59208dSJustin T. Gibbs 1527a59208dSJustin T. Gibbs /* Remove this entry from the devstat queue */ 1537a59208dSJustin T. Gibbs STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 1547a59208dSJustin T. Gibbs } 1557a59208dSJustin T. Gibbs 1567a59208dSJustin T. Gibbs /* 1577a59208dSJustin T. Gibbs * Record a transaction start. 1587a59208dSJustin T. Gibbs */ 1597a59208dSJustin T. Gibbs void 1607a59208dSJustin T. Gibbs devstat_start_transaction(struct devstat *ds) 1617a59208dSJustin T. Gibbs { 1627a59208dSJustin T. Gibbs /* sanity check */ 1637a59208dSJustin T. Gibbs if (ds == NULL) 1647a59208dSJustin T. Gibbs return; 1657a59208dSJustin T. Gibbs 1667a59208dSJustin T. Gibbs /* 1677a59208dSJustin T. Gibbs * We only want to set the start time when we are going from idle 1687a59208dSJustin T. Gibbs * to busy. The start time is really the start of the latest busy 1697a59208dSJustin T. Gibbs * period. 1707a59208dSJustin T. Gibbs */ 17139b3c6a9SBruce Evans if (ds->busy_count == 0) 1727a59208dSJustin T. Gibbs getmicrouptime(&ds->start_time); 1737a59208dSJustin T. Gibbs ds->busy_count++; 1747a59208dSJustin T. Gibbs } 1757a59208dSJustin T. Gibbs 1767a59208dSJustin T. Gibbs /* 1777a59208dSJustin T. Gibbs * Record the ending of a transaction, and incrment the various counters. 1787a59208dSJustin T. Gibbs */ 1797a59208dSJustin T. Gibbs void 1807a59208dSJustin T. Gibbs devstat_end_transaction(struct devstat *ds, u_int32_t bytes, 1817a59208dSJustin T. Gibbs devstat_tag_type tag_type, devstat_trans_flags flags) 1827a59208dSJustin T. Gibbs { 1837a59208dSJustin T. Gibbs struct timeval busy_time; 1847a59208dSJustin T. Gibbs 1857a59208dSJustin T. Gibbs /* sanity check */ 1867a59208dSJustin T. Gibbs if (ds == NULL) 1877a59208dSJustin T. Gibbs return; 1887a59208dSJustin T. Gibbs 1897a59208dSJustin T. Gibbs getmicrouptime(&ds->last_comp_time); 1907a59208dSJustin T. Gibbs ds->busy_count--; 1917a59208dSJustin T. Gibbs 1927a59208dSJustin T. Gibbs /* 1937a59208dSJustin T. Gibbs * There might be some transactions (DEVSTAT_NO_DATA) that don't 1947a59208dSJustin T. Gibbs * transfer any data. 1957a59208dSJustin T. Gibbs */ 1967a59208dSJustin T. Gibbs if (flags == DEVSTAT_READ) { 1977a59208dSJustin T. Gibbs ds->bytes_read += bytes; 1987a59208dSJustin T. Gibbs ds->num_reads++; 1997a59208dSJustin T. Gibbs } else if (flags == DEVSTAT_WRITE) { 2007a59208dSJustin T. Gibbs ds->bytes_written += bytes; 2017a59208dSJustin T. Gibbs ds->num_writes++; 202f80d57eeSPoul-Henning Kamp } else if (flags == DEVSTAT_FREE) { 203f80d57eeSPoul-Henning Kamp ds->bytes_freed += bytes; 204f80d57eeSPoul-Henning Kamp ds->num_frees++; 2057a59208dSJustin T. Gibbs } else 2067a59208dSJustin T. Gibbs ds->num_other++; 2077a59208dSJustin T. Gibbs 2087a59208dSJustin T. Gibbs /* 2097a59208dSJustin T. Gibbs * Keep a count of the various tag types sent. 2107a59208dSJustin T. Gibbs */ 2118db3b947SPoul-Henning Kamp if ((ds->flags & DEVSTAT_NO_ORDERED_TAGS) == 0 && 212f80d57eeSPoul-Henning Kamp tag_type != DEVSTAT_TAG_NONE) 2137a59208dSJustin T. Gibbs ds->tag_types[tag_type]++; 2147a59208dSJustin T. Gibbs 2157a59208dSJustin T. Gibbs /* 2167a59208dSJustin T. Gibbs * We only update the busy time when we go idle. Otherwise, this 2177a59208dSJustin T. Gibbs * calculation would require many more clock cycles. 2187a59208dSJustin T. Gibbs */ 2197a59208dSJustin T. Gibbs if (ds->busy_count == 0) { 2207a59208dSJustin T. Gibbs /* Calculate how long we were busy */ 2217a59208dSJustin T. Gibbs busy_time = ds->last_comp_time; 2227a59208dSJustin T. Gibbs timevalsub(&busy_time, &ds->start_time); 2237a59208dSJustin T. Gibbs 2247a59208dSJustin T. Gibbs /* Add our busy time to the total busy time. */ 2257a59208dSJustin T. Gibbs timevaladd(&ds->busy_time, &busy_time); 226d9e371b9SKenneth D. Merry } else if (ds->busy_count < 0) 227a795f8bbSKenneth D. Merry printf("devstat_end_transaction: HELP!! busy_count " 228a795f8bbSKenneth D. Merry "for %s%d is < 0 (%d)!\n", ds->device_name, 229a795f8bbSKenneth D. Merry ds->unit_number, ds->busy_count); 2307a59208dSJustin T. Gibbs } 2317a59208dSJustin T. Gibbs 232f80d57eeSPoul-Henning Kamp void 233f80d57eeSPoul-Henning Kamp devstat_end_transaction_buf(struct devstat *ds, struct buf *bp) 234f80d57eeSPoul-Henning Kamp { 235f80d57eeSPoul-Henning Kamp devstat_trans_flags flg; 236f80d57eeSPoul-Henning Kamp 23721144e3bSPoul-Henning Kamp if (bp->b_iocmd == BIO_DELETE) 238f80d57eeSPoul-Henning Kamp flg = DEVSTAT_FREE; 23921144e3bSPoul-Henning Kamp else if (bp->b_iocmd == BIO_READ) 240f80d57eeSPoul-Henning Kamp flg = DEVSTAT_READ; 241f80d57eeSPoul-Henning Kamp else 242f80d57eeSPoul-Henning Kamp flg = DEVSTAT_WRITE; 243f80d57eeSPoul-Henning Kamp 244c8a90c31SPoul-Henning Kamp devstat_end_transaction(ds, bp->b_bcount - bp->b_resid, 245c244d2deSPoul-Henning Kamp (bp->b_ioflags & BIO_ORDERED) ? 246c8a90c31SPoul-Henning Kamp DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 247f80d57eeSPoul-Henning Kamp } 248f80d57eeSPoul-Henning Kamp 249282ac69eSPoul-Henning Kamp void 250282ac69eSPoul-Henning Kamp devstat_end_transaction_bio(struct devstat *ds, struct bio *bp) 251282ac69eSPoul-Henning Kamp { 252282ac69eSPoul-Henning Kamp devstat_trans_flags flg; 253282ac69eSPoul-Henning Kamp 254282ac69eSPoul-Henning Kamp if (bp->bio_cmd == BIO_DELETE) 255282ac69eSPoul-Henning Kamp flg = DEVSTAT_FREE; 256282ac69eSPoul-Henning Kamp else if (bp->bio_cmd == BIO_READ) 257282ac69eSPoul-Henning Kamp flg = DEVSTAT_READ; 258282ac69eSPoul-Henning Kamp else 259282ac69eSPoul-Henning Kamp flg = DEVSTAT_WRITE; 260282ac69eSPoul-Henning Kamp 261282ac69eSPoul-Henning Kamp devstat_end_transaction(ds, bp->bio_bcount - bp->bio_resid, 262282ac69eSPoul-Henning Kamp (bp->bio_flags & BIO_ORDERED) ? 263282ac69eSPoul-Henning Kamp DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 264282ac69eSPoul-Henning Kamp } 265282ac69eSPoul-Henning Kamp 2667a59208dSJustin T. Gibbs /* 2677a59208dSJustin T. Gibbs * This is the sysctl handler for the devstat package. The data pushed out 2687a59208dSJustin T. Gibbs * on the kern.devstat.all sysctl variable consists of the current devstat 2697a59208dSJustin T. Gibbs * generation number, and then an array of devstat structures, one for each 2707a59208dSJustin T. Gibbs * device in the system. 2717a59208dSJustin T. Gibbs * 2727a59208dSJustin T. Gibbs * I'm really not too fond of this method of doing things, but there really 2737a59208dSJustin T. Gibbs * aren't that many alternatives. We must have some method of making sure 2747a59208dSJustin T. Gibbs * that the generation number the user gets corresponds with the data the 2757a59208dSJustin T. Gibbs * user gets. If the user makes a separate sysctl call to get the 2767a59208dSJustin T. Gibbs * generation, and then a sysctl call to get the device statistics, the 2777a59208dSJustin T. Gibbs * device list could have changed in that brief period of time. By 2787a59208dSJustin T. Gibbs * supplying the generation number along with the statistics output, we can 2797a59208dSJustin T. Gibbs * guarantee that the generation number and the statistics match up. 2807a59208dSJustin T. Gibbs */ 2817a59208dSJustin T. Gibbs static int 2827a59208dSJustin T. Gibbs sysctl_devstat SYSCTL_HANDLER_ARGS 2837a59208dSJustin T. Gibbs { 2847a59208dSJustin T. Gibbs int error, i; 2857a59208dSJustin T. Gibbs struct devstat *nds; 2867a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 2877a59208dSJustin T. Gibbs 2887a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 2897a59208dSJustin T. Gibbs return(EINVAL); 2907a59208dSJustin T. Gibbs 2917a59208dSJustin T. Gibbs error = 0; 2927a59208dSJustin T. Gibbs devstat_head = &device_statq; 2937a59208dSJustin T. Gibbs 2947a59208dSJustin T. Gibbs /* 2957a59208dSJustin T. Gibbs * First push out the generation number. 2967a59208dSJustin T. Gibbs */ 297bcc6a3daSKenneth D. Merry error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 2987a59208dSJustin T. Gibbs 2997a59208dSJustin T. Gibbs /* 3007a59208dSJustin T. Gibbs * Now push out all the devices. 3017a59208dSJustin T. Gibbs */ 3022e3c8fcbSPoul-Henning Kamp for (i = 0, nds = STAILQ_FIRST(devstat_head); 3037a59208dSJustin T. Gibbs (nds != NULL) && (i < devstat_num_devs) && (error == 0); 3042e3c8fcbSPoul-Henning Kamp nds = STAILQ_NEXT(nds, dev_links), i++) 3057a59208dSJustin T. Gibbs error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 3067a59208dSJustin T. Gibbs 3077a59208dSJustin T. Gibbs return(error); 3087a59208dSJustin T. Gibbs } 3097a59208dSJustin T. Gibbs 3107a59208dSJustin T. Gibbs /* 3117a59208dSJustin T. Gibbs * Sysctl entries for devstat. The first one is a node that all the rest 3127a59208dSJustin T. Gibbs * hang off of. 3137a59208dSJustin T. Gibbs */ 3147a59208dSJustin T. Gibbs SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 3157a59208dSJustin T. Gibbs 3167a59208dSJustin T. Gibbs SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 3173d177f46SBill Fumerola 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 3187a59208dSJustin T. Gibbs /* 3197a59208dSJustin T. Gibbs * Export the number of devices in the system so that userland utilities 3207a59208dSJustin T. Gibbs * can determine how much memory to allocate to hold all the devices. 3217a59208dSJustin T. Gibbs */ 3223d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 3233d177f46SBill Fumerola &devstat_num_devs, 0, "Number of devices in the devstat list"); 324bcc6a3daSKenneth D. Merry SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 325486bddb0SDoug Rabson &devstat_generation, "Devstat list generation"); 3263d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 3273d177f46SBill Fumerola &devstat_version, 0, "Devstat list version number"); 328