1 /* 2 * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/sys/kern/subr_devstat.c,v 1.17.2.2 2000/08/03 00:09:32 ps Exp $ 29 * $DragonFly: src/sys/kern/subr_devstat.c,v 1.4 2006/12/23 00:35:04 swildner Exp $ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/buf.h> 36 #include <sys/sysctl.h> 37 38 #include <sys/devicestat.h> 39 40 static int devstat_num_devs; 41 static long devstat_generation; 42 static int devstat_version = DEVSTAT_VERSION; 43 static int devstat_current_devnumber; 44 45 static STAILQ_HEAD(devstatlist, devstat) device_statq; 46 47 /* 48 * Take a malloced and zeroed devstat structure given to us, fill it in 49 * and add it to the queue of devices. 50 */ 51 void 52 devstat_add_entry(struct devstat *ds, const char *dev_name, 53 int unit_number, u_int32_t block_size, 54 devstat_support_flags flags, 55 devstat_type_flags device_type, 56 devstat_priority priority) 57 { 58 struct devstatlist *devstat_head; 59 struct devstat *ds_tmp; 60 61 if (ds == NULL) 62 return; 63 64 if (devstat_num_devs == 0) 65 STAILQ_INIT(&device_statq); 66 67 devstat_generation++; 68 devstat_num_devs++; 69 70 devstat_head = &device_statq; 71 72 /* 73 * Priority sort. Each driver passes in its priority when it adds 74 * its devstat entry. Drivers are sorted first by priority, and 75 * then by probe order. 76 * 77 * For the first device, we just insert it, since the priority 78 * doesn't really matter yet. Subsequent devices are inserted into 79 * the list using the order outlined above. 80 */ 81 if (devstat_num_devs == 1) 82 STAILQ_INSERT_TAIL(devstat_head, ds, dev_links); 83 else { 84 for (ds_tmp = STAILQ_FIRST(devstat_head); ds_tmp != NULL; 85 ds_tmp = STAILQ_NEXT(ds_tmp, dev_links)) { 86 struct devstat *ds_next; 87 88 ds_next = STAILQ_NEXT(ds_tmp, dev_links); 89 90 /* 91 * If we find a break between higher and lower 92 * priority items, and if this item fits in the 93 * break, insert it. This also applies if the 94 * "lower priority item" is the end of the list. 95 */ 96 if ((priority <= ds_tmp->priority) 97 && ((ds_next == NULL) 98 || (priority > ds_next->priority))) { 99 STAILQ_INSERT_AFTER(devstat_head, ds_tmp, ds, 100 dev_links); 101 break; 102 } else if (priority > ds_tmp->priority) { 103 /* 104 * If this is the case, we should be able 105 * to insert ourselves at the head of the 106 * list. If we can't, something is wrong. 107 */ 108 if (ds_tmp == STAILQ_FIRST(devstat_head)) { 109 STAILQ_INSERT_HEAD(devstat_head, 110 ds, dev_links); 111 break; 112 } else { 113 STAILQ_INSERT_TAIL(devstat_head, 114 ds, dev_links); 115 kprintf("devstat_add_entry: HELP! " 116 "sorting problem detected " 117 "for %s%d\n", dev_name, 118 unit_number); 119 break; 120 } 121 } 122 } 123 } 124 125 ds->device_number = devstat_current_devnumber++; 126 ds->unit_number = unit_number; 127 strncpy(ds->device_name, dev_name, DEVSTAT_NAME_LEN); 128 ds->device_name[DEVSTAT_NAME_LEN - 1] = '\0'; 129 ds->block_size = block_size; 130 ds->flags = flags; 131 ds->device_type = device_type; 132 ds->priority = priority; 133 getmicrotime(&ds->dev_creation_time); 134 } 135 136 /* 137 * Remove a devstat structure from the list of devices. 138 */ 139 void 140 devstat_remove_entry(struct devstat *ds) 141 { 142 struct devstatlist *devstat_head; 143 144 if (ds == NULL) 145 return; 146 147 devstat_generation++; 148 devstat_num_devs--; 149 150 devstat_head = &device_statq; 151 152 /* Remove this entry from the devstat queue */ 153 STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); 154 } 155 156 /* 157 * Record a transaction start. 158 */ 159 void 160 devstat_start_transaction(struct devstat *ds) 161 { 162 /* sanity check */ 163 if (ds == NULL) 164 return; 165 166 /* 167 * We only want to set the start time when we are going from idle 168 * to busy. The start time is really the start of the latest busy 169 * period. 170 */ 171 if (atomic_fetchadd_int(&ds->busy_count, 1) == 0) 172 getmicrouptime(&ds->start_time); 173 } 174 175 /* 176 * Record the ending of a transaction, and incrment the various counters. 177 */ 178 void 179 devstat_end_transaction(struct devstat *ds, u_int32_t bytes, 180 devstat_tag_type tag_type, devstat_trans_flags flags) 181 { 182 struct timeval busy_time; 183 int busy_count; 184 185 /* sanity check */ 186 if (ds == NULL) 187 return; 188 189 getmicrouptime(&ds->last_comp_time); 190 busy_count = atomic_fetchadd_int(&ds->busy_count, -1) - 1; 191 192 /* 193 * There might be some transactions (DEVSTAT_NO_DATA) that don't 194 * transfer any data. 195 */ 196 if (flags == DEVSTAT_READ) { 197 ds->bytes_read += bytes; 198 ds->num_reads++; 199 } else if (flags == DEVSTAT_WRITE) { 200 ds->bytes_written += bytes; 201 ds->num_writes++; 202 } else if (flags == DEVSTAT_FREE) { 203 ds->bytes_freed += bytes; 204 ds->num_frees++; 205 } else 206 ds->num_other++; 207 208 /* 209 * Keep a count of the various tag types sent. 210 */ 211 if ((ds->flags & DEVSTAT_NO_ORDERED_TAGS) == 0 && 212 tag_type != DEVSTAT_TAG_NONE) 213 ds->tag_types[tag_type]++; 214 215 /* 216 * We only update the busy time when we go idle. Otherwise, this 217 * calculation would require many more clock cycles. 218 */ 219 if (busy_count == 0) { 220 /* Calculate how long we were busy */ 221 busy_time = ds->last_comp_time; 222 timevalsub(&busy_time, &ds->start_time); 223 224 /* Add our busy time to the total busy time. */ 225 timevaladd(&ds->busy_time, &busy_time); 226 } else if (busy_count < 0) { 227 kprintf("devstat_end_transaction: HELP!! busy_count " 228 "for %s%d is < 0 (%d)!\n", ds->device_name, 229 ds->unit_number, ds->busy_count); 230 } 231 } 232 233 void 234 devstat_end_transaction_buf(struct devstat *ds, struct buf *bp) 235 { 236 devstat_trans_flags flg; 237 238 switch(bp->b_cmd) { 239 case BUF_CMD_FREEBLKS: 240 flg = DEVSTAT_FREE; 241 break; 242 case BUF_CMD_READ: 243 flg = DEVSTAT_READ; 244 break; 245 default: 246 flg = DEVSTAT_WRITE; 247 break; 248 } 249 devstat_end_transaction(ds, bp->b_bcount - bp->b_resid, 250 (bp->b_flags & B_ORDERED) ? 251 DEVSTAT_TAG_ORDERED : DEVSTAT_TAG_SIMPLE, flg); 252 } 253 254 /* 255 * This is the sysctl handler for the devstat package. The data pushed out 256 * on the kern.devstat.all sysctl variable consists of the current devstat 257 * generation number, and then an array of devstat structures, one for each 258 * device in the system. 259 * 260 * I'm really not too fond of this method of doing things, but there really 261 * aren't that many alternatives. We must have some method of making sure 262 * that the generation number the user gets corresponds with the data the 263 * user gets. If the user makes a separate sysctl call to get the 264 * generation, and then a sysctl call to get the device statistics, the 265 * device list could have changed in that brief period of time. By 266 * supplying the generation number along with the statistics output, we can 267 * guarantee that the generation number and the statistics match up. 268 */ 269 static int 270 sysctl_devstat(SYSCTL_HANDLER_ARGS) 271 { 272 int error, i; 273 struct devstat *nds; 274 struct devstatlist *devstat_head; 275 276 if (devstat_num_devs == 0) 277 return(EINVAL); 278 279 error = 0; 280 devstat_head = &device_statq; 281 282 /* 283 * First push out the generation number. 284 */ 285 error = SYSCTL_OUT(req, &devstat_generation, sizeof(long)); 286 287 /* 288 * Now push out all the devices. 289 */ 290 for (i = 0, nds = STAILQ_FIRST(devstat_head); 291 (nds != NULL) && (i < devstat_num_devs) && (error == 0); 292 nds = STAILQ_NEXT(nds, dev_links), i++) 293 error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); 294 295 return(error); 296 } 297 298 /* 299 * Sysctl entries for devstat. The first one is a node that all the rest 300 * hang off of. 301 */ 302 SYSCTL_NODE(_kern, OID_AUTO, devstat, CTLFLAG_RD, 0, "Device Statistics"); 303 304 SYSCTL_PROC(_kern_devstat, OID_AUTO, all, CTLFLAG_RD|CTLTYPE_OPAQUE, 305 0, 0, sysctl_devstat, "S,devstat", "All devices in the devstat list"); 306 /* 307 * Export the number of devices in the system so that userland utilities 308 * can determine how much memory to allocate to hold all the devices. 309 */ 310 SYSCTL_INT(_kern_devstat, OID_AUTO, numdevs, CTLFLAG_RD, 311 &devstat_num_devs, 0, "Number of devices in the devstat list"); 312 SYSCTL_LONG(_kern_devstat, OID_AUTO, generation, CTLFLAG_RD, 313 &devstat_generation, 0, "Devstat list generation"); 314 SYSCTL_INT(_kern_devstat, OID_AUTO, version, CTLFLAG_RD, 315 &devstat_version, 0, "Devstat list version number"); 316