1 1.14 rillig /* $NetBSD: drvstats.c,v 1.14 2021/11/27 22:16:42 rillig Exp $ */ 2 1.1 blymn 3 1.1 blymn /* 4 1.1 blymn * Copyright (c) 1996 John M. Vinopal 5 1.1 blymn * All rights reserved. 6 1.1 blymn * 7 1.1 blymn * Redistribution and use in source and binary forms, with or without 8 1.1 blymn * modification, are permitted provided that the following conditions 9 1.1 blymn * are met: 10 1.1 blymn * 1. Redistributions of source code must retain the above copyright 11 1.1 blymn * notice, this list of conditions and the following disclaimer. 12 1.1 blymn * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 blymn * notice, this list of conditions and the following disclaimer in the 14 1.1 blymn * documentation and/or other materials provided with the distribution. 15 1.1 blymn * 3. All advertising materials mentioning features or use of this software 16 1.1 blymn * must display the following acknowledgement: 17 1.1 blymn * This product includes software developed for the NetBSD Project 18 1.1 blymn * by John M. Vinopal. 19 1.1 blymn * 4. The name of the author may not be used to endorse or promote products 20 1.1 blymn * derived from this software without specific prior written permission. 21 1.1 blymn * 22 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 1.1 blymn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 1.1 blymn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 1.1 blymn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 1.1 blymn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 blymn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 blymn * SUCH DAMAGE. 33 1.1 blymn */ 34 1.1 blymn 35 1.1 blymn #include <sys/param.h> 36 1.1 blymn #include <sys/sched.h> 37 1.1 blymn #include <sys/sysctl.h> 38 1.1 blymn #include <sys/time.h> 39 1.1 blymn #include <sys/iostat.h> 40 1.1 blymn 41 1.1 blymn #include <err.h> 42 1.1 blymn #include <fcntl.h> 43 1.1 blymn #include <limits.h> 44 1.1 blymn #include <stdio.h> 45 1.1 blymn #include <stdlib.h> 46 1.1 blymn #include <string.h> 47 1.1 blymn #include <unistd.h> 48 1.1 blymn #include "drvstats.h" 49 1.1 blymn 50 1.1 blymn /* Structures to hold the statistics. */ 51 1.1 blymn struct _drive cur, last; 52 1.1 blymn 53 1.1 blymn extern int hz; 54 1.1 blymn 55 1.1 blymn /* sysctl hw.drivestats buffer. */ 56 1.1 blymn static struct io_sysctl *drives = NULL; 57 1.1 blymn 58 1.1 blymn /* Backward compatibility references. */ 59 1.4 christos size_t ndrive = 0; 60 1.1 blymn int *drv_select; 61 1.1 blymn char **dr_name; 62 1.1 blymn 63 1.1 blymn /* Missing from <sys/time.h> */ 64 1.1 blymn #define timerset(tvp, uvp) do { \ 65 1.1 blymn ((uvp)->tv_sec = (tvp)->tv_sec); \ 66 1.1 blymn ((uvp)->tv_usec = (tvp)->tv_usec); \ 67 1.14 rillig } while (0) 68 1.1 blymn 69 1.1 blymn /* 70 1.1 blymn * Take the delta between the present values and the last recorded 71 1.1 blymn * values, storing the present values in the 'last' structure, and 72 1.1 blymn * the delta values in the 'cur' structure. 73 1.1 blymn */ 74 1.1 blymn void 75 1.1 blymn drvswap(void) 76 1.1 blymn { 77 1.1 blymn u_int64_t tmp; 78 1.5 lukem size_t i; 79 1.1 blymn 80 1.1 blymn #define SWAP(fld) do { \ 81 1.1 blymn tmp = cur.fld; \ 82 1.1 blymn cur.fld -= last.fld; \ 83 1.1 blymn last.fld = tmp; \ 84 1.14 rillig } while (0) 85 1.1 blymn 86 1.10 mlelstv #define DELTA(x) do { \ 87 1.10 mlelstv timerclear(&tmp_timer); \ 88 1.10 mlelstv timerset(&(cur.x), &tmp_timer); \ 89 1.10 mlelstv timersub(&tmp_timer, &(last.x), &(cur.x)); \ 90 1.10 mlelstv timerclear(&(last.x)); \ 91 1.10 mlelstv timerset(&tmp_timer, &(last.x)); \ 92 1.14 rillig } while (0) 93 1.10 mlelstv 94 1.1 blymn for (i = 0; i < ndrive; i++) { 95 1.1 blymn struct timeval tmp_timer; 96 1.1 blymn 97 1.1 blymn if (!cur.select[i]) 98 1.1 blymn continue; 99 1.1 blymn 100 1.11 mlelstv /* 101 1.11 mlelstv * When a drive is replaced with one of the same 102 1.11 mlelstv * name, the previous statistics are invalid. Try 103 1.11 mlelstv * to detect this by validating counters and timestamp 104 1.11 mlelstv */ 105 1.11 mlelstv if ((cur.rxfer[i] == 0 && cur.wxfer[i] == 0) 106 1.11 mlelstv || cur.rxfer[i] - last.rxfer[i] > INT64_MAX 107 1.11 mlelstv || cur.wxfer[i] - last.wxfer[i] > INT64_MAX 108 1.11 mlelstv || cur.seek[i] - last.seek[i] > INT64_MAX 109 1.11 mlelstv || (cur.timestamp[i].tv_sec == 0 && 110 1.11 mlelstv cur.timestamp[i].tv_usec == 0)) { 111 1.11 mlelstv 112 1.11 mlelstv last.rxfer[i] = cur.rxfer[i]; 113 1.11 mlelstv last.wxfer[i] = cur.wxfer[i]; 114 1.11 mlelstv last.seek[i] = cur.seek[i]; 115 1.11 mlelstv last.rbytes[i] = cur.rbytes[i]; 116 1.11 mlelstv last.wbytes[i] = cur.wbytes[i]; 117 1.11 mlelstv 118 1.11 mlelstv timerclear(&last.wait[i]); 119 1.11 mlelstv timerclear(&last.time[i]); 120 1.11 mlelstv timerclear(&last.waitsum[i]); 121 1.11 mlelstv timerclear(&last.busysum[i]); 122 1.11 mlelstv timerclear(&last.timestamp[i]); 123 1.11 mlelstv } 124 1.11 mlelstv 125 1.1 blymn /* Delta Values. */ 126 1.1 blymn SWAP(rxfer[i]); 127 1.1 blymn SWAP(wxfer[i]); 128 1.1 blymn SWAP(seek[i]); 129 1.1 blymn SWAP(rbytes[i]); 130 1.1 blymn SWAP(wbytes[i]); 131 1.1 blymn 132 1.10 mlelstv DELTA(wait[i]); 133 1.10 mlelstv DELTA(time[i]); 134 1.10 mlelstv DELTA(waitsum[i]); 135 1.10 mlelstv DELTA(busysum[i]); 136 1.11 mlelstv DELTA(timestamp[i]); 137 1.1 blymn } 138 1.1 blymn } 139 1.1 blymn 140 1.1 blymn void 141 1.1 blymn tkswap(void) 142 1.1 blymn { 143 1.1 blymn u_int64_t tmp; 144 1.1 blymn 145 1.1 blymn SWAP(tk_nin); 146 1.1 blymn SWAP(tk_nout); 147 1.1 blymn } 148 1.1 blymn 149 1.1 blymn void 150 1.1 blymn cpuswap(void) 151 1.1 blymn { 152 1.1 blymn double etime; 153 1.1 blymn u_int64_t tmp; 154 1.1 blymn int i, state; 155 1.1 blymn 156 1.1 blymn for (i = 0; i < CPUSTATES; i++) 157 1.1 blymn SWAP(cp_time[i]); 158 1.1 blymn 159 1.1 blymn etime = 0; 160 1.1 blymn for (state = 0; state < CPUSTATES; ++state) { 161 1.1 blymn etime += cur.cp_time[state]; 162 1.1 blymn } 163 1.1 blymn if (etime == 0) 164 1.1 blymn etime = 1; 165 1.1 blymn etime /= hz; 166 1.1 blymn etime /= cur.cp_ncpu; 167 1.1 blymn 168 1.1 blymn cur.cp_etime = etime; 169 1.1 blymn } 170 1.10 mlelstv #undef DELTA 171 1.1 blymn #undef SWAP 172 1.1 blymn 173 1.1 blymn /* 174 1.1 blymn * Read the drive statistics for each drive in the drive list. 175 1.1 blymn * Also collect statistics for tty i/o and CPU ticks. 176 1.1 blymn */ 177 1.1 blymn void 178 1.1 blymn drvreadstats(void) 179 1.1 blymn { 180 1.11 mlelstv size_t size, i, j, count; 181 1.1 blymn int mib[3]; 182 1.1 blymn 183 1.7 joerg mib[0] = CTL_HW; 184 1.7 joerg mib[1] = HW_IOSTATS; 185 1.7 joerg mib[2] = sizeof(struct io_sysctl); 186 1.7 joerg 187 1.7 joerg size = ndrive * sizeof(struct io_sysctl); 188 1.7 joerg if (sysctl(mib, 3, drives, &size, NULL, 0) < 0) 189 1.7 joerg err(1, "sysctl hw.iostats failed"); 190 1.11 mlelstv /* recalculate array length */ 191 1.11 mlelstv count = size / sizeof(struct io_sysctl); 192 1.10 mlelstv 193 1.11 mlelstv #define COPYF(x,k,l) cur.x[k] = drives[l].x 194 1.11 mlelstv #define COPYT(x,k,l) do { \ 195 1.11 mlelstv cur.x[k].tv_sec = drives[l].x##_sec; \ 196 1.11 mlelstv cur.x[k].tv_usec = drives[l].x##_usec; \ 197 1.14 rillig } while (0) 198 1.10 mlelstv 199 1.11 mlelstv for (i = 0, j = 0; i < ndrive && j < count; i++) { 200 1.11 mlelstv 201 1.11 mlelstv /* 202 1.11 mlelstv * skip removed entries 203 1.11 mlelstv * 204 1.11 mlelstv * we cannot detect entries replaced with 205 1.11 mlelstv * devices of the same name (e.g. unplug/replug). 206 1.11 mlelstv */ 207 1.11 mlelstv if (strcmp(cur.name[i], drives[j].name)) { 208 1.11 mlelstv cur.select[i] = 0; 209 1.11 mlelstv continue; 210 1.11 mlelstv } 211 1.11 mlelstv 212 1.11 mlelstv COPYF(rxfer, i, j); 213 1.11 mlelstv COPYF(wxfer, i, j); 214 1.11 mlelstv COPYF(seek, i, j); 215 1.11 mlelstv COPYF(rbytes, i, j); 216 1.11 mlelstv COPYF(wbytes, i, j); 217 1.11 mlelstv 218 1.11 mlelstv COPYT(wait, i, j); 219 1.11 mlelstv COPYT(time, i, j); 220 1.11 mlelstv COPYT(waitsum, i, j); 221 1.11 mlelstv COPYT(busysum, i, j); 222 1.11 mlelstv COPYT(timestamp, i, j); 223 1.10 mlelstv 224 1.11 mlelstv ++j; 225 1.7 joerg } 226 1.1 blymn 227 1.11 mlelstv /* shrink table to new size */ 228 1.11 mlelstv ndrive = j; 229 1.11 mlelstv 230 1.10 mlelstv mib[0] = CTL_KERN; 231 1.7 joerg mib[1] = KERN_TKSTAT; 232 1.7 joerg mib[2] = KERN_TKSTAT_NIN; 233 1.7 joerg size = sizeof(cur.tk_nin); 234 1.7 joerg if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) 235 1.7 joerg cur.tk_nin = 0; 236 1.7 joerg 237 1.7 joerg mib[2] = KERN_TKSTAT_NOUT; 238 1.7 joerg size = sizeof(cur.tk_nout); 239 1.7 joerg if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) 240 1.7 joerg cur.tk_nout = 0; 241 1.1 blymn 242 1.1 blymn size = sizeof(cur.cp_time); 243 1.4 christos (void)memset(cur.cp_time, 0, size); 244 1.7 joerg mib[0] = CTL_KERN; 245 1.7 joerg mib[1] = KERN_CP_TIME; 246 1.7 joerg if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) 247 1.7 joerg (void)memset(cur.cp_time, 0, sizeof(cur.cp_time)); 248 1.1 blymn } 249 1.10 mlelstv #undef COPYT 250 1.10 mlelstv #undef COPYF 251 1.1 blymn 252 1.1 blymn /* 253 1.1 blymn * Read collect statistics for tty i/o. 254 1.1 blymn */ 255 1.1 blymn 256 1.1 blymn void 257 1.1 blymn tkreadstats(void) 258 1.1 blymn { 259 1.1 blymn size_t size; 260 1.1 blymn int mib[3]; 261 1.1 blymn 262 1.7 joerg mib[0] = CTL_KERN; 263 1.7 joerg mib[1] = KERN_TKSTAT; 264 1.7 joerg mib[2] = KERN_TKSTAT_NIN; 265 1.7 joerg size = sizeof(cur.tk_nin); 266 1.7 joerg if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) 267 1.7 joerg cur.tk_nin = 0; 268 1.7 joerg 269 1.7 joerg mib[2] = KERN_TKSTAT_NOUT; 270 1.7 joerg size = sizeof(cur.tk_nout); 271 1.7 joerg if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) 272 1.7 joerg cur.tk_nout = 0; 273 1.1 blymn } 274 1.1 blymn 275 1.1 blymn /* 276 1.1 blymn * Read collect statistics for CPU ticks. 277 1.1 blymn */ 278 1.1 blymn 279 1.1 blymn void 280 1.1 blymn cpureadstats(void) 281 1.1 blymn { 282 1.1 blymn size_t size; 283 1.1 blymn int mib[2]; 284 1.1 blymn 285 1.1 blymn size = sizeof(cur.cp_time); 286 1.4 christos (void)memset(cur.cp_time, 0, size); 287 1.7 joerg mib[0] = CTL_KERN; 288 1.7 joerg mib[1] = KERN_CP_TIME; 289 1.7 joerg if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) 290 1.7 joerg (void)memset(cur.cp_time, 0, sizeof(cur.cp_time)); 291 1.1 blymn } 292 1.1 blymn 293 1.1 blymn /* 294 1.1 blymn * Perform all of the initialization and memory allocation needed to 295 1.1 blymn * track drive statistics. 296 1.1 blymn */ 297 1.1 blymn int 298 1.1 blymn drvinit(int selected) 299 1.1 blymn { 300 1.1 blymn struct clockinfo clockinfo; 301 1.5 lukem size_t size, i; 302 1.1 blymn static int once = 0; 303 1.5 lukem int mib[3]; 304 1.1 blymn 305 1.1 blymn if (once) 306 1.1 blymn return (1); 307 1.1 blymn 308 1.7 joerg mib[0] = CTL_HW; 309 1.7 joerg mib[1] = HW_NCPU; 310 1.7 joerg size = sizeof(cur.cp_ncpu); 311 1.7 joerg if (sysctl(mib, 2, &cur.cp_ncpu, &size, NULL, 0) == -1) 312 1.7 joerg err(1, "sysctl hw.ncpu failed"); 313 1.7 joerg 314 1.7 joerg mib[0] = CTL_KERN; 315 1.7 joerg mib[1] = KERN_CLOCKRATE; 316 1.7 joerg size = sizeof(clockinfo); 317 1.7 joerg if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1) 318 1.7 joerg err(1, "sysctl kern.clockrate failed"); 319 1.7 joerg hz = clockinfo.stathz; 320 1.7 joerg if (!hz) 321 1.7 joerg hz = clockinfo.hz; 322 1.7 joerg 323 1.7 joerg mib[0] = CTL_HW; 324 1.7 joerg mib[1] = HW_IOSTATS; 325 1.7 joerg mib[2] = sizeof(struct io_sysctl); 326 1.7 joerg if (sysctl(mib, 3, NULL, &size, NULL, 0) == -1) 327 1.7 joerg err(1, "sysctl hw.drivestats failed"); 328 1.7 joerg ndrive = size / sizeof(struct io_sysctl); 329 1.1 blymn 330 1.7 joerg if (size == 0) { 331 1.7 joerg warnx("No drives attached."); 332 1.1 blymn } else { 333 1.7 joerg drives = (struct io_sysctl *)malloc(size); 334 1.7 joerg if (drives == NULL) 335 1.7 joerg errx(1, "Memory allocation failure."); 336 1.1 blymn } 337 1.1 blymn 338 1.1 blymn /* Allocate space for the statistics. */ 339 1.1 blymn cur.time = calloc(ndrive, sizeof(struct timeval)); 340 1.10 mlelstv cur.wait = calloc(ndrive, sizeof(struct timeval)); 341 1.10 mlelstv cur.waitsum = calloc(ndrive, sizeof(struct timeval)); 342 1.10 mlelstv cur.busysum = calloc(ndrive, sizeof(struct timeval)); 343 1.11 mlelstv cur.timestamp = calloc(ndrive, sizeof(struct timeval)); 344 1.1 blymn cur.rxfer = calloc(ndrive, sizeof(u_int64_t)); 345 1.1 blymn cur.wxfer = calloc(ndrive, sizeof(u_int64_t)); 346 1.1 blymn cur.seek = calloc(ndrive, sizeof(u_int64_t)); 347 1.1 blymn cur.rbytes = calloc(ndrive, sizeof(u_int64_t)); 348 1.1 blymn cur.wbytes = calloc(ndrive, sizeof(u_int64_t)); 349 1.13 he cur.scale = calloc(ndrive, sizeof(int)); 350 1.1 blymn last.time = calloc(ndrive, sizeof(struct timeval)); 351 1.10 mlelstv last.wait = calloc(ndrive, sizeof(struct timeval)); 352 1.10 mlelstv last.waitsum = calloc(ndrive, sizeof(struct timeval)); 353 1.10 mlelstv last.busysum = calloc(ndrive, sizeof(struct timeval)); 354 1.11 mlelstv last.timestamp = calloc(ndrive, sizeof(struct timeval)); 355 1.1 blymn last.rxfer = calloc(ndrive, sizeof(u_int64_t)); 356 1.1 blymn last.wxfer = calloc(ndrive, sizeof(u_int64_t)); 357 1.1 blymn last.seek = calloc(ndrive, sizeof(u_int64_t)); 358 1.1 blymn last.rbytes = calloc(ndrive, sizeof(u_int64_t)); 359 1.1 blymn last.wbytes = calloc(ndrive, sizeof(u_int64_t)); 360 1.1 blymn cur.select = calloc(ndrive, sizeof(int)); 361 1.1 blymn cur.name = calloc(ndrive, sizeof(char *)); 362 1.1 blymn 363 1.10 mlelstv if (cur.time == NULL || cur.wait == NULL || 364 1.10 mlelstv cur.waitsum == NULL || cur.busysum == NULL || 365 1.11 mlelstv cur.timestamp == NULL || 366 1.10 mlelstv cur.rxfer == NULL || cur.wxfer == NULL || 367 1.10 mlelstv cur.seek == NULL || cur.rbytes == NULL || 368 1.10 mlelstv cur.wbytes == NULL || 369 1.10 mlelstv last.time == NULL || last.wait == NULL || 370 1.10 mlelstv last.waitsum == NULL || last.busysum == NULL || 371 1.11 mlelstv last.timestamp == NULL || 372 1.10 mlelstv last.rxfer == NULL || last.wxfer == NULL || 373 1.10 mlelstv last.seek == NULL || last.rbytes == NULL || 374 1.10 mlelstv last.wbytes == NULL || 375 1.1 blymn cur.select == NULL || cur.name == NULL) 376 1.1 blymn errx(1, "Memory allocation failure."); 377 1.1 blymn 378 1.1 blymn /* Set up the compatibility interfaces. */ 379 1.1 blymn drv_select = cur.select; 380 1.1 blymn dr_name = cur.name; 381 1.1 blymn 382 1.12 dholland /* Read the drive names and set initial selection. */ 383 1.7 joerg mib[0] = CTL_HW; /* Should be still set from */ 384 1.7 joerg mib[1] = HW_IOSTATS; /* ... above, but be safe... */ 385 1.7 joerg mib[2] = sizeof(struct io_sysctl); 386 1.7 joerg if (sysctl(mib, 3, drives, &size, NULL, 0) == -1) 387 1.7 joerg err(1, "sysctl hw.iostats failed"); 388 1.11 mlelstv /* Recalculate array length */ 389 1.11 mlelstv ndrive = size / sizeof(struct io_sysctl); 390 1.7 joerg for (i = 0; i < ndrive; i++) { 391 1.11 mlelstv cur.name[i] = strndup(drives[i].name, sizeof(drives[i].name)); 392 1.11 mlelstv if (cur.name[i] == NULL) 393 1.11 mlelstv errx(1, "Memory allocation failure"); 394 1.7 joerg cur.select[i] = selected; 395 1.1 blymn } 396 1.1 blymn 397 1.1 blymn /* Never do this initialization again. */ 398 1.1 blymn once = 1; 399 1.1 blymn return (1); 400 1.1 blymn } 401