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.
This commit is contained in:
mlelstv 2017-03-05 23:07:12 +00:00
parent 3ffd56c5a6
commit ba576b71a7
14 changed files with 403 additions and 110 deletions

View File

@ -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 drivers are
.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 before a command to the disk is
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

View File

@ -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 flags)
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 pmf_qual_t *qual) {
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 *arg, XenbusState new_state)
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);

View File

@ -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);

View File

@ -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);

View File

@ -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, struct buf *bp)
* 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 buf *bp)
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 buf *bp)
if (error == EAGAIN) {
dksc->sc_deferred = bp;
disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
disk_wait(&dksc->sc_dkdev);
break;
}

View File

@ -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);

View File

@ -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>
@ -274,6 +274,16 @@ disk_destroy(struct disk *diskp)
mutex_destroy(&diskp->dk_rawlock);
}
/*
* 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.
*/

View File

@ -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;
if (stats->io_busy++ == 0)
getmicrouptime(&stats->io_timestamp);
KASSERT(stats->io_wait >= 0); /* > 0 when iostat_wait is used */
KASSERT(stats->io_busy >= 0);
getmicrouptime(&dv_time);
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);
timersub(&dv_time, &stats->io_timestamp, &diff_time);
timeradd(&stats->io_time, &diff_time, &stats->io_time);
stats->io_timestamp = dv_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;
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.rxfer = stats->io_rxfer;
sdrive.wxfer = stats->io_wxfer;
sdrive.seek = stats->io_seek;
sdrive.bytes = stats->io_rbytes + stats->io_wbytes;
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.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.xfer = stats->io_rxfer + stats->io_wxfer;
sdrive.rbytes = stats->io_rbytes;
sdrive.wbytes = stats->io_wbytes;
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)));

View File

@ -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 (*)(device_t), device_t, 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 *);

View File

@ -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); /* the iostatlist is a TAILQ */
#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 *);

View File

@ -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.");

View File

@ -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. */

View File

@ -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 options, and all
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 .

View File

@ -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, 1991, 1993\
#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");
@ -422,6 +441,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)
{
@ -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;
}
}