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> 357a59208dSJustin T. Gibbs #include <sys/sysctl.h> 367a59208dSJustin T. Gibbs 377a59208dSJustin T. Gibbs #include <sys/devicestat.h> 387a59208dSJustin T. Gibbs 397a59208dSJustin T. Gibbs static int devstat_num_devs; 40bcc6a3daSKenneth D. Merry static long devstat_generation; 417a59208dSJustin T. Gibbs static int devstat_version = DEVSTAT_VERSION; 427a59208dSJustin T. Gibbs static int devstat_current_devnumber; 437a59208dSJustin T. Gibbs 44e3975643SJake Burkholder static STAILQ_HEAD(devstatlist, devstat) device_statq; 457a59208dSJustin T. Gibbs 467a59208dSJustin T. Gibbs /* 477a59208dSJustin T. Gibbs * Take a malloced and zeroed devstat structure given to us, fill it in 487a59208dSJustin T. Gibbs * and add it to the queue of devices. 497a59208dSJustin T. Gibbs */ 507a59208dSJustin T. Gibbs void 5114177d72SGarrett Wollman devstat_add_entry(struct devstat *ds, const char *dev_name, 527a59208dSJustin T. Gibbs int unit_number, u_int32_t block_size, 537a59208dSJustin T. Gibbs devstat_support_flags flags, 542a888f93SKenneth D. Merry devstat_type_flags device_type, 552a888f93SKenneth D. Merry devstat_priority priority) 567a59208dSJustin T. Gibbs { 577a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 582a888f93SKenneth D. Merry struct devstat *ds_tmp; 597a59208dSJustin T. Gibbs 607a59208dSJustin T. Gibbs if (ds == NULL) 617a59208dSJustin T. Gibbs return; 627a59208dSJustin T. Gibbs 637a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 647a59208dSJustin T. Gibbs STAILQ_INIT(&device_statq); 657a59208dSJustin T. Gibbs 667a59208dSJustin T. Gibbs devstat_generation++; 677a59208dSJustin T. Gibbs devstat_num_devs++; 687a59208dSJustin T. Gibbs 697a59208dSJustin T. Gibbs devstat_head = &device_statq; 707a59208dSJustin T. Gibbs 712a888f93SKenneth D. Merry /* 722a888f93SKenneth D. Merry * Priority sort. Each driver passes in its priority when it adds 732a888f93SKenneth D. Merry * its devstat entry. Drivers are sorted first by priority, and 742a888f93SKenneth D. Merry * then by probe order. 752a888f93SKenneth D. Merry * 762a888f93SKenneth D. Merry * For the first device, we just insert it, since the priority 772a888f93SKenneth D. Merry * doesn't really matter yet. Subsequent devices are inserted into 782a888f93SKenneth D. Merry * the list using the order outlined above. 792a888f93SKenneth D. Merry */ 802a888f93SKenneth D. Merry if (devstat_num_devs == 1) 817a59208dSJustin T. Gibbs STAILQ_INSERT_TAIL(devstat_head, ds, dev_links); 822a888f93SKenneth D. Merry else { 832a888f93SKenneth D. Merry for (ds_tmp = STAILQ_FIRST(devstat_head); ds_tmp != NULL; 842a888f93SKenneth D. Merry ds_tmp = STAILQ_NEXT(ds_tmp, dev_links)) { 852a888f93SKenneth D. Merry struct devstat *ds_next; 862a888f93SKenneth D. Merry 872a888f93SKenneth D. Merry ds_next = STAILQ_NEXT(ds_tmp, dev_links); 882a888f93SKenneth D. Merry 892a888f93SKenneth D. Merry /* 902a888f93SKenneth D. Merry * If we find a break between higher and lower 912a888f93SKenneth D. Merry * priority items, and if this item fits in the 922a888f93SKenneth D. Merry * break, insert it. This also applies if the 932a888f93SKenneth D. Merry * "lower priority item" is the end of the list. 942a888f93SKenneth D. Merry */ 952a888f93SKenneth D. Merry if ((priority <= ds_tmp->priority) 962a888f93SKenneth D. Merry && ((ds_next == NULL) 972a888f93SKenneth D. Merry || (priority > ds_next->priority))) { 982a888f93SKenneth D. Merry STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 992a888f93SKenneth D. Merry dev_links); 1002a888f93SKenneth D. Merry break; 1012a888f93SKenneth D. Merry } else if (priority > ds_tmp->priority) { 1022a888f93SKenneth D. Merry /* 1032a888f93SKenneth D. Merry * If this is the case, we should be able 1042a888f93SKenneth D. Merry * to insert ourselves at the head of the 1052a888f93SKenneth D. Merry * list. If we can't, something is wrong. 1062a888f93SKenneth D. Merry */ 1072a888f93SKenneth D. Merry if (ds_tmp == STAILQ_FIRST(devstat_head)) { 1082a888f93SKenneth D. Merry STAILQ_INSERT_HEAD(devstat_head, 1092a888f93SKenneth D. Merry ds, dev_links); 1102a888f93SKenneth D. Merry break; 1112a888f93SKenneth D. Merry } else { 1122a888f93SKenneth D. Merry STAILQ_INSERT_TAIL(devstat_head, 1132a888f93SKenneth D. Merry ds, dev_links); 1142a888f93SKenneth D. Merry printf("devstat_add_entry: HELP! " 1152a888f93SKenneth D. Merry "sorting problem detected " 1162a888f93SKenneth D. Merry "for %s%d\n", dev_name, 1172a888f93SKenneth D. Merry unit_number); 1182a888f93SKenneth D. Merry break; 1192a888f93SKenneth D. Merry } 1202a888f93SKenneth D. Merry } 1212a888f93SKenneth D. Merry } 1222a888f93SKenneth D. Merry } 1237a59208dSJustin T. Gibbs 1247a59208dSJustin T. Gibbs ds->device_number = devstat_current_devnumber++; 1257a59208dSJustin T. Gibbs ds->unit_number = unit_number; 1267a59208dSJustin T. Gibbs strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 1272a888f93SKenneth D. Merry ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 1287a59208dSJustin T. Gibbs ds->block_size = block_size; 1297a59208dSJustin T. Gibbs ds->flags = flags; 1307a59208dSJustin T. Gibbs ds->device_type = device_type; 1312a888f93SKenneth D. Merry ds->priority = priority; 1327a59208dSJustin T. Gibbs getmicrotime(&ds->dev_creation_time); 1337a59208dSJustin T. Gibbs } 1347a59208dSJustin T. Gibbs 1357a59208dSJustin T. Gibbs /* 1367a59208dSJustin T. Gibbs * Remove a devstat structure from the list of devices. 1377a59208dSJustin T. Gibbs */ 1387a59208dSJustin T. Gibbs void 1397a59208dSJustin T. Gibbs devstat_remove_entry(struct devstat *ds) 1407a59208dSJustin T. Gibbs { 1417a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 1427a59208dSJustin T. Gibbs 1437a59208dSJustin T. Gibbs if (ds == NULL) 1447a59208dSJustin T. Gibbs return; 1457a59208dSJustin T. Gibbs 1467a59208dSJustin T. Gibbs devstat_generation++; 1477a59208dSJustin T. Gibbs devstat_num_devs--; 1487a59208dSJustin T. Gibbs 1497a59208dSJustin T. Gibbs devstat_head = &device_statq; 1507a59208dSJustin T. Gibbs 1517a59208dSJustin T. Gibbs /* Remove this entry from the devstat queue */ 152e3975643SJake Burkholder STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 1537a59208dSJustin T. Gibbs } 1547a59208dSJustin T. Gibbs 1557a59208dSJustin T. Gibbs /* 1567a59208dSJustin T. Gibbs * Record a transaction start. 1577a59208dSJustin T. Gibbs */ 1587a59208dSJustin T. Gibbs void 1597a59208dSJustin T. Gibbs devstat_start_transaction(struct devstat *ds) 1607a59208dSJustin T. Gibbs { 1617a59208dSJustin T. Gibbs /* sanity check */ 1627a59208dSJustin T. Gibbs if (ds == NULL) 1637a59208dSJustin T. Gibbs return; 1647a59208dSJustin T. Gibbs 1657a59208dSJustin T. Gibbs /* 1667a59208dSJustin T. Gibbs * We only want to set the start time when we are going from idle 1677a59208dSJustin T. Gibbs * to busy. The start time is really the start of the latest busy 1687a59208dSJustin T. Gibbs * period. 1697a59208dSJustin T. Gibbs */ 17039b3c6a9SBruce Evans if (ds->busy_count == 0) 1717a59208dSJustin T. Gibbs getmicrouptime(&ds->start_time); 1727a59208dSJustin T. Gibbs ds->busy_count++; 1737a59208dSJustin T. Gibbs } 1747a59208dSJustin T. Gibbs 1757a59208dSJustin T. Gibbs /* 1767a59208dSJustin T. Gibbs * Record the ending of a transaction, and incrment the various counters. 1777a59208dSJustin T. Gibbs */ 1787a59208dSJustin T. Gibbs void 1797a59208dSJustin T. Gibbs devstat_end_transaction(struct devstat *ds, u_int32_t bytes, 1807a59208dSJustin T. Gibbs devstat_tag_type tag_type, devstat_trans_flags flags) 1817a59208dSJustin T. Gibbs { 1827a59208dSJustin T. Gibbs struct timeval busy_time; 1837a59208dSJustin T. Gibbs 1847a59208dSJustin T. Gibbs /* sanity check */ 1857a59208dSJustin T. Gibbs if (ds == NULL) 1867a59208dSJustin T. Gibbs return; 1877a59208dSJustin T. Gibbs 1887a59208dSJustin T. Gibbs getmicrouptime(&ds->last_comp_time); 1897a59208dSJustin T. Gibbs ds->busy_count--; 1907a59208dSJustin T. Gibbs 1917a59208dSJustin T. Gibbs /* 1927a59208dSJustin T. Gibbs * There might be some transactions (DEVSTAT_NO_DATA) that don't 1937a59208dSJustin T. Gibbs * transfer any data. 1947a59208dSJustin T. Gibbs */ 1957a59208dSJustin T. Gibbs if (flags == DEVSTAT_READ) { 1967a59208dSJustin T. Gibbs ds->bytes_read += bytes; 1977a59208dSJustin T. Gibbs ds->num_reads++; 1987a59208dSJustin T. Gibbs } else if (flags == DEVSTAT_WRITE) { 1997a59208dSJustin T. Gibbs ds->bytes_written += bytes; 2007a59208dSJustin T. Gibbs ds->num_writes++; 201f80d57eeSPoul-Henning Kamp } else if (flags == DEVSTAT_FREE) { 202f80d57eeSPoul-Henning Kamp ds->bytes_freed += bytes; 203f80d57eeSPoul-Henning Kamp ds->num_frees++; 2047a59208dSJustin T. Gibbs } else 2057a59208dSJustin T. Gibbs ds->num_other++; 2067a59208dSJustin T. Gibbs 2077a59208dSJustin T. Gibbs /* 2087a59208dSJustin T. Gibbs * Keep a count of the various tag types sent. 2097a59208dSJustin T. Gibbs */ 2108db3b947SPoul-Henning Kamp if ((ds->flags & DEVSTAT_NO_ORDERED_TAGS) == 0 && 211f80d57eeSPoul-Henning Kamp tag_type != DEVSTAT_TAG_NONE) 2127a59208dSJustin T. Gibbs ds->tag_types[tag_type]++; 2137a59208dSJustin T. Gibbs 2147a59208dSJustin T. Gibbs /* 2157a59208dSJustin T. Gibbs * We only update the busy time when we go idle. Otherwise, this 2167a59208dSJustin T. Gibbs * calculation would require many more clock cycles. 2177a59208dSJustin T. Gibbs */ 2187a59208dSJustin T. Gibbs if (ds->busy_count == 0) { 2197a59208dSJustin T. Gibbs /* Calculate how long we were busy */ 2207a59208dSJustin T. Gibbs busy_time = ds->last_comp_time; 2217a59208dSJustin T. Gibbs timevalsub(&busy_time, &ds->start_time); 2227a59208dSJustin T. Gibbs 2237a59208dSJustin T. Gibbs /* Add our busy time to the total busy time. */ 2247a59208dSJustin T. Gibbs timevaladd(&ds->busy_time, &busy_time); 225d9e371b9SKenneth D. Merry } else if (ds->busy_count < 0) 226a795f8bbSKenneth D. Merry printf("devstat_end_transaction: HELP!! busy_count " 227a795f8bbSKenneth D. Merry "for %s%d is < 0 (%d)!\n", ds->device_name, 228a795f8bbSKenneth D. Merry ds->unit_number, ds->busy_count); 2297a59208dSJustin T. Gibbs } 2307a59208dSJustin T. Gibbs 231f80d57eeSPoul-Henning Kamp void 232282ac69eSPoul-Henning Kamp devstat_end_transaction_bio(struct devstat *ds, struct bio *bp) 233282ac69eSPoul-Henning Kamp { 234282ac69eSPoul-Henning Kamp devstat_trans_flags flg; 235282ac69eSPoul-Henning Kamp 236282ac69eSPoul-Henning Kamp if (bp->bio_cmd == BIO_DELETE) 237282ac69eSPoul-Henning Kamp flg = DEVSTAT_FREE; 238282ac69eSPoul-Henning Kamp else if (bp->bio_cmd == BIO_READ) 239282ac69eSPoul-Henning Kamp flg = DEVSTAT_READ; 240282ac69eSPoul-Henning Kamp else 241282ac69eSPoul-Henning Kamp flg = DEVSTAT_WRITE; 242282ac69eSPoul-Henning Kamp 243282ac69eSPoul-Henning Kamp devstat_end_transaction(ds, bp->bio_bcount - bp->bio_resid, 244282ac69eSPoul-Henning Kamp (bp->bio_flags & BIO_ORDERED) ? 245282ac69eSPoul-Henning Kamp DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 246282ac69eSPoul-Henning Kamp } 247282ac69eSPoul-Henning Kamp 2487a59208dSJustin T. Gibbs /* 2497a59208dSJustin T. Gibbs * This is the sysctl handler for the devstat package. The data pushed out 2507a59208dSJustin T. Gibbs * on the kern.devstat.all sysctl variable consists of the current devstat 2517a59208dSJustin T. Gibbs * generation number, and then an array of devstat structures, one for each 2527a59208dSJustin T. Gibbs * device in the system. 2537a59208dSJustin T. Gibbs * 2547a59208dSJustin T. Gibbs * I'm really not too fond of this method of doing things, but there really 2557a59208dSJustin T. Gibbs * aren't that many alternatives. We must have some method of making sure 2567a59208dSJustin T. Gibbs * that the generation number the user gets corresponds with the data the 2577a59208dSJustin T. Gibbs * user gets. If the user makes a separate sysctl call to get the 2587a59208dSJustin T. Gibbs * generation, and then a sysctl call to get the device statistics, the 2597a59208dSJustin T. Gibbs * device list could have changed in that brief period of time. By 2607a59208dSJustin T. Gibbs * supplying the generation number along with the statistics output, we can 2617a59208dSJustin T. Gibbs * guarantee that the generation number and the statistics match up. 2627a59208dSJustin T. Gibbs */ 2637a59208dSJustin T. Gibbs static int 2647a59208dSJustin T. Gibbs sysctl_devstat SYSCTL_HANDLER_ARGS 2657a59208dSJustin T. Gibbs { 2667a59208dSJustin T. Gibbs int error, i; 2677a59208dSJustin T. Gibbs struct devstat *nds; 2687a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 2697a59208dSJustin T. Gibbs 2707a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 2717a59208dSJustin T. Gibbs return(EINVAL); 2727a59208dSJustin T. Gibbs 2737a59208dSJustin T. Gibbs error = 0; 2747a59208dSJustin T. Gibbs devstat_head = &device_statq; 2757a59208dSJustin T. Gibbs 2767a59208dSJustin T. Gibbs /* 2777a59208dSJustin T. Gibbs * First push out the generation number. 2787a59208dSJustin T. Gibbs */ 279bcc6a3daSKenneth D. Merry error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 2807a59208dSJustin T. Gibbs 2817a59208dSJustin T. Gibbs /* 2827a59208dSJustin T. Gibbs * Now push out all the devices. 2837a59208dSJustin T. Gibbs */ 2842e3c8fcbSPoul-Henning Kamp for (i = 0, nds = STAILQ_FIRST(devstat_head); 2857a59208dSJustin T. Gibbs (nds != NULL) && (i < devstat_num_devs) && (error == 0); 2862e3c8fcbSPoul-Henning Kamp nds = STAILQ_NEXT(nds, dev_links), i++) 2877a59208dSJustin T. Gibbs error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 2887a59208dSJustin T. Gibbs 2897a59208dSJustin T. Gibbs return(error); 2907a59208dSJustin T. Gibbs } 2917a59208dSJustin T. Gibbs 2927a59208dSJustin T. Gibbs /* 2937a59208dSJustin T. Gibbs * Sysctl entries for devstat. The first one is a node that all the rest 2947a59208dSJustin T. Gibbs * hang off of. 2957a59208dSJustin T. Gibbs */ 2967a59208dSJustin T. Gibbs SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 2977a59208dSJustin T. Gibbs 2987a59208dSJustin T. Gibbs SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 2993d177f46SBill Fumerola 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 3007a59208dSJustin T. Gibbs /* 3017a59208dSJustin T. Gibbs * Export the number of devices in the system so that userland utilities 3027a59208dSJustin T. Gibbs * can determine how much memory to allocate to hold all the devices. 3037a59208dSJustin T. Gibbs */ 3043d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 3053d177f46SBill Fumerola &devstat_num_devs, 0, "Number of devices in the devstat list"); 306bcc6a3daSKenneth D. Merry SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 307486bddb0SDoug Rabson &devstat_generation, "Devstat list generation"); 3083d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 3093d177f46SBill Fumerola &devstat_version, 0, "Devstat list version number"); 310