1 /* 2 * Copyright (c) 1997, 1998 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 * $Id$ 29 */ 30 31 #include <sys/types.h> 32 #include <sys/sysctl.h> 33 #include <sys/errno.h> 34 #include <sys/dkstat.h> 35 36 #include <err.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include "devstat.h" 42 43 char devstat_errbuf[DEVSTAT_ERRBUF_SIZE]; 44 45 /* 46 * Table to match descriptive strings with device types. These are in 47 * order from most common to least common to speed search time. 48 */ 49 struct devstat_match_table match_table[] = { 50 {"da", DEVSTAT_TYPE_DIRECT, DEVSTAT_MATCH_TYPE}, 51 {"cd", DEVSTAT_TYPE_CDROM, DEVSTAT_MATCH_TYPE}, 52 {"scsi", DEVSTAT_TYPE_IF_SCSI, DEVSTAT_MATCH_IF}, 53 {"ide", DEVSTAT_TYPE_IF_IDE, DEVSTAT_MATCH_IF}, 54 {"other", DEVSTAT_TYPE_IF_OTHER, DEVSTAT_MATCH_IF}, 55 {"worm", DEVSTAT_TYPE_WORM, DEVSTAT_MATCH_TYPE}, 56 {"sa", DEVSTAT_TYPE_SEQUENTIAL,DEVSTAT_MATCH_TYPE}, 57 {"pass", DEVSTAT_TYPE_PASS, DEVSTAT_MATCH_PASS}, 58 {"optical", DEVSTAT_TYPE_OPTICAL, DEVSTAT_MATCH_TYPE}, 59 {"array", DEVSTAT_TYPE_STORARRAY, DEVSTAT_MATCH_TYPE}, 60 {"changer", DEVSTAT_TYPE_CHANGER, DEVSTAT_MATCH_TYPE}, 61 {"scanner", DEVSTAT_TYPE_SCANNER, DEVSTAT_MATCH_TYPE}, 62 {"printer", DEVSTAT_TYPE_PRINTER, DEVSTAT_MATCH_TYPE}, 63 {"floppy", DEVSTAT_TYPE_FLOPPY, DEVSTAT_MATCH_TYPE}, 64 {"proc", DEVSTAT_TYPE_PROCESSOR, DEVSTAT_MATCH_TYPE}, 65 {"comm", DEVSTAT_TYPE_COMM, DEVSTAT_MATCH_TYPE}, 66 {"enclosure", DEVSTAT_TYPE_ENCLOSURE, DEVSTAT_MATCH_TYPE}, 67 {NULL, 0, 0} 68 }; 69 70 /* 71 * Local function declarations. 72 */ 73 static int compare_select(const void *arg1, const void *arg2); 74 75 int 76 getnumdevs(void) 77 { 78 size_t numdevsize; 79 int numdevs; 80 char *func_name = "getnumdevs"; 81 82 numdevsize = sizeof(int); 83 84 /* 85 * Find out how many devices we have in the system. 86 */ 87 if (sysctlbyname("kern.devstat.numdevs", &numdevs, 88 &numdevsize, NULL, 0) == -1) { 89 sprintf(devstat_errbuf, "%s: error getting number of devices\n" 90 "%s: %s", func_name, func_name, strerror(errno)); 91 return(-1); 92 } else 93 return(numdevs); 94 } 95 96 /* 97 * This is an easy way to get the generation number, but the generation is 98 * supplied in a more atmoic manner by the kern.devstat.all sysctl. 99 * Because this generation sysctl is separate from the statistics sysctl, 100 * the device list and the generation could change between the time that 101 * this function is called and the device list is retreived. 102 */ 103 int 104 getgeneration(void) 105 { 106 size_t gensize; 107 int generation; 108 char *func_name = "getgeneration"; 109 110 gensize = sizeof(int); 111 112 /* 113 * Get the current generation number. 114 */ 115 if (sysctlbyname("kern.devstat.generation", &generation, 116 &gensize, NULL, 0) == -1) { 117 sprintf(devstat_errbuf,"%s: error getting devstat generation\n" 118 "%s: %s", func_name, func_name, strerror(errno)); 119 return(-1); 120 } else 121 return(generation); 122 } 123 124 /* 125 * Get the current devstat version. The return value of this function 126 * should be compared with DEVSTAT_VERSION, which is defined in 127 * sys/devicestat.h. This will enable userland programs to determine 128 * whether they are out of sync with the kernel. 129 */ 130 int 131 getversion(void) 132 { 133 size_t versize; 134 int version; 135 char *func_name = "getversion"; 136 137 versize = sizeof(int); 138 139 /* 140 * Get the current devstat version. 141 */ 142 if (sysctlbyname("kern.devstat.version", &version, &versize, 143 NULL, 0) == -1) { 144 sprintf(devstat_errbuf, "%s: error getting devstat version\n" 145 "%s: %s", func_name, func_name, strerror(errno)); 146 return(-1); 147 } else 148 return(version); 149 } 150 151 /* 152 * Check the devstat version we know about against the devstat version the 153 * kernel knows about. If they don't match, print an error into the 154 * devstat error buffer, and return -1. If they match, return 0. 155 */ 156 int 157 checkversion(void) 158 { 159 int retval = 0; 160 int errlen = 0; 161 char *func_name = "checkversion"; 162 163 if (getversion() != DEVSTAT_VERSION) { 164 char tmpstr[256]; 165 166 errlen = snprintf(devstat_errbuf, DEVSTAT_ERRBUF_SIZE, 167 "%s: userland devstat version %d is not " 168 "the same as the kernel\n%s: devstat " 169 "version %d\n", func_name, DEVSTAT_VERSION, 170 func_name, getversion()); 171 172 if (getversion() < DEVSTAT_VERSION) 173 snprintf(tmpstr, sizeof(tmpstr), 174 "%s: you really should know better" 175 " than to upgrade your\n%s: " 176 "userland binaries without " 177 "upgrading your kernel\n", 178 func_name, func_name); 179 else 180 snprintf(tmpstr, sizeof(tmpstr), 181 "%s: you really should know better" 182 " than to upgrade your kernel " 183 "without\n%s: upgrading your " 184 "userland binaries\n", 185 func_name, func_name); 186 187 strncat(devstat_errbuf, tmpstr, 188 DEVSTAT_ERRBUF_SIZE - errlen - 1); 189 190 retval = -1; 191 } 192 193 return(retval); 194 } 195 196 /* 197 * Get the current list of devices and statistics, and the current 198 * generation number. 199 * 200 * Return values: 201 * -1 -- error 202 * 0 -- device list is unchanged 203 * 1 -- device list has changed 204 */ 205 int 206 getdevs(struct statinfo *stats) 207 { 208 int error; 209 size_t dssize; 210 int oldnumdevs, oldgeneration; 211 int retval = 0; 212 struct devinfo *dinfo; 213 char *func_name = "getdevs"; 214 215 dinfo = stats->dinfo; 216 217 if (dinfo == NULL) { 218 sprintf(devstat_errbuf, "%s: stats->dinfo was NULL", func_name); 219 return(-1); 220 } 221 222 oldnumdevs = dinfo->numdevs; 223 oldgeneration = dinfo->generation; 224 225 /* 226 * If this is our first time through, mem_ptr will be null. 227 */ 228 if (dinfo->mem_ptr == NULL) { 229 /* 230 * Get the number of devices. If it's negative, it's an 231 * error. Don't bother setting the error string, since 232 * getnumdevs() has already done that for us. 233 */ 234 if ((dinfo->numdevs = getnumdevs()) < 0) 235 return(-1); 236 237 /* 238 * The kern.devstat.all sysctl returns the current generation 239 * number, as well as all the devices. So we need four 240 * bytes more. 241 */ 242 dssize =(dinfo->numdevs * sizeof(struct devstat)) + sizeof(int); 243 dinfo->mem_ptr = (u_int8_t *)malloc(dssize); 244 } else 245 dssize =(dinfo->numdevs * sizeof(struct devstat)) + sizeof(int); 246 247 /* Get the current time when we get the stats */ 248 gettimeofday(&stats->busy_time, NULL); 249 250 /* 251 * Request all of the devices. We only really allow for one 252 * ENOMEM failure. It would, of course, be possible to just go in 253 * a loop and keep reallocing the device structure until we don't 254 * get ENOMEM back. I'm not sure it's worth it, though. If 255 * devices are being added to the system that quickly, maybe the 256 * user can just wait until all devices are added. 257 */ 258 if ((error = sysctlbyname("kern.devstat.all", dinfo->mem_ptr, 259 &dssize, NULL, 0)) == -1) { 260 /* 261 * If we get ENOMEM back, that means that there are 262 * more devices now, so we need to allocate more 263 * space for the device array. 264 */ 265 if (errno == ENOMEM) { 266 /* 267 * No need to set the error string here, getnumdevs() 268 * will do that if it fails. 269 */ 270 if ((dinfo->numdevs = getnumdevs()) < 0) 271 return(-1); 272 273 dssize = (dinfo->numdevs * sizeof(struct devstat)) + 274 sizeof(int); 275 dinfo->mem_ptr = (u_int8_t *)realloc(dinfo->mem_ptr, 276 dssize); 277 if ((error = sysctlbyname("kern.devstat.all", 278 dinfo->mem_ptr, &dssize, NULL, 0)) == -1) { 279 sprintf(devstat_errbuf, 280 "%s: error getting device stats\n" 281 "%s: %s", func_name, func_name, 282 strerror(errno)); 283 return(-1); 284 } 285 } else { 286 sprintf(devstat_errbuf, 287 "%s: error getting device stats\n" 288 "%s: %s", func_name, func_name, 289 strerror(errno)); 290 return(-1); 291 } 292 } 293 294 /* 295 * The sysctl spits out the generation as the first four bytes, 296 * then all of the device statistics structures. 297 */ 298 dinfo->generation = *(int *)dinfo->mem_ptr; 299 300 /* 301 * If the generation has changed, and if the current number of 302 * devices is not the same as the number of devices recorded in the 303 * devinfo structure, it is likely that the device list has shrunk. 304 * The reason that it is likely that the device list has shrunk in 305 * this case is that if the device list has grown, the sysctl above 306 * will return an ENOMEM error, and we will reset the number of 307 * devices and reallocate the device array. If the second sysctl 308 * fails, we will return an error and therefore never get to this 309 * point. If the device list has shrunk, the sysctl will not 310 * return an error since we have more space allocated than is 311 * necessary. So, in the shrinkage case, we catch it here and 312 * reallocate the array so that we don't use any more space than is 313 * necessary. 314 */ 315 if (oldgeneration != dinfo->generation) { 316 if (getnumdevs() != dinfo->numdevs) { 317 if ((dinfo->numdevs = getnumdevs()) < 0) 318 return(-1); 319 dssize = (dinfo->numdevs * sizeof(struct devstat)) + 320 sizeof(int); 321 dinfo->mem_ptr = (u_int8_t *)realloc(dinfo->mem_ptr, 322 dssize); 323 } 324 retval = 1; 325 } 326 327 dinfo->devices = (struct devstat *)(dinfo->mem_ptr + sizeof(int)); 328 329 return(retval); 330 } 331 332 /* 333 * selectdevs(): 334 * 335 * Devices are selected/deselected based upon the following criteria: 336 * - devices specified by the user on the command line 337 * - devices matching any device type expressions given on the command line 338 * - devices with the highest I/O, if 'top' mode is enabled 339 * - the first n unselected devices in the device list, if maxshowdevs 340 * devices haven't already been selected and if the user has not 341 * specified any devices on the command line and if we're in "add" mode. 342 * 343 * Input parameters: 344 * - device selection list (dev_select) 345 * - current number of devices selected (num_selected) 346 * - total number of devices in the selection list (num_selections) 347 * - devstat generation as of the last time selectdevs() was called 348 * (select_generation) 349 * - current devstat generation (current_generation) 350 * - current list of devices and statistics (devices) 351 * - number of devices in the current device list (numdevs) 352 * - compiled version of the command line device type arguments (matches) 353 * - This is optional. If the number of devices is 0, this will be ignored. 354 * - The matching code pays attention to the current selection mode. So 355 * if you pass in a matching expression, it will be evaluated based 356 * upon the selection mode that is passed in. See below for details. 357 * - number of device type matching expressions (num_matches) 358 * - Set to 0 to disable the matching code. 359 * - list of devices specified on the command line by the user (dev_selections) 360 * - number of devices selected on the command line by the user 361 * (num_dev_selections) 362 * - Our selection mode. There are four different selection modes: 363 * - add mode. (DS_SELECT_ADD) Any devices matching devices explicitly 364 * selected by the user or devices matching a pattern given by the 365 * user will be selected in addition to devices that are already 366 * selected. Additional devices will be selected, up to maxshowdevs 367 * number of devices. 368 * - only mode. (DS_SELECT_ONLY) Only devices matching devices 369 * explicitly given by the user or devices matching a pattern 370 * given by the user will be selected. No other devices will be 371 * selected. 372 * - addonly mode. (DS_SELECT_ADDONLY) This is similar to add and 373 * only. Basically, this will not de-select any devices that are 374 * current selected, as only mode would, but it will also not 375 * gratuitously select up to maxshowdevs devices as add mode would. 376 * - remove mode. (DS_SELECT_REMOVE) Any devices matching devices 377 * explicitly selected by the user or devices matching a pattern 378 * given by the user will be de-selected. 379 * - maximum number of devices we can select (maxshowdevs) 380 * - flag indicating whether or not we're in 'top' mode (perf_select) 381 * 382 * Output data: 383 * - the device selection list may be modified and passed back out 384 * - the number of devices selected and the total number of items in the 385 * device selection list may be changed 386 * - the selection generation may be changed to match the current generation 387 * 388 * Return values: 389 * -1 -- error 390 * 0 -- selected devices are unchanged 391 * 1 -- selected devices changed 392 */ 393 int 394 selectdevs(struct device_selection **dev_select, int *num_selected, 395 int *num_selections, int *select_generation, 396 int current_generation, struct devstat *devices, int numdevs, 397 struct devstat_match *matches, int num_matches, 398 char **dev_selections, int num_dev_selections, 399 devstat_select_mode select_mode, int maxshowdevs, int perf_select) 400 { 401 register int i, j, k; 402 int init_selections = 0, init_selected_var = 0; 403 struct device_selection *old_dev_select = NULL; 404 int old_num_selections = 0, old_num_selected; 405 int selection_number = 0; 406 int changed = 0, found = 0; 407 408 if ((dev_select == NULL) || (devices == NULL) || (numdevs <= 0)) 409 return(-1); 410 411 /* 412 * We always want to make sure that we have as many dev_select 413 * entries as there are devices. 414 */ 415 /* 416 * In this case, we haven't selected devices before. 417 */ 418 if (*dev_select == NULL) { 419 *dev_select = (struct device_selection *)malloc(numdevs * 420 sizeof(struct device_selection)); 421 *select_generation = current_generation; 422 init_selections = 1; 423 changed = 1; 424 /* 425 * In this case, we have selected devices before, but the device 426 * list has changed since we last selected devices, so we need to 427 * either enlarge or reduce the size of the device selection list. 428 */ 429 } else if (*num_selections != numdevs) { 430 *dev_select = (struct device_selection *)realloc(*dev_select, 431 numdevs * sizeof(struct device_selection)); 432 *select_generation = current_generation; 433 init_selections = 1; 434 /* 435 * In this case, we've selected devices before, and the selection 436 * list is the same size as it was the last time, but the device 437 * list has changed. 438 */ 439 } else if (*select_generation < current_generation) { 440 *select_generation = current_generation; 441 init_selections = 1; 442 } 443 444 /* 445 * If we're in "only" mode, we want to clear out the selected 446 * variable since we're going to select exactly what the user wants 447 * this time through. 448 */ 449 if (select_mode == DS_SELECT_ONLY) 450 init_selected_var = 1; 451 452 /* 453 * In all cases, we want to back up the number of selected devices. 454 * It is a quick and accurate way to determine whether the selected 455 * devices have changed. 456 */ 457 old_num_selected = *num_selected; 458 459 /* 460 * We want to make a backup of the current selection list if 461 * the list of devices has changed, or if we're in performance 462 * selection mode. In both cases, we don't want to make a backup 463 * if we already know for sure that the list will be different. 464 * This is certainly the case if this is our first time through the 465 * selection code. 466 */ 467 if (((init_selected_var != 0) || (init_selections != 0) 468 || (perf_select != 0)) && (changed == 0)){ 469 old_dev_select = (struct device_selection *)malloc( 470 *num_selections * sizeof(struct device_selection)); 471 old_num_selections = *num_selections; 472 bcopy(*dev_select, old_dev_select, 473 sizeof(struct device_selection) * *num_selections); 474 } 475 476 if (init_selections != 0) { 477 bzero(*dev_select, sizeof(struct device_selection) * numdevs); 478 479 for (i = 0; i < numdevs; i++) { 480 (*dev_select)[i].device_number = 481 devices[i].device_number; 482 strncpy((*dev_select)[i].device_name, 483 devices[i].device_name, 484 DEVSTAT_NAME_LEN); 485 (*dev_select)[i].unit_number = devices[i].unit_number; 486 (*dev_select)[i].position = i; 487 } 488 *num_selections = numdevs; 489 } else if (init_selected_var != 0) { 490 for (i = 0; i < numdevs; i++) 491 (*dev_select)[i].selected = 0; 492 } 493 494 /* we haven't gotten around to selecting anything yet.. */ 495 if ((select_mode == DS_SELECT_ONLY) || (init_selections != 0) 496 || (init_selected_var != 0)) 497 *num_selected = 0; 498 499 /* 500 * Look through any devices the user specified on the command line 501 * and see if they match known devices. If so, select them. 502 */ 503 for (i = 0; (i < *num_selections) && (num_dev_selections > 0); i++) { 504 char tmpstr[80]; 505 506 sprintf(tmpstr, "%s%d", (*dev_select)[i].device_name, 507 (*dev_select)[i].unit_number); 508 for (j = 0; j < num_dev_selections; j++) { 509 if (strcmp(tmpstr, dev_selections[j]) == 0) { 510 /* 511 * Here we do different things based on the 512 * mode we're in. If we're in add or 513 * addonly mode, we only select this device 514 * if it hasn't already been selected. 515 * Otherwise, we would be unnecessarily 516 * changing the selection order and 517 * incrementing the selection count. If 518 * we're in only mode, we unconditionally 519 * select this device, since in only mode 520 * any previous selections are erased and 521 * manually specified devices are the first 522 * ones to be selected. If we're in remove 523 * mode, we de-select the specified device and 524 * decrement the selection count. 525 */ 526 switch(select_mode) { 527 case DS_SELECT_ADD: 528 case DS_SELECT_ADDONLY: 529 if ((*dev_select)[i].selected) 530 break; 531 /* FALLTHROUGH */ 532 case DS_SELECT_ONLY: 533 (*dev_select)[i].selected = 534 ++selection_number; 535 (*num_selected)++; 536 break; 537 case DS_SELECT_REMOVE: 538 (*dev_select)[i].selected = 0; 539 (*num_selected)--; 540 /* 541 * This isn't passed back out, we 542 * just use it to keep track of 543 * how many devices we've removed. 544 */ 545 num_dev_selections--; 546 break; 547 } 548 break; 549 } 550 } 551 } 552 553 /* 554 * Go through the user's device type expressions and select devices 555 * accordingly. We only do this if the number of devices already 556 * selected is less than the maximum number we can show. 557 */ 558 for (i = 0; (i < num_matches) && (*num_selected < maxshowdevs); i++) { 559 /* We should probably indicate some error here */ 560 if ((matches[i].match_fields == DEVSTAT_MATCH_NONE) 561 || (matches[i].num_match_categories <= 0)) 562 continue; 563 564 for (j = 0; j < numdevs; j++) { 565 int num_match_categories; 566 567 num_match_categories = matches[i].num_match_categories; 568 569 /* 570 * Determine whether or not the current device 571 * matches the given matching expression. This if 572 * statement consists of three components: 573 * - the device type check 574 * - the device interface check 575 * - the passthrough check 576 * If a the matching test is successful, it 577 * decrements the number of matching categories, 578 * and if we've reached the last element that 579 * needed to be matched, the if statement succeeds. 580 * 581 */ 582 if ((((matches[i].match_fields & DEVSTAT_MATCH_TYPE)!=0) 583 && ((devices[j].device_type & DEVSTAT_TYPE_MASK) == 584 (matches[i].device_type & DEVSTAT_TYPE_MASK)) 585 &&(((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0) 586 || (((matches[i].match_fields & 587 DEVSTAT_MATCH_PASS) == 0) 588 && ((devices[j].device_type & 589 DEVSTAT_TYPE_PASS) == 0))) 590 && (--num_match_categories == 0)) 591 || (((matches[i].match_fields & DEVSTAT_MATCH_IF) != 0) 592 && ((devices[j].device_type & DEVSTAT_TYPE_IF_MASK) == 593 (matches[i].device_type & DEVSTAT_TYPE_IF_MASK)) 594 &&(((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0) 595 || (((matches[i].match_fields & 596 DEVSTAT_MATCH_PASS) == 0) 597 && ((devices[j].device_type & 598 DEVSTAT_TYPE_PASS) == 0))) 599 && (--num_match_categories == 0)) 600 || (((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0) 601 && ((devices[j].device_type & DEVSTAT_TYPE_PASS) != 0) 602 && (--num_match_categories == 0))) { 603 604 /* 605 * This is probably a non-optimal solution 606 * to the problem that the devices in the 607 * device list will not be in the same 608 * order as the devices in the selection 609 * array. 610 */ 611 for (k = 0; k < numdevs; k++) { 612 if ((*dev_select)[k].position == j) { 613 found = 1; 614 break; 615 } 616 } 617 618 /* 619 * There shouldn't be a case where a device 620 * in the device list is not in the 621 * selection list...but it could happen. 622 */ 623 if (found != 1) { 624 fprintf(stderr, "selectdevs: couldn't" 625 " find %s%d in selection " 626 "list\n", 627 devices[j].device_name, 628 devices[j].unit_number); 629 break; 630 } 631 632 /* 633 * We do different things based upon the 634 * mode we're in. If we're in add or only 635 * mode, we go ahead and select this device 636 * if it hasn't already been selected. If 637 * it has already been selected, we leave 638 * it alone so we don't mess up the 639 * selection ordering. Manually specified 640 * devices have already been selected, and 641 * they have higher priority than pattern 642 * matched devices. If we're in remove 643 * mode, we de-select the given device and 644 * decrement the selected count. 645 */ 646 switch(select_mode) { 647 case DS_SELECT_ADD: 648 case DS_SELECT_ADDONLY: 649 case DS_SELECT_ONLY: 650 if ((*dev_select)[k].selected != 0) 651 break; 652 (*dev_select)[k].selected = 653 ++selection_number; 654 (*num_selected)++; 655 break; 656 case DS_SELECT_REMOVE: 657 (*dev_select)[k].selected = 0; 658 (*num_selected)--; 659 break; 660 } 661 } 662 } 663 } 664 665 /* 666 * Here we implement "top" mode. Devices are sorted in the 667 * selection array based on two criteria: whether or not they are 668 * selected (not selection number, just the fact that they are 669 * selected!) and the number of bytes in the "bytes" field of the 670 * selection structure. The bytes field generally must be kept up 671 * by the user. In the future, it may be maintained by library 672 * functions, but for now the user has to do the work. 673 * 674 * At first glance, it may seem wrong that we don't go through and 675 * select every device in the case where the user hasn't specified 676 * any devices or patterns. In fact, though, it won't make any 677 * difference in the device sorting. In that particular case (i.e. 678 * when we're in "add" or "only" mode, and the user hasn't 679 * specified anything) the first time through no devices will be 680 * selected, so the only criterion used to sort them will be their 681 * performance. The second time through, and every time thereafter, 682 * all devices will be selected, so again selection won't matter. 683 */ 684 if (perf_select != 0) { 685 686 /* Sort the device array by throughput */ 687 qsort(*dev_select, *num_selections, 688 sizeof(struct device_selection), 689 compare_select); 690 691 if (*num_selected == 0) { 692 /* 693 * Here we select every device in the array, if it 694 * isn't already selected. Because the 'selected' 695 * variable in the selection array entries contains 696 * the selection order, the devstats routine can show 697 * the devices that were selected first. 698 */ 699 for (i = 0; i < *num_selections; i++) { 700 if ((*dev_select)[i].selected == 0) { 701 (*dev_select)[i].selected = 702 ++selection_number; 703 (*num_selected)++; 704 } 705 } 706 } else { 707 selection_number = 0; 708 for (i = 0; i < *num_selections; i++) { 709 if ((*dev_select)[i].selected != 0) { 710 (*dev_select)[i].selected = 711 ++selection_number; 712 } 713 } 714 } 715 } 716 717 /* 718 * If we're in the "add" selection mode and if we haven't already 719 * selected maxshowdevs number of devices, go through the array and 720 * select any unselected devices. If we're in "only" mode, we 721 * obviously don't want to select anything other than what the user 722 * specifies. If we're in "remove" mode, it probably isn't a good 723 * idea to go through and select any more devices, since we might 724 * end up selecting something that the user wants removed. Through 725 * more complicated logic, we could actually figure this out, but 726 * that would probably require combining this loop with the various 727 * selections loops above. 728 */ 729 if ((select_mode == DS_SELECT_ADD) && (*num_selected < maxshowdevs)) { 730 for (i = 0; i < *num_selections; i++) 731 if ((*dev_select)[i].selected == 0) { 732 (*dev_select)[i].selected = ++selection_number; 733 (*num_selected)++; 734 } 735 } 736 737 /* 738 * Look at the number of devices that have been selected. If it 739 * has changed, set the changed variable. Otherwise, if we've 740 * made a backup of the selection list, compare it to the current 741 * selection list to see if the selected devices have changed. 742 */ 743 if ((changed == 0) && (old_num_selected != *num_selected)) 744 changed = 1; 745 else if ((changed == 0) && (old_dev_select != NULL)) { 746 /* 747 * Now we go through the selection list and we look at 748 * it three different ways. 749 */ 750 for (i = 0; (i < *num_selections) && (changed == 0) && 751 (i < old_num_selections); i++) { 752 /* 753 * If the device at index i in both the new and old 754 * selection arrays has the same device number and 755 * selection status, it hasn't changed. We 756 * continue on to the next index. 757 */ 758 if (((*dev_select)[i].device_number == 759 old_dev_select[i].device_number) 760 && ((*dev_select)[i].selected == 761 old_dev_select[i].selected)) 762 continue; 763 764 /* 765 * Now, if we're still going through the if 766 * statement, the above test wasn't true. So we 767 * check here to see if the device at index i in 768 * the current array is the same as the device at 769 * index i in the old array. If it is, that means 770 * that its selection number has changed. Set 771 * changed to 1 and exit the loop. 772 */ 773 else if ((*dev_select)[i].device_number == 774 old_dev_select[i].device_number) { 775 changed = 1; 776 break; 777 } 778 /* 779 * If we get here, then the device at index i in 780 * the current array isn't the same device as the 781 * device at index i in the old array. 782 */ 783 else { 784 int found = 0; 785 786 /* 787 * Search through the old selection array 788 * looking for a device with the same 789 * device number as the device at index i 790 * in the current array. If the selection 791 * status is the same, then we mark it as 792 * found. If the selection status isn't 793 * the same, we break out of the loop. 794 * Since found isn't set, changed will be 795 * set to 1 below. 796 */ 797 for (j = 0; j < old_num_selections; j++) { 798 if (((*dev_select)[i].device_number == 799 old_dev_select[j].device_number) 800 && ((*dev_select)[i].selected == 801 old_dev_select[j].selected)){ 802 found = 1; 803 break; 804 } 805 else if ((*dev_select)[i].device_number 806 == old_dev_select[j].device_number) 807 break; 808 } 809 if (found == 0) 810 changed = 1; 811 } 812 } 813 } 814 if (old_dev_select != NULL) 815 free(old_dev_select); 816 817 return(changed); 818 } 819 820 /* 821 * Comparison routine for qsort() above. Note that the comparison here is 822 * backwards -- generally, it should return a value to indicate whether 823 * arg1 is <, =, or > arg2. Instead, it returns the opposite. The reason 824 * it returns the opposite is so that the selection array will be sorted in 825 * order of decreasing performance. We sort on two parameters. The first 826 * sort key is whether or not one or the other of the devices in question 827 * has been selected. If one of them has, and the other one has not, the 828 * selected device is automatically more important than the unselected 829 * device. If neither device is selected, we judge the devices based upon 830 * performance. 831 */ 832 static int 833 compare_select(const void *arg1, const void *arg2) 834 { 835 if ((((struct device_selection *)arg1)->selected) 836 && (((struct device_selection *)arg2)->selected == 0)) 837 return(-1); 838 else if ((((struct device_selection *)arg1)->selected == 0) 839 && (((struct device_selection *)arg2)->selected)) 840 return(1); 841 else if (((struct device_selection *)arg2)->bytes < 842 ((struct device_selection *)arg1)->bytes) 843 return(-1); 844 else if (((struct device_selection *)arg2)->bytes > 845 ((struct device_selection *)arg1)->bytes) 846 return(1); 847 else 848 return(0); 849 } 850 851 /* 852 * Take a string with the general format "arg1,arg2,arg3", and build a 853 * device matching expression from it. 854 */ 855 int 856 buildmatch(char *match_str, struct devstat_match **matches, int *num_matches) 857 { 858 char *tstr[5]; 859 char **tempstr; 860 int num_args; 861 register int i, j; 862 char *func_name = "buildmatch"; 863 864 /* We can't do much without a string to parse */ 865 if (match_str == NULL) { 866 sprintf(devstat_errbuf, "%s: no match expression", func_name); 867 return(-1); 868 } 869 870 /* 871 * Break the (comma delimited) input string out into separate strings. 872 */ 873 for (tempstr = tstr, num_args = 0; 874 (*tempstr = strsep(&match_str, ",")) != NULL && (num_args < 5); 875 num_args++) 876 if (**tempstr != '\0') 877 if (++tempstr >= &tstr[5]) 878 break; 879 880 /* The user gave us too many type arguments */ 881 if (num_args > 3) { 882 sprintf(devstat_errbuf, "%s: too many type arguments", 883 func_name); 884 return(-1); 885 } 886 887 /* 888 * Since you can't realloc a pointer that hasn't been malloced 889 * first, we malloc first and then realloc. 890 */ 891 if (*num_matches == 0) 892 *matches = (struct devstat_match *)malloc( 893 sizeof(struct devstat_match)); 894 else 895 *matches = (struct devstat_match *)realloc(*matches, 896 sizeof(struct devstat_match) * (*num_matches + 1)); 897 898 /* Make sure the current entry is clear */ 899 bzero(&matches[0][*num_matches], sizeof(struct devstat_match)); 900 901 /* 902 * Step through the arguments the user gave us and build a device 903 * matching expression from them. 904 */ 905 for (i = 0; i < num_args; i++) { 906 char *tempstr2, *tempstr3; 907 908 /* 909 * Get rid of leading white space. 910 */ 911 tempstr2 = tstr[i]; 912 while (isspace(*tempstr2) && (*tempstr2 != '\0')) 913 tempstr2++; 914 915 /* 916 * Get rid of trailing white space. 917 */ 918 tempstr3 = &tempstr2[strlen(tempstr2) - 1]; 919 920 while ((*tempstr3 != '\0') && (tempstr3 > tempstr2) 921 && (isspace(*tempstr3))) { 922 *tempstr3 = '\0'; 923 tempstr3--; 924 } 925 926 /* 927 * Go through the match table comparing the user's 928 * arguments to known device types, interfaces, etc. 929 */ 930 for (j = 0; match_table[j].match_str != NULL; j++) { 931 /* 932 * We do case-insensitive matching, in case someone 933 * wants to enter "SCSI" instead of "scsi" or 934 * something like that. Only compare as many 935 * characters as are in the string in the match 936 * table. This should help if someone tries to use 937 * a super-long match expression. 938 */ 939 if (strncasecmp(tempstr2, match_table[j].match_str, 940 strlen(match_table[j].match_str)) == 0) { 941 /* 942 * Make sure the user hasn't specified two 943 * items of the same type, like "da" and 944 * "cd". One device cannot be both. 945 */ 946 if (((*matches)[*num_matches].match_fields & 947 match_table[j].match_field) != 0) { 948 sprintf(devstat_errbuf, 949 "%s: cannot have more than " 950 "one match item in a single " 951 "category", func_name); 952 return(-1); 953 } 954 /* 955 * If we've gotten this far, we have a 956 * winner. Set the appropriate fields in 957 * the match entry. 958 */ 959 (*matches)[*num_matches].match_fields |= 960 match_table[j].match_field; 961 (*matches)[*num_matches].device_type |= 962 match_table[j].type; 963 (*matches)[*num_matches].num_match_categories++; 964 break; 965 } 966 } 967 /* 968 * We should have found a match in the above for loop. If 969 * not, that means the user entered an invalid device type 970 * or interface. 971 */ 972 if ((*matches)[*num_matches].num_match_categories != (i + 1)) { 973 sprintf(devstat_errbuf, 974 "%s: unknown match item \"%s\"", func_name, 975 tstr[i]); 976 return(-1); 977 } 978 } 979 980 (*num_matches)++; 981 982 return(0); 983 } 984 985 /* 986 * Compute a number of device statistics. Only one field is mandatory, and 987 * that is "current". Everything else is optional. The caller passes in 988 * pointers to variables to hold the various statistics he desires. If he 989 * doesn't want a particular staistic, he should pass in a NULL pointer. 990 * Return values: 991 * 0 -- success 992 * -1 -- failure 993 */ 994 int 995 compute_stats(struct devstat *current, struct devstat *previous, 996 long double etime, u_int64_t *total_bytes, 997 u_int64_t *total_transfers, u_int64_t *total_blocks, 998 long double *kb_per_transfer, long double *transfers_per_second, 999 long double *mb_per_second, long double *blocks_per_second, 1000 long double *ms_per_transaction) 1001 { 1002 u_int64_t totalbytes, totaltransfers, totalblocks; 1003 char *func_name = "compute_stats"; 1004 1005 /* 1006 * current is the only mandatory field. 1007 */ 1008 if (current == NULL) { 1009 sprintf(devstat_errbuf, "%s: current stats structure was NULL", 1010 func_name); 1011 return(-1); 1012 } 1013 1014 totalbytes = (current->bytes_written + current->bytes_read) - 1015 ((previous) ? (previous->bytes_written + 1016 previous->bytes_read) : 0); 1017 1018 if (total_bytes) 1019 *total_bytes = totalbytes; 1020 1021 totaltransfers = (current->num_reads + 1022 current->num_writes + 1023 current->num_other) - 1024 ((previous) ? 1025 (previous->num_reads + 1026 previous->num_writes + 1027 previous->num_other) : 0); 1028 if (total_transfers) 1029 *total_transfers = totaltransfers; 1030 1031 if (transfers_per_second) { 1032 if (etime > 0.0) { 1033 *transfers_per_second = totaltransfers; 1034 *transfers_per_second /= etime; 1035 } else 1036 *transfers_per_second = 0.0; 1037 } 1038 1039 if (kb_per_transfer) { 1040 *kb_per_transfer = totalbytes; 1041 *kb_per_transfer /= 1024; 1042 if (totaltransfers > 0) 1043 *kb_per_transfer /= totaltransfers; 1044 else 1045 *kb_per_transfer = 0.0; 1046 } 1047 1048 if (mb_per_second) { 1049 *mb_per_second = totalbytes; 1050 *mb_per_second /= 1024 * 1024; 1051 if (etime > 0.0) 1052 *mb_per_second /= etime; 1053 else 1054 *mb_per_second = 0.0; 1055 } 1056 1057 totalblocks = totalbytes; 1058 if (current->block_size > 0) 1059 totalblocks /= current->block_size; 1060 else 1061 totalblocks /= 512; 1062 1063 if (total_blocks) 1064 *total_blocks = totalblocks; 1065 1066 if (blocks_per_second) { 1067 *blocks_per_second = totalblocks; 1068 if (etime > 0.0) 1069 *blocks_per_second /= etime; 1070 else 1071 *blocks_per_second = 0.0; 1072 } 1073 1074 if (ms_per_transaction) { 1075 if (totaltransfers > 0) { 1076 *ms_per_transaction = etime; 1077 *ms_per_transaction /= totaltransfers; 1078 *ms_per_transaction *= 1000; 1079 } else 1080 *ms_per_transaction = 0.0; 1081 } 1082 1083 return(0); 1084 } 1085 1086 long double 1087 compute_etime(struct timeval cur_time, struct timeval prev_time) 1088 { 1089 struct timeval busy_time; 1090 u_int64_t busy_usec; 1091 long double etime; 1092 1093 timersub(&cur_time, &prev_time, &busy_time); 1094 1095 busy_usec = busy_time.tv_sec; 1096 busy_usec *= 1000000; 1097 busy_usec += busy_time.tv_usec; 1098 etime = busy_usec; 1099 etime /= 1000000; 1100 1101 return(etime); 1102 } 1103