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 * 283d177f46SBill Fumerola * $Id: subr_devstat.c,v 1.10 1999/04/11 02:27:06 eivind Exp $ 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> 347a59208dSJustin T. Gibbs #include <sys/time.h> 357a59208dSJustin T. Gibbs #include <sys/types.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 int s; 597a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 602a888f93SKenneth D. Merry struct devstat *ds_tmp; 617a59208dSJustin T. Gibbs 627a59208dSJustin T. Gibbs if (ds == NULL) 637a59208dSJustin T. Gibbs return; 647a59208dSJustin T. Gibbs 657a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 667a59208dSJustin T. Gibbs STAILQ_INIT(&device_statq); 677a59208dSJustin T. Gibbs 687a59208dSJustin T. Gibbs devstat_generation++; 697a59208dSJustin T. Gibbs devstat_num_devs++; 707a59208dSJustin T. Gibbs 717a59208dSJustin T. Gibbs devstat_head = &device_statq; 727a59208dSJustin T. Gibbs 732a888f93SKenneth D. Merry /* 742a888f93SKenneth D. Merry * Priority sort. Each driver passes in its priority when it adds 752a888f93SKenneth D. Merry * its devstat entry. Drivers are sorted first by priority, and 762a888f93SKenneth D. Merry * then by probe order. 772a888f93SKenneth D. Merry * 782a888f93SKenneth D. Merry * For the first device, we just insert it, since the priority 792a888f93SKenneth D. Merry * doesn't really matter yet. Subsequent devices are inserted into 802a888f93SKenneth D. Merry * the list using the order outlined above. 812a888f93SKenneth D. Merry */ 822a888f93SKenneth D. Merry if (devstat_num_devs == 1) 837a59208dSJustin T. Gibbs STAILQ_INSERT_TAIL(devstat_head, ds, dev_links); 842a888f93SKenneth D. Merry else { 852a888f93SKenneth D. Merry for (ds_tmp = STAILQ_FIRST(devstat_head); ds_tmp != NULL; 862a888f93SKenneth D. Merry ds_tmp = STAILQ_NEXT(ds_tmp, dev_links)) { 872a888f93SKenneth D. Merry struct devstat *ds_next; 882a888f93SKenneth D. Merry 892a888f93SKenneth D. Merry ds_next = STAILQ_NEXT(ds_tmp, dev_links); 902a888f93SKenneth D. Merry 912a888f93SKenneth D. Merry /* 922a888f93SKenneth D. Merry * If we find a break between higher and lower 932a888f93SKenneth D. Merry * priority items, and if this item fits in the 942a888f93SKenneth D. Merry * break, insert it. This also applies if the 952a888f93SKenneth D. Merry * "lower priority item" is the end of the list. 962a888f93SKenneth D. Merry */ 972a888f93SKenneth D. Merry if ((priority <= ds_tmp->priority) 982a888f93SKenneth D. Merry && ((ds_next == NULL) 992a888f93SKenneth D. Merry || (priority > ds_next->priority))) { 1002a888f93SKenneth D. Merry STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 1012a888f93SKenneth D. Merry dev_links); 1022a888f93SKenneth D. Merry break; 1032a888f93SKenneth D. Merry } else if (priority > ds_tmp->priority) { 1042a888f93SKenneth D. Merry /* 1052a888f93SKenneth D. Merry * If this is the case, we should be able 1062a888f93SKenneth D. Merry * to insert ourselves at the head of the 1072a888f93SKenneth D. Merry * list. If we can't, something is wrong. 1082a888f93SKenneth D. Merry */ 1092a888f93SKenneth D. Merry if (ds_tmp == STAILQ_FIRST(devstat_head)) { 1102a888f93SKenneth D. Merry STAILQ_INSERT_HEAD(devstat_head, 1112a888f93SKenneth D. Merry ds, dev_links); 1122a888f93SKenneth D. Merry break; 1132a888f93SKenneth D. Merry } else { 1142a888f93SKenneth D. Merry STAILQ_INSERT_TAIL(devstat_head, 1152a888f93SKenneth D. Merry ds, dev_links); 1162a888f93SKenneth D. Merry printf("devstat_add_entry: HELP! " 1172a888f93SKenneth D. Merry "sorting problem detected " 1182a888f93SKenneth D. Merry "for %s%d\n", dev_name, 1192a888f93SKenneth D. Merry unit_number); 1202a888f93SKenneth D. Merry break; 1212a888f93SKenneth D. Merry } 1222a888f93SKenneth D. Merry } 1232a888f93SKenneth D. Merry } 1242a888f93SKenneth D. Merry } 1257a59208dSJustin T. Gibbs 1267a59208dSJustin T. Gibbs ds->device_number = devstat_current_devnumber++; 1277a59208dSJustin T. Gibbs ds->unit_number = unit_number; 1287a59208dSJustin T. Gibbs strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 1292a888f93SKenneth D. Merry ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 1307a59208dSJustin T. Gibbs ds->block_size = block_size; 1317a59208dSJustin T. Gibbs ds->flags = flags; 1327a59208dSJustin T. Gibbs ds->device_type = device_type; 1332a888f93SKenneth D. Merry ds->priority = priority; 1347a59208dSJustin T. Gibbs 1357a59208dSJustin T. Gibbs s = splclock(); 1367a59208dSJustin T. Gibbs getmicrotime(&ds->dev_creation_time); 1377a59208dSJustin T. Gibbs splx(s); 1387a59208dSJustin T. Gibbs } 1397a59208dSJustin T. Gibbs 1407a59208dSJustin T. Gibbs /* 1417a59208dSJustin T. Gibbs * Remove a devstat structure from the list of devices. 1427a59208dSJustin T. Gibbs */ 1437a59208dSJustin T. Gibbs void 1447a59208dSJustin T. Gibbs devstat_remove_entry(struct devstat *ds) 1457a59208dSJustin T. Gibbs { 1467a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 1477a59208dSJustin T. Gibbs 1487a59208dSJustin T. Gibbs if (ds == NULL) 1497a59208dSJustin T. Gibbs return; 1507a59208dSJustin T. Gibbs 1517a59208dSJustin T. Gibbs devstat_generation++; 1527a59208dSJustin T. Gibbs devstat_num_devs--; 1537a59208dSJustin T. Gibbs 1547a59208dSJustin T. Gibbs devstat_head = &device_statq; 1557a59208dSJustin T. Gibbs 1567a59208dSJustin T. Gibbs /* Remove this entry from the devstat queue */ 1577a59208dSJustin T. Gibbs STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 1587a59208dSJustin T. Gibbs } 1597a59208dSJustin T. Gibbs 1607a59208dSJustin T. Gibbs /* 1617a59208dSJustin T. Gibbs * Record a transaction start. 1627a59208dSJustin T. Gibbs */ 1637a59208dSJustin T. Gibbs void 1647a59208dSJustin T. Gibbs devstat_start_transaction(struct devstat *ds) 1657a59208dSJustin T. Gibbs { 1667a59208dSJustin T. Gibbs int s; 1677a59208dSJustin T. Gibbs 1687a59208dSJustin T. Gibbs /* sanity check */ 1697a59208dSJustin T. Gibbs if (ds == NULL) 1707a59208dSJustin T. Gibbs return; 1717a59208dSJustin T. Gibbs 1727a59208dSJustin T. Gibbs /* 1737a59208dSJustin T. Gibbs * We only want to set the start time when we are going from idle 1747a59208dSJustin T. Gibbs * to busy. The start time is really the start of the latest busy 1757a59208dSJustin T. Gibbs * period. 1767a59208dSJustin T. Gibbs */ 1777a59208dSJustin T. Gibbs if (ds->busy_count == 0) { 1787a59208dSJustin T. Gibbs s = splclock(); 1797a59208dSJustin T. Gibbs getmicrouptime(&ds->start_time); 1807a59208dSJustin T. Gibbs splx(s); 1817a59208dSJustin T. Gibbs } 1827a59208dSJustin T. Gibbs ds->busy_count++; 1837a59208dSJustin T. Gibbs } 1847a59208dSJustin T. Gibbs 1857a59208dSJustin T. Gibbs /* 1867a59208dSJustin T. Gibbs * Record the ending of a transaction, and incrment the various counters. 1877a59208dSJustin T. Gibbs */ 1887a59208dSJustin T. Gibbs void 1897a59208dSJustin T. Gibbs devstat_end_transaction(struct devstat *ds, u_int32_t bytes, 1907a59208dSJustin T. Gibbs devstat_tag_type tag_type, devstat_trans_flags flags) 1917a59208dSJustin T. Gibbs { 1927a59208dSJustin T. Gibbs int s; 1937a59208dSJustin T. Gibbs struct timeval busy_time; 1947a59208dSJustin T. Gibbs 1957a59208dSJustin T. Gibbs /* sanity check */ 1967a59208dSJustin T. Gibbs if (ds == NULL) 1977a59208dSJustin T. Gibbs return; 1987a59208dSJustin T. Gibbs 1997a59208dSJustin T. Gibbs s = splclock(); 2007a59208dSJustin T. Gibbs getmicrouptime(&ds->last_comp_time); 2017a59208dSJustin T. Gibbs splx(s); 2027a59208dSJustin T. Gibbs 2037a59208dSJustin T. Gibbs ds->busy_count--; 2047a59208dSJustin T. Gibbs 2057a59208dSJustin T. Gibbs /* 2067a59208dSJustin T. Gibbs * There might be some transactions (DEVSTAT_NO_DATA) that don't 2077a59208dSJustin T. Gibbs * transfer any data. 2087a59208dSJustin T. Gibbs */ 2097a59208dSJustin T. Gibbs if (flags == DEVSTAT_READ) { 2107a59208dSJustin T. Gibbs ds->bytes_read += bytes; 2117a59208dSJustin T. Gibbs ds->num_reads++; 2127a59208dSJustin T. Gibbs } else if (flags == DEVSTAT_WRITE) { 2137a59208dSJustin T. Gibbs ds->bytes_written += bytes; 2147a59208dSJustin T. Gibbs ds->num_writes++; 2157a59208dSJustin T. Gibbs } else 2167a59208dSJustin T. Gibbs ds->num_other++; 2177a59208dSJustin T. Gibbs 2187a59208dSJustin T. Gibbs /* 2197a59208dSJustin T. Gibbs * Keep a count of the various tag types sent. 2207a59208dSJustin T. Gibbs */ 2217a59208dSJustin T. Gibbs if (tag_type != DEVSTAT_TAG_NONE) 2227a59208dSJustin T. Gibbs ds->tag_types[tag_type]++; 2237a59208dSJustin T. Gibbs 2247a59208dSJustin T. Gibbs /* 2257a59208dSJustin T. Gibbs * We only update the busy time when we go idle. Otherwise, this 2267a59208dSJustin T. Gibbs * calculation would require many more clock cycles. 2277a59208dSJustin T. Gibbs */ 2287a59208dSJustin T. Gibbs if (ds->busy_count == 0) { 2297a59208dSJustin T. Gibbs /* Calculate how long we were busy */ 2307a59208dSJustin T. Gibbs busy_time = ds->last_comp_time; 2317a59208dSJustin T. Gibbs timevalsub(&busy_time, &ds->start_time); 2327a59208dSJustin T. Gibbs 2337a59208dSJustin T. Gibbs /* Add our busy time to the total busy time. */ 2347a59208dSJustin T. Gibbs timevaladd(&ds->busy_time, &busy_time); 235d9e371b9SKenneth D. Merry } else if (ds->busy_count < 0) 236a795f8bbSKenneth D. Merry printf("devstat_end_transaction: HELP!! busy_count " 237a795f8bbSKenneth D. Merry "for %s%d is < 0 (%d)!\n", ds->device_name, 238a795f8bbSKenneth D. Merry ds->unit_number, ds->busy_count); 2397a59208dSJustin T. Gibbs } 2407a59208dSJustin T. Gibbs 2417a59208dSJustin T. Gibbs /* 2427a59208dSJustin T. Gibbs * This is the sysctl handler for the devstat package. The data pushed out 2437a59208dSJustin T. Gibbs * on the kern.devstat.all sysctl variable consists of the current devstat 2447a59208dSJustin T. Gibbs * generation number, and then an array of devstat structures, one for each 2457a59208dSJustin T. Gibbs * device in the system. 2467a59208dSJustin T. Gibbs * 2477a59208dSJustin T. Gibbs * I'm really not too fond of this method of doing things, but there really 2487a59208dSJustin T. Gibbs * aren't that many alternatives. We must have some method of making sure 2497a59208dSJustin T. Gibbs * that the generation number the user gets corresponds with the data the 2507a59208dSJustin T. Gibbs * user gets. If the user makes a separate sysctl call to get the 2517a59208dSJustin T. Gibbs * generation, and then a sysctl call to get the device statistics, the 2527a59208dSJustin T. Gibbs * device list could have changed in that brief period of time. By 2537a59208dSJustin T. Gibbs * supplying the generation number along with the statistics output, we can 2547a59208dSJustin T. Gibbs * guarantee that the generation number and the statistics match up. 2557a59208dSJustin T. Gibbs */ 2567a59208dSJustin T. Gibbs static int 2577a59208dSJustin T. Gibbs sysctl_devstat SYSCTL_HANDLER_ARGS 2587a59208dSJustin T. Gibbs { 2597a59208dSJustin T. Gibbs int error, i; 2607a59208dSJustin T. Gibbs struct devstat *nds; 2617a59208dSJustin T. Gibbs struct devstatlist *devstat_head; 2627a59208dSJustin T. Gibbs 2637a59208dSJustin T. Gibbs if (devstat_num_devs == 0) 2647a59208dSJustin T. Gibbs return(EINVAL); 2657a59208dSJustin T. Gibbs 2667a59208dSJustin T. Gibbs error = 0; 2677a59208dSJustin T. Gibbs devstat_head = &device_statq; 2687a59208dSJustin T. Gibbs 2697a59208dSJustin T. Gibbs /* 2707a59208dSJustin T. Gibbs * First push out the generation number. 2717a59208dSJustin T. Gibbs */ 272bcc6a3daSKenneth D. Merry error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 2737a59208dSJustin T. Gibbs 2747a59208dSJustin T. Gibbs /* 2757a59208dSJustin T. Gibbs * Now push out all the devices. 2767a59208dSJustin T. Gibbs */ 2777a59208dSJustin T. Gibbs for (i = 0, nds = devstat_head->stqh_first; 2787a59208dSJustin T. Gibbs (nds != NULL) && (i < devstat_num_devs) && (error == 0); 2797a59208dSJustin T. Gibbs nds = nds->dev_links.stqe_next, i++) 2807a59208dSJustin T. Gibbs error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 2817a59208dSJustin T. Gibbs 2827a59208dSJustin T. Gibbs return(error); 2837a59208dSJustin T. Gibbs } 2847a59208dSJustin T. Gibbs 2857a59208dSJustin T. Gibbs /* 2867a59208dSJustin T. Gibbs * Sysctl entries for devstat. The first one is a node that all the rest 2877a59208dSJustin T. Gibbs * hang off of. 2887a59208dSJustin T. Gibbs */ 2897a59208dSJustin T. Gibbs SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 2907a59208dSJustin T. Gibbs 2917a59208dSJustin T. Gibbs SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 2923d177f46SBill Fumerola 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 2937a59208dSJustin T. Gibbs /* 2947a59208dSJustin T. Gibbs * Export the number of devices in the system so that userland utilities 2957a59208dSJustin T. Gibbs * can determine how much memory to allocate to hold all the devices. 2967a59208dSJustin T. Gibbs */ 2973d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 2983d177f46SBill Fumerola &devstat_num_devs, 0, "Number of devices in the devstat list"); 299bcc6a3daSKenneth D. Merry SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 300486bddb0SDoug Rabson &devstat_generation, "Devstat list generation"); 3013d177f46SBill Fumerola SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 3023d177f46SBill Fumerola &devstat_version, 0, "Devstat list version number"); 303