Subject: statistics gathering for tape drives
To: None <tech-kern@netbsd.org>
From: Brett Lymn <blymn@baesystems.com.au>
List: tech-kern
Date: 07/27/2005 22:07:22
Folks,
I noticed that our tape drives were not instrumented so there was no
way one could monitor the read/write/utiliation of a tape drive on a
NetBSD system. I decided to fix this, now I can do:
[blymn@siren] iostat -x st0 5
device read KB/t r/s time MB/s write KB/t w/s time MB/s
st0 0.00 9 0.48 0.27 10.21 53 0.48 0.53
st0 0.00 0 0.98 0.00 64.00 80 0.98 5.00
st0 0.00 0 0.98 0.00 64.00 79 0.98 4.94
st0 0.00 0 0.97 0.00 64.00 79 0.97 4.95
st0 0.00 0 0.98 0.00 64.00 79 0.98 4.94
... this is a DLT4000 attached to a Adaptec 29160, these figures are
when I am dd'ing 1Mb blocks of zeros to the tape device. The
manufacturer rates the drive at 6Mb/s for compressed data so the above
numbers are not too shabby. I have also fixed vmstat and systat to
show the tape statistics.
Below is the patch that modifies all the relevant files, there are
some new files you need to fetch if you want to play with this, these
files are on ftp.netbsd.org under /pub/NetBSD/misc/blymn/tape_stats/
in a file called extra_files.tar.gz. Unpack the tar file in the root
of your NetBSD src directory, apply the patch from the same place.
Comments and feedback are welcome, if there are no major problems
noted with the code I shall commit it to the tree for all to enjoy.
...diff follows...
Index: sys/dev/scsipi/st.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/st.c,v
retrieving revision 1.182
diff -u -r1.182 st.c
--- sys/dev/scsipi/st.c 16 Jul 2005 05:12:26 -0000 1.182
+++ sys/dev/scsipi/st.c 27 Jul 2005 12:26:48 -0000
@@ -76,6 +76,8 @@
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
+#include <sys/tape.h>
+#include <sys/sysctl.h>
#include <dev/scsipi/scsi_spc.h>
#include <dev/scsipi/scsipi_all.h>
@@ -347,6 +349,14 @@
stdone
};
+/*
+ * A global list of all tape drives attached to the system. May grow or
+ * shrink over time.
+ */
+struct tapelist_head tapelist = TAILQ_HEAD_INITIALIZER(tapelist);
+int tape_count = 0; /* number of drives in global tapelist */
+struct simplelock tapelist_slock = SIMPLELOCK_INITIALIZER;
+
#if defined(ST_ENABLE_EARLYWARN)
#define ST_INIT_FLAGS ST_EARLYWARN
#else
@@ -362,6 +372,7 @@
{
struct scsipibus_attach_args *sa = aux;
struct scsipi_periph *periph = sa->sa_periph;
+ int s;
SC_DEBUG(periph, SCSIPI_DB2, ("stattach: "));
@@ -378,6 +389,28 @@
st->flags = ST_INIT_FLAGS;
+ /* Allocate and initialise statistics */
+ st->stats = (struct tape *) malloc(sizeof(struct tape), M_DEVBUF,
+ M_WAITOK);
+ st->stats->rxfer = st->stats->wxfer = st->stats->rbytes = 0;
+ st->stats->wbytes = st->stats->busy = 0;
+
+ /*
+ * Set the attached timestamp.
+ */
+ s = splclock();
+ st->stats->attachtime = mono_time;
+ splx(s);
+
+ /* and clear the utilisation time */
+ timerclear(&st->stats->time);
+
+ /* link the tape drive to the tapelist */
+ simple_lock(&tapelist_slock);
+ TAILQ_INSERT_TAIL(&tapelist, st->stats, link);
+ tape_count++;
+ simple_unlock(&tapelist_slock);
+
/*
* Set up the buf queue for this device
*/
@@ -410,6 +443,8 @@
(st->flags & ST_READONLY) ? "protected" : "enabled");
}
+ st->stats->name = st->sc_dev.dv_xname;
+
#if NRND > 0
rnd_attach_source(&st->rnd_source, st->sc_dev.dv_xname,
RND_TYPE_TAPE, 0);
@@ -465,6 +500,16 @@
vdevgone(bmaj, mn, mn+STNMINOR-1, VBLK);
vdevgone(cmaj, mn, mn+STNMINOR-1, VCHR);
+ if (tape_count == 0) {
+ printf("%s detach: tape_count already zero\n",
+ st->sc_dev.dv_xname);
+ } else {
+ simple_lock(&tapelist_slock);
+ TAILQ_REMOVE(&tapelist, st->stats, link);
+ tape_count--;
+ simple_unlock(&tapelist_slock);
+ free(st->stats, M_DEVBUF);
+ }
#if NRND > 0
/* Unhook the entropy source. */
@@ -1162,7 +1207,7 @@
struct buf *bp;
struct scsi_rw_tape cmd;
struct scsipi_xfer *xs;
- int flags, error;
+ int flags, error, s;
SC_DEBUG(periph, SCSIPI_DB2, ("ststart "));
/*
@@ -1199,6 +1244,12 @@
if ((bp = BUFQ_PEEK(&st->buf_queue)) == NULL)
return;
+ if (st->stats->busy++ == 0) {
+ s = splclock();
+ st->stats->timestamp = mono_time;
+ splx(s);
+ }
+
/*
* only FIXEDBLOCK devices have pending I/O or space operations.
*/
@@ -1325,6 +1376,8 @@
{
struct st_softc *st = (void *)xs->xs_periph->periph_dev;
struct buf *bp = xs->bp;
+ int s;
+ struct timeval st_time, diff_time;
if (bp) {
bp->b_error = error;
@@ -1336,6 +1389,33 @@
st->flags |= ST_WRITTEN;
else
st->flags &= ~ST_WRITTEN;
+
+ if (st->stats->busy-- == 0) {
+ /* this is not really fatal so we don't panic */
+ printf("%s: busy < 0, Oops.\n", st->stats->name);
+ st->stats->busy = 0;
+ } else {
+ s = splclock();
+ st_time = mono_time;
+ splx(s);
+
+ timersub(&st_time, &st->stats->timestamp, &diff_time);
+ timeradd(&st->stats->time, &diff_time,
+ &st->stats->time);
+
+ st->stats->timestamp = st_time;
+ if (bp->b_bcount > 0) {
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ st->stats->wbytes += bp->b_bcount;
+ st->stats->wxfer++;
+ } else {
+ st->stats->rbytes += bp->b_bcount;
+ st->stats->rxfer++;
+ }
+ }
+ }
+
+
#if NRND > 0
rnd_add_uint32(&st->rnd_source, bp->b_blkno);
#endif
@@ -2393,3 +2473,108 @@
/* Not implemented. */
return (ENXIO);
}
+
+int
+sysctl_hw_tapenames(SYSCTLFN_ARGS)
+{
+ char bf[TAPENAMELEN + 1];
+ char *where = oldp;
+ struct tape *tapep;
+ size_t needed, left, slen;
+ int error, first;
+
+ if (newp != NULL)
+ return (EPERM);
+ if (namelen != 0)
+ return (EINVAL);
+
+ first = 1;
+ error = 0;
+ needed = 0;
+ left = *oldlenp;
+
+ simple_lock(&tapelist_slock);
+ for (tapep = TAILQ_FIRST(&tapelist); tapep != NULL;
+ tapep = TAILQ_NEXT(tapep, link)) {
+ if (where == NULL)
+ needed += strlen(tapep->name) + 1;
+ else {
+ memset(bf, 0, sizeof(bf));
+ if (first) {
+ strncpy(bf, tapep->name, sizeof(bf));
+ first = 0;
+ } else {
+ bf[0] = ' ';
+ strncpy(bf + 1, tapep->name, sizeof(bf) - 1);
+ }
+ bf[TAPENAMELEN] = '\0';
+ slen = strlen(bf);
+ if (left < slen + 1)
+ break;
+ /* +1 to copy out the trailing NUL byte */
+ error = copyout(bf, where, slen + 1);
+ if (error)
+ break;
+ where += slen;
+ needed += slen;
+ left -= slen;
+ }
+ }
+ simple_unlock(&tapelist_slock);
+ *oldlenp = needed;
+ return (error);
+}
+
+int
+sysctl_hw_tapestats(SYSCTLFN_ARGS)
+{
+ struct tape_sysctl stape;
+ struct tape *tapep;
+ char *where = oldp;
+ size_t tocopy, left;
+ int error;
+
+ if (newp != NULL)
+ return (EPERM);
+
+ tocopy = name[0];
+
+ if (where == NULL) {
+ *oldlenp = tape_count * tocopy;
+ return (0);
+ }
+
+ error = 0;
+ left = *oldlenp;
+ memset(&stape, 0, sizeof(stape));
+ *oldlenp = 0;
+
+ simple_lock(&tapelist_slock);
+ TAILQ_FOREACH(tapep, &tapelist, link) {
+ if (left < tocopy)
+ break;
+ strncpy(stape.name, tapep->name, sizeof(stape.name));
+ stape.xfer = tapep->rxfer + tapep->wxfer;
+ stape.rxfer = tapep->rxfer;
+ stape.wxfer = tapep->wxfer;
+ stape.bytes = tapep->rbytes + tapep->wbytes;
+ stape.rbytes = tapep->rbytes;
+ stape.wbytes = tapep->wbytes;
+ stape.attachtime_sec = tapep->attachtime.tv_sec;
+ stape.attachtime_usec = tapep->attachtime.tv_usec;
+ stape.timestamp_sec = tapep->timestamp.tv_sec;
+ stape.timestamp_usec = tapep->timestamp.tv_usec;
+ stape.time_sec = tapep->time.tv_sec;
+ stape.time_usec = tapep->time.tv_usec;
+ stape.busy = tapep->busy;
+
+ error = copyout(&stape, where, min(tocopy, sizeof(stape)));
+ if (error)
+ break;
+ where += tocopy;
+ *oldlenp += tocopy;
+ left -= tocopy;
+ }
+ simple_unlock(&tapelist_slock);
+ return (error);
+}
Index: sys/dev/scsipi/stvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/stvar.h,v
retrieving revision 1.13
diff -u -r1.13 stvar.h
--- sys/dev/scsipi/stvar.h 29 May 2005 22:00:50 -0000 1.13
+++ sys/dev/scsipi/stvar.h 27 Jul 2005 12:26:49 -0000
@@ -150,6 +150,9 @@
/* operations */
struct callout sc_callout; /* restarting the queue after */
/* transient error */
+
+ struct tape *stats; /* statistics for the drive */
+
#if NRND > 0
rndsource_element_t rnd_source;
#endif
Index: sys/kern/init_sysctl.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_sysctl.c,v
retrieving revision 1.47
diff -u -r1.47 init_sysctl.c
--- sys/kern/init_sysctl.c 16 Jul 2005 22:47:18 -0000 1.47
+++ sys/kern/init_sysctl.c 27 Jul 2005 12:26:49 -0000
@@ -891,6 +891,18 @@
CTL_HW, HW_DISKSTATS, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
+ CTLTYPE_STRING, "tapenames",
+ SYSCTL_DESCR("List of tape devices present"),
+ sysctl_hw_tapenames, 0, NULL, 0,
+ CTL_HW, HW_TAPENAMES, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_STRUCT, "tapestats",
+ SYSCTL_DESCR("Statistics on tape drive operation"),
+ sysctl_hw_tapestats, 0, NULL, 0,
+ CTL_HW, HW_TAPESTATS, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
CTLTYPE_STRING, "machine_arch",
SYSCTL_DESCR("Machine CPU class"),
NULL, 0, machine_arch, 0,
Index: sys/sys/Makefile
===================================================================
RCS file: /cvsroot/src/sys/sys/Makefile,v
retrieving revision 1.73
diff -u -r1.73 Makefile
--- sys/sys/Makefile 22 May 2005 12:44:24 -0000 1.73
+++ sys/sys/Makefile 27 Jul 2005 12:26:50 -0000
@@ -26,7 +26,7 @@
siginfo.h signal.h signalvar.h sigtypes.h socket.h \
socketvar.h sockio.h stat.h statvfs.h syscall.h syscallargs.h \
sysctl.h stdint.h swap.h syslimits.h syslog.h systm.h \
- tablet.h termios.h time.h timeb.h timepps.h times.h \
+ tablet.h tape.h termios.h time.h timeb.h timepps.h times.h \
timex.h tprintf.h trace.h tree.h tty.h ttychars.h ttycom.h \
ttydefaults.h ttydev.h types.h \
ucontext.h ucred.h uio.h un.h unistd.h unpcb.h user.h utsname.h uuid.h \
Index: sys/sys/sysctl.h
===================================================================
RCS file: /cvsroot/src/sys/sys/sysctl.h,v
retrieving revision 1.138
diff -u -r1.138 sysctl.h
--- sys/sys/sysctl.h 20 Jun 2005 02:49:19 -0000 1.138
+++ sys/sys/sysctl.h 27 Jul 2005 12:26:50 -0000
@@ -705,7 +705,9 @@
#define HW_CNMAGIC 12 /* string: console magic sequence(s) */
#define HW_PHYSMEM64 13 /* quad: total memory (bytes) */
#define HW_USERMEM64 14 /* quad: non-kernel memory (bytes) */
-#define HW_MAXID 15 /* number of valid hw ids */
+#define HW_TAPENAMES 15 /* string: tape drive names */
+#define HW_TAPESTATS 16 /* struct: tapestats[] */
+#define HW_MAXID 16 /* number of valid hw ids */
#define CTL_HW_NAMES { \
{ 0, 0 }, \
@@ -1065,6 +1067,8 @@
*/
int sysctl_hw_disknames(SYSCTLFN_PROTO);
int sysctl_hw_diskstats(SYSCTLFN_PROTO);
+int sysctl_hw_tapenames(SYSCTLFN_PROTO);
+int sysctl_hw_tapestats(SYSCTLFN_PROTO);
int sysctl_kern_vnode(SYSCTLFN_PROTO);
int sysctl_net_inet_ip_ports(SYSCTLFN_PROTO);
int sysctl_consdev(SYSCTLFN_PROTO);
Index: usr.bin/systat/Makefile
===================================================================
RCS file: /cvsroot/src/usr.bin/systat/Makefile,v
retrieving revision 1.30
diff -u -r1.30 Makefile
--- usr.bin/systat/Makefile 26 Feb 2005 22:12:33 -0000 1.30
+++ usr.bin/systat/Makefile 27 Jul 2005 12:26:51 -0000
@@ -11,7 +11,7 @@
CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/vmstat -DSUPPORT_UTMP -DSUPPORT_UTMPX \
-I${NETBSDSRCDIR}/usr.bin/who
CWARNFLAGS+= -Wno-format-y2k
-SRCS= bufcache.c cmds.c cmdtab.c disks.c df.c dkstats.c fetch.c \
+SRCS= bufcache.c cmds.c cmdtab.c disks.c df.c dkstats.c tpstats.c fetch.c \
globalcmds.c icmp.c iostat.c ip.c keyboard.c main.c mbufs.c \
netcmds.c netstat.c pigs.c ps.c swap.c tcp.c vmstat.c utmpentry.c
DPADD= ${LIBCURSES} ${LIBM} ${LIBKVM}
Index: usr.bin/systat/iostat.c
===================================================================
RCS file: /cvsroot/src/usr.bin/systat/iostat.c,v
retrieving revision 1.32
diff -u -r1.32 iostat.c
--- usr.bin/systat/iostat.c 26 Feb 2005 22:28:23 -0000 1.32
+++ usr.bin/systat/iostat.c 27 Jul 2005 12:26:51 -0000
@@ -44,6 +44,7 @@
#include "systat.h"
#include "extern.h"
#include "dkstats.h"
+#include "tpstats.h"
static int linesperregion;
static double etime;
@@ -55,6 +56,7 @@
static void histogram(double, int, double);
static int numlabels(int);
static int stats(int, int, int);
+static int tpstats(int, int, int);
static void stat1(int, int);
@@ -81,7 +83,9 @@
{
dkinit(1);
+ tpinit(1);
dkreadstats();
+ tpreadstats();
return(1);
}
@@ -91,7 +95,13 @@
if (dk_ndrive == 0)
return;
- dkreadstats();
+ else
+ dkreadstats();
+
+ if (tp_ndrive == 0)
+ return;
+ else
+ tpreadstats();
}
#define INSET 14
@@ -101,7 +111,7 @@
{
int row;
- if (dk_ndrive == 0) {
+ if ((dk_ndrive == 0) && (tp_ndrive == 0)) {
error("No drives defined.");
return;
}
@@ -130,6 +140,10 @@
for (ndrives = 0, i = 0; i < dk_ndrive; i++)
if (cur.dk_select[i])
ndrives++;
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i])
+ ndrives++;
+
regions = howmany(ndrives, DRIVESPERLINE);
/*
* Deduct -regions for blank line after each scrolling region.
@@ -142,14 +156,22 @@
if (linesperregion < 3)
linesperregion = 3;
col = 0;
- for (i = 0; i < dk_ndrive; i++)
- if (cur.dk_select[i]) {
+ for (i = 0; i < (dk_ndrive + tp_ndrive); i++)
+ if (((i < dk_ndrive) && (cur.dk_select[i])) ||
+ ((i >= dk_ndrive) && (cur_tape.select[i - dk_ndrive]))) {
if (col + COLWIDTH - 1 > getmaxx(wnd)) {
col = 0, row += linesperregion + 1;
if (row > getmaxy(wnd) - (linesperregion))
break;
}
- mvwprintw(wnd, row, col + 5, "%s", cur.dk_name[i]);
+
+ if (i < dk_ndrive)
+ mvwprintw(wnd, row, col + 5, "%s",
+ cur.dk_name[i]);
+ else
+ mvwprintw(wnd, row, col + 5, "%s",
+ cur_tape.name[i - dk_ndrive]);
+
if (read_write)
mvwprintw(wnd, row, col + 11 + secs * 5,
"(write)");
@@ -188,6 +210,20 @@
if (secs)
mvwaddstr(wnd, row++, 0, " msec|");
}
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i]) {
+ if (row > getmaxy(wnd) - linesperregion)
+ break;
+ mvwprintw(wnd, row++, 0, "%7.7s kBps|",
+ cur_tape.name[i]);
+ mvwaddstr(wnd, row++, 0, " tps|");
+ if (read_write) {
+ mvwprintw(wnd, row++, 0, " (write) kBps|");
+ mvwaddstr(wnd, row++, 0, " tps|");
+ }
+ if (secs)
+ mvwaddstr(wnd, row++, 0, " msec|");
+ }
return (row);
}
@@ -199,6 +235,7 @@
if (dk_ndrive == 0)
return;
dkswap();
+ tpswap();
etime = cur.cp_etime;
row = 1;
@@ -216,6 +253,12 @@
break;
row = stats(row, INSET, i);
}
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i]) {
+ if (row > getmaxy(wnd) - linesperregion)
+ break;
+ row = tpstats(row, INSET, i);
+ }
return;
}
col = 0;
@@ -237,6 +280,20 @@
(void) stats(row + 3, col, i);
col += COLWIDTH;
}
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i]) {
+ if (col + COLWIDTH - 1 > getmaxx(wnd)) {
+ col = 0, row += linesperregion + 1;
+ if (row > getmaxy(wnd) - (linesperregion + 1))
+ break;
+ wmove(wnd, row + linesperregion, 0);
+ wdeleteln(wnd);
+ wmove(wnd, row + 3, 0);
+ winsertln(wnd);
+ }
+ (void) stats(row + 3, col, i);
+ col += COLWIDTH;
+ }
}
static int
@@ -287,6 +344,54 @@
return (row);
}
+static int
+tpstats(int row, int col, int dn)
+{
+ double atime, rwords, wwords;
+ uint64_t rxfer;
+
+ /* time busy in disk activity */
+ atime = (double)cur_tape.time[dn].tv_sec +
+ ((double)cur_tape.time[dn].tv_usec / (double)1000000);
+
+ /* # of k transferred */
+ rwords = cur_tape.rbytes[dn] / 1024.0;
+ wwords = cur_tape.wbytes[dn] / 1024.0;
+ rxfer = cur_tape.rxfer[dn];
+ if (!read_write) {
+ rwords = wwords;
+ rxfer += cur_tape.wxfer[dn];
+ }
+ if (numbers) {
+ mvwprintw(wnd, row, col, "%5.0f%4.0f",
+ rwords / etime, rxfer / etime);
+ if (secs)
+ wprintw(wnd, "%5.1f", atime / etime);
+ if (read_write)
+ wprintw(wnd, " %5.0f%4.0f",
+ wwords / etime, cur_tape.wxfer[dn] / etime);
+ return (row);
+ }
+
+ wmove(wnd, row++, col);
+ histogram(rwords / etime, 50, 0.5);
+ wmove(wnd, row++, col);
+ histogram(rxfer / etime, 50, 0.5);
+ if (read_write) {
+ wmove(wnd, row++, col);
+ histogram(wwords / etime, 50, 0.5);
+ wmove(wnd, row++, col);
+ histogram(cur_tape.wxfer[dn] / etime, 50, 0.5);
+ }
+
+ if (secs) {
+ wmove(wnd, row++, col);
+ atime *= 1000; /* In milliseconds */
+ histogram(atime / etime, 50, 0.5);
+ }
+ return (row);
+}
+
static void
stat1(int row, int o)
{
Index: usr.bin/systat/vmstat.c
===================================================================
RCS file: /cvsroot/src/usr.bin/systat/vmstat.c,v
retrieving revision 1.60
diff -u -r1.60 vmstat.c
--- usr.bin/systat/vmstat.c 22 May 2005 14:00:59 -0000 1.60
+++ usr.bin/systat/vmstat.c 27 Jul 2005 12:26:51 -0000
@@ -56,6 +56,7 @@
#include "systat.h"
#include "extern.h"
#include "dkstats.h"
+#include "tpstats.h"
#include "utmpentry.h"
static struct Info {
@@ -79,6 +80,7 @@
static void copyinfo(struct Info *, struct Info *);
static float cputime(int);
static void dinfo(int, int, int);
+static void tinfo(int, int, int);
static void getinfo(struct Info *, enum state);
static void putint(int, int, int, int);
static void putfloat(double, int, int, int, int, int);
@@ -221,6 +223,8 @@
hertz = stathz ? stathz : hz;
if (!dkinit(1))
return(0);
+ if (!tpinit(1))
+ return(0);
/* Old style interrupt counts - deprecated */
nintr = (namelist[X_EINTRCNT].n_value -
@@ -423,6 +427,7 @@
if (state == TIME) {
dkswap();
+ tpswap();
etime = cur.cp_etime;
/* < 5 ticks - ignore this trash */
if ((etime * hertz) < 1.0) {
@@ -551,16 +556,23 @@
PUTRATE(uvmexp.intrs, GENSTATROW + 1, GENSTATCOL + 17, 5);
PUTRATE(uvmexp.softs, GENSTATROW + 1, GENSTATCOL + 22, 5);
PUTRATE(uvmexp.faults, GENSTATROW + 1, GENSTATCOL + 27, 6);
- for (l = 0, i = 0, r = DISKROW, c = DISKCOL; i < dk_ndrive; i++) {
- if (!dk_select[i])
- continue;
+ for (l = 0, i = 0, r = DISKROW, c = DISKCOL;
+ i < (dk_ndrive + tp_ndrive); i++) {
+ if (i < dk_ndrive) {
+ if (!dk_select[i])
+ continue;
+ } else {
+ if (!tp_select[i - dk_ndrive])
+ continue;
+ }
+
if (disk_horiz)
c += DISKCOLWIDTH;
else
r++;
if (c + DISKCOLWIDTH > DISKCOLEND) {
if (disk_horiz && LINES - 1 - DISKROW >
- (DISKCOLEND - DISKCOL) / DISKCOLWIDTH) {
+ (DISKCOLEND - DISKCOL) / DISKCOLWIDTH) {
disk_horiz = 0;
relabel = 1;
}
@@ -568,7 +580,7 @@
}
if (r >= LINES - 1) {
if (!disk_horiz && LINES - 1 - DISKROW <
- (DISKCOLEND - DISKCOL) / DISKCOLWIDTH) {
+ (DISKCOLEND - DISKCOL) / DISKCOLWIDTH) {
disk_horiz = 1;
relabel = 1;
}
@@ -576,7 +588,10 @@
}
l++;
- dinfo(i, r, c);
+ if (i < dk_ndrive)
+ dinfo(i, r, c);
+ else
+ tinfo(i - dk_ndrive, r, c);
}
/* blank out if we lost any disks */
for (i = l; i < last_disks; i++) {
@@ -735,6 +750,7 @@
int i;
dkreadstats();
+ tpreadstats();
NREAD(X_NCHSTATS, &stats->nchstats, sizeof stats->nchstats);
if (nintr)
NREAD(X_INTRCNT, stats->intrcnt, nintr * LONG);
@@ -810,5 +826,32 @@
putint(100, r, c, DISKCOLWIDTH);
else
putfloat(atime, r, c, DISKCOLWIDTH, 1, 1);
+}
+
+static void
+tinfo(int dn, int r, int c)
+{
+ double atime;
+
+ mvprintw(r, c, "%*.*s", DISKCOLWIDTH, DISKCOLWIDTH, tp_name[dn]);
+ ADV;
+ ADV; /* skip over the seeks column - not relevant for tape drives */
+
+ putint((int)((cur_tape.rxfer[dn]+cur_tape.wxfer[dn])/etime+0.5),
+ r, c, DISKCOLWIDTH);
+ ADV;
+ puthumanint((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / etime + 0.5,
+ r, c, DISKCOLWIDTH);
+ ADV;
+
+ /* time busy in disk activity */
+ atime = cur_tape.time[dn].tv_sec + cur_tape.time[dn].tv_usec / 1000000.0;
+ atime = atime * 100.0 / etime;
+ if (atime >= 100)
+ putint(100, r, c, DISKCOLWIDTH);
+ else
+ putfloat(atime, r, c, DISKCOLWIDTH, 1, 1);
#undef ADV
}
+
+
Index: usr.bin/vmstat/Makefile
===================================================================
RCS file: /cvsroot/src/usr.bin/vmstat/Makefile,v
retrieving revision 1.21
diff -u -r1.21 Makefile
--- usr.bin/vmstat/Makefile 26 Feb 2005 21:19:18 -0000 1.21
+++ usr.bin/vmstat/Makefile 27 Jul 2005 12:26:51 -0000
@@ -4,7 +4,7 @@
PROG= vmstat
WARNS=3
-SRCS= dkstats.c vmstat.c
+SRCS= dkstats.c tpstats.c vmstat.c
MAN= vmstat.1
DPADD= ${LIBKVM}
LDADD= -lkvm
Index: usr.bin/vmstat/vmstat.c
===================================================================
RCS file: /cvsroot/src/usr.bin/vmstat/vmstat.c,v
retrieving revision 1.135
diff -u -r1.135 vmstat.c
--- usr.bin/vmstat/vmstat.c 2 Jun 2005 04:34:57 -0000 1.135
+++ usr.bin/vmstat/vmstat.c 27 Jul 2005 12:26:52 -0000
@@ -133,6 +133,7 @@
#include <util.h>
#include "dkstats.h"
+#include "tpstats.h"
/*
* General namelist
@@ -252,6 +253,7 @@
void cpustats(void);
void deref_kptr(const void *, void *, size_t, const char *);
void dkstats(void);
+void tpstats(void);
void doevcnt(int verbose);
void dohashstat(int, int, const char *);
void dointr(int verbose);
@@ -404,6 +406,7 @@
struct winsize winsize;
dkinit(0); /* Initialize disk stats, no disks selected. */
+ tpinit(0);
(void)setgid(getgid()); /* don't need privs anymore */
@@ -515,6 +518,13 @@
++ndrives;
break;
}
+ for (i = 0; i < tp_ndrive; i++) {
+ if (strcmp(tp_name[i], *argv))
+ continue;
+ tp_select[i] = 1;
+ ++ndrives;
+ break;
+ }
}
for (i = 0; i < dk_ndrive && ndrives < 3; i++) {
if (dk_select[i])
@@ -522,6 +532,14 @@
dk_select[i] = 1;
++ndrives;
}
+
+ for (i = 0; i < tp_ndrive && ndrives < 3; i++) {
+ if (tp_select[i])
+ continue;
+ tp_select[i] = 1;
+ ++ndrives;
+ }
+
return (argv);
}
@@ -660,6 +678,7 @@
(void)printf("%4lu ", rate(uvmexp.pdfreed - ouvmexp.pdfreed));
(void)printf("%4lu ", rate(uvmexp.pdscans - ouvmexp.pdscans));
dkstats();
+ tpstats();
(void)printf("%4lu %4lu %3lu ",
rate(uvmexp.intrs - ouvmexp.intrs),
rate(uvmexp.syscalls - ouvmexp.syscalls),
@@ -881,6 +900,24 @@
}
void
+tpstats(void)
+{
+ int dn;
+ double etime;
+
+ /* Calculate tape stat deltas. */
+ tpswap();
+ etime = cur.cp_etime;
+
+ for (dn = 0; dn < tp_ndrive; ++dn) {
+ if (!tp_select[dn])
+ continue;
+ (void)printf("%2.0f ",
+ (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
+ }
+}
+
+void
cpustats(void)
{
int state;
Index: usr.sbin/iostat/Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/iostat/Makefile,v
retrieving revision 1.20
diff -u -r1.20 Makefile
--- usr.sbin/iostat/Makefile 18 Sep 2002 03:54:30 -0000 1.20
+++ usr.sbin/iostat/Makefile 27 Jul 2005 12:26:52 -0000
@@ -11,7 +11,7 @@
CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/vmstat
# dkstats.c pulled in from ../../usr.bin/vmstat
-SRCS= dkstats.c iostat.c
+SRCS= dkstats.c tpstats.c iostat.c
DPADD= ${LIBKVM}
LDADD= -lkvm
Index: usr.sbin/iostat/iostat.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/iostat/iostat.c,v
retrieving revision 1.44
diff -u -r1.44 iostat.c
--- usr.sbin/iostat/iostat.c 7 Jul 2005 22:31:45 -0000 1.44
+++ usr.sbin/iostat/iostat.c 27 Jul 2005 12:26:52 -0000
@@ -89,6 +89,7 @@
#include <unistd.h>
#include "dkstats.h"
+#include "tpstats.h"
/* Namelist and memory files. */
char *nlistf, *memf;
@@ -194,7 +195,9 @@
defdrives /= 18; /* XXX magic number */
dkinit(0);
+ tpinit(0);
dkreadstats();
+ tpreadstats();
ndrives = selectdrives(argc, argv);
if (ndrives == 0) {
/* No drives are selected. No need to show disk stats. */
@@ -223,14 +226,18 @@
hdrcnt = winlines - 4;
}
- if (!ISSET(todo, SHOW_TOTALS))
+ if (!ISSET(todo, SHOW_TOTALS)) {
dkswap();
+ tpswap();
+ }
+
display();
if (reps >= 0 && --reps <= 0)
break;
nanosleep(&tv, NULL);
dkreadstats();
+ tpreadstats();
}
exit(0);
}
@@ -263,16 +270,26 @@
if (ISSET(todo, SHOW_TTY))
(void)printf(" tty");
- if (ISSET(todo, SHOW_STATS_1))
+ if (ISSET(todo, SHOW_STATS_1)) {
for (i = 0; i < dk_ndrive; i++)
if (cur.dk_select[i])
(void)printf(" %9.9s ", cur.dk_name[i]);
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i])
+ (void)printf(" %9.9s ",
+ cur_tape.name[i]);
+ }
- if (ISSET(todo, SHOW_STATS_2))
+ if (ISSET(todo, SHOW_STATS_2)) {
for (i = 0; i < dk_ndrive; i++)
if (cur.dk_select[i])
(void)printf(" %9.9s ", cur.dk_name[i]);
-
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i])
+ (void)printf(" %9.9s ",
+ cur_tape.name[i]);
+ }
+
if (ISSET(todo, SHOW_CPU))
(void)printf(" CPU");
@@ -290,12 +307,23 @@
else
(void)printf(" KB/t t/s MB/s ");
}
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i]) {
+ if (ISSET(todo, SHOW_TOTALS))
+ (void)printf(" KB/t xfr MB ");
+ else
+ (void)printf(" KB/t t/s MB/s ");
+ }
}
- if (ISSET(todo, SHOW_STATS_2))
+ if (ISSET(todo, SHOW_STATS_2)) {
for (i = 0; i < dk_ndrive; i++)
if (cur.dk_select[i])
(void)printf(" KB xfr time ");
+ for (i = 0; i < tp_ndrive; i++)
+ if (cur_tape.select[i])
+ (void)printf(" KB xfr time ");
+ }
if (ISSET(todo, SHOW_CPU))
(void)printf(" us ni sy in id");
@@ -420,6 +448,123 @@
}
static void
+tape_stats(double etime)
+{
+ int dn;
+ double atime, mbps;
+
+ for (dn = 0; dn < tp_ndrive; ++dn) {
+ if (!cur_tape.select[dn])
+ continue;
+ /* average Kbytes per transfer. */
+ if (cur_tape.rxfer[dn] + cur_tape.wxfer[dn])
+ mbps = ((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
+ 1024.0) / (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]);
+ else
+ mbps = 0.0;
+ (void)printf(" %5.2f", mbps);
+
+ /* average transfers per second. */
+ (void)printf(" %4.0f",
+ (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
+
+ /* time busy in disk activity */
+ atime = (double)cur_tape.time[dn].tv_sec +
+ ((double)cur_tape.time[dn].tv_usec / (double)1000000);
+
+ /* Megabytes per second. */
+ if (atime != 0.0)
+ mbps = (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
+ (double)(1024 * 1024);
+ else
+ mbps = 0;
+ (void)printf(" %5.2f ", mbps / etime);
+ }
+}
+
+static void
+tape_stats2(double etime)
+{
+ int dn;
+ double atime;
+
+ for (dn = 0; dn < tp_ndrive; ++dn) {
+ if (!cur_tape.select[dn])
+ continue;
+
+ /* average kbytes per second. */
+ (void)printf(" %5.0f",
+ (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / 1024.0 / etime);
+
+ /* average transfers per second. */
+ (void)printf(" %5.0f",
+ (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
+
+ /* average time busy in disk activity */
+ atime = (double)cur_tape.time[dn].tv_sec +
+ ((double)cur_tape.time[dn].tv_usec / (double)1000000);
+ (void)printf(" %4.2f ", atime / etime);
+ }
+}
+
+static void
+tape_statsx(double etime)
+{
+ int dn;
+ double atime, kbps;
+
+ for (dn = 0; dn < tp_ndrive; ++dn) {
+ if (!cur_tape.select[dn])
+ continue;
+
+ (void)printf("%-8.8s", cur_tape.name[dn]);
+
+ /* average read Kbytes per transfer */
+ if (cur.dk_rxfer[dn])
+ kbps = (cur_tape.rbytes[dn] / 1024.0) / cur_tape.rxfer[dn];
+ else
+ kbps = 0.0;
+ (void)printf(" %8.2f", kbps);
+
+ /* average read transfers
+ (per second) */
+ (void)printf(" %6.0f", cur_tape.rxfer[dn] / etime);
+
+ /* time read busy in disk activity */
+ atime = (double)cur_tape.time[dn].tv_sec +
+ ((double)cur_tape.time[dn].tv_usec / (double)1000000);
+ (void)printf(" %6.2f", atime / etime);
+
+ /* average read megabytes
+ (per second) */
+ (void)printf(" %8.2f",
+ cur_tape.rbytes[dn] / (1024.0 * 1024) / etime);
+
+
+ /* average write Kbytes per transfer */
+ if (cur_tape.wxfer[dn])
+ kbps = (cur_tape.wbytes[dn] / 1024.0) / cur_tape.wxfer[dn];
+ else
+ kbps = 0.0;
+ (void)printf(" %8.2f", kbps);
+
+ /* average write transfers
+ (per second) */
+ (void)printf(" %6.0f", cur_tape.wxfer[dn] / etime);
+
+ /* time write busy in disk activity */
+ atime = (double)cur_tape.time[dn].tv_sec +
+ ((double)cur_tape.time[dn].tv_usec / (double)1000000);
+ (void)printf(" %6.2f", atime / etime);
+
+ /* average write megabytes
+ (per second) */
+ (void)printf(" %8.2f\n",
+ cur_tape.wbytes[dn] / (1024.0 * 1024) / etime);
+ }
+}
+
+static void
cpustats(void)
{
int state;
@@ -461,17 +606,24 @@
if (ISSET(todo, SHOW_STATS_X)) {
disk_statsx(etime);
+ tape_statsx(etime);
goto out;
}
if (ISSET(todo, SHOW_TTY))
printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
- if (ISSET(todo, SHOW_STATS_1))
+ if (ISSET(todo, SHOW_STATS_1)) {
disk_stats(etime);
+ tape_stats(etime);
+ }
+
- if (ISSET(todo, SHOW_STATS_2))
+ if (ISSET(todo, SHOW_STATS_2)) {
disk_stats2(etime);
+ tape_stats2(etime);
+ }
+
if (ISSET(todo, SHOW_CPU))
cpustats();
@@ -510,6 +662,13 @@
cur.dk_select[i] = 1;
++ndrives;
}
+
+ for (i = 0; i < tp_ndrive; i++) {
+ if (strcmp(cur_tape.name[i], *argv))
+ continue;
+ cur_tape.select[i] = 1;
+ ++ndrives;
+ }
}
if (ndrives == 0 && tried == 0) {
@@ -518,9 +677,15 @@
* if none specified.
*/
maxdrives = (ISSET(todo, SHOW_STATS_X) ||
- dk_ndrive < defdrives) ? dk_ndrive : defdrives;
+ (dk_ndrive + tp_ndrive) < defdrives)
+ ? (dk_ndrive + tp_ndrive) : defdrives;
for (i = 0; i < maxdrives; i++) {
- cur.dk_select[i] = 1;
+ if (i >= dk_ndrive) {
+ cur_tape.select[i - dk_ndrive] = 1;
+ } else {
+ cur.dk_select[i] = 1;
+ }
+
++ndrives;
if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
break;
--
Brett Lymn