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;
 		}
 	}

Reply via email to