Module Name: src
Committed By: mlelstv
Date: Sun Mar 5 23:07:12 UTC 2017
Modified Files:
src/share/man/man9: disk.9
src/sys/arch/xen/xen: xbd_xenbus.c
src/sys/dev: ccd.c dksubr.c
src/sys/dev/ata: wd.c
src/sys/dev/dkwedge: dk.c
src/sys/kern: subr_disk.c subr_iostat.c
src/sys/sys: disk.h iostat.h
src/usr.bin/vmstat: drvstats.c drvstats.h
src/usr.sbin/iostat: iostat.8 iostat.c
Log Message:
Enhance disk metrics by calculating a weighted sum that is incremented
by the number of concurrent I/O requests. Also introduce a new disk_wait()
function to measure requests waiting in a bufq.
iostat -y now reports data about waiting and active requests.
So far only drivers using dksubr and dk, ccd, wd and xbd collect data about
waiting requests.
To generate a diff of this commit:
cvs rdiff -u -r1.43 -r1.44 src/share/man/man9/disk.9
cvs rdiff -u -r1.75 -r1.76 src/sys/arch/xen/xen/xbd_xenbus.c
cvs rdiff -u -r1.168 -r1.169 src/sys/dev/ccd.c
cvs rdiff -u -r1.95 -r1.96 src/sys/dev/dksubr.c
cvs rdiff -u -r1.427 -r1.428 src/sys/dev/ata/wd.c
cvs rdiff -u -r1.95 -r1.96 src/sys/dev/dkwedge/dk.c
cvs rdiff -u -r1.117 -r1.118 src/sys/kern/subr_disk.c
cvs rdiff -u -r1.21 -r1.22 src/sys/kern/subr_iostat.c
cvs rdiff -u -r1.69 -r1.70 src/sys/sys/disk.h
cvs rdiff -u -r1.10 -r1.11 src/sys/sys/iostat.h
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/vmstat/drvstats.c
cvs rdiff -u -r1.3 -r1.4 src/usr.bin/vmstat/drvstats.h
cvs rdiff -u -r1.24 -r1.25 src/usr.sbin/iostat/iostat.8
cvs rdiff -u -r1.63 -r1.64 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/share/man/man9/disk.9
diff -u src/share/man/man9/disk.9:1.43 src/share/man/man9/disk.9:1.44
--- src/share/man/man9/disk.9:1.43 Mon Jan 23 11:42:03 2017
+++ src/share/man/man9/disk.9 Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: disk.9,v 1.43 2017/01/23 11:42:03 abhinav Exp $
+.\" $NetBSD: disk.9,v 1.44 2017/03/05 23:07:12 mlelstv Exp $
.\"
.\" Copyright (c) 1995, 1996 Jason R. Thorpe.
.\" All rights reserved.
@@ -40,6 +40,7 @@
.Nm disk_begindetach ,
.Nm disk_detach ,
.Nm disk_destroy ,
+.Nm disk_wait ,
.Nm disk_busy ,
.Nm disk_unbusy ,
.Nm disk_isbusy ,
@@ -61,6 +62,8 @@
.Ft void
.Fn disk_destroy "struct disk *"
.Ft void
+.Fn disk_wait "struct disk *"
+.Ft void
.Fn disk_busy "struct disk *"
.Ft void
.Fn disk_unbusy "struct disk *" "long bcount" "int read"
@@ -167,19 +170,25 @@ If the count drops below zero, panic.
.It Fn disk_destroy
Release resources used by the disk structure when it is no longer
required.
+.It Fn disk_wait
+Disk timings are measured by counting the number of queued
+requests (wait counter) and requests issued to the hardware (busy counter)
+and keeping timestamp when the counters change. The time interval between
+two changes of a counter is accumulated into a total and also multiplied
+by the counter value and the accumulated into a sum. Both values can be
+used to determine how much time is spent in the driver queue or in-flight
+to the hardware as well as the average number of requests in either state.
+.Fn disk_wait
+increment the disk's wait counter and handles the accumulation.
.It Fn disk_busy
-Increment the disk's
-.Dq busy counter .
-If this counter goes from 0 to 1, set the timestamp corresponding to
-this transfer.
+Decrements the disk's wait counter and increments the disk's
+.Dq busy counter ,
+and handles either accumulation. If the wait counter is still zero, it
+is assumed that the driver hasn't been updated to call
+.Fn disk_wait ,
+then only the values from the busy counter are available.
.It Fn disk_unbusy
-Decrement a disk's busy counter.
-If the count drops below zero, panic.
-Get the current time, subtract it from the disk's timestamp, and add
-the difference to the disk's running total.
-Set the disk's timestamp to the current time.
-If the provided byte count is greater than 0, add it to the disk's
-running total and increment the number of transfers performed by the disk.
+Decrement the disk's busy counter and handles the accumulation.
The third argument
.Ar read
specifies the direction of I/O;
@@ -212,6 +221,7 @@ The functions typically called by device
.Fn disk_begindetach ,
.Fn disk_detach ,
.Fn disk_destroy ,
+.Fn disk_wait ,
.Fn disk_busy ,
.Fn disk_unbusy ,
and
@@ -403,8 +413,9 @@ const struct dkdriver foodkdriver = {
.Pp
Once the disk is attached, metrics may be gathered on that disk.
In order to gather metrics data, the driver must tell the framework when
-the disk starts and stops operations.
+the disk queues, starts and stops operations.
This functionality is provided by the
+.Fn disk_wait ,
.Fn disk_busy
and
.Fn disk_unbusy
@@ -413,6 +424,7 @@ Because
.Nm struct disk
is part of device driver private data it needs to be guarded.
Mutual exclusion must be done by driver
+.Fn disk_wait ,
.Fn disk_busy
and
.Fn disk_unbusy
@@ -423,8 +435,22 @@ routine should be called immediately bef
sent, e.g.:
.Bd -literal
void
-foostart(sc)
- struct foo_softc *sc;
+foostrategy(struct buf *bp)
+{
+ [ . . . ]
+
+ mutex_enter(\*[Am]sc-\*[Gt]sc_dk_mtx);
+ disk_wait(\*[Am]sc-\*[Gt]sc_dk);
+
+ /* Put buffer onto drive's transfer queue */
+
+ mutex_exit(\*[Am]sc-\*[Gt]sc_dk_mtx);
+
+ foostart(sc);
+}
+
+void
+foostart(struct foo_softc *sc)
{
[ . . . ]
@@ -444,26 +470,15 @@ foostart(sc)
}
.Ed
.Pp
-When
-.Fn disk_busy
-is called, a timestamp is taken if the disk's busy counter moves from
-0 to 1, indicating the disk has gone from an idle to non-idle state.
-At the end of a transaction, the
+The routine
.Fn disk_unbusy
-routine should be called.
-This routine performs some consistency checks,
-such as ensuring that the calls to
+performs some consistency checks, such as ensuring that the calls to
.Fn disk_busy
and
.Fn disk_unbusy
are balanced.
-This routine also performs the actual metrics calculation.
-A timestamp is taken and the difference from the timestamp taken in
-.Fn disk_busy
-is added to the disk's total running time.
-The disk's timestamp is then updated in case there is more than one
-pending transfer on the disk.
-A byte count is also added to the disk's running total, and if greater than
+It also performs the final steps of the metrics calcuation.
+A byte count is added to the disk's running total, and if greater than
zero, the number of transfers the disk has performed is incremented.
The third argument
.Ar read
@@ -506,6 +521,7 @@ foodone(xfer)
is used to get status of disk device it returns true if device is
currently busy and false if it is not.
Like
+.Fn disk_wait ,
.Fn disk_busy
and
.Fn disk_unbusy
Index: src/sys/arch/xen/xen/xbd_xenbus.c
diff -u src/sys/arch/xen/xen/xbd_xenbus.c:1.75 src/sys/arch/xen/xen/xbd_xenbus.c:1.76
--- src/sys/arch/xen/xen/xbd_xenbus.c:1.75 Sun Oct 25 07:51:16 2015
+++ src/sys/arch/xen/xen/xbd_xenbus.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: xbd_xenbus.c,v 1.75 2015/10/25 07:51:16 maxv Exp $ */
+/* $NetBSD: xbd_xenbus.c,v 1.76 2017/03/05 23:07:12 mlelstv Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@@ -50,7 +50,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c,v 1.75 2015/10/25 07:51:16 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c,v 1.76 2017/03/05 23:07:12 mlelstv Exp $");
#include "opt_xen.h"
@@ -327,7 +327,7 @@ xbd_xenbus_detach(device_t dev, int flag
sc->sc_shutdown = BLKIF_SHUTDOWN_LOCAL;
/* wait for requests to complete */
while (sc->sc_backend_status == BLKIF_STATE_CONNECTED &&
- sc->sc_dksc.sc_dkdev.dk_stats->io_busy > 0)
+ disk_isbusy(&sc->sc_dksc.sc_dkdev))
tsleep(xbd_xenbus_detach, PRIBIO, "xbddetach", hz/2);
xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateClosing);
@@ -392,7 +392,7 @@ xbd_xenbus_suspend(device_t dev, const p
s = splbio();
/* wait for requests to complete, then suspend device */
while (sc->sc_backend_status == BLKIF_STATE_CONNECTED &&
- sc->sc_dksc.sc_dkdev.dk_stats->io_busy > 0)
+ disk_isbusy(&sc->sc_dksc.sc_dkdev))
tsleep(xbd_xenbus_suspend, PRIBIO, "xbdsuspend", hz/2);
hypervisor_mask_event(sc->sc_evtchn);
@@ -530,7 +530,7 @@ static void xbd_backend_changed(void *ar
sc->sc_shutdown = BLKIF_SHUTDOWN_REMOTE;
/* wait for requests to complete */
while (sc->sc_backend_status == BLKIF_STATE_CONNECTED &&
- sc->sc_dksc.sc_dkdev.dk_stats->io_busy > 0)
+ disk_isbusy(&sc->sc_dksc.sc_dkdev))
tsleep(xbd_xenbus_detach, PRIBIO, "xbddetach", hz/2);
splx(s);
xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateClosed);
Index: src/sys/dev/ccd.c
diff -u src/sys/dev/ccd.c:1.168 src/sys/dev/ccd.c:1.169
--- src/sys/dev/ccd.c:1.168 Sun Nov 20 02:35:19 2016
+++ src/sys/dev/ccd.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ccd.c,v 1.168 2016/11/20 02:35:19 pgoyette Exp $ */
+/* $NetBSD: ccd.c,v 1.169 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998, 1999, 2007, 2009 The NetBSD Foundation, Inc.
@@ -88,7 +88,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.168 2016/11/20 02:35:19 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.169 2017/03/05 23:07:12 mlelstv Exp $");
#if defined(_KERNEL_OPT)
#include "opt_compat_netbsd.h"
@@ -815,10 +815,11 @@ ccdstart(struct ccd_softc *cs)
KASSERT(mutex_owned(cs->sc_iolock));
- disk_busy(&cs->sc_dkdev);
bp = bufq_get(cs->sc_bufq);
KASSERT(bp != NULL);
+ disk_busy(&cs->sc_dkdev);
+
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdstart(%s, %p)\n", cs->sc_xname, bp);
Index: src/sys/dev/dksubr.c
diff -u src/sys/dev/dksubr.c:1.95 src/sys/dev/dksubr.c:1.96
--- src/sys/dev/dksubr.c:1.95 Sat Feb 25 15:19:00 2017
+++ src/sys/dev/dksubr.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: dksubr.c,v 1.95 2017/02/25 15:19:00 mlelstv Exp $ */
+/* $NetBSD: dksubr.c,v 1.96 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998, 1999, 2002, 2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dksubr.c,v 1.95 2017/02/25 15:19:00 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dksubr.c,v 1.96 2017/03/05 23:07:12 mlelstv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -339,6 +339,7 @@ dk_strategy_defer(struct dk_softc *dksc,
* Queue buffer only
*/
mutex_enter(&dksc->sc_iolock);
+ disk_wait(&dksc->sc_dkdev);
bufq_put(dksc->sc_bufq, bp);
mutex_exit(&dksc->sc_iolock);
@@ -375,8 +376,10 @@ dk_start(struct dk_softc *dksc, struct b
mutex_enter(&dksc->sc_iolock);
- if (bp != NULL)
+ if (bp != NULL) {
+ disk_wait(&dksc->sc_dkdev);
bufq_put(dksc->sc_bufq, bp);
+ }
/*
* If another thread is running the queue, increment
@@ -417,6 +420,7 @@ dk_start(struct dk_softc *dksc, struct b
if (error == EAGAIN) {
dksc->sc_deferred = bp;
disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
+ disk_wait(&dksc->sc_dkdev);
break;
}
Index: src/sys/dev/ata/wd.c
diff -u src/sys/dev/ata/wd.c:1.427 src/sys/dev/ata/wd.c:1.428
--- src/sys/dev/ata/wd.c:1.427 Sun Nov 20 02:35:19 2016
+++ src/sys/dev/ata/wd.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: wd.c,v 1.427 2016/11/20 02:35:19 pgoyette Exp $ */
+/* $NetBSD: wd.c,v 1.428 2017/03/05 23:07:12 mlelstv Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@@ -54,7 +54,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.427 2016/11/20 02:35:19 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.428 2017/03/05 23:07:12 mlelstv Exp $");
#include "opt_ata.h"
@@ -605,6 +605,7 @@ wdstrategy(struct buf *bp)
/* Queue transfer on drive, activate drive and controller if idle. */
s = splbio();
+ disk_wait(&wd->sc_dk);
bufq_put(wd->sc_q, bp);
wdstart(wd);
splx(s);
Index: src/sys/dev/dkwedge/dk.c
diff -u src/sys/dev/dkwedge/dk.c:1.95 src/sys/dev/dkwedge/dk.c:1.96
--- src/sys/dev/dkwedge/dk.c:1.95 Mon Feb 27 21:27:07 2017
+++ src/sys/dev/dkwedge/dk.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: dk.c,v 1.95 2017/02/27 21:27:07 jdolecek Exp $ */
+/* $NetBSD: dk.c,v 1.96 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 2004, 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dk.c,v 1.95 2017/02/27 21:27:07 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dk.c,v 1.96 2017/03/05 23:07:12 mlelstv Exp $");
#ifdef _KERNEL_OPT
#include "opt_dkwedge.h"
@@ -1249,6 +1249,7 @@ dkstrategy(struct buf *bp)
/* Place it in the queue and start I/O on the unit. */
mutex_enter(&sc->sc_iolock);
sc->sc_iopend++;
+ disk_wait(&sc->sc_dk);
bufq_put(sc->sc_bufq, bp);
mutex_exit(&sc->sc_iolock);
Index: src/sys/kern/subr_disk.c
diff -u src/sys/kern/subr_disk.c:1.117 src/sys/kern/subr_disk.c:1.118
--- src/sys/kern/subr_disk.c:1.117 Tue Feb 28 00:33:36 2017
+++ src/sys/kern/subr_disk.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_disk.c,v 1.117 2017/02/28 00:33:36 jakllsch Exp $ */
+/* $NetBSD: subr_disk.c,v 1.118 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 1996, 1997, 1999, 2000, 2009 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_disk.c,v 1.117 2017/02/28 00:33:36 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_disk.c,v 1.118 2017/03/05 23:07:12 mlelstv Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
@@ -275,6 +275,16 @@ disk_destroy(struct disk *diskp)
}
/*
+ * Mark the disk as having work queued for metrics collection.
+ */
+void
+disk_wait(struct disk *diskp)
+{
+
+ iostat_wait(diskp->dk_stats);
+}
+
+/*
* Mark the disk as busy for metrics collection.
*/
void
Index: src/sys/kern/subr_iostat.c
diff -u src/sys/kern/subr_iostat.c:1.21 src/sys/kern/subr_iostat.c:1.22
--- src/sys/kern/subr_iostat.c:1.21 Sat Oct 18 08:33:29 2014
+++ src/sys/kern/subr_iostat.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_iostat.c,v 1.21 2014/10/18 08:33:29 snj Exp $ */
+/* $NetBSD: subr_iostat.c,v 1.22 2017/03/05 23:07:12 mlelstv Exp $ */
/* NetBSD: subr_disk.c,v 1.69 2005/05/29 22:24:15 christos Exp */
/*-
@@ -68,7 +68,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_iostat.c,v 1.21 2014/10/18 08:33:29 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_iostat.c,v 1.22 2017/03/05 23:07:12 mlelstv Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
@@ -183,37 +183,106 @@ iostat_free(struct io_stats *stats)
}
/*
- * Increment a iostat busy counter. If the counter is going from
- * 0 to 1, set the timestamp.
+ * multiply timeval by unsigned integer and add to result
+ */
+static void
+timermac(struct timeval *a, uint64_t count, struct timeval *res)
+{
+ struct timeval part = *a;
+
+ while (count) {
+ if (count & 1)
+ timeradd(res, &part, res);
+ timeradd(&part, &part, &part);
+ count >>= 1;
+ }
+}
+
+/*
+ * Increment the iostat wait counter.
+ * Accumulate wait time and timesum.
+ *
+ * Wait time is spent in the device bufq.
+ */
+void
+iostat_wait(struct io_stats *stats)
+{
+ struct timeval dv_time, diff_time;
+ int32_t count;
+
+ KASSERT(stats->io_wait >= 0);
+
+ getmicrouptime(&dv_time);
+
+ timersub(&dv_time, &stats->io_waitstamp, &diff_time);
+ count = stats->io_wait++;
+ if (count != 0) {
+ timermac(&diff_time, count, &stats->io_waitsum);
+ timeradd(&stats->io_waittime, &diff_time, &stats->io_waittime);
+ }
+ stats->io_waitstamp = dv_time;
+}
+
+/*
+ * Decrement the iostat wait counter.
+ * Increment the iostat busy counter.
+ * Accumulate wait and busy times and timesums.
+ *
+ * Busy time is spent being processed by the device.
+ *
+ * Old devices do not yet measure wait time, so skip
+ * processing it if the counter is still zero.
*/
void
iostat_busy(struct io_stats *stats)
{
+ struct timeval dv_time, diff_time;
+ int32_t count;
+
+ KASSERT(stats->io_wait >= 0); /* > 0 when iostat_wait is used */
+ KASSERT(stats->io_busy >= 0);
+
+ getmicrouptime(&dv_time);
- if (stats->io_busy++ == 0)
- getmicrouptime(&stats->io_timestamp);
+ timersub(&dv_time, &stats->io_waitstamp, &diff_time);
+ if (stats->io_wait != 0) {
+ count = stats->io_wait--;
+ timermac(&diff_time, count, &stats->io_waitsum);
+ timeradd(&stats->io_waittime, &diff_time, &stats->io_waittime);
+ }
+ stats->io_waitstamp = dv_time;
+
+ timersub(&dv_time, &stats->io_busystamp, &diff_time);
+ count = stats->io_busy++;
+ if (count != 0) {
+ timermac(&diff_time, count, &stats->io_busysum);
+ timeradd(&stats->io_busytime, &diff_time, &stats->io_busytime);
+ }
+ stats->io_busystamp = dv_time;
}
/*
- * Decrement a iostat busy counter, increment the byte count, total busy
- * time, and reset the timestamp.
+ * Decrement the iostat busy counter, increment the byte count.
+ * Accumulate busy time and timesum.
*/
void
iostat_unbusy(struct io_stats *stats, long bcount, int read)
{
struct timeval dv_time, diff_time;
+ int32_t count;
- if (stats->io_busy-- == 0) {
- printf("%s: busy < 0\n", stats->io_name);
- panic("iostat_unbusy");
- }
+ KASSERT(stats->io_busy > 0);
getmicrouptime(&dv_time);
+ stats->io_timestamp = dv_time;
- timersub(&dv_time, &stats->io_timestamp, &diff_time);
- timeradd(&stats->io_time, &diff_time, &stats->io_time);
+ /* any op */
+ timersub(&dv_time, &stats->io_busystamp, &diff_time);
+ count = stats->io_busy--;
+ timermac(&diff_time, count, &stats->io_busysum);
+ timeradd(&stats->io_busytime, &diff_time, &stats->io_busytime);
+ stats->io_busystamp = dv_time;
- stats->io_timestamp = dv_time;
if (bcount > 0) {
if (read) {
stats->io_rbytes += bcount;
@@ -352,20 +421,38 @@ sysctl_hw_iostats(SYSCTLFN_ARGS)
TAILQ_FOREACH(stats, &iostatlist, io_link) {
if (left < tocopy)
break;
+
strncpy(sdrive.name, stats->io_name, sizeof(sdrive.name));
- sdrive.xfer = stats->io_rxfer + stats->io_wxfer;
+ sdrive.attachtime_sec = stats->io_attachtime.tv_sec;
+ sdrive.attachtime_usec = stats->io_attachtime.tv_usec;
+ sdrive.timestamp_sec = stats->io_busystamp.tv_sec;
+ sdrive.timestamp_usec = stats->io_busystamp.tv_usec;
+
+ sdrive.time_sec = stats->io_busytime.tv_sec;
+ sdrive.time_usec = stats->io_busytime.tv_usec;
+
+ sdrive.seek = stats->io_seek;
+
sdrive.rxfer = stats->io_rxfer;
sdrive.wxfer = stats->io_wxfer;
- sdrive.seek = stats->io_seek;
- sdrive.bytes = stats->io_rbytes + stats->io_wbytes;
+ sdrive.xfer = stats->io_rxfer + stats->io_wxfer;
+
sdrive.rbytes = stats->io_rbytes;
sdrive.wbytes = stats->io_wbytes;
- sdrive.attachtime_sec = stats->io_attachtime.tv_sec;
- sdrive.attachtime_usec = stats->io_attachtime.tv_usec;
- sdrive.timestamp_sec = stats->io_timestamp.tv_sec;
- sdrive.timestamp_usec = stats->io_timestamp.tv_usec;
- sdrive.time_sec = stats->io_time.tv_sec;
- sdrive.time_usec = stats->io_time.tv_usec;
+ sdrive.bytes = stats->io_rbytes + stats->io_wbytes;
+
+ sdrive.wait_sec = stats->io_waittime.tv_sec;
+ sdrive.wait_usec = stats->io_waittime.tv_usec;
+
+ sdrive.time_sec = stats->io_busytime.tv_sec;
+ sdrive.time_usec = stats->io_busytime.tv_usec;
+
+ sdrive.waitsum_sec = stats->io_waitsum.tv_sec;
+ sdrive.waitsum_usec = stats->io_waitsum.tv_usec;
+
+ sdrive.busysum_sec = stats->io_busysum.tv_sec;
+ sdrive.busysum_usec = stats->io_busysum.tv_usec;
+
sdrive.busy = stats->io_busy;
error = copyout(&sdrive, where, min(tocopy, sizeof(sdrive)));
Index: src/sys/sys/disk.h
diff -u src/sys/sys/disk.h:1.69 src/sys/sys/disk.h:1.70
--- src/sys/sys/disk.h:1.69 Thu Dec 8 12:21:54 2016
+++ src/sys/sys/disk.h Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: disk.h,v 1.69 2016/12/08 12:21:54 mlelstv Exp $ */
+/* $NetBSD: disk.h,v 1.70 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 1996, 1997, 2004 The NetBSD Foundation, Inc.
@@ -534,6 +534,7 @@ int disk_begindetach(struct disk *, int
void disk_detach(struct disk *);
void disk_init(struct disk *, const char *, const struct dkdriver *);
void disk_destroy(struct disk *);
+void disk_wait(struct disk *);
void disk_busy(struct disk *);
void disk_unbusy(struct disk *, long, int);
bool disk_isbusy(struct disk *);
Index: src/sys/sys/iostat.h
diff -u src/sys/sys/iostat.h:1.10 src/sys/sys/iostat.h:1.11
--- src/sys/sys/iostat.h:1.10 Sat Apr 4 07:30:09 2009
+++ src/sys/sys/iostat.h Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: iostat.h,v 1.10 2009/04/04 07:30:09 ad Exp $ */
+/* $NetBSD: iostat.h,v 1.11 2017/03/05 23:07:12 mlelstv Exp $ */
/*-
* Copyright (c) 1996, 1997, 2004, 2009 The NetBSD Foundation, Inc.
@@ -66,6 +66,18 @@ struct io_sysctl {
u_int64_t rbytes;
u_int64_t wxfer;
u_int64_t wbytes;
+ /*
+ * New queue stats
+ * accumulated wait time (iostat_wait .. iostat_busy)
+ * accumulated wait sum (wait time * count)
+ * accumulated busy sum (busy time * count)
+ */
+ u_int32_t wait_sec;
+ u_int32_t wait_usec;
+ u_int32_t waitsum_sec;
+ u_int32_t waitsum_usec;
+ u_int32_t busysum_sec;
+ u_int32_t busysum_usec;
};
/*
@@ -78,6 +90,7 @@ struct io_stats {
void *io_parent; /* pointer to what we are attached to */
int io_type; /* type of device the state belong to */
int io_busy; /* busy counter */
+ int io_wait; /* wait counter */
u_int64_t io_rxfer; /* total number of read transfers */
u_int64_t io_wxfer; /* total number of write transfers */
u_int64_t io_seek; /* total independent seek operations */
@@ -85,7 +98,12 @@ struct io_stats {
u_int64_t io_wbytes; /* total bytes written */
struct timeval io_attachtime; /* time disk was attached */
struct timeval io_timestamp; /* timestamp of last unbusy */
- struct timeval io_time; /* total time spent busy */
+ struct timeval io_busystamp; /* timestamp of last busy */
+ struct timeval io_waitstamp; /* timestamp of last wait */
+ struct timeval io_busysum; /* accumulated wait * time */
+ struct timeval io_waitsum; /* accumulated busy * time */
+ struct timeval io_busytime; /* accumlated time busy */
+ struct timeval io_waittime; /* accumlated time waiting */
TAILQ_ENTRY(io_stats) io_link;
};
@@ -96,6 +114,7 @@ TAILQ_HEAD(iostatlist_head, io_stats); /
#ifdef _KERNEL
void iostat_init(void);
+void iostat_wait(struct io_stats *);
void iostat_busy(struct io_stats *);
void iostat_unbusy(struct io_stats *, long, int);
bool iostat_isbusy(struct io_stats *);
Index: src/usr.bin/vmstat/drvstats.c
diff -u src/usr.bin/vmstat/drvstats.c:1.9 src/usr.bin/vmstat/drvstats.c:1.10
--- src/usr.bin/vmstat/drvstats.c:1.9 Fri Jun 13 11:26:37 2014
+++ src/usr.bin/vmstat/drvstats.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: drvstats.c,v 1.9 2014/06/13 11:26:37 joerg Exp $ */
+/* $NetBSD: drvstats.c,v 1.10 2017/03/05 23:07:12 mlelstv Exp $ */
/*
* Copyright (c) 1996 John M. Vinopal
@@ -83,6 +83,14 @@ drvswap(void)
last.fld = tmp; \
} while (/* CONSTCOND */0)
+#define DELTA(x) do { \
+ timerclear(&tmp_timer); \
+ timerset(&(cur.x), &tmp_timer); \
+ timersub(&tmp_timer, &(last.x), &(cur.x)); \
+ timerclear(&(last.x)); \
+ timerset(&tmp_timer, &(last.x)); \
+} while (/* CONSTCOND */0)
+
for (i = 0; i < ndrive; i++) {
struct timeval tmp_timer;
@@ -96,12 +104,10 @@ drvswap(void)
SWAP(rbytes[i]);
SWAP(wbytes[i]);
- /* Delta Time. */
- timerclear(&tmp_timer);
- timerset(&(cur.time[i]), &tmp_timer);
- timersub(&tmp_timer, &(last.time[i]), &(cur.time[i]));
- timerclear(&(last.time[i]));
- timerset(&tmp_timer, &(last.time[i]));
+ DELTA(wait[i]);
+ DELTA(time[i]);
+ DELTA(waitsum[i]);
+ DELTA(busysum[i]);
}
}
@@ -135,6 +141,7 @@ cpuswap(void)
cur.cp_etime = etime;
}
+#undef DELTA
#undef SWAP
/*
@@ -154,17 +161,28 @@ drvreadstats(void)
size = ndrive * sizeof(struct io_sysctl);
if (sysctl(mib, 3, drives, &size, NULL, 0) < 0)
err(1, "sysctl hw.iostats failed");
+
+#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; \
+} while (/* CONSTCOND */0)
+
for (i = 0; i < ndrive; i++) {
- cur.rxfer[i] = drives[i].rxfer;
- cur.wxfer[i] = drives[i].wxfer;
- cur.seek[i] = drives[i].seek;
- cur.rbytes[i] = drives[i].rbytes;
- cur.wbytes[i] = drives[i].wbytes;
- cur.time[i].tv_sec = drives[i].time_sec;
- cur.time[i].tv_usec = drives[i].time_usec;
+
+ 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);
}
- mib[0] = CTL_KERN;
+ mib[0] = CTL_KERN;
mib[1] = KERN_TKSTAT;
mib[2] = KERN_TKSTAT_NIN;
size = sizeof(cur.tk_nin);
@@ -183,6 +201,8 @@ drvreadstats(void)
if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0)
(void)memset(cur.cp_time, 0, sizeof(cur.cp_time));
}
+#undef COPYT
+#undef COPYF
/*
* Read collect statistics for tty i/o.
@@ -272,12 +292,18 @@ drvinit(int selected)
/* Allocate space for the statistics. */
cur.time = calloc(ndrive, sizeof(struct timeval));
+ cur.wait = calloc(ndrive, sizeof(struct timeval));
+ cur.waitsum = calloc(ndrive, sizeof(struct timeval));
+ cur.busysum = 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));
cur.rbytes = calloc(ndrive, sizeof(u_int64_t));
cur.wbytes = calloc(ndrive, sizeof(u_int64_t));
last.time = calloc(ndrive, sizeof(struct timeval));
+ last.wait = calloc(ndrive, sizeof(struct timeval));
+ last.waitsum = calloc(ndrive, sizeof(struct timeval));
+ last.busysum = 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));
@@ -286,12 +312,16 @@ drvinit(int selected)
cur.select = calloc(ndrive, sizeof(int));
cur.name = calloc(ndrive, sizeof(char *));
- if (cur.time == NULL || cur.rxfer == NULL ||
- cur.wxfer == NULL || cur.seek == NULL ||
- cur.rbytes == NULL || cur.wbytes == NULL ||
- last.time == NULL || last.rxfer == NULL ||
- last.wxfer == NULL || last.seek == NULL ||
- last.rbytes == NULL || last.wbytes == NULL ||
+ if (cur.time == NULL || cur.wait == NULL ||
+ cur.waitsum == NULL || cur.busysum == 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.rxfer == NULL || last.wxfer == NULL ||
+ last.seek == NULL || last.rbytes == NULL ||
+ last.wbytes == NULL ||
cur.select == NULL || cur.name == NULL)
errx(1, "Memory allocation failure.");
Index: src/usr.bin/vmstat/drvstats.h
diff -u src/usr.bin/vmstat/drvstats.h:1.3 src/usr.bin/vmstat/drvstats.h:1.4
--- src/usr.bin/vmstat/drvstats.h:1.3 Tue Oct 17 15:13:08 2006
+++ src/usr.bin/vmstat/drvstats.h Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: drvstats.h,v 1.3 2006/10/17 15:13:08 christos Exp $ */
+/* $NetBSD: drvstats.h,v 1.4 2017/03/05 23:07:12 mlelstv Exp $ */
/*
* Copyright (c) 1996 John M. Vinopal
@@ -45,7 +45,10 @@ struct _drive {
u_int64_t *seek; /* # of seeks (currently unused). */
u_int64_t *rbytes; /* # of bytes read. */
u_int64_t *wbytes; /* # of bytes written. */
- struct timeval *time; /* Time spent in disk i/o. */
+ 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 */
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.sbin/iostat/iostat.8
diff -u src/usr.sbin/iostat/iostat.8:1.24 src/usr.sbin/iostat/iostat.8:1.25
--- src/usr.sbin/iostat/iostat.8:1.24 Thu Jul 9 13:26:52 2015
+++ src/usr.sbin/iostat/iostat.8 Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: iostat.8,v 1.24 2015/07/09 13:26:52 mrg Exp $
+.\" $NetBSD: iostat.8,v 1.25 2017/03/05 23:07:12 mlelstv Exp $
.\"
.\" Copyright (c) 1985, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -39,7 +39,7 @@
statistics
.Sh SYNOPSIS
.Nm
-.Op Fl CdDITx
+.Op Fl CdDITxy
.Op Fl c Ar count
.Op Fl w Ar wait
.Op Ar drives
@@ -127,6 +127,8 @@ This option overrides all other display
disks are displayed unless specific disks
are provided as arguments.
Additionally, separate read and write statistics are displayed.
+.It Fl y
+Shows the extended statistics and additional queuing statistics.
.El
.Pp
.Nm
@@ -172,6 +174,24 @@ Kilobytes transferred
Disk transfers
.It time
Seconds spent in disk activity
+.Pp
+.El
+With the
+.Fl y
+flag, the following queuing measurements are added
+.Bl -tag -width indent -compact
+.It wait
+Number of I/O requests queued up
+.It actv
+Number of currently active I/O requests
+.It wsvc_t
+Average waiting time of an I/O request in milliseconds
+.It asvc_t
+Average duration of an I/O request in milliseconds
+.It wtime
+Seconds spent in the waiting queue.
+Queuing data might not be available from all drivers
+and is then shown as zeros.
.El
.It cpu
.Bl -tag -width indent -compact
@@ -205,3 +225,7 @@ The
.Fl x
option was added in
.Nx 1.4 .
+Collection of queueing values and the
+.Fl y
+option were added in
+.Nx 8.0 .
Index: src/usr.sbin/iostat/iostat.c
diff -u src/usr.sbin/iostat/iostat.c:1.63 src/usr.sbin/iostat/iostat.c:1.64
--- src/usr.sbin/iostat/iostat.c:1.63 Sun Oct 25 02:47:17 2015
+++ src/usr.sbin/iostat/iostat.c Sun Mar 5 23:07:12 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: iostat.c,v 1.63 2015/10/25 02:47:17 mrg Exp $ */
+/* $NetBSD: iostat.c,v 1.64 2017/03/05 23:07:12 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.63 2015/10/25 02:47:17 mrg Exp $");
+__RCSID("$NetBSD: iostat.c,v 1.64 2017/03/05 23:07:12 mlelstv Exp $");
#endif
#endif /* not lint */
@@ -107,13 +107,17 @@ static int wincols = 80;
#define SHOW_STATS_1 (1<<2)
#define SHOW_STATS_2 (1<<3)
#define SHOW_STATS_X (1<<4)
+#define SHOW_STATS_Y (1<<5)
#define SHOW_TOTALS (1<<7)
-#define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
+#define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X | SHOW_STATS_Y)
static void cpustats(void);
static void drive_stats(double);
static void drive_stats2(double);
static void drive_statsx(double);
+static void drive_statsy(double);
+static void drive_statsy_io(double, double, double);
+static void drive_statsy_q(double, double, double, double, double, double);
static void sig_header(int);
static volatile int do_header;
static void header(void);
@@ -128,7 +132,7 @@ main(int argc, char *argv[])
struct timespec tv;
struct ttysize ts;
- while ((ch = getopt(argc, argv, "Cc:dDITw:x")) != -1)
+ while ((ch = getopt(argc, argv, "Cc:dDITw:xy")) != -1)
switch (ch) {
case 'c':
if ((reps = atoi(optarg)) <= 0)
@@ -159,6 +163,10 @@ main(int argc, char *argv[])
todo &= ~SHOW_STATS_ALL;
todo |= SHOW_STATS_X;
break;
+ case 'y':
+ todo &= ~SHOW_STATS_ALL;
+ todo |= SHOW_STATS_Y;
+ break;
case '?':
default:
usage();
@@ -172,6 +180,10 @@ main(int argc, char *argv[])
todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
todo |= SHOW_STATS_X;
}
+ if (ISSET(todo, SHOW_STATS_Y)) {
+ todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS);
+ todo |= SHOW_STATS_Y;
+ }
if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
if (ts.ts_lines)
@@ -197,7 +209,7 @@ main(int argc, char *argv[])
if (todo == 0)
errx(1, "no drives");
}
- if (ISSET(todo, SHOW_STATS_X))
+ if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y))
lines = ndrives;
else
lines = 1;
@@ -257,6 +269,13 @@ header(void)
return;
}
+ if (ISSET(todo, SHOW_STATS_Y)) {
+ (void)printf("device read KB/t r/s MB/s write KB/t w/s MB/s");
+ (void)printf(" wait actv wsvc_t asvc_t wtime time");
+ (void)printf("\n");
+ return;
+ }
+
if (ISSET(todo, SHOW_TTY))
(void)printf(" tty");
@@ -423,6 +442,77 @@ drive_statsx(double etime)
}
static void
+drive_statsy_io(double elapsed, double count, double volume)
+{
+ double kbps;
+
+ /* average Kbytes per transfer */
+ if (count)
+ kbps = (volume / 1024.0) / count;
+ else
+ kbps = 0.0;
+ (void)printf(" %8.2f", kbps);
+
+ /* average transfers (per second) */
+ (void)printf(" %6.0f", count / elapsed);
+
+ /* average megabytes (per second) */
+ (void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
+}
+
+static void
+drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
+{
+ /* average wait queue length */
+ (void)printf(" %6.1f", waitsum / elapsed);
+
+ /* average busy queue length */
+ (void)printf(" %6.1f", busysum / elapsed);
+
+ /* average wait time */
+ (void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
+
+ /* average service time */
+ (void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
+
+ /* time waiting for drive activity */
+ (void)printf(" %6.2f", wait / elapsed);
+
+ /* time busy in drive activity */
+ (void)printf(" %6.2f", busy / elapsed);
+}
+
+static void
+drive_statsy(double etime)
+{
+ size_t dn;
+ double atime, await, abusysum, awaitsum;
+
+ for (dn = 0; dn < ndrive; ++dn) {
+ if (!cur.select[dn])
+ continue;
+
+ (void)printf("%-8.8s", cur.name[dn]);
+
+ atime = (double)cur.time[dn].tv_sec +
+ ((double)cur.time[dn].tv_usec / (double)1000000);
+ await = (double)cur.wait[dn].tv_sec +
+ ((double)cur.wait[dn].tv_usec / (double)1000000);
+ abusysum = (double)cur.busysum[dn].tv_sec +
+ ((double)cur.busysum[dn].tv_usec / (double)1000000);
+ 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]);
+ (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]);
+
+ (void)printf("\n");
+ }
+}
+
+static void
cpustats(void)
{
int state;
@@ -442,7 +532,7 @@ static void
usage(void)
{
- (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] "
+ (void)fprintf(stderr, "usage: iostat [-CdDITxy] [-c count] "
"[-w wait] [drives]\n");
exit(1);
}
@@ -467,6 +557,11 @@ display(void)
goto out;
}
+ if (ISSET(todo, SHOW_STATS_Y)) {
+ drive_statsy(etime);
+ goto out;
+ }
+
if (ISSET(todo, SHOW_TTY))
printf("%4.0f %5.0f", cur.tk_nin / etime, cur.tk_nout / etime);
@@ -525,14 +620,15 @@ selectdrives(int argc, char *argv[])
* Pick up to defdrives (or all if -x is given) drives
* if none specified.
*/
- maxdrives = (ISSET(todo, SHOW_STATS_X) ||
+ maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
(int)ndrive < defdrives)
? (int)(ndrive) : defdrives;
for (i = 0; i < maxdrives; i++) {
cur.select[i] = 1;
++ndrives;
- if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
+ if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
+ ndrives == defdrives)
break;
}
}