Module Name: src Committed By: mlelstv Date: Tue Jul 4 21:19:33 UTC 2017
Modified Files: src/usr.bin/vmstat: drvstats.c drvstats.h vmstat.c src/usr.sbin/iostat: iostat.c Log Message: Use I/O timestamps to compute disk statistics for better precision. Disk statistics are collected in a fixed size array, that got corrupted when a disk was detached. Adapt by skipping entries of detached disks and detect reused disknames at the array end. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/usr.bin/vmstat/drvstats.c cvs rdiff -u -r1.4 -r1.5 src/usr.bin/vmstat/drvstats.h cvs rdiff -u -r1.216 -r1.217 src/usr.bin/vmstat/vmstat.c cvs rdiff -u -r1.64 -r1.65 src/usr.sbin/iostat/iostat.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/vmstat/drvstats.c diff -u src/usr.bin/vmstat/drvstats.c:1.10 src/usr.bin/vmstat/drvstats.c:1.11 --- src/usr.bin/vmstat/drvstats.c:1.10 Sun Mar 5 23:07:12 2017 +++ src/usr.bin/vmstat/drvstats.c Tue Jul 4 21:19:33 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: drvstats.c,v 1.10 2017/03/05 23:07:12 mlelstv Exp $ */ +/* $NetBSD: drvstats.c,v 1.11 2017/07/04 21:19:33 mlelstv Exp $ */ /* * Copyright (c) 1996 John M. Vinopal @@ -97,6 +97,31 @@ drvswap(void) if (!cur.select[i]) continue; + /* + * When a drive is replaced with one of the same + * name, the previous statistics are invalid. Try + * to detect this by validating counters and timestamp + */ + if ((cur.rxfer[i] == 0 && cur.wxfer[i] == 0) + || cur.rxfer[i] - last.rxfer[i] > INT64_MAX + || cur.wxfer[i] - last.wxfer[i] > INT64_MAX + || cur.seek[i] - last.seek[i] > INT64_MAX + || (cur.timestamp[i].tv_sec == 0 && + cur.timestamp[i].tv_usec == 0)) { + + last.rxfer[i] = cur.rxfer[i]; + last.wxfer[i] = cur.wxfer[i]; + last.seek[i] = cur.seek[i]; + last.rbytes[i] = cur.rbytes[i]; + last.wbytes[i] = cur.wbytes[i]; + + timerclear(&last.wait[i]); + timerclear(&last.time[i]); + timerclear(&last.waitsum[i]); + timerclear(&last.busysum[i]); + timerclear(&last.timestamp[i]); + } + /* Delta Values. */ SWAP(rxfer[i]); SWAP(wxfer[i]); @@ -108,6 +133,7 @@ drvswap(void) DELTA(time[i]); DELTA(waitsum[i]); DELTA(busysum[i]); + DELTA(timestamp[i]); } } @@ -151,7 +177,7 @@ cpuswap(void) void drvreadstats(void) { - size_t size, i; + size_t size, i, j, count; int mib[3]; mib[0] = CTL_HW; @@ -161,27 +187,46 @@ drvreadstats(void) size = ndrive * sizeof(struct io_sysctl); if (sysctl(mib, 3, drives, &size, NULL, 0) < 0) err(1, "sysctl hw.iostats failed"); + /* recalculate array length */ + count = size / sizeof(struct io_sysctl); -#define COPYF(x,k) cur.x[k] = drives[k].x -#define COPYT(x,k) do { \ - cur.x[k].tv_sec = drives[k].x##_sec; \ - cur.x[k].tv_usec = drives[k].x##_usec; \ +#define COPYF(x,k,l) cur.x[k] = drives[l].x +#define COPYT(x,k,l) do { \ + cur.x[k].tv_sec = drives[l].x##_sec; \ + cur.x[k].tv_usec = drives[l].x##_usec; \ } while (/* CONSTCOND */0) - for (i = 0; i < ndrive; i++) { + for (i = 0, j = 0; i < ndrive && j < count; i++) { + + /* + * skip removed entries + * + * we cannot detect entries replaced with + * devices of the same name (e.g. unplug/replug). + */ + if (strcmp(cur.name[i], drives[j].name)) { + cur.select[i] = 0; + continue; + } + + COPYF(rxfer, i, j); + COPYF(wxfer, i, j); + COPYF(seek, i, j); + COPYF(rbytes, i, j); + COPYF(wbytes, i, j); + + COPYT(wait, i, j); + COPYT(time, i, j); + COPYT(waitsum, i, j); + COPYT(busysum, i, j); + COPYT(timestamp, i, j); - COPYF(rxfer, i); - COPYF(wxfer, i); - COPYF(seek, i); - COPYF(rbytes, i); - COPYF(wbytes, i); - - COPYT(wait, i); - COPYT(time, i); - COPYT(waitsum, i); - COPYT(busysum, i); + ++j; } + /* shrink table to new size */ + ndrive = j; + mib[0] = CTL_KERN; mib[1] = KERN_TKSTAT; mib[2] = KERN_TKSTAT_NIN; @@ -295,6 +340,7 @@ drvinit(int selected) cur.wait = calloc(ndrive, sizeof(struct timeval)); cur.waitsum = calloc(ndrive, sizeof(struct timeval)); cur.busysum = calloc(ndrive, sizeof(struct timeval)); + cur.timestamp = calloc(ndrive, sizeof(struct timeval)); cur.rxfer = calloc(ndrive, sizeof(u_int64_t)); cur.wxfer = calloc(ndrive, sizeof(u_int64_t)); cur.seek = calloc(ndrive, sizeof(u_int64_t)); @@ -304,6 +350,7 @@ drvinit(int selected) last.wait = calloc(ndrive, sizeof(struct timeval)); last.waitsum = calloc(ndrive, sizeof(struct timeval)); last.busysum = calloc(ndrive, sizeof(struct timeval)); + last.timestamp = calloc(ndrive, sizeof(struct timeval)); last.rxfer = calloc(ndrive, sizeof(u_int64_t)); last.wxfer = calloc(ndrive, sizeof(u_int64_t)); last.seek = calloc(ndrive, sizeof(u_int64_t)); @@ -314,11 +361,13 @@ drvinit(int selected) if (cur.time == NULL || cur.wait == NULL || cur.waitsum == NULL || cur.busysum == NULL || + cur.timestamp == NULL || cur.rxfer == NULL || cur.wxfer == NULL || cur.seek == NULL || cur.rbytes == NULL || cur.wbytes == NULL || last.time == NULL || last.wait == NULL || last.waitsum == NULL || last.busysum == NULL || + last.timestamp == NULL || last.rxfer == NULL || last.wxfer == NULL || last.seek == NULL || last.rbytes == NULL || last.wbytes == NULL || @@ -335,8 +384,12 @@ drvinit(int selected) mib[2] = sizeof(struct io_sysctl); if (sysctl(mib, 3, drives, &size, NULL, 0) == -1) err(1, "sysctl hw.iostats failed"); + /* Recalculate array length */ + ndrive = size / sizeof(struct io_sysctl); for (i = 0; i < ndrive; i++) { - cur.name[i] = drives[i].name; + cur.name[i] = strndup(drives[i].name, sizeof(drives[i].name)); + if (cur.name[i] == NULL) + errx(1, "Memory allocation failure"); cur.select[i] = selected; } Index: src/usr.bin/vmstat/drvstats.h diff -u src/usr.bin/vmstat/drvstats.h:1.4 src/usr.bin/vmstat/drvstats.h:1.5 --- src/usr.bin/vmstat/drvstats.h:1.4 Sun Mar 5 23:07:12 2017 +++ src/usr.bin/vmstat/drvstats.h Tue Jul 4 21:19:33 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: drvstats.h,v 1.4 2017/03/05 23:07:12 mlelstv Exp $ */ +/* $NetBSD: drvstats.h,v 1.5 2017/07/04 21:19:33 mlelstv Exp $ */ /* * Copyright (c) 1996 John M. Vinopal @@ -39,7 +39,7 @@ /* poseur disk entry to hold the information we're interested in. */ struct _drive { int *select; /* Display stats for selected disks. */ - char **name; /* Disk names (sd0, wd1, etc). */ + char **name; /* Disk names (sd0, wd1, etc). */ u_int64_t *rxfer; /* # of read transfers. */ u_int64_t *wxfer; /* # of write transfers. */ u_int64_t *seek; /* # of seeks (currently unused). */ @@ -47,8 +47,9 @@ struct _drive { u_int64_t *wbytes; /* # of bytes written. */ struct timeval *time; /* Time spent in disk i/o. */ struct timeval *wait; /* Time spent in queue waiting. */ - struct timeval *busysum; /* Time busy * queue length */ - struct timeval *waitsum; /* Time waiting * queue length */ + struct timeval *busysum; /* Time busy * queue length */ + struct timeval *waitsum; /* Time waiting * queue length */ + struct timeval *timestamp; /* Disk sample time */ u_int64_t tk_nin; /* TTY Chars in. */ u_int64_t tk_nout; /* TTY Chars out. */ u_int64_t cp_time[CPUSTATES]; /* System timer ticks. */ Index: src/usr.bin/vmstat/vmstat.c diff -u src/usr.bin/vmstat/vmstat.c:1.216 src/usr.bin/vmstat/vmstat.c:1.217 --- src/usr.bin/vmstat/vmstat.c:1.216 Thu Jan 5 07:53:20 2017 +++ src/usr.bin/vmstat/vmstat.c Tue Jul 4 21:19:33 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: vmstat.c,v 1.216 2017/01/05 07:53:20 ryo Exp $ */ +/* $NetBSD: vmstat.c,v 1.217 2017/07/04 21:19:33 mlelstv Exp $ */ /*- * Copyright (c) 1998, 2000, 2001, 2007 The NetBSD Foundation, Inc. @@ -70,7 +70,7 @@ __COPYRIGHT("@(#) Copyright (c) 1980, 19 #if 0 static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/95"; #else -__RCSID("$NetBSD: vmstat.c,v 1.216 2017/01/05 07:53:20 ryo Exp $"); +__RCSID("$NetBSD: vmstat.c,v 1.217 2017/07/04 21:19:33 mlelstv Exp $"); #endif #endif /* not lint */ @@ -1113,20 +1113,23 @@ void drvstats(int *ovflwp) { size_t dn; - double etime; + double dtime; int ovflw = *ovflwp; /* Calculate disk stat deltas. */ cpuswap(); drvswap(); tkswap(); - etime = cur.cp_etime; for (dn = 0; dn < ndrive; ++dn) { + /* elapsed time for disk stats */ + dtime = (double)cur.timestamp[dn].tv_sec + + ((double)cur.timestamp[dn].tv_usec / (double)1000000); + if (!drv_select[dn]) continue; PRWORD(ovflw, " %*.0f", 3, 1, - (cur.rxfer[dn] + cur.wxfer[dn]) / etime); + (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); } *ovflwp = ovflw; } Index: src/usr.sbin/iostat/iostat.c diff -u src/usr.sbin/iostat/iostat.c:1.64 src/usr.sbin/iostat/iostat.c:1.65 --- src/usr.sbin/iostat/iostat.c:1.64 Sun Mar 5 23:07:12 2017 +++ src/usr.sbin/iostat/iostat.c Tue Jul 4 21:19:33 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: iostat.c,v 1.64 2017/03/05 23:07:12 mlelstv Exp $ */ +/* $NetBSD: iostat.c,v 1.65 2017/07/04 21:19:33 mlelstv Exp $ */ /* * Copyright (c) 1996 John M. Vinopal @@ -71,7 +71,7 @@ __COPYRIGHT("@(#) Copyright (c) 1986, 19 #if 0 static char sccsid[] = "@(#)iostat.c 8.3 (Berkeley) 4/28/95"; #else -__RCSID("$NetBSD: iostat.c,v 1.64 2017/03/05 23:07:12 mlelstv Exp $"); +__RCSID("$NetBSD: iostat.c,v 1.65 2017/07/04 21:19:33 mlelstv Exp $"); #endif #endif /* not lint */ @@ -112,6 +112,7 @@ static int wincols = 80; #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X | SHOW_STATS_Y) static void cpustats(void); +static double drive_time(double, int); static void drive_stats(double); static void drive_stats2(double); static void drive_statsx(double); @@ -321,15 +322,32 @@ header(void) printf("\n"); } +static double +drive_time(double etime, int dn) +{ + if (ISSET(todo, SHOW_TOTALS)) + return etime; + + if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) { + etime = (double)cur.timestamp[dn].tv_sec + + ((double)cur.timestamp[dn].tv_usec / (double)1000000); + } + + return etime; +} + static void drive_stats(double etime) { size_t dn; - double atime, mbps; + double atime, dtime, mbps; for (dn = 0; dn < ndrive; ++dn) { if (!cur.select[dn]) continue; + + dtime = drive_time(etime, dn); + /* average Kbytes per transfer. */ if (cur.rxfer[dn] + cur.wxfer[dn]) mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) / @@ -341,7 +359,7 @@ drive_stats(double etime) /* average transfers per second. */ (void)printf(" %4.0f", - (cur.rxfer[dn] + cur.wxfer[dn]) / etime); + (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); /* time busy in drive activity */ atime = (double)cur.time[dn].tv_sec + @@ -353,7 +371,7 @@ drive_stats(double etime) (double)(1024 * 1024); else mbps = 0; - mbps /= etime; + mbps /= dtime; (void)printf(" %5.*f ", MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); } @@ -363,24 +381,26 @@ static void drive_stats2(double etime) { size_t dn; - double atime; + double atime, dtime; for (dn = 0; dn < ndrive; ++dn) { if (!cur.select[dn]) continue; + dtime = drive_time(etime, dn); + /* average kbytes per second. */ (void)printf(" %5.0f", - (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / etime); + (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / dtime); /* average transfers per second. */ (void)printf(" %5.0f", - (cur.rxfer[dn] + cur.wxfer[dn]) / etime); + (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); /* average time busy in drive activity */ atime = (double)cur.time[dn].tv_sec + ((double)cur.time[dn].tv_usec / (double)1000000); - (void)printf(" %4.2f ", atime / etime); + (void)printf(" %4.2f ", atime / dtime); } } @@ -388,12 +408,14 @@ static void drive_statsx(double etime) { size_t dn; - double atime, kbps; + double atime, dtime, kbps; for (dn = 0; dn < ndrive; ++dn) { if (!cur.select[dn]) continue; + dtime = drive_time(etime, dn); + (void)printf("%-8.8s", cur.name[dn]); /* average read Kbytes per transfer */ @@ -405,17 +427,17 @@ drive_statsx(double etime) /* average read transfers (per second) */ - (void)printf(" %6.0f", cur.rxfer[dn] / etime); + (void)printf(" %6.0f", cur.rxfer[dn] / dtime); /* time read busy in drive activity */ atime = (double)cur.time[dn].tv_sec + ((double)cur.time[dn].tv_usec / (double)1000000); - (void)printf(" %6.2f", atime / etime); + (void)printf(" %6.2f", atime / dtime); /* average read megabytes (per second) */ (void)printf(" %8.2f", - cur.rbytes[dn] / (1024.0 * 1024) / etime); + cur.rbytes[dn] / (1024.0 * 1024) / dtime); /* average write Kbytes per transfer */ @@ -427,17 +449,17 @@ drive_statsx(double etime) /* average write transfers (per second) */ - (void)printf(" %6.0f", cur.wxfer[dn] / etime); + (void)printf(" %6.0f", cur.wxfer[dn] / dtime); /* time write busy in drive activity */ atime = (double)cur.time[dn].tv_sec + ((double)cur.time[dn].tv_usec / (double)1000000); - (void)printf(" %6.2f", atime / etime); + (void)printf(" %6.2f", atime / dtime); /* average write megabytes (per second) */ (void)printf(" %8.2f\n", - cur.wbytes[dn] / (1024.0 * 1024) / etime); + cur.wbytes[dn] / (1024.0 * 1024) / dtime); } } @@ -486,12 +508,14 @@ static void drive_statsy(double etime) { size_t dn; - double atime, await, abusysum, awaitsum; + double atime, await, abusysum, awaitsum, dtime; for (dn = 0; dn < ndrive; ++dn) { if (!cur.select[dn]) continue; + dtime = drive_time(etime, dn); + (void)printf("%-8.8s", cur.name[dn]); atime = (double)cur.time[dn].tv_sec + @@ -503,10 +527,10 @@ drive_statsy(double etime) awaitsum = (double)cur.waitsum[dn].tv_sec + ((double)cur.waitsum[dn].tv_usec / (double)1000000); - drive_statsy_io(etime, cur.rxfer[dn], cur.rbytes[dn]); + drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]); (void)printf(" "); - drive_statsy_io(etime, cur.wxfer[dn], cur.wbytes[dn]); - drive_statsy_q(etime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]); + drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]); + drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]); (void)printf("\n"); }