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 { 8337d40066SPoul-Henning Kamp STAILQ_FOREACH(ds_tmp, devstat_head, dev_links) { 842a888f93SKenneth D. Merry struct devstat *ds_next; 852a888f93SKenneth D. Merry 862a888f93SKenneth D. Merry ds_next = STAILQ_NEXT(ds_tmp, dev_links); 872a888f93SKenneth D. Merry 882a888f93SKenneth D. Merry /* 892a888f93SKenneth D. Merry * If we find a break between higher and lower 902a888f93SKenneth D. Merry * priority items, and if this item fits in the 912a888f93SKenneth D. Merry * break, insert it. This also applies if the 922a888f93SKenneth D. Merry * "lower priority item" is the end of the list. 932a888f93SKenneth D. Merry */ 942a888f93SKenneth D. Merry if ((priority <= ds_tmp->priority) 952a888f93SKenneth D. Merry && ((ds_next == NULL) 962a888f93SKenneth D. Merry || (priority > ds_next->priority))) { 972a888f93SKenneth D. Merry STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 982a888f93SKenneth D. Merry dev_links); 992a888f93SKenneth D. Merry break; 1002a888f93SKenneth D. Merry } else if (priority > ds_tmp->priority) { 1012a888f93SKenneth D. Merry /* 1022a888f93SKenneth D. Merry * If this is the case, we should be able 1032a888f93SKenneth D. Merry * to insert ourselves at the head of the 1042a888f93SKenneth D. Merry * list. If we can't, something is wrong. 1052a888f93SKenneth D. Merry */ 1062a888f93SKenneth D. Merry if (ds_tmp == STAILQ_FIRST(devstat_head)) { 1072a888f93SKenneth D. Merry STAILQ_INSERT_HEAD(devstat_head, 1082a888f93SKenneth D. Merry ds, dev_links); 1092a888f93SKenneth D. Merry break; 1102a888f93SKenneth D. Merry } else { 1112a888f93SKenneth D. Merry STAILQ_INSERT_TAIL(devstat_head, 1122a888f93SKenneth D. Merry ds, dev_links); 1132a888f93SKenneth D. Merry printf("devstat_add_entry: HELP! " 1142a888f93SKenneth D. Merry "sorting problem detected " 1152a888f93SKenneth D. Merry "for %s%d\n", dev_name, 1162a888f93SKenneth D. Merry unit_number); 1172a888f93SKenneth D. Merry break; 1182a888f93SKenneth D. Merry } 1192a888f93SKenneth D. Merry } 1202a888f93SKenneth D. Merry } 1212a888f93SKenneth D. Merry } 1227a59208dSJustin T. Gibbs 1237a59208dSJustin T. Gibbs ds->device_number = devstat_current_devnumber++; 1247a59208dSJustin T. Gibbs ds->unit_number = unit_number; 1257a59208dSJustin T. Gibbs strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 1262a888f93SKenneth D. Merry ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 1277a59208dSJustin T. Gibbs ds->block_size = block_size; 1287a59208dSJustin T. Gibbs ds->flags = flags; 1297a59208dSJustin T. Gibbs ds->device_type = device_type; 1302a888f93SKenneth D. Merry ds->priority = priority; 1317a59208dSJustin T. Gibbs getmicrotime(&ds->dev_creation_time); 1327a59208dSJustin T. Gibbs } 1337a59208dSJustin T. Gibbs 1347a59208dSJustin T. Gibbs /* 1357a59208dSJustin T. Gibbs * Remove a devstat structure from the list of devices. 1367a59208dSJustin T. Gibbs */ 1377a59208dSJustin T. Gibbs void 1387a59208dSJustin T. Gibbs devstat_remove_entry(struct devstat *ds) 1397a59208dSJustin T. Gibbs { 1407a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 1417a59208dSJustin T. Gibbs 1427a59208dSJustin T. Gibbs if (ds == NULL) 1437a59208dSJustin T. Gibbs return; 1447a59208dSJustin T. Gibbs 1457a59208dSJustin T. Gibbs devstat_generation++; 1467a59208dSJustin T. Gibbs devstat_num_devs--; 1477a59208dSJustin T. Gibbs 1487a59208dSJustin T. Gibbs devstat_head = &device_statq; 1497a59208dSJustin T. Gibbs 1507a59208dSJustin T. Gibbs /* Remove this entry from the devstat queue */ 151e3975643SJake Burkholder STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 1527a59208dSJustin T. Gibbs } 1537a59208dSJustin T. Gibbs 1547a59208dSJustin T. Gibbs /* 1557a59208dSJustin T. Gibbs * Record a transaction start. 1567a59208dSJustin T. Gibbs */ 1577a59208dSJustin T. Gibbs void 1587a59208dSJustin T. Gibbs devstat_start_transaction(struct devstat *ds) 1597a59208dSJustin T. Gibbs { 1607a59208dSJustin T. Gibbs /* sanity check */ 1617a59208dSJustin T. Gibbs if (ds == NULL) 1627a59208dSJustin T. Gibbs return; 1637a59208dSJustin T. Gibbs 1647a59208dSJustin T. Gibbs /* 1657a59208dSJustin T. Gibbs * We only want to set the start time when we are going from idle 1667a59208dSJustin T. Gibbs * to busy. The start time is really the start of the latest busy 1677a59208dSJustin T. Gibbs * period. 1687a59208dSJustin T. Gibbs */ 16939b3c6a9SBruce Evans if (ds->busy_count == 0) 1707a59208dSJustin T. Gibbs getmicrouptime(&ds->start_time); 1717a59208dSJustin T. Gibbs ds->busy_count++; 1727a59208dSJustin T. Gibbs } 1737a59208dSJustin T. Gibbs 1747a59208dSJustin T. Gibbs /* 1757a59208dSJustin T. Gibbs * Record the ending of a transaction, and incrment the various counters. 1767a59208dSJustin T. Gibbs */ 1777a59208dSJustin T. Gibbs void 1787a59208dSJustin T. Gibbs devstat_end_transaction(struct devstat *ds, u_int32_t bytes, 1797a59208dSJustin T. Gibbs devstat_tag_type tag_type, devstat_trans_flags flags) 1807a59208dSJustin T. Gibbs { 1817a59208dSJustin T. Gibbs struct timeval busy_time; 1827a59208dSJustin T. Gibbs 1837a59208dSJustin T. Gibbs /* sanity check */ 1847a59208dSJustin T. Gibbs if (ds == NULL) 1857a59208dSJustin T. Gibbs return; 1867a59208dSJustin T. Gibbs 1877a59208dSJustin T. Gibbs getmicrouptime(&ds->last_comp_time); 1887a59208dSJustin T. Gibbs ds->busy_count--; 1897a59208dSJustin T. Gibbs 1907a59208dSJustin T. Gibbs /* 1917a59208dSJustin T. Gibbs * There might be some transactions (DEVSTAT_NO_DATA) that don't 1927a59208dSJustin T. Gibbs * transfer any data. 1937a59208dSJustin T. Gibbs */ 1947a59208dSJustin T. Gibbs if (flags == DEVSTAT_READ) { 1957a59208dSJustin T. Gibbs ds->bytes_read += bytes; 1967a59208dSJustin T. Gibbs ds->num_reads++; 1977a59208dSJustin T. Gibbs } else if (flags == DEVSTAT_WRITE) { 1987a59208dSJustin T. Gibbs ds->bytes_written += bytes; 1997a59208dSJustin T. Gibbs ds->num_writes++; 200f80d57eeSPoul-Henning Kamp } else if (flags == DEVSTAT_FREE) { 201f80d57eeSPoul-Henning Kamp ds->bytes_freed += bytes; 202f80d57eeSPoul-Henning Kamp ds->num_frees++; 2037a59208dSJustin T. Gibbs } else 2047a59208dSJustin T. Gibbs ds->num_other++; 2057a59208dSJustin T. Gibbs 2067a59208dSJustin T. Gibbs /* 2077a59208dSJustin T. Gibbs * Keep a count of the various tag types sent. 2087a59208dSJustin T. Gibbs */ 2098db3b947SPoul-Henning Kamp if ((ds->flags & DEVSTAT_NO_ORDERED_TAGS) == 0 && 210f80d57eeSPoul-Henning Kamp tag_type != DEVSTAT_TAG_NONE) 2117a59208dSJustin T. Gibbs ds->tag_types[tag_type]++; 2127a59208dSJustin T. Gibbs 2137a59208dSJustin T. Gibbs /* 2147a59208dSJustin T. Gibbs * We only update the busy time when we go idle. Otherwise, this 2157a59208dSJustin T. Gibbs * calculation would require many more clock cycles. 2167a59208dSJustin T. Gibbs */ 2177a59208dSJustin T. Gibbs if (ds->busy_count == 0) { 2187a59208dSJustin T. Gibbs /* Calculate how long we were busy */ 2197a59208dSJustin T. Gibbs busy_time = ds->last_comp_time; 2207a59208dSJustin T. Gibbs timevalsub(&busy_time, &ds->start_time); 2217a59208dSJustin T. Gibbs 2227a59208dSJustin T. Gibbs /* Add our busy time to the total busy time. */ 2237a59208dSJustin T. Gibbs timevaladd(&ds->busy_time, &busy_time); 224d9e371b9SKenneth D. Merry } else if (ds->busy_count < 0) 225a795f8bbSKenneth D. Merry printf("devstat_end_transaction: HELP!! busy_count " 226a795f8bbSKenneth D. Merry "for %s%d is < 0 (%d)!\n", ds->device_name, 227a795f8bbSKenneth D. Merry ds->unit_number, ds->busy_count); 2287a59208dSJustin T. Gibbs } 2297a59208dSJustin T. Gibbs 230f80d57eeSPoul-Henning Kamp void 231282ac69eSPoul-Henning Kamp devstat_end_transaction_bio(struct devstat *ds, struct bio *bp) 232282ac69eSPoul-Henning Kamp { 233282ac69eSPoul-Henning Kamp devstat_trans_flags flg; 234282ac69eSPoul-Henning Kamp 235282ac69eSPoul-Henning Kamp if (bp->bio_cmd == BIO_DELETE) 236282ac69eSPoul-Henning Kamp flg = DEVSTAT_FREE; 237282ac69eSPoul-Henning Kamp else if (bp->bio_cmd == BIO_READ) 238282ac69eSPoul-Henning Kamp flg = DEVSTAT_READ; 239282ac69eSPoul-Henning Kamp else 240282ac69eSPoul-Henning Kamp flg = DEVSTAT_WRITE; 241282ac69eSPoul-Henning Kamp 242282ac69eSPoul-Henning Kamp devstat_end_transaction(ds, bp->bio_bcount - bp->bio_resid, 243282ac69eSPoul-Henning Kamp (bp->bio_flags & BIO_ORDERED) ? 244282ac69eSPoul-Henning Kamp DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 245282ac69eSPoul-Henning Kamp } 246282ac69eSPoul-Henning Kamp 2477a59208dSJustin T. Gibbs /* 2487a59208dSJustin T. Gibbs * This is the sysctl handler for the devstat package. The data pushed out 2497a59208dSJustin T. Gibbs * on the kern.devstat.all sysctl variable consists of the current devstat 2507a59208dSJustin T. Gibbs * generation number, and then an array of devstat structures, one for each 2517a59208dSJustin T. Gibbs * device in the system. 2527a59208dSJustin T. Gibbs * 2537a59208dSJustin T. Gibbs * I'm really not too fond of this method of doing things, but there really 2547a59208dSJustin T. Gibbs * aren't that many alternatives. We must have some method of making sure 2557a59208dSJustin T. Gibbs * that the generation number the user gets corresponds with the data the 2567a59208dSJustin T. Gibbs * user gets. If the user makes a separate sysctl call to get the 2577a59208dSJustin T. Gibbs * generation, and then a sysctl call to get the device statistics, the 2587a59208dSJustin T. Gibbs * device list could have changed in that brief period of time. By 2597a59208dSJustin T. Gibbs * supplying the generation number along with the statistics output, we can 2607a59208dSJustin T. Gibbs * guarantee that the generation number and the statistics match up. 2617a59208dSJustin T. Gibbs */ 2627a59208dSJustin T. Gibbs static int 26382d9ae4eSPoul-Henning Kamp sysctl_devstat(SYSCTL_HANDLER_ARGS) 2647a59208dSJustin T. Gibbs { 2657a59208dSJustin T. Gibbs int error, i; 2667a59208dSJustin T. Gibbs struct devstat *nds; 2677a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 2687a59208dSJustin T. Gibbs 2697a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 2707a59208dSJustin T. Gibbs return(EINVAL); 2717a59208dSJustin T. Gibbs 2727a59208dSJustin T. Gibbs error = 0; 2737a59208dSJustin T. Gibbs devstat_head = &device_statq; 2747a59208dSJustin T. Gibbs 2757a59208dSJustin T. Gibbs /* 2767a59208dSJustin T. Gibbs * First push out the generation number. 2777a59208dSJustin T. Gibbs */ 278bcc6a3daSKenneth D. Merry error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 2797a59208dSJustin T. Gibbs 2807a59208dSJustin T. Gibbs /* 2817a59208dSJustin T. Gibbs * Now push out all the devices. 2827a59208dSJustin T. Gibbs */ 2832e3c8fcbSPoul-Henning Kamp for (i = 0, nds = STAILQ_FIRST(devstat_head); 2847a59208dSJustin T. Gibbs (nds != NULL) && (i < devstat_num_devs) && (error == 0); 2852e3c8fcbSPoul-Henning Kamp nds = STAILQ_NEXT(nds, dev_links), i++) 2867a59208dSJustin T. Gibbs error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 2877a59208dSJustin T. Gibbs 2887a59208dSJustin T. Gibbs return(error); 2897a59208dSJustin T. Gibbs } 2907a59208dSJustin T. Gibbs 2917a59208dSJustin T. Gibbs /* 2927a59208dSJustin T. Gibbs * Sysctl entries for devstat. The first one is a node that all the rest 2937a59208dSJustin T. Gibbs * hang off of. 2947a59208dSJustin T. Gibbs */ 2957a59208dSJustin T. Gibbs SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 2967a59208dSJustin T. Gibbs 2977a59208dSJustin T. Gibbs SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 2983d177f46SBill Fumerola 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 2997a59208dSJustin T. Gibbs /* 3007a59208dSJustin T. Gibbs * Export the number of devices in the system so that userland utilities 3017a59208dSJustin T. Gibbs * can determine how much memory to allocate to hold all the devices. 3027a59208dSJustin T. Gibbs */ 3033d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 3043d177f46SBill Fumerola &devstat_num_devs, 0, "Number of devices in the devstat list"); 305bcc6a3daSKenneth D. Merry SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 3069701cd40SJohn Baldwin &devstat_generation, 0, "Devstat list generation"); 3073d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 3083d177f46SBill Fumerola &devstat_version, 0, "Devstat list version number"); 309