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